NameError: name 'top' is not defined - python

I am trying to use Tkinter to create a GUI, the code is:
from tkinter import *
class LoginFrame(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
# initialize the login screen UI
def initUI(self):
self.parent.title("Login Screen")
# create a menu bar
menubar = Menu(top)
# create a help menu
helpmenu = Menu(menubar, tearoff=0)
helpmenu.add_command(label="About", command=about)
menubar.add_cascade(label="Help", menu=helpmenu)
# display the menu
self.parent.config(menu=menubar)
#----------------------------------------------------------------------
def about():
"""about info"""
print("This is a Tkinter demo")
# create a button
#----------------------------------------------------------------------
def make_button(parent, command, caption=NONE, side=top, width=0, **options): # name error 'top' is not defined
"""make a button"""
btn = Button(parent, text=caption, command=command)
if side != top:
btn.pack(side=side)
else:
btn.pack()
return btn
def main():
top = Tk()
# Set up login frame properties
top.title("Login Screen")
# create a login button
login_btn = make_button(top, about, "Login")
top.mainloop()
if __name__ == '__main__':
main()
I tried to run the code, python gave me the following error:
builtins.NameError: name 'top' is not defined

You only define top in main, not at global scope, and even if it was at global scope, you defined it after make_button; default arguments in Python are evaluated once, at definition time, not looked up at call time.
The best approach would probably be to make most of your functions into class methods, and have the class itself create a top attribute.
But for the time being, you could do a minimalist change:
# Use None as a default at definition time, since top doesn't exist yet
def make_button(parent, command, caption=NONE, side=None, width=0, **options):
"""make a button"""
if side is None: # Convert None to top at call time
side = top
btn = Button(parent, text=caption, command=command)
if side is not top: # Minor tweak: Use identity test over equality
btn.pack(side=side)
else:
btn.pack()
return btn
def main():
global top # Make top a global then define it
top = Tk()
... rest of main ...
Note that this is still not great code; without main being executed, there is no top global defined, so your code is only usable as the main program, not as an importable module without a lot of hackery.

You are referring to top in the make_button parameter list - where you say side=top, but haven't actually defined top before that function. There is no global called top.
You can't set it as the default for a parameter until it is defined.

I also got same error, but I realized, I need to use upper case for "TOP" not "Top", after I used uppercase, it worked for me.
frame = Frame(root)
frame.pack()
root.title("Calcu_Displayframe")
num_1=StringVar()
topframe = Frame(root)
topframe.pack(side=TOP)
txtDisplay=Entry(frame, textvariable=num_1, bd=20, insertwidth=1, font=30)
txtDisplay.pack(side=TOP)
root.mainloop()

if you import tkinter like this : import tkinter as tk
then the pack gonna be test.pack(tk.TOP)
if you import tkinter like this : from tkinter import *
then the gonna be test.pack(TOP)

Related

Open existing class with button in another class

I have a problem with opening existing class from button which is in another class.
I don't have any idea how to make it working because I know I should in function add (Toplevel) but it's also not working.
class na_dane(object):
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.frame.pack()
# CODE ........
def close_windows(self):
self.master.destroy()
# this code opens another window (class) which I have defined but not included here...
def new_window(self):
self.newWindow = tk.Toplevel.iconify(self.master)
self.app = sinus(self.newWindow)
# here is the main class where when I click button it should open na_dane Class
class menu(na_dane):
def __init__(self, master):
self.master = master
przycisk_Jednostkowo_Liniowa = tk.Button(
text="Funkcja jednostkowo-liniowa",
width=250,
height=2,
bg="blue",
fg="yellow"
).pack()
# HERE IS THE PROBLEM WHERE I TRY TO BIND BUTTON TO CLASS
przycisk_Jednostkowo_Liniowa.bind("<Button>", lambda e: na_dane(master))
przycisk_Jednostkowo_Liniowa.pack()
def main():
root = tk.Tk()
root.geometry("250x200")
root.title("GRUPA 3")
root['bg'] = '#FFFFFF'
app = menu(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem seems to be that you attempt to pack przycisk_Jednostkowo_Liniowa in the same line as it is created. What this line of code is doing is creating a button with the characteristics described in your arguments, applying .pack() to it, then assigning the output of that .pack() execution to przycisk_Jednostkowo_Liniowa. Because .pack() doesn't have a return value, przycisk_Jednostkowo_Liniowa becomes None, so when you later attempt to bind a lambda to it, it doesn't recognize .bind() as a valid method. I tested the code with that .pack() removed and it worked for me.

Updating value of attribute of a created instance of a class from an instance of another class created later, in tkinter python 3

MainTicTacToe.py
import tkinter as tk
import MenubarCommand as mbc
class Game(tk.Frame):
def __init__(self,parent):
tk.Frame.__init__(self,parent)
self.parnt=parent
# self.parnt.geometry('500x300')
self.parnt.title("Tic Tac Toe")
# self.pack()
menubar=tk.Menu(parent)
# 'settings' menu
settingsOption=tk.Menu(menubar, tearoff=0)
settingsOption.add_command(label='Player Settings', command=self.doit)
settingsOption.add_command(label='Board Settins', command=self.doit)
menubar.add_cascade(label='Setings', menu=settingsOption)
# without using this method, menubar isn't shown in Frame
self.parnt.config(menu=menubar)
def doit(self):
root=self.win()
set=mbc.playerSettings(root)
# print(set.p1name)
root.mainloop()
def win(self):
return tk.Tk()
def main():
root=tk.Tk()
Game(root)
root.mainloop()
main()
MenubarCommand.py
import tkinter as tk
class playerSettings(tk.Frame):
def __init__(self,parent=tk.Frame):
tk.Frame.__init__(self,parent)
self.parnt=parent
self.parnt.title("Player Setting")
self.p1name='Player 1'
self.p2name='Player 2'
self.p1symbol='x'
self.p2symbol='o'
# **********************************#
self.p1NameLabel = tk.Label(parent, text='Player 1: Name ')
self.p1NameLabel.grid(row=0, column=0)
self.p1NameEntry = tk.Entry(parent)
self.p1NameEntry.insert(0,self.p1name)
self.p1NameEntry.bind('<FocusIn>', lambda event:self.p1NameEntry.delete(0,'end'))
self.p1NameEntry.grid(row=0, column=1)
apply=tk.Button(parent, text="Apply Settings", fg='white', bg='gray', command=self.saveStat)
apply.grid(row=2, rowspan=1, columnspan=4)
def saveStat(self):
print('Settings Window Destroyed')
self.p1name=self.p1NameEntry.get()
print(self.p1name)
self.parnt.destroy()
I want to change the value of attribute of an instance in one file from the instance of another class in another file already created.
When I change default Player name in MenubarComman.py file, I want to access the changed name from MainTicTacToe.py class. How can I do this?
I'm new new in Python.
Thanks in Advance.
You problems stem from 2 instances of Tk(), and sloppy programming, i.e. sometimes you use parent, and sometimes self.parnt which is a bad habit to get into, so everything is changed to self.top so an error will pop up if any of those two remains.. You also have to have a way to signal when PlayerSetting() is finished. The way the program is structured, the only way that I know of is to check for "alive" using recursion. You could also have PlayerSettings call a function in Game() when finished, which would print the value. I have cleaned up your code and it works as I understand it should.. Note that the 2 classes are in the same file to make it easier to test and post here.
import tkinter as tk
##import MenubarCommand as mbc
class Game():
def __init__(self,parent):
self.parnt=parent
# self.parnt.geometry('500x300')
self.parnt.title("Tic Tac Toe")
# self.pack()
menubar=tk.Menu(parent)
# 'settings' menu
settingsOption=tk.Menu(menubar, tearoff=0, bg="yellow")
settingsOption.add_command(label='Player Settings', command=self.doit)
settingsOption.add_command(label='Board Settins', command=self.doit)
menubar.add_cascade(label='Setings', menu=settingsOption)
# without using this method, menubar isn't shown in Frame
self.parnt.config(menu=menubar)
def doit(self):
## do not open more than one Tk() instance
##root=self.win()
self.top=tk.Toplevel(self.parnt)
self.set_it=PlayerSettings(self.top)
self.get_variable()
##root.mainloop()
def get_variable(self):
""" check continuously if PlayerSettings has exited and
if so, get the Entry's value
"""
if self.set_it:
self.parnt.after(1000, self.get_variable)
else:
print("from Game", self.set_it.p1name)
def win(self):
return tk.Tk()
class PlayerSettings():
def __init__(self, parent):
self.top=parent
self.p1name='Player 1'
self.p2name='Player 2'
self.p1symbol='x'
self.p2symbol='o'
# **********************************#
self.p1NameLabel = tk.Label(self.top, text='Player 1: Name ', bg="lightblue")
self.p1NameLabel.grid(row=0, column=0)
self.p1NameEntry = tk.Entry(self.top)
self.p1NameEntry.focus_set()
self.p1NameEntry.insert(0,self.p1name)
##self.p1NameEntry.bind('<FocusIn>', lambda event:self.p1NameEntry.delete(0,'end'))
self.p1NameEntry.grid(row=0, column=1, sticky="nsew")
apply=tk.Button(self.top, text="Apply Settings", fg='white', bg='gray', command=self.saveStat)
apply.grid(row=2, rowspan=1, columnspan=4)
def saveStat(self):
self.p1name=self.p1NameEntry.get()
print(self.p1name)
print('Settings Window Destroyed')
self.top.destroy()
root=tk.Tk()
Game(root)
root.mainloop()

Allow user to change default text in tkinter entry widget.

I'm writing a python script that requires the user to enter the name of a folder. For most cases, the default will suffice, but I want an entry box to appear that allows the user to over-ride the default. Here's what I have:
from Tkinter import *
import time
def main():
#some stuff
def getFolderName():
master = Tk()
folderName = Entry(master)
folderName.pack()
folderName.insert(END, 'dat' + time.strftime('%m%d%Y'))
folderName.focus_set()
createDirectoryName = folderName.get()
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
return
b = Button(master, text="OK and Close", width=10, command=callback)
b.pack()
mainloop()
return createDirectoryName
getFolderName()
#other stuff happens....
return
if __name__ == '__main__':
main()
I know next to nothing about tkInter and have 2 questions.
Is over-riding the default entry using global createDirectoryName within the callback function the best way to do this?
How can I make the button close the window when you press it.
I've tried
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
master.destroy
but that simply destroys the window upon running the script.
I don't know how experienced are you in Tkinter, but I suggest you use classes.
try:
from tkinter import * #3.x
except:
from Tkinter import * #2.x
class anynamehere(Tk): #you can make the class inherit from Tk directly,
def __init__(self): #__init__ is a special methoed that gets called anytime the class does
Tk.__init__(self) #it has to be called __init__
#further code here e.g.
self.frame = Frame()
self.frame.pack()
self.makeUI()
self.number = 0 # this will work in the class anywhere so you don't need global all the time
def makeUI(self):
#code to make the UI
self.number = 1 # no need for global
#answer to question No.2
Button(frame, command = self.destroy).pack()
anyname = anynamehere() #remember it alredy has Tk
anyname.mainloop()
Also why do you want to override the deafult Entry behavior ?
The solution would be to make another button and bind a command to it like this
self.enteredtext = StringVar()
self.entry = Entry(frame, textvariable = self.enteredtext)
self.entry.pack()
self.button = Button(frame, text = "Submit", command = self.getfolder, #someother options, check tkitner documentation for full list)
self.button.pack()
def getfolder(self): #make the UI in one method, command in other I suggest
text = self.enteredtext.get()
#text now has whats been entered to the entry, do what you need to with it

Setting a tkinter variables using a generic set_value() method

I'd like to have a generic method that will change the value of a tkinter variable. I want to be able to do this from a menu option that brings up a new dialog box which is from a generic method that can be used for different variables. the code I have so far is below:
import sys
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
import sequencer as seq
class View(ttk.Frame):
"""Main Gui class"""
def __init__(self, master = None):
ttk.Frame.__init__(self, master, borderwidth=5, width=450, height=500)
self.master = master
self.grid(column=0, row=0, sticky=(N, S, E, W))
self.columnconfigure(0, weight=1)
###############################
### User editable variables ###
self.precision = IntVar(value=4, name='precision')
self.sensitivity = IntVar(value = 50, name='sensitivity')
### User editable variables ###
###############################
self.create_menus()
def create_menus(self):
"""Produces the menu layout for the main window"""
self.master.option_add('*tearOff', FALSE)
self.menubar = Menu(self.master)
self.master['menu'] = self.menubar
# Menu Variables
menu_file = Menu(self.menubar)
menu_edit = Menu(self.menubar)
# Add the menus to the menubar and assign their variables
self.menubar.add_cascade(menu=menu_file, label="File")
self.menubar.add_cascade(menu=menu_edit, label = "Edit")
### ADD COMMANDS TO THE MENUES ###
### File ###
menu_file.add_command(label="Quit", command=self.master.destroy)
### Edit ###
menu_edit.add_command(label="Backbone", command=lambda : self.edit_backbone())
menu_edit.add_command(label="Precision", command=lambda : self.precision.set(self.set_value_int("Precision")))
menu_edit.add_command(label="Sensitivity", command=lambda : self.sensitivity.set(self.set_value_int("Sensitivity")))
menu_edit.add_command(label="print precision", command=lambda : print(self.precision.get()))
menu_edit.add_command(label="print sensitivity", command=lambda : print(self.sensitivity.get()))
def set_value_int(self, name):
"""Standards dialog that return a user define value of a specific type"""
t = Toplevel(self)
t.title("Set " + name)
label = ttk.Label(t, text="Set "+name)
label.grid(row=0)
entry = ttk.Entry(t)
entry.grid(row=1)
cancel = ttk.Button(t, text="Cancel", command=lambda : t.destroy())
cancel.grid(column=0, row=2)
okey = ttk.Button(t, text="Okey", command=lambda : okey(entry.get()))
okey.grid(column=1, row=2)
def okey(value):
"""return value according to type"""
try:
t.destroy()
return int(value)
except:
self.error_box("value must be and integer")
def error_box(self, error_message="Unknown error"):
"""(Frame, String) -> None
Opens an window with an Okey button and a custom error message"""
t=Toplevel(self)
t.title("Error")
label = ttk.Label(t, text=error_message)
label.grid(row=0)
okey = ttk.Button(t, text="Okey", command=lambda : t.destroy())
okey.grid(row=1)
if __name__ == "__main__":
root = Tk()
root.title("Sequencer")
view = View(root)
root.mainloop()
print("End")
The Edit-> print xxxxxx commands are purely for testing purposes to check if the values have changed. If these are executed before trying to change the values of precision or sensitivity then they work as expected.
If you try to change either of the tkinter variables in the way I have tried to do they become None types and I can't see why. I can only assume that you are not allowed to change them in the way that I have but I can't think of another way to do it without having a separated method for each variable which I'd like to avoid.
Baicly I'd like the user to be able to customise the variables precision and sensitivity and use the same method in the code to change the values.
Extra but not necessarily vital:- If there is a way to define which type the variable should be in the methods arguments as well that would be even better as I will have other variables for the user to change later and they will be of different types.
Any help would be much appreciated. I have tried to be as clear as I can but let me know if anything is not.
Thanks
self.set_value_int always returns None, so it's always going to set your variable to none.
Instead of trying to write a complex lambda that is hard to debug, put all of your logic inside the function. Have the function set the value. All you need to do is tell it what variable to set:
menu_edit.add_command(label="Precision",
command=lambda name="Precision", var=self.precision: self.set_value_int(name, var))
...
def set_value_int(self, name, var):
...
def okey():
s = entry.get()
try:
var.set(int(s))
...

Move tkinter label using button command

I am trying to recreate the boardgame monopoly using python and tkinter. I know how to place a label on a canvas or a frame, but how should I do this command is being run from another function in the class? I tried it using some function within the class Board, but then the error rises that the label, canvas, etc. are not defined as this happens in __init__(self,parent). How can I solve these errors? Or should I take a different approach to this? Hope I made my problem clear.
import tkFileDialog
from random import randint
class Board(Frame):
def __init__(self,parent):
##create the board
frame = Frame(parent)
frame.pack()
Frame.__init__(self,parent)
frame2 = Frame(frame)
frame2.pack()
c=Canvas(frame2,width=480,height=480)
c.pack(expand=YES,fill=BOTH)
c.background=PhotoImage(file='Board.gif')
c.create_image(0,0,image=c.background,anchor='nw')
##Add player 1
player1=PhotoImage(file='plane.gif')
label_player1 = Label(c,image=player1)
label_player1.image=player1
label_player1.place(x=430,y=420)
##Add player 2
player2=PhotoImage(file='car.gif')
label_player2 = Label(c,image=player2)
label_player2.image=player2
label_player2.place(x=430,y=450)
button = Button(frame, text="Next turn", command=self.next_turn)
button.pack()
button = Button(frame, text="Roll the dice", command=self.roll)
button.pack()
def roll(self):
number=randint(2,12)
if b==0:
self.place_player_down()
return number
def place_player_down(self):
for i in range(number+1):
h=int(430-i*30)
while h>=0:
player2=PhotoImage(file='car.gif')
label_player2 = Label(c,image=player2)
label_player2.image=player2
label_player2.place(x=h,y=420)
root = Tk()
board = Board(root)
board.pack()
root.mainloop()
The approach is correct (wrap your Tkinter widgets in a class with the event handler functions as methods), but you forgot to set the widgets as attributes of the class using the reference to self:
class Board(Frame):
def __init__(self,parent):
# ...
self.c = Canvas(frame2,width=480,height=480)
self.c.pack(expand=YES,fill=BOTH)
# ...
def place_player_down(self):
# Use 'self.c', not just 'c'
I think you want to do something similar with the value number, but it that case I would send it as an argument to place_player_down:
def roll(self):
number=randint(2,12)
if b==0:
self.place_player_down(number)
return number # Keep in mind that this value is returned but not used anymore
def place_player_down(self, number):
# Use 'number'

Categories

Resources