Python Tkinter instance has no attribute 'tk' - python-2.7

AttributeError: MyGUI instance has no attribute 'tk'
Also, how do I make the created window have a fixed size and not be able to resize with the mouse? Or after changing label value by clicking on button.
My code:
from Tkinter import*
class MyGUI(Frame):
def __init__(self):
self.__mainWindow = Tk()
#lbl
self.labelText = 'label message'
self.depositLabel = Label(self.__mainWindow, text = self.labelText)
#buttons
self.hi_there = Button(self.__mainWindow)
self.hi_there["text"] = "Hello",
self.hi_there["command"] = self.testeo
self.QUIT = Button(self.__mainWindow)
self.QUIT["text"] = "QUIT"
self.QUIT["fg"] = "red"
self.QUIT["command"] = self.quit
#place on view
self.depositLabel.pack()
self.hi_there.pack() #placed in order!
self.QUIT.pack()
#What does it do?
mainloop()
def testeo(self):
self.depositLabel['text'] = 'c2value'
print "testeo"
def depositCallBack(self,event):
self.labelText = 'change the value'
print(self.labelText)
self.depositLabel['text'] = 'change the value'
myGUI = MyGUI()
What's wrong?
Thanks

You should invoke the super constructor for Frame. Not sure, but I guess this will set the tk attribute that the quit command relies on. After that, there's no need to create your own Tk() instance.
def __init__(self):
Frame.__init__(self)
# self.__mainWindow = Tk()
Of course, you will also have to change the constructor calls for your widgets accordingly, e.g.,
self.hi_there = Button(self) # self, not self.__mainWindow
or better (or at least shorter): set all the attributes directly in the constructors:
self.hi_there = Button(self, text="Hello", command=self.testeo)
Also add self.pack() to your constructor.
(Alternatively, you could change the quit command to self.__mainWindow.quit, but I think the above is better style for creating Frames, see e.g. here.)

This error typically means you are calling SomeTKSuperClass.__init__ and forgetting the first parameter, which must be self. Remember that __init__ is a class method (static function) in this context, not an instance method, which means that you must explicitly pass it self.

Related

tkinter Checkbox from list loop. How rebuild (update) checkboxes when new list arrives?

In the tkinter app I'm building (win10 python 3.8) each time I open a new file I get a new list that I distribute to textboxes (Ok), combobox(ok), etc. When I open the first list checkbox loop builds ok, next file called checkboxes don't change. I can't update checkboxes, I mean, remove the previous list and insert another one. In the example I used to buttons (in app askopenfilename), lists build one bellow other. I need one replacing the other. I believe I need to use grid.clear() or destroy, but how? Thanks in advance.
from tkinter import *
root = Tk()
root.geometry('400x400')
my_friends = ['Donald', 'Daisy', 'Uncle Scrooge', 'Ze Carioca']
my_heroes = ['Captain America', 'Hulk', 'Spider Man', 'Black Widow',
'Wanda Maximoff', 'Vision', 'Winter Soldier']
what_list = ' '
def list_my_friends():
global what_list
what_list = my_friends
create_cbox()
def list_my_heroes():
global what_list
what_list = my_heroes
create_cbox()
def create_cbox():
for index, friend in enumerate(what_list):
current_var = tk.StringVar()
current_box = tk.Checkbutton(root, text= friend,
variable = current_var,
onvalue = friend,
offvalue = '')
current_box.pack()
button1= tk.Button(root, text = "my_friends",command=lambda:list_my_friends()).pack()
button2= tk.Button(root, text = "my_heroes",command=lambda:list_my_heroes()).pack()
root.mainloop()
You can put those checkbuttons in a frame, then it is more easier to remove old checkbuttons before populating new checkbuttons:
def create_cbox():
# remove existing checkbuttons
for w in container.winfo_children():
w.destroy()
# populate new checkbuttons
for friend in what_list:
current_var = tk.StringVar()
# use container as the parent
current_box = tk.Checkbutton(container, text=friend,
variable=current_var,
onvalue=friend,
offvalue='')
current_box.pack(anchor='w')
tk.Button(root, text="my_friends", command=list_my_friends).pack()
tk.Button(root, text="my_heroes", command=list_my_heroes).pack()
# frame to hold the checkbuttons
container = tk.Frame(root)
container.pack(padx=10, pady=10)
worked with little adjustments as I'm working in class:
class PageEight(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent) # parent is Frame
self.controller = controller
# to adequate to widgets outside the self.container_pg8:
self.rowspan_list = len(what_list)
#container:
self.container_pg8 = tk.Frame(self)
self.container_pg8.grid(row=2,
rowspan = self.rowspan_list,
column=1,)

Python 3 Tkinter - How to call a function from another class

I am trying to get my save button to call a function from another class. I would like to click on the save button as much as I want and it should print "hello people" every time. Though, I am having trouble in getting the save button to work.
import tkinter as tk
from tkinter import filedialog
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack()
self.createWidgets()
def createWidgets(self):
#save button
self.saveLabel = tk.Label(self.parent, text="Save File", padx=10, pady=10)
self.saveLabel.pack()
#When I click the button save, I would like it to call the test function in the documentMaker class
self.saveButton = tk.Button(self.parent, text = "Save", command = documentMaker.test(self))
self.saveButton.pack()
class documentMaker():
def test(self):
print ("hello people")
root = tk.Tk()
app = Application(root)
app.master.title('Sample application')
object = documentMaker()
object.test()
app.mainloop()
In your documentMaker class, change the test method to a #staticmethod:
class documentMaker():
#staticmethod
def test(cls):
print ("hello people")
Then your saveButton's command can be:
command = documentMaker.test
A staticmethod is bound to the class, not to an instance of the class like an instance method. So, we can call it from the class's name directly. If you did not want it to be a staticmethod, you could keep it an instance method and have the command line change to:
command = documentMaker().test

Multiple Windows in Tkinter (Python 2.7)

So, for an assignment that I am supposed to do for my programming course, I ran into a bit of an issue. We never really went through how to create child windows // secondary windows, and for some reason the things I add in the first window are not showing up?...
So far my code looks like:
from Tkinter import*
class Window(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.button1 = Button(self, text="Create new window", command=self.New_Window)
self.button1.pack(fill=BOTH)
def New_Window(self):
win = Toplevel(self)
win.title("New Window")
etiquette1 = Label(root, text = "Text shenanigans")
etiquette1.pack()
if __name__ == "__main__":
root = Tk()
main = Window(root)
main.mainloop()
Also, another question is how can I name the first window? It's just called "tk" by default, but if I try to do something like the example below, it does not work.
self.__init__.title("Main Window")
The first argument when creating widgets is the "parent" -- the widget into which the new widget goes. If you want a label to be in the Toplevel, make the Toplevel be the parent:
etiquette1 = Label(win, ...)
To set the title of the window, call the title method:
root.title("This is the root window")
...
win.title("This is the second window")

Getting the choice of optionmenu right after selection Python

I was wondering if there is any way for me to see what the User has selected among the list displaying, let's say: ["Apple","Orange","Grapes"] right after they select either one of them?
Like when user clicks the optionbox, and clicks Apple, Tkinter will return something
then if he switches his selection to, let's say, Orange, then that will also return something on the spot.
Thanks!
How to put parameter correctly?
from Tkinter import *
def option_changed(a):
print "the user chose the value {}".format(variable.get())
print a
master = Tk()
a = "Foo"
variable = StringVar(master)
variable.set("Apple") # default value
variable.trace("w", option_changed(a))
w = OptionMenu(master, variable, "Apple", "Orange", "Grapes")
w.pack()
mainloop()
Trace the StringVar.
from Tkinter import *
def option_changed(*args):
print "the user chose the value {}".format(variable.get())
print a
master = Tk()
a = "Foo"
variable = StringVar(master)
variable.set("Apple") # default value
variable.trace("w", option_changed)
w = OptionMenu(master, variable, "Apple", "Orange", "Grapes")
w.pack()
mainloop()
Here, option_changed will be called whenever the user chooses an option from the option menu.
You can wrap the trace argument in a lambda to specify your own parameters.
def option_changed(foo, bar, baz):
#do stuff
#...
variable.trace("w", lambda *args: option_changed(qux, 23, "hello"))
When I come across widgets with annoying interfaces - such as OptionMenu, I generally will write a class around it to abstract away the annoying attributes. In this case, I really dislike the verbosity of using the StringVar every time I want to create a dropdown, so I simply created a DropDown class which includes the StringVar within the class (written in Python 3.5, but translates easily to all):
class DropDown(tk.OptionMenu):
"""
Classic drop down entry
Example use:
# create the dropdown and grid
dd = DropDown(root, ['one', 'two', 'three'])
dd.grid()
# define a callback function that retrieves the currently selected option
def callback():
print(dd.get())
# add the callback function to the dropdown
dd.add_callback(callback)
"""
def __init__(self, parent, options: list, initial_value: str=None):
"""
Constructor for drop down entry
:param parent: the tk parent frame
:param options: a list containing the drop down options
:param initial_value: the initial value of the dropdown
"""
self.var = tk.StringVar(parent)
self.var.set(initial_value if initial_value else options[0])
self.option_menu = tk.OptionMenu.__init__(self, parent, self.var, *options)
self.callback = None
def add_callback(self, callback: callable):
"""
Add a callback on change
:param callback: callable function
:return:
"""
def internal_callback(*args):
callback()
self.var.trace("w", internal_callback)
def get(self):
"""
Retrieve the value of the dropdown
:return:
"""
return self.var.get()
def set(self, value: str):
"""
Set the value of the dropdown
:param value: a string representing the
:return:
"""
self.var.set(value)
Example usage:
# create the dropdown and grid, this is the ONLY required code
dd = DropDown(root, ['one', 'two', 'three'])
dd.grid()
# optionally, define a callback function that retrieves the currently selected option then add that callback to the dropdown
def callback():
print(dd.get())
dd.add_callback(callback)
Edited to add: Not long after creating this post, I got annoyed with a few other attributes of tk and ended up creating a package called tk_tools to make dropdowns and checkbuttons easier along with addressing other annoyances.

Invoking destroy() method on window after mainloop in tkinter

I'm relatively new to Python and Tkinter and I am striving to get my head over how mainloop and the after method work.
More specifically, I want to create a splashScreen, which goes away after a timeframe, and then the actual mainWindow is shown.
My code looks like this:
class SplashScreen:
# some code here
def destroyMe(self):
self.destroy()
def destroySplashScreen(self, timeToSleep=0):
if timeToSleep > 0:
self.master.after(timeToSleep*1000, self.destroyMe())
# some other code here
if __name__ == '__main__':
root = Toplevel()
mySP = SplashScreen(root)
mySP.populateSplashScreen()
mySP.destroySplashScreen(5)
root.mainloop()
However, what the code does is to create the whole window after the timeframe given (5 sec.) without any content. Instead, it should create it, wait 5 sec. and then destroy it.
Working example
after expects only function name (without ()).
destroy() needs self.master
from Tkinter import *
class SplashScreen:
# some code here
def __init__(self, master):
self.master = master
self.master.title("SplashScreen")
def destroyMe(self):
self.master.destroy()
def destroySplashScreen(self, timeToSleep=0):
if timeToSleep > 0:
self.master.after(timeToSleep*1000, self.destroyMe)
# some other code here
if __name__ == '__main__':
root = Toplevel()
mySP = SplashScreen(root)
#mySP.populateSplashScreen()
mySP.destroySplashScreen(3)
root.mainloop()
BTW: Toplevel is used to create "child" window so (in example) it create automaticly "parent" window - so I add title("SplashScreen")
BTW: if you will use command= in widget - it also expects function name without ().
If you use (in command or after) function with () you run that function and result is assigned to command= or use as parameter for after.