Best way to structure a tkinter application? - python
The following is the overall structure of my typical python tkinter program.
def funA():
def funA1():
def funA12():
# stuff
def funA2():
# stuff
def funB():
def funB1():
# stuff
def funB2():
# stuff
def funC():
def funC1():
# stuff
def funC2():
# stuff
root = tk.Tk()
button1 = tk.Button(root, command=funA)
button1.pack()
button2 = tk.Button(root, command=funB)
button2.pack()
button3 = tk.Button(root, command=funC)
button3.pack()
funA funB and funC will bring up another Toplevel windows with widgets when user click on button 1, 2, 3.
I am wondering if this is the right way to write a python tkinter program? Sure, it will work even if I write this way, but is it the best way? It sounds stupid but when I see the code other people written, their code is not messed up with bunch of functions and mostly they have classes.
Is there any specific structure that we should follow as good practice? How should I plan before start writing a python program?
I know there is no such thing as best practice in programming and I am not asking for it either. I just want some advice and explanations to keep me on the right direction as I am learning Python by myself.
I advocate an object oriented approach. This is the template that I start out with:
# Use Tkinter for python 2, tkinter for python 3
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
<create the rest of your GUI here>
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
The important things to notice are:
I don't use a wildcard import. I import the package as "tk", which requires that I prefix all commands with tk.. This prevents global namespace pollution, plus it makes the code completely obvious when you are using Tkinter classes, ttk classes, or some of your own.
The main application is a class. This gives you a private namespace for all of your callbacks and private functions, and just generally makes it easier to organize your code. In a procedural style you have to code top-down, defining functions before using them, etc. With this method you don't since you don't actually create the main window until the very last step. I prefer inheriting from tk.Frame just because I typically start by creating a frame, but it is by no means necessary.
If your app has additional toplevel windows, I recommend making each of those a separate class, inheriting from tk.Toplevel. This gives you all of the same advantages mentioned above -- the windows are atomic, they have their own namespace, and the code is well organized. Plus, it makes it easy to put each into its own module once the code starts to get large.
Finally, you might want to consider using classes for every major portion of your interface. For example, if you're creating an app with a toolbar, a navigation pane, a statusbar, and a main area, you could make each one of those classes. This makes your main code quite small and easy to understand:
class Navbar(tk.Frame): ...
class Toolbar(tk.Frame): ...
class Statusbar(tk.Frame): ...
class Main(tk.Frame): ...
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.statusbar = Statusbar(self, ...)
self.toolbar = Toolbar(self, ...)
self.navbar = Navbar(self, ...)
self.main = Main(self, ...)
self.statusbar.pack(side="bottom", fill="x")
self.toolbar.pack(side="top", fill="x")
self.navbar.pack(side="left", fill="y")
self.main.pack(side="right", fill="both", expand=True)
Since all of those instances share a common parent, the parent effectively becomes the "controller" part of a model-view-controller architecture. So, for example, the main window could place something on the statusbar by calling self.parent.statusbar.set("Hello, world"). This allows you to define a simple interface between the components, helping to keep coupling to a minimun.
Putting each of your top-level windows into it's own separate class gives you code re-use and better code organization. Any buttons and relevant methods that are present in the window should be defined inside this class. Here's an example (taken from here):
import tkinter as tk
class Demo1:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.button1 = tk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window)
self.button1.pack()
self.frame.pack()
def new_window(self):
self.newWindow = tk.Toplevel(self.master)
self.app = Demo2(self.newWindow)
class Demo2:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
self.quitButton.pack()
self.frame.pack()
def close_windows(self):
self.master.destroy()
def main():
root = tk.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
Also see:
simple hello world from tkinter docs
Tkinter example code for multiple windows, why won't buttons load correctly?
Tkinter: How to Show / Hide a Window
Hope that helps.
This isn't a bad structure; it will work just fine. However, you do have to have functions in a function to do commands when someone clicks on a button or something
So what you could do is write classes for these then have methods in the class that handle commands for the button clicks and such.
Here's an example:
import tkinter as tk
class Window1:
def __init__(self, master):
pass
# Create labels, entries,buttons
def button_click(self):
pass
# If button is clicked, run this method and open window 2
class Window2:
def __init__(self, master):
#create buttons,entries,etc
def button_method(self):
#run this when button click to close window
self.master.destroy()
def main(): #run mianloop
root = tk.Tk()
app = Window1(root)
root.mainloop()
if __name__ == '__main__':
main()
Usually tk programs with multiple windows are multiple big classes and in the __init__ all the entries, labels etc are created and then each method is to handle button click events
There isn't really a right way to do it, whatever works for you and gets the job done as long as its readable and you can easily explain it because if you cant easily explain your program, there probably is a better way to do it.
Take a look at Thinking in Tkinter.
OOP should be the approach and frame should be a class variable instead of instance variable.
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame,
text="QUIT", fg="red",
command=frame.quit)
self.button.pack(side=LEFT)
self.slogan = Button(frame,
text="Hello",
command=self.write_slogan)
self.slogan.pack(side=LEFT)
def write_slogan(self):
print "Tkinter is easy to use!"
root = Tk()
app = App(root)
root.mainloop()
Reference: http://www.python-course.eu/tkinter_buttons.php
My preferred way of doing it is like Bryan Oakley's answer.
Here's an example, made by Sentdex on Youtube, go check his "GUIs with Tkinter" playlist.
I think it's really relevant to put it here because it's a great example for the OP, and so it also answers this answer that was upped by 35 people and wasn't answered;
#Bryan Oakley do you know any good sample codes on internet that i can
study their structure? – Chris Aung Jul 5 '13 at 8:35
import tkinter as tk
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
"""
tkinter example app with OOP
"""
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for frame_class in (StartPage,PageOne, PageTwo):
frame = frame_class(container, self)
self.frames[frame_class] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
"""
Put specific frame on top
"""
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
"""
Starting frame for app
"""
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent,bg='grey')
label = tk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button_page1 = tk.Button(self, text = 'Visit Page 1', command= lambda: controller.show_frame(PageOne))
button_page1.pack()
button_page2 = tk.Button(self, text = 'Visit Page 2', command= lambda: controller.show_frame(PageTwo))
button_page2.pack()
class PageOne(tk.Frame):
"""
First page of program
"""
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent,bg='light blue')
label = tk.Label(self, text="Page one", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button_home = tk.Button(self, text = 'Back to Home', command= lambda: controller.show_frame(StartPage))
button_home.pack()
button_home = tk.Button(self, text = 'Go to page2', command= lambda: controller.show_frame(PageTwo))
button_home.pack()
class PageTwo(tk.Frame):
"""
First page of program
"""
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent,bg='light green')
label = tk.Label(self, text="Page two", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button_home = tk.Button(self, text = 'Back to Home', command= lambda: controller.show_frame(StartPage))
button_home.pack()
button_home = tk.Button(self, text = 'Go to page1', command= lambda: controller.show_frame(PageOne))
button_home.pack()
app = SeaofBTCapp()
app.mainloop()
Find the code here also : [https://pythonprogramming.net/change-show-new-frame-tkinter/]
Organizing your application using class make it easy to you and others who work with you to debug problems and improve the app easily.
You can easily organize your application like this:
class hello(Tk):
def __init__(self):
super(hello, self).__init__()
self.btn = Button(text = "Click me", command=close)
self.btn.pack()
def close():
self.destroy()
app = hello()
app.mainloop()
Probably the best way to learn how to structure your program is by reading other people's code, especially if it's a large program to which many people have contributed. After looking at the code of many projects, you should get an idea of what the consensus style should be.
Python, as a language, is special in that there are some strong guidelines as to how you should format your code. The first is the so-called "Zen of Python":
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
On a more practical level, there is PEP8, the style guide for Python.
With those in mind, I would say that your code style doesn't really fit, particularly the nested functions. Find a way to flatten those out, either by using classes or moving them into separate modules. This will make the structure of your program much easier to understand.
I personally do not use the objected oriented approach, mostly because it a) only get in the way; b) you will never reuse that as a module.
but something that is not discussed here, is that you must use threading or multiprocessing. Always. otherwise your application will be awful.
just do a simple test: start a window, and then fetch some URL or anything else. changes are your UI will not be updated while the network request is happening. Meaning, your application window will be broken. depend on the OS you are on, but most times, it will not redraw, anything you drag over the window will be plastered on it, until the process is back to the TK mainloop.
Related
I am trying to edit a top level window
So I heard there was a method in which you could make a separate tk.Toplevel class I have been attempting to use it but I dont know exactly whats wrong I really need help here is the code as of now I will keep trying to make adjustments but its not easy import tkinter as tk from tkinter import ttk class App(tk.Tk): def __init__(self, title: str): super().__init__() self.title(title) self.label = ttk.Label(self, text="Hello, World!") self.label.pack(side=tk.TOP) self.button = ttk.Button(self, text="Button", command=self.Alter) self.button.pack(side=tk.RIGHT) class ThemeWindow(tk.Toplevel): def __init__(self, parent): super().__init__(parent) self.title("NEW") self.parent = parent self.label = tk.Label(parent, text="Hello, World!") self.label.pack(side=tk.TOP) self.button = tk.Button(parent, text="Button", command=parent.Alter) self.button.pack(side=tk.RIGHT) I have edited and used examples given in a course I am taking but clearly I am misreading.
The key part is AttributeError: '_tkinter.tkapp' object has no attribute 'Alter'' - that's telling you that your App class (which inherits from Tk) has no attribute called Alter. The problem is caused by your App class, and more specifically by self.button = ttk.Button(self, text="Button", command=self.Alter) You're telling self.button to call the function self.Alter as its callback function (the command parameter), but you haven't defined a function called Alter within the App class. You need to do something like this: class App(tk.Tk): def __init__(self, title: str): super().__init__() self.title(title) self.label = ttk.Label(self, text="Hello, World!") self.label.pack(side=tk.TOP) self.button = ttk.Button( self, text="Button", command=self.alter # don't use uppercase in function names! ) self.button.pack(side=tk.RIGHT) def alter(self): # define a function 'alter' within this class print('Hello') # do whatever you need this function to do here Now when button is pressed, it will be able to call alter, which (in this case) should print "Hello" Likewise, your ThemeWindow class should be able to call this via parent.alter Again, note that I've changed the name from "Alter" to "alter" to adhere to Python naming conventions.
Tkinter OOP - Class Instance Management With Multiple Top Level Windows
My initial approach is based on the following example: Best way to structure a tkinter application? I am trying to create a more complex application involving multiple sub applications each displayed in a separate top level window. All sub applications need to be able to exchange information among each other. Hence, I intend to use class variables in the main application in order to accomplish this requirement. At the moment I am stuck at one point: I do not know how to properly implement the destruction of the sub application class instance once it has been created. The "app_one" window can be opened exactly one time and then it can be properly closed. Of course, if the window is opened a second time an error occurs since only the "Toplevel" window instance has been destroyed, but not the sub application class instance itself. How do I properly implement the destruction of the sub application class instance upon closing the "Toplevel" window ? Besides that, I am also very grateful for any other advise on how to improve the structure of my template below in terms of following "Python" as well as "tkinter" best practice paradigms: import tkinter import tkinter.filedialog class main_app(tkinter.Tk): root = None var_one = None sub_app_one_instance = None sub_app_two_instance = None sub_app_thr_instance = None def __init__(self): super().__init__() main_app.root = self # <<< in order to be able to refer to root window in other classes main_app.var_one = 99 # <<< class variables in order to exchange data between main app and sub apps main_app.sub_app_one_instance = None main_app.sub_app_two_instance = None main_app.sub_app_thr_instance = None self.create_tkinter_interface_main_app() def create_tkinter_button(self, button_destination, button_text, button_width): tkinter_button = tkinter.Button(button_destination, text=button_text, width=button_width) return tkinter_button def create_tkinter_label(self, label_destination, label_text): tkinter_label = tkinter.Label(label_destination, text=label_text) return tkinter_label def create_tkinter_interface_main_app(self): frame_main = tkinter.Frame(self) frame_sub_one = tkinter.Frame(frame_main, bg="red") frame_sub_two = tkinter.Frame(frame_main, bg="green") frame_sub_thr = tkinter.Frame(frame_main, bg="blue") label_app = self.create_tkinter_label(frame_main, "application") label_one = self.create_tkinter_label(frame_sub_one, "menu one") label_two = self.create_tkinter_label(frame_sub_two, "menu two") label_thr = self.create_tkinter_label(frame_sub_thr, "menu thr") button_one = self.create_tkinter_button(frame_sub_one, "app_one", 20) button_one.configure(command=self.sub_app_one_open) button_two = self.create_tkinter_button(frame_sub_one, "app_two", 20) label_app.pack(side=tkinter.TOP, fill=tkinter.X, expand=tkinter.NO) frame_sub_one.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=tkinter.YES) frame_sub_two.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=tkinter.YES) frame_sub_thr.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=tkinter.YES) label_one.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10) button_one.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10) button_two.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10) label_two.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10) label_thr.pack(side=tkinter.TOP, fill=tkinter.NONE, expand=tkinter.NO, padx=10, pady=10) frame_main.pack(fill=tkinter.BOTH, expand=tkinter.TRUE) def sub_app_one_open(self): if not main_app.sub_app_one_instance == None: main_app.sub_app_one_instance.sub_app_one_move_to_front() return None else: main_app.sub_app_one_instance = sub_app_one() class sub_app_one(main_app): # <<< inherit from main application in order to be able to access class variables def __init__(self): self.sub_app_one_toplevel_instance = tkinter.Toplevel(main_app.root) self.sub_app_one_toplevel_instance.protocol("WM_DELETE_WINDOW", self.sub_app_one_close_toplevel) print(main_app.var_one) # <<< access information from main app def sub_app_one_move_to_front(self): self.sub_app_one_toplevel_instance.attributes("-topmost", 0x01) # <<< set window state self.sub_app_one_toplevel_instance.attributes("-topmost", 0x00) # <<< reset window state def sub_app_one_close_toplevel(self): self.sub_app_one_toplevel_instance.destroy() self.sub_app_one_toplevel_instance = None if __name__ == "__main__": main_app_instance = main_app() main_app_instance.mainloop()
# <<< inherit from main application in order to be able to access class variables This is not how inheritance works. If sub_app_one needs access to variables in the main application, it should get them via the instance of main_app. You should definitely not be inheriting from main_app. When you use inheritance, you are saying that the subclass is a main class. Meaning, you end up with two main classes. One is the original, and one is exactly like the original, with some modifications. That means you end up with two instances of tk.Tk which is not how tkinter is designed to be used. You can do it, but the behavior is not intuitive, and is rarely the right thing to do. What you should do instead is pass in the instance of main_app into your sub_app_one when creating the instance. It would look something like this: class main_app(tkinter.Tk): ... def sub_app_one_open(self): ... main_app.sub_app_one_instance = sub_app_one(main_app=self) ... class sub_app_one(): def __init__(self, main_app): self.main_app = main_app ... As for the handling of deleting the window, the simplest solution is to make your child classes subclasses of tk.Toplevel. With that, you can use tkinter's built-in event handling to know when the widget has been destroyed. Here's a working example: import tkinter as tk class Main(tk.Tk): def __init__(self): super().__init__() self.sub_app_one = None b1 = tk.Button(self, text="Open Sub App One", command=self.sub_app_one_open) b1.pack(padx=20, pady=20) def sub_app_one_open(self): if self.sub_app_one is None: self.sub_app_one = Sub_app_one(self) else: self.sub_app_one.deiconify() self.sub_app_one.bind("<Destroy>", self._child_destroyed) def _child_destroyed(self, event): if event.widget == self.sub_app_one: print("sub_app_one has been destroyed") self.sub_app_one = None class Sub_app_one(tk.Toplevel): def __init__(self, main_app): self.main_app = main_app super().__init__(main_app) label = tk.Label(self, text="This is sub app one") label.pack(padx=50, pady=50) main = Main() main.mainloop()
Here is some advice that I can give after having worked on a application like this for the past few weeks. I will split my answer into two parts: Some examples from my own code Advice that will be helpful if this is a long-term project Examples from my code Managing your GUI I would advise you to have one main class that manages your application. This class puts all the frames/pages of your applicaton on top of each other and raises the one you need to the top. I have a separate file for every frame, since the code for each of them can get very long. This is the 'Main.py' file: import tkinter as tk #importing frames that we will create soon! from Frame1 import Frame1 frome Frame2 import Frame2 frome Frame3 import Frame3 class Main(tk.Tk): def __init__(self): tk.Tk.__init__(self) #Sets the screen size of the GUI self.width= self.winfo_screenwidth() self.height= self.winfo_screenheight() self.geometry(str(self.width) + 'x' + str(self.height) + '+0+0') self.title('project_title') #The container is a frame that contains the projects's frames container = tk.Frame(self, height=self.height, width=self.width) #Pack the container to the root container.pack(side="top", fill="both", expand=True) #Fixes pack location of the container using grid container.grid_rowconfigure(0, weight=1) container.grid_columnconfigure(0, weight=1) #Create empty dictionary of frames self.frames = {} #Use container as master frame for imported frames for Frame in (Frame1, Frame2, Frame3): self.frames[Frame] = Frame(container) self.frames[Frame].grid(row=0, column=0, sticky="nsew") #Define buttons to switch GUI pages self.frame1_btn = tk.Button(self, text='Frame1', width=15, font=('Arial', 10), command=self.show_frame1) self.frame1_btn.place(relx=0.75, rely=0.025, anchor='center') self.frame2_btn = tk.Button(self, text='Frame2', width=15, font=('Arial', 10), command=self.show_frame2) self.frame2_btn.place(relx=0.85, rely=0.025, anchor='center') self.frame3_btn = tk.Button(self, text='Frame3', width=15, font=('Arial', 10), command=self.show_frame3) self.frame3_btn.place(relx=0.95, rely=0.025, anchor='center') #The start page is raised to the top at the beginning. self.show_frame1() #Raises the selected frame to the top. def show_frame1(self): frame = self.frames[Frame1] frame.tkraise() def show_frame2(self): frame = self.frames[Frame2] frame.tkraise() def show_frame3(self): frame = self.frames[Frame3] frame.tkraise() def main(): app = Main() app.mainloop() if __name__ == '__main__': main() You have a 'main' file that manages the GUI. Now you have to create the frames that will later be the pages of your application. Best practice: keep them each in a separate file and import them in Main.py! This is an example Frame.py file: import tkinter as tk class Frame1(tk.Frame): def __init__(self, ParentFrame): tk.Frame.__init__(self, ParentFrame) #The label acts as a title for each main frame. self.label = tk.Label(self, text='App_Name: Frame1', font=('Arial', 20)) self.label.pack(padx=10, pady=10, anchor='w') Repeat this for as many frames as you need. You should then have the following files in your directory: Main.py, Frame1.py, Frame2.py, Frame3.py, etc. Try running the Main.py file! The GUI still looks boring right now, but you will fill each frame with your own code. Destroying frames Let's say you have a start page that you only want to use once. It should be destroyed after its use. Let our 'Frame1.py' file be that start page. The code for it should look like this: import tkinter as tk class Frame1(tk.Frame): def __init__(self, ParentFrame): tk.Frame.__init__(self, ParentFrame) #The label acts as a title for each main frame. self.label = tk.Label(self, text='App_Name: Frame1', font=('Arial', 20)) self.label.pack(padx=10, pady=10, anchor='w') self.selfdestruct_btn = tk.Button(self, text='selfdestruct', command=self.selfdestruct) self.selfdestruct_btn.pack(pady=20) def selfdestruct(self): self.destroy() If you click the 'selfdestruct' button on the start page, this frame will be properly destroyed. However, you can still see the 'Frame 1' button that you defined in 'Main.py', even if clicking it produces no result. Go back to the 'Main.py' file and delete the part where you defined this button. You don't need it anyway, since the start page will be raised to the top by the line 'self.show_frame1()' when you start your application. I hope that helps you with your question. Here are some other tips I would like to give you: Further advice File scructure A good file structure helps you to have an overview of your code and will make fixing errors much easier. You might want to have a directory that is similar to this: Main.py file Folder for Frame1 Folder for Frame2 Folder for Frame3 possibly a database any other major components of your application Why use folders for each frame of your application? When you write the code for each of these frames, you will probably start filling each frames with subframes. Each subframe has its own function and may need lots of code. I would therefore define a class for every major subframe you use that is described by lots of code. If you have multiple of those subframe classes, you might even want to have a 'subframes' folder within the frame folder. You can also put those files in the frame folder that are used only by this frame. Lets say you have to perform some complex calculation and define a class that does just that. You can keep this class in a separate file in the frame folder. Fast GUI loadup Try not making your computer load a bunch of unncessary stuff when it starts your program. The best case scenario would be that it only has to pack all of your pages/frames and their buttons. Don't make it load huge picture files or anything of that sort. Make it do these thing by the click of a button instead. Pretty won't win any prizes ...at least that's probably not the case here. I wouldn't spend too much time formatting each button perfectly before moving on, since you are likely to move things around all the time anyway. Once you have all elements that you need packed to a frame, you can think about formatting. That's all I'm going to share for now. Feel free to ask if you have any follow up questions! Best, Ansel
What does frame.__init__ do? - tkinter
I am watching Sentdex's Tkinter tutorial and there are some related problems that arise: Basic Class Questions that I'm just new to: How come Frame follows after the declaration of the Window class (sorry for a basic class question)? How is self.pack working without specifying what to pack? What does frame.__init__ contribute to this code? The code is this: from tkinter import * class Window(Frame): def __init__(self, master=None): Frame.__init__(self, master, bg='LightBlue') self.master = master self.init_window() def init_window(self): self.master.title("GUI") self.master.geometry('400x400') self.pack(fill=BOTH, expand=1) quitButton = Button(self, text='Quit') quitButton.place(x=0, y=0) root = Tk() app = Window(root) root.mainloop() Thanks in advance!
The class Window inherits from tk.Frame, this is what (Frame) after Window means. In such a situation, Window is also a tk.Frame, hence when calling pack() on self, it is in essence packing itself. It is likely less confusing to avoid star imports: import tkinter as tk class Window(tk.Frame): # <-- avoid star imports def __init__(self, master=None): self.master = master super().__init__(master, bg='LightBlue') # <-- use super instead of hardcoding the parent class self.init_window() def init_window(self): self.master.title("GUI") self.master.geometry('400x400') self.pack(fill=tk.BOTH, expand=True) self.quit_button = tk.Button(self, text='Quit', command=self.master.destroy) self.quit_button.pack() if __name__ == '__main__': root = tk.Tk() app = Window(root) root.mainloop()
Frame is in brackets so that the class Window can inherit the methods of the tkinter.Frame class. The Frame.init function initialises the class as a tkinter Frame. The self.pack() line packs the Frame into self.master, which was created a couple of lines before. Have a look at some basic classes and build up from there. I did this tutorial a little while ago and found that I had to spend a little time having a look at classes first. I used some youtube videos I think, good luck!
I can't get an image to load on the canvas in Tkinter python
I am having a big issue. The Canvas loads perfectly but the image does not display. I started Python 1 week ago and I have no clue why does is not working. Can anyone please show me the way to solve the issue of the image not loading on the canvas? from Tkinter import * from PIL import ImageTk from PIL import Image class Fake_Virus: def __init__(self, master): self.master = master master.title("Totally not a virus!") b = Button(master, text="Help", command=self.prank) b.pack(padx=10, pady=10, side=LEFT) quit = Button(master, text="Close", command=self.close_window) quit.pack(padx=10, pady=10, side=RIGHT) photo = PhotoImage("eh.gif") label = Label(image=photo) label.image = photo # keep a reference! label.pack() f = Frame(master, height=150, width=150) f.pack_propagate(0) # don't shrink f.pack() def prank(self): print "work" return def close_window(self): root.destroy() return root = Tk() my_gui = Fake_Virus(root) root.mainloop()
You should use the file option to initialize the photo image object. This means you need to change photo = PhotoImage("eh.gif") to photo = PhotoImage(file="eh.gif") Now your code will work. But a working code is not necessarily a good code. There are other issues with your code. Let me go through them quickly: It is better to code import Tkinter as Tk than from Tkinter import * Why that hyphen in your class name? Follow PEP8 so that, in the futur, people will find it easy to review and understand your code. Good that you have written self.master = master (read complete code to know why) but then you have never used it. This means you made a good decision and you render it useless. You set the title of the window within the initializer. It is better if you do that in a separate function so that whenever you want to add additional settings to your GUI (such as the size, font or whatever) you will only add code to that function instead of vomiting lot of trash inside the initializer which rather needs to be clean. None of the widgets you created is 'selfed' (you may read Why explicit self has to stay) It is better you create the widgets in a separate function otherwise your __init__() will be dirty. Why do you use return in prank() and close_window()? By default, Python functions that do not return something return None anyway so it is useless to code that. Why did you pack one button to left and the other one to right and then no pack siding for the label? Read about the pack() geometry manager. Why you did not attach the label to a parent widget as you did for the 2 other buttons? All Tkinter widgets need to be clung into a parent widget. The grand parent of those widgets is an instance of Tkinter.Tk() Why did you create that frame and then you never used it? You are not doing anything with it, so ..? Given these remarks, I want to provide you an improved -but not perfect- version of your program. You can then follow this 'philosophy' to add or modifying existing widgets: #!/usr/bin/env python # -*- coding: utf-8 -*- import Tkinter as Tk from PIL import ImageTk class FakeVirus: def __init__(self, master): self.master = master self.configure_gui() self.create_widgets() def configure_gui(self): self.master.title('Totally not a virus!') def create_widgets(self): self.create_buttons() self.create_label_for_image() def create_buttons(self): self.help = Tk.Button(self.master, text='Help', command=self.prank) self.help.pack(side=Tk.LEFT) self.quit = Tk.Button(self.master, text='Close', command=self.close_window) self.quit.pack(side=Tk.LEFT) def create_label_for_image(self): self.image_label = Tk.Label(self.master) self.image_label.pack(side=Tk.LEFT) self.load_image_to_label() def load_image_to_label(self): self.photo = ImageTk.PhotoImage(file='eh.gif') self.image_label.image = self.photo self.image_label.config(image=self.photo) def prank(self): print "work" def close_window(self): root.destroy() if __name__ == '__main__': root = Tk.Tk() my_gui = FakeVirus(root) root.mainloop() The output of the above program is:
Tkinter Button unable to find callback command
I'm working on a program that reads and parses a config file for an embedded hardware system I'm working on. I'm trying to use tkinter and python to make a simple GUI for reading and writing this file. I have the file IO and parsing mostly working, but I'm having trouble getting all the parts integrated with the GUI. I want to read the information off of the device and then populate the entry fields with the relevant info. The problem that I'm having is I keep getting the error "in initUI opnBut = Button(butFrm, text="Open", command=openCfg) NameError: name 'openCfg' is not defined" I can get it to find the function if I put it outside the Application class but then I can't figure out how to reference the fields inside the window I've made to update them. Any help would be greatly appreciated. My code segment is included below. class Application(Frame): def openCfg(): name = getNameFromFile() nameEntry.insert(0, name) def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.parent.title("Config Reader") self.pack(fill=BOTH, expand=True) nameFrm = Frame(self) nameFrm.pack(fill=X) nameLbl = Label(nameFrm, text="Device Name",width=20) nameLbl.pack(side=LEFT,padx=5, pady=5) nameEntry = Entry(nameFrm) nameEntry.pack(fill=X, padx=5, pady=5) butFrm = Frame(self) butFrm.pack(fill=X) opnBut = Button(butFrm, text="Open Cfg", command=openCfg) opnBut.pack(fill=X, padx=5,pady=5) root = Tk() root.geometry("600x600") app = Application(root) app.mainloop()
items which are defined in the class namespace are bound to the class. In this case, you have a couple options... One option is that you could make it a staticmethod: class Application(Frame): #staticmethod def openCfg(): name = getNameFromFile() nameEntry.insert(0, name) And then bind the button as: opnBut = Button(butFrm, text="Open Cfg", command=self.openCfg) However, staticmethods are rarely necessary. In this case, I might just move the definition of openCfg out of the class and keep everything else as it is: def openCfg(): name = getNameFromFile() nameEntry.insert(0, name) class Application(Frame): ... On a second look, this will probably still bring up a NameError for nameEntry. You'll probably want a first-class method: class Application(Frame): def openCfg(self): name = getNameFromFile() self.nameEntry.insert(0, name) And of course, when you create nameEntry, you'll need to make it a member of the Application class. ... self.nameEntry = Entry(nameFrm) self.nameEntry.pack(fill=X, padx=5, pady=5) ...