This is my first time using Tkinter and really trying to get use to using classes in Python. I wonder if I'm using self entirely too much, but here is my code:
from Tkinter import *
class MainFrame(object):
def __init__(self):
self.display()
#display all the labels for the main frame
def labels(self):
#adding x-cord label
x_cord_label = Label(self.top, text = 'X-Coordinate').grid(row=0,column = 0)
#calls the visible features
def display(self):
self.top = Tk()
self.labels()
self.entries()
self.buttons()
#must remain at bottom or wont be displayed
self.top.mainloop()
#display all the entry boxes for the main frame
def entries(self):
x_cord_entry = Entry(self.top,bd=5).grid(row=0,column = 1)
def buttons(self):
x_cord_button = Button(self.top, text = 'Enter').grid(row=0,column = 2)
I am planning on main just having the mainframe being generated.
main.py
from mainFrame import MainFrame
def main():
window = MainFrame()
if __name__=="__main__":
main()
1 Answer 1
Python has a set of rules for how documentation strings should look like. These strings are called docstring
s in Python and the corresponding PEP is PEP257. You basically just move your comments from above the function name inside and make them strings:
def display(self):
"""Calls the visible features."""
self.top = Tk()
self.labels()
self.entries()
self.buttons()
# must remain at bottom or wont be displayed
self.top.mainloop()
Python also has an official style-guide, PEP8, which recommends adding a single space after the #
in a comment, as well as using a single blank line between class methods (and two between class or normal function definitions). It also recommends using a space after a comma in an argument list, but no spaces surrounding =
for keyword arguments.
Your method display
could be moved completely to the __init__
, but it is also fine as a separate method, in case you ever want to call it again to reset your application / display it again after changing some values. I would swap the display
and labels
methods, though, it makes for easier reading.
Regarding your doubt if you are overusing self
, i think you are rather underusing it than overusing. I'm not exactly sure how Tkinter
handles objects, but in Python in general, objects can and will be garbage-collected once they go outside of scope. So, after e.g. the method labels
ran, the local variable x_coord_label
will be removed and the associated object deleted (unless it is still being referenced from somewhere else, like the Tk
main loop for example). So I would store these labels/buttons etc also as class members. This is handy if you ever want to modify them as well (changing the text label on a button is a quite regular feature...).
In general, you should avoid using from X import *
, because it clutters your global namespace and might lead to name conflicts. With Tkinter
, many people use it anyway, mostly because one tends to use a lot of its functions, so selectively importing from TKinter import Button, Label, ...
becomes too long and unreadable and always writing Tkinter.Button
is also quite verbose. As a compromise you can use import Tkinter as tk
, which is shorter and still has its own namespace.
Final code:
import Tkinter as tk
class MainFrame(object):
def __init__(self):
self.display()
def display(self):
"""Calls the visible features."""
self.top = tk.Tk()
self.labels()
self.entries()
self.buttons()
# must remain at bottom or wont be displayed
self.top.mainloop()
def labels(self):
"""Display all the labels for the main frame."""
# adding x-cord label
self.x_cord_label = tk.Label(
self.top, text='X-Coordinate').grid(row=0, column=0)
def entries(self):
"""Display all the entry boxes for the main frame."""
self.x_cord_entry = tk.Entry(self.top, bd=5).grid(row=0, column=1)
def buttons(self):
self.x_cord_button = tk.Button(
self.top, text='Enter').grid(row=0, column=2)
If you want to extend this further, you probably want to add more than one button. In this case, you will need to decide on a datastructure to hold your texts and buttons. So in the end it might look more like this:
class MainFrame(object):
def __init__(self, button_texts):
self.button_texts = button_texts
self.top = tk.Tk()
self.setup_rows()
self.top.mainloop()
def setup_rows(self):
"""
Display all the rows with labels, entries and buttons
for the main frame.
"""
self.rows = []
top = self.top
for i, text in enumerate(self.button_texts):
row = [tk.Label(top, text=text).grid(row=i, column=0),
tk.Entry(top, bd=5).grid(row=i, column=1),
tk.Button(top, text='Enter').grid(row=i, column=2)]
self.rows.append(row)
-
1\$\begingroup\$ In Python, it's also frowned upon to
import *
instead of the specific things to be used (e.g.from math import sqrt
) \$\endgroup\$code_dredd– code_dredd2017年06月22日 10:56:42 +00:00Commented Jun 22, 2017 at 10:56 -
\$\begingroup\$ @ray Normally I would agree 100%, but with
Tkinter
this is quite often done anyway, because one uses quite a lot of its functions, so explicit importing from would be too long and always writingTkinter
would be too lengthy. I'll edit it to useimport Tkinter as tk
, as a compromise, though. \$\endgroup\$Graipher– Graipher2017年06月22日 11:21:08 +00:00Commented Jun 22, 2017 at 11:21 -
\$\begingroup\$ I understand. I think just noting this as an Tk-specific exception would be enough. I tried to use Tk once. After needing ~20 minutes to produce a main window with a single button I said "!@$ this !@#" and used PyQt instead. \$\endgroup\$code_dredd– code_dredd2017年06月22日 11:27:35 +00:00Commented Jun 22, 2017 at 11:27
-
\$\begingroup\$ I agree with @ray. To cut down on typing use
import tkinter as tk
and then usetk.Button
, etc. It makes the code easier to maintain, easier to read, etc. \$\endgroup\$Bryan Oakley– Bryan Oakley2017年06月22日 11:48:46 +00:00Commented Jun 22, 2017 at 11:48 -
\$\begingroup\$ @Bryan Edited the answer to state that. \$\endgroup\$Graipher– Graipher2017年06月22日 12:57:18 +00:00Commented Jun 22, 2017 at 12:57