I've got a Tkinter application that I'm writing in Python 3.
I've got one module which contains a class in which I create all my widgets. I'm trying to update a progress bar from a separate module that does the heavy lifting.
#GUI.py
import tkinter as tk
from tkinter.filedialog import askopenfilename, askdirectory
import tkinter.ttk as ttk
import Work_Module
progress_bar = None
class Application(tk.Frame):
def __init__(self, master=None):
master.title('CCD Creator Version 1.0')
tk.Frame.__init__(self, master, width=500)
self.pack()
self.create_widgets()
def create_widgets(self):
global progress_bar
progress_bar = tk.ttk.Progressbar(orient=tk.HORIZONTAL, length=200, mode='determinate')
progress_bar.pack(side="bottom")
self.run = tk.Button(self, text=" Run ", fg="green", command=self.do_work)
self.run.pack(side="top")
#Continue creating various widgets that don't need to be updated by other modules
def do_work(self):
Work_Module.main()
def main():
root = tk.Tk()
app = Application(master=root)
app.mainloop()
if __name__ == '__main__':
main()
Here is the module with a class that does the "heavy lifting" that I want to track the progress on.
#Work_Module.py
class Work:
def __init__(self):
#Do some intensive work
main():
for idx, job in enumerate(work_list):
Work()
import GUI
percentage_done = idx / len(job_list) * 100
GUI.progress_bar.step(percentage_done)
The problem I'm running into is that progress_bar has a value of None in the Work_Module. It's assigned an object in the GUI module, but that object isn't retained when trying to reference it from the Work_Module. I know that importing GUI in the main method is kind of strange, and I suspect that this is some sort of namespace issue or circular import problem, but I can't seem to think of a way to work around this.
One way is to inject the progress_bar into Work_Module just before you call the mainloop():
def main():
root = tk.Tk()
app = Application(master=root)
Work_Module.progress_bar = progress_bar
app.mainloop()
If you like, you can also dispense with the global aspect of the progress_bar:
def create_widgets(self):
# global progress_bar (no longer needed)
# add 'self'
self.progress_bar = tk.ttk.Progressbar(orient=tk.HORIZONTAL, length=200, mode='determinate')
self.progress_bar.pack(side="bottom")
...
def main():
root = tk.Tk()
app = Application(master=root)
Work_Module.progress_bar = self.progress_bar # add 'self'
app.mainloop()
Related
I am writing an application in tkinter consisting of several modules in which there are classes. Each module to a separate page of the app. As I move the buttons between the pages "next", "previous" it opens a new window for me every time. How do I make it so that each time calling pages opens in the same window?
I give draft code.
thank you for your help :D
task1.py
import tkinter as tk
from Test.modul.task1 import FirstPage1
class FirstPage0:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page(self):
FirstPage1(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
tk.Button(self.root, text="Start page", command=self.get_second_page).pack()
tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage0(tk.Tk())
first.get_run_first_page()
task2.py
import tkinter as tk
class FirstPage1:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page1(self):
from Test.task import FirstPage0
FirstPage0(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
tk.Button(self.root, text="Back", command=self.get_second_page1).pack()
tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage1(tk.Tk())
first.get_run_first_page()
Solution
#AdrianSz, you wanted to make the buttons not stack under each other. There are three ways to do so. One, is to keep only one button and change its command and text parameters each time when the frames change. Another would be to unpack the button not needed and pack the button needed. The third would be to pack the buttons in the root window instead of frame and change the text and command parameters. I would recommend the second method as it is easier and less prone to errors.
Code
task1.py
import tkinter as tk
from Test.modul.task1 import FirstPage1
class FirstPage0:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page(self):
self.root.pg_0_btn_start.pack_forget()
self.root_pg_0_btn_exit.pack_forget()
FirstPage1(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
self.root.pg_0_btn_start = tk.Button(self.root, text="Start page",
command=self.get_second_page).pack()
self.root_pg_0_btn_exit = tk.Button(self.root, text="Exit",
command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage0(tk.Tk())
first.get_run_first_page()
task2.py
import tkinter as tk
class FirstPage1:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page1(self):
from Test.task import FirstPage0
self.root.pg_1_btn_back.pack_forget()
self.root_pg_1_btn_exit.pack_forget()
FirstPage0(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
self.root.pg_1_btn_back = tk.Button(self.root, text="Back", command=self.get_second_page1).pack()
self.root.pg_1_btn_exit = tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage1(tk.Tk())
first.get_run_first_page()
Note: I couldn't test this code from my side so if there are any errors, please comment on this answer
Suggestions
Since you are using modules for these, just make them inherit a class specified in a different file and operate them both from that file. There you can use self to access the methods of a subclass because the subclasses instantiate the base class and thus the self is a object of the subclass and is passed to the base class. The type of code you used is quite confusing too. I have added code to give you your wanted output using the principles I mentioned here. Hope this helped!
I have this simple code with self.btn1
from tkinter import Tk, ttk, messagebox
import tkinter as tk
class Main(tk.Frame):
def __init__(self, root):
super().__init__(root)
self.btn1 = ttk.Button(self, text="test")
self.btn1.pack()
if __name__ == "__main__":
root = tk.Tk()
app = Main(root)
app.pack()
root.mainloop()
and this code without self button
from tkinter import Tk, ttk, messagebox
import tkinter as tk
class Main(tk.Frame):
def __init__(self, root):
super().__init__(root)
btn1 = ttk.Button(self, text="test")
btn1.pack()
if __name__ == "__main__":
root = tk.Tk()
app = Main(root)
app.pack()
root.mainloop()
Both of them work similarly, but what's the difference, which one should I use?
The only real difference lies in how easy it is to retrieve the reference to the Button instance should you need one. With the former, it's just app.btn1. With the latter, it's app.winfo_children()[0].
>>> app.winfo_children()[0] is app.btn1
True
The difference is that btn1 is a local variable that is only available in the function in which it is used. self.btn1 is an instance variable that is available in every function within the class.
from tkinter import*
import tkinter.messagebox
from tkinter import ttk
import random
import time
import datetime
def main():
root = Tk()
app = Login(root)
class Login:
def __init__(self, master):
self.master = master
self.master.title("Billing Login System")
self.master.geometry("1350x750+0+0")
self.master.config(bg = 'cadet blue')
self.frame = Frame(self.master,bg='cadet blue')
self.frame.pack()
#Some code here
..(Login Conditions)
..
#
#After authentication this window should pop up
class customer:
def __init__(self, root):
self.root = root
self.root.title("eZ Billing System")
self.root.geometry("1350x750+0+0")
self.root.config(bg="cadet blue")
self.frame = Frame(self.root,bg='cadet blue')
self.frame.pack()
#some code here
if __name__ == '__main__':
main()
This code works but the problem is that when i run the file no error shows up or warnings and neither does any window shows but if i run any other python program then this windows pops up and no problems.
I am new to this and cant figure out whats wrong.
I guess you aren't creating main window instance.
That's why it is not showing error but not showing any output even.
Try adding this :
root.mainloop()
I am trying the following code about python:
import tkinter
from tkinter import *
from tkinter.messagebox import askokcancel
class Quitter(Frame):
def _init__(self,parent=None):
Frame.__init__(self,parent)
self.pack()
widget=Button(self,text='Quit',command=self.quit)
widget.pack(side=TOP,expand=YES,fill=BOTH)
def quit(self):
ans=askokcancel('Verify exit',"You want to quit?")
if ans:Frame.quit(self)
if __name__=='__main__':Quitter().mainloop()
When executing it, I get a frame like this:
But where is the Quit button?
As mentioned in the comments, you have a typo in __init__().
Further, you probably want to structure your app as follows: (1) not importing tkinter in the main namespace, (2) keep track of the root/parent in the tk.Frame class, and use root.destroy() i/o quit()
import tkinter as tk
from tkinter.messagebox import askokcancel
class Quitter(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.widget = tk.Button(self.parent, text='Quit', command=self.quit)
self.widget.pack()
def quit(self):
ans = askokcancel('Verify exit', "You want to quit?")
if ans:
self.parent.destroy()
if __name__ == "__main__":
root = tk.Tk()
Quitter(root)
root.mainloop()
You will find more info in this thread.
When the below code is run there's an additional, empty window appears. Why does that happen, and how can it be fixed?
import tkinter as tk
class Deneme(tk.Tk):
def __init__(self):
super().__init__()
self.smartGui()
def smartGui(self):
tk.Label(self, text="Name").grid(row=0,column=0)
tk.Entry(self).grid(row=0,column=1, columnspan=2)
tk.Label(self, text="Surname").grid(row=1,column=0)
tk.Entry(self).grid(row=1,column=1, columnspan=2)
tk.Label(self, text="City").grid(row=0,column=4)
tk.Entry(self).grid(row=0,column=5, columnspan=2)
tk.Label(self, text="Explain").grid(row=3, column=0)
def main():
root = tk.Tk()
app = Deneme()
root.mainloop()
if __name__ == "__main__":
main()
Simplest way would be to replace:
def main():
root = tk.Tk()
app = Deneme()
root.mainloop()
with:
def main():
app = Deneme()
app.mainloop()
There are two windows displayed, as there are two instances of Tk, root and app. app is an instance of Tk as well, as Deneme inherits from Tk.
Note: It's not suggested to have multiple instances of Tk. If you'd want multiple windows, later on, you should use Toplevel instead of Tk.
Additionally, you could simply inherit Deneme from Frame instead of Tk but you'd want to configure it a bit more for it to allow you to assign its parent as root. Then create its instance like app = Deneme(root).