Question :
I’m learning to write event driven programs using tkinter and am using Fredrik Lundh’s excellent tutorial.
In that, he mentions that it’s better to define a class (App) for the frame and run the program as an instance of the class instead of just starting it up thus:
root = Tk()
w = Label(root, text = 'hello, world!')
w.pack()
root.mainloop()
I have 3 questions:
-
Is it bad programming practice to do it in this simpler way?
-
If I do define a class, with callback functions bound to the widgets, do the functions all have to be within the class itself? i.e. Can I have a button that says go within the class, which when I click runs an elaborate routine that’s been defined outside the class?
-
Is it bad practice to then take the result generate outside the class and display it within?
Essentially, I can make the program work by jumping in and out of the class but am not sure if it’s bad practice to do so (a la global variables).
Answer #1:
- You will want to use classes as your application gets larger. Instead of having to wrap your mind around the entire code, you can concentrate on a class at a time.
- You are not restricted to using only the methods in a class. Your code may utilize external functions or classes to get information or even modify arguments given to them.
- No, that is how would probably display the information. Alternatively, you might use a file to output your results, and it may be possible to print to the console if it is present.
Example:
import tkinter
import random
class Application(tkinter.Frame):
@classmethod
def main(cls):
root = tkinter.Tk()
frame = cls(root)
frame.grid()
root.mainloop()
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master, cnf, **kw)
self.w = tkinter.Label(self, text='Hello, world!')
self.w.grid()
self.v = tkinter.Button(self, text='Press Me', command=self.click)
self.v.grid()
self.u = tkinter.Button(self, text='Me Too!',
command=lambda: external_mutator(self.w))
self.u.grid()
def click(self):
self.w['text'] = external_function(3)
def external_function(ndigits):
return round(random.random(), ndigits)
def external_mutator(widget):
widget['text'] = external_function(6)
print('Hello to you too!') # shown on console if present
if __name__ == '__main__':
Application.main()
Alternative to the main
classmethod:
import tkinter
import random
class Main(tkinter.Tk):
def __init__(self, screenName=None, baseName=None, className='Tk',
useTk=1, sync=0, use=None):
super().__init__(screenName, baseName, className,
useTk, sync, use)
frame = Application(self)
frame.grid()
self.mainloop()
class Application(tkinter.Frame):
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master, cnf, **kw)
self.w = tkinter.Label(self, text='Hello, world!')
self.w.grid()
self.v = tkinter.Button(self, text='Press Me', command=self.click)
self.v.grid()
self.u = tkinter.Button(self, text='Me Too!',
command=lambda: external_mutator(self.w))
self.u.grid()
def click(self):
self.w['text'] = external_function(3)
def external_function(ndigits):
return round(random.random(), ndigits)
def external_mutator(widget):
widget['text'] = external_function(6)
print('Hello to you too!') # shown on console if present
if __name__ == '__main__':
Main()
Answer #2:
- in general yes… for smaller programs it may not be too problematic but as complexity grows it is nice to have things consolidated into classes
- yes
- not really … (globals tend to be bad practice…) but what you describe is message encapsulation and passing
Answer #3:
1) I would say for this example, it’s not necessarily wrong. The program runs. The real worry is that as you start to make more complex programs, you may want a more structured format. Classes can help.
2) The functions do not all have to be within the class itself. Methods may call outside functions. The main reason to have a method as opposed to a function is that the method has ready access to all of the object’s attributes. You want to avoid a function reaching inside an object to manipulate the attributes.
3) It’s better to pass in variables to a class than to define globals, mostly because it can get difficult to maintain scope.