I'm trying to open few windows one after the other in tkinter. They are closing itself after set amount of time. Creating first window breaks my loop (it doesn't even reach print instruction). What am I doing wrong? Here is my code:
import tkinter as tk
class Subwindow:
def __init__(self, time):
self.time = time
self.window = tk.Toplevel()
self.window.geometry("300x300+1000+200")
self.window.wm_attributes("-topmost", 1)
tk.Label(self.window, text=time).pack(anchor=tk.CENTER)
self.update_time()
self.window.mainloop()
def update_time(self):
if self.time:
self.window.title(str(self.time))
self.time -= 1
self.window.after(1000, self.update_time)
else:
self.window.destroy()
class Window:
def __init__(self):
self.window = tk.Tk()
self.initialize()
self.window.mainloop()
def initialize(self):
buttons = tk.Frame(self.window)
tk.Button(self.window, text="Start", width=5, command=self.start).pack(in_=buttons, side="left")
tk.Button(self.window, text="Exit", width=5, command=self.close).pack(in_=buttons, side="left")
buttons.place(relx=0.97, rely=0.95, anchor=tk.SE)
def close(self):
self.window.destroy()
quit()
def start(self):
for x in [5,10,15]:
sub = Subwindow(x)
print(x)
Window()
Explain me please how can I fix it to get them opening one by one?
You should not call mainloop more than once. Remove the call to mainloop inside Subwindow.__init__.
Related
I created a GUI using Python, Tkinter. Everything seems to be fine. I have added several options to my Menu tab. In the File section I have added a feature to exit the GUI by clickinh onto the Exit button(not the button though, but a menu option). Here is the code that I have written.
from tkinter import *
from tkinter import filedialog, RIGHT
class Filedialog:
def directory(self):
content = filedialog.askdirectory()
return content
class Main(Frame):
def __init__(self):
self.opening_times = 0
self.current_image = 0
self.images = []
self.root = Tk()
self.root.title("Y")
self.root.configure(bg='pale turquoise')
self.root.geometry("800x800")
self.frame = Frame(self.root)
self.frame.pack(side=RIGHT)
self.frame = Frame.__init__(self, self.root)
self.widgets()
self.root.mainloop()
def widgets(self):
self.menu = Menu(self.root)
self.root.config(menu=self.menu)
self.filemenu = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_separator()
self.filemenu.add_command(label="Save")
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.onExit)
#self.editmenu = Menu(self.menu, tearoff=0)
#self.menu.add_cascade(label='Edit', menu=self.editmenu)
#self.editmenu.add_command(label="Undo")
#self.editmenu.add_command(label="Redo")
def onExit(self):
self.root.quit()
if __name__ == "__main__":
Main()
I have almost succeeded. But the problem arises when I click on the exit. The function does what it has to. But the GUI window was not closing, also it freezes there. There was no response at all until I force the window to close externally.
I also tried doing:
self.quit()
/
self.exit()
But the response is the same as the GUI freezes and not letting me do any other activities. I had to close complete program to get the access again.
I am not getting the error at least, to know what is wrong.
Please help me find out what is wrong/solve this problem.
Thank you for your time.
Let me correct your code
from sys import exit
from tkinter import *
from tkinter import filedialog, RIGHT
class Filedialog:
def directory(self):
content = filedialog.askdirectory()
return content
class Main(Frame):
def __init__(self):
self.opening_times = 0
self.current_image = 0
self.images = []
self.root = Tk()
self.root.title("Y")
self.root.configure(bg='pale turquoise')
self.root.geometry("800x800")
self.frame = Frame(self.root)
self.frame.pack(side=RIGHT)
self.frame = Frame.__init__(self, self.root)
self.widgets()
self.root.mainloop()
def widgets(self):
self.menu = Menu(self.root)
self.root.config(menu=self.menu)
self.filemenu = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_separator()
self.filemenu.add_command(label="Save")
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.onExit)
#self.editmenu = Menu(self.menu, tearoff=0)
#self.menu.add_cascade(label='Edit', menu=self.editmenu)
#self.editmenu.add_command(label="Undo")
#self.editmenu.add_command(label="Redo")
def onExit(self):
self.root.destroy()
exit()
if __name__ == "__main__":
Main()
You need to use destroy() instead of quit() and you should use exit to quit from the console
The method you have to use is destroy. For example:
def onExit(self):
self.root.destroy()
Use destroy() method instead of quit.
You can use the method tk.destroy() to close a window. The built-in functions exit() and quit() close the Python console.
Working example
from tkinter import *
tk = Tk()
tk.title("Closing window demonstration")
def close_window():
tk.destroy()
quit = Button(tk, text = "Quit", command = close_window)
quit.pack()
tk.mainloop()
The following code will show a small Tkinter user interface, however my buttons will not trigger the loop called copy_loop. I have been trying for several hours and I cannot figure out how to fix this. Any help will be very appreciated.
import tkinter as tk
import threading
class App():
def __init__(self, master):
self.isrunning = False
self.button1 = tk.Button(main, text='start')
self.button1.bind = ("<Button-1>", self.startrunning)
self.button1.pack()
self.button2 = tk.Button(main, text='stop')
self.button2.bind = ("<Button-1>", self.stoprunning)
self.button2.pack()
def startrunning(self, event):
self.isrunning = True
t = threading.Thread(target=self.copy_loop)
t.start()
def stoprunning(self, event):
self.isrunning = False
def copy_loop(self):
while self.isrunning:
print("Running...")
main = tk.Tk()
app = App(main)
main.mainloop()
bind is a function, not an attribute. Where you have:
self.button1.bind = ("<Button-1>", self.startrunning)
you are actually overwriting the bind function on your instance. It is now a tuple.
Instead, do:
self.button1.bind("<Button-1>", self.startrunning)
And likewise for button2.
You have an equals sign where there shouldn't be one. The command should be:
self.button1 = tk.Button(main, text='start')
self.button1.bind("<Button-1>", self.startrunning)
Or the much neater:
self.button1 = tk.Button(main, text='start', command=self.startrunning)
And your method signatures should have event=None:
def startrunning(self, event=None):
Or even more flexible:
def startrunning(self, *args):
I am new to tkinter and would like to do a UI based on the flags shown.
Basically, i would like to close one windows and open another window with the present state or delete the text and show another text with the present state.
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.callback)
label = tk.Label(self.root, text="Start Initialization")
label.pack()
self.root.mainloop()
class QQQ:
def quit(self):
self.delete(1.0,END)
class Appo(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.callback)
label = tk.Label(self.root, text="Initialization ended")
label.pack()
self.root.mainloop()
for i in range(100000):
time.sleep(0.5)
print(i)
if(i==1):
app = App()
time.sleep(1)
qqq=QQQ()
if(i==10):
app=Appo()
If all you want to do is change the text of the label, then use the config method on the label. The App, Appo, and QQQ classes, as well as the for loop can be combined into a single class as:
import Tkinter as tk #Python 2
#import tkinter as tk #Python 3
import threading
import time
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.root = tk.Tk()
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root.protocol("WM_DELETE_WINDOW", self.callback)
label = tk.Label(self.root, text="Initial Text") # You can use text=""
label.pack()
for i in range(100000):
time.sleep(0.5)
print (i)
if i == 1:
label.config(text="Start Initialization")
time.sleep(1)
label.config(text="")
if i == 10:
label.config(text="Initialization ended")
#self.root.mainloop()
app = App()
app.root.mainloop()
It might be better to use the after method of tkinter for time delays instead of time.sleep()
I am creating 2 window in my program and i am using two class, since the code is complex, i separate it in 2 different python file. After i imported the second window file, how can i make sure it open without having this error which show in this picture
The original result should look like this after the new window button clicked:
Coding for Main Window:
from tkinter import *
import classGUIProgram
class Window(Tk):
def __init__(self, parent):
Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.geometry("600x400+30+30")
self.wButton = Button(self, text='newWindow', command = self.OnButtonClick)
self.wButton.pack()
def OnButtonClick(classGUIProgram):
classGUIProgram.top = Toplevel()
master = Tk()
b = classGUIProgram.HappyButton(master)
master.mainloop()
if __name__ == "__main__":
window = Window(None)
window.title("title")
window.mainloop()
Coding for Second Window:
from tkinter import *
class HappyButton:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.printButton = Button(frame, text="Print message", command=self.printMessage)
self.printButton.pack(side=LEFT)
self.quitButton = Button(frame, text="Quit", command= quit)
self.quitButton.pack(side=LEFT)
self.downloadHistoryCB=Checkbutton(frame, text="Download History")
self.downloadHistoryCB.pack(side=LEFT)
def printMessage(self):
print("Wow this actually worked!")
master = Tk()
b = HappyButton(master)
master.mainloop()
You're creating extra Tk windows. Here is an example of using Toplevel widgets and another file.
mainWindow.py
import tkinter as tk
import secondWindow as sW
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Main Window")
self.geometry("600x400+30+30")
tk.Button(self, text = "New Window", command = self.new_window).pack()
tk.Button(self, text = "Close Window", command = self.close).pack()
self._second_window = None
def new_window(self):
# This prevents multiple clicks opening multiple windows
if self._second_window is not None:
return
self._second_window = sW.SubWindow(self)
def close(self):
# Destory the 2nd window and reset the value to None
if self._second_window is not None:
self._second_window.destroy()
self._second_window = None
if __name__ == '__main__':
window = MainWindow()
window.mainloop()
secondWindow.py
import tkinter as tk
class SubWindow(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.title("Sub Window")
self.geometry("400x300+30+30")
# Change what happens when you click the X button
# This is done so changes also reflect in the main window class
self.protocol('WM_DELETE_WINDOW', master.close)
tk.Button(self, text = "Print", command = self.printMessage).pack()
def printMessage(self):
print("Wow this actually worked!")
When using another file be sure to not have any global code you don't want running. Your classes don't have to inherit from Tk and Toplevel, this is just an example. But you need to ensure you only ever have one instance of Tk otherwise you get the behaviour you encountered
I have a basic python class that creates a window using the standard Tkinter library:
import Tkinter
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def lock_func(self):
while 1==1:
print "blah"
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=self.lock_func)
self.processBtn.pack()
app = GUI(None)
app.mainloop()
when I hit the Process button, the window doesn't respond.
I want to be able to close the program (using the x button) whene the lock_func is runing.
You could use a generator to hold the state within the loop, and use yield to relinquish control back to the main loop. Then use self.after to repeatedly call the generator's next method to simulate the effect of while True -- but doing it in a way which is friendly to Tkinter's main loop.
import Tkinter as tk
class App(object):
def __init__(self, master):
self.master = master
self.initialize()
def lock_func(self):
def step():
while True:
print("blah")
self.nextstep_id = self.master.after(1, nextstep)
yield
nextstep = step().next
self.nextstep_id = self.master.after(1, nextstep)
def stop(self):
self.master.after_cancel(self.nextstep_id)
print("stopped")
def initialize(self):
self.nextstep_id = 0
self.process_button = tk.Button(self.master, text="Process",
command=self.lock_func)
self.stop_button = tk.Button(self.master, text="Stop",
command=self.stop)
self.process_button.pack()
self.stop_button.pack(expand='yes', fill='x')
root = tk.Tk()
app = App(root)
root.mainloop()
You can use the window.update() method too keep your GUI active and functional after every time you change something on it. During the roots mainloop, this happens automatically but if you're prolonging the mainloop it's probably a good idea to do it manually your self. Put the window.update() in the loop that is taking a while. Note: window is a Tk() object
One way is to use threading:
import Tkinter
import thread
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def lock_func(self):
while 1==1:
print "blah"
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
self.processBtn.pack()
app = GUI(None)
app.mainloop()
However, it will keep printing until you close the Python console.
To stop it, you can use another button that changes a variable:
import Tkinter
import thread
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.shouldPrint = True
self.initialize()
def lock_func(self):
while self.shouldPrint:
print "blah"
def setShouldPrint(self, value):
self.shouldPrint = value
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
self.stopBtn = Tkinter.Button(self, text = "Stop", command = lambda: self.setShouldPrint(False))
self.processBtn.grid(row = 1)
self.stopBtn.grid(row = 2)
app = GUI(None)
app.mainloop()