I'm trying to put a info popup window to the user to advise him that a file is being created and that he must wait until it's created. I've a master frame that creates a popup window that shows the Progressbar with a message. This popupwindow must be destroyed as soon as the file has been created on the system.
This is my try:
import os
from Tkinter import *
import ttk
class UI(Frame):
def __init__(self,master):
Frame.__init__(self, master)
self.master = master
self.initUI()
def initUI(self):
popup = Toplevel(self)
txt = Label(popup, text="Please wait until the file is created").grid(row=0, column=0)
progressbar = ttk.Progressbar(popup, orient=HORIZONTAL, length=200, mode='indeterminate')
progressbar.grid(row=1, column=0)
progressbar.start()
self.checkfile()
progressbar.stop()
popup.destroy()
def checkfile(self):
while os.path.exists("myfile.txt") == False:
print "not created yet"
if __name__ == "__main__":
root = Tk()
aplicacion = UI(root)
root.mainloop()
The problem is that the UI get's freezed and I can't see any window. I think I must use Threads to solve this problem right? Do I've to make two threads, one for the UI and the other one for the checkfile function, or with one is enough?
It would be highly appreciated if someone could add the Threads to my code to make it work as I've never use them and I'm totally lost.
Thanks in advance.
while loop cause the UI unreponsive.
Use Widget.after instead to periodically checkfile method.
def initUI(self):
self.popup = popup = Toplevel(self)
Label(popup, text="Please wait until the file is created").grid(
row=0, column=0)
self.progressbar = progressbar = ttk.Progressbar(popup,
orient=HORIZONTAL, length=200, mode='indeterminate')
progressbar.grid(row=1, column=0)
progressbar.start()
self.checkfile()
def checkfile(self):
if os.path.exists("myfile.txt"):
print 'found it'
self.progressbar.stop()
self.popup.destroy()
else:
print 'not created yet'
self.after(100, self.checkfile) # Call this method after 100 ms.
What modified:
Used after instead of while loop.
Made progressbar, popup accessible in checkfile method by making them instance attribute.
Moved progressbar.stop, popup.destroy to checkfile method.
Related
I am trying to call a new window by pushing the button of an existing window. The original window should close when the new window is being created. When I push the button the new window will show up as expected but additionally a blank window will appear. Using tk.Tk() or tk.Toplevel() will lead to the same result.
Later in the program I want to destroy the created window again. When using tk.Tk() closing the blank window by mouse will create an error "application has been destroyed" when the destroy() method gets applicated to the new window.
import tkinter as tk
from tkinter import messagebox
def main():
root = tk.Tk()
root.title("Hauptmenü")
Menue = MainMenue(root)
Menue.pack()
root.mainloop()
class MainMenue(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button_rennen = tk.Button(self, text="New Window", width=20, command=self.call_bet)
self.button_rennen.pack()
def call_bet(self):
self.destroy()
root2 = tk.Tk()
Bet = BetFrame(root2)
Bet.pack()
class BetFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button = tk.Button(text="Wette platzieren",
command=self.question)
self.button.pack()
def question(self):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
self.destroy()
main()
I am creating a new class for every new window since the original program should return some variables.
I know that there are already many questions to this topic but for me none of these seemed quite to fit and helped to find the solution for my problem. I am grateful for any help!
Look at this:
import tkinter as tk
from tkinter import messagebox
class MainMenue(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button_rennen = tk.Button(self, text="New Window", width=20,
command=self.call_bet)
self.button_rennen.pack()
def call_bet(self):
# `self.master` is the window
Bet = BetFrame(self.master)
Bet.pack()
self.destroy()
class BetFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
# You need to tell the button that its master should be this BetFrame
# If you don't it will assume you mean the window so
# `self.destroy()` isn't going to destroy the button.
self.button = tk.Button(self, text="Wette platzieren", command=self.question)
self.button.pack()
def question(self):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
self.destroy()
def main():
root = tk.Tk()
Menue = MainMenue(root)
Menue.pack()
root.mainloop()
main()
Your code is great but it was 2 mistakes:
Instead of creating a new window is it better to reuse the old one.
When you create your button in your BetFrame class, you don't pass anything for the master argument (the first argument). That is why the button isn't destroyed when you destroy the BetFrame object using self.destroy()
I'm currently creating a login system which connects to a main application. The user uses the log in system to log in through a series of window, and after this is done, the log in window should close and the main program should open.
I've created a small example of my project. The issue I'm getting is that StringVar() doesn't seem to work in the second window.
Code for the first window:
# Python program to create a window linking to the second window
from tkinter import *
import tkinter as tk
import test2
class Window(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
frame = Frame(self)
frame.grid()
# creates a button which closes this window and opens the next
button2 = Button(frame, text='next frame', command=lambda: self.open_window())
button2.grid()
def open_window(self):
# runs the 'openWindow' function from the second file named 'test2'
test2.openWindow(app, test2)
if __name__ == '__main__':
app = Window()
app.mainloop()
Code for the second window:
# Second window named 'test2' which is displayed after the button in the first window is pressed
from tkinter import *
import tkinter as tk
def openWindow(app, window):
Application = window.Window2()
app.withdraw() # closes the first window
Application.mainloop() # opens the second window
class Window2(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
frame = Frame(self)
frame.grid()
self.var = StringVar() # creates the StringVar()
self.var.set("default")
# creates an entry which is linked to self.var
entry = Entry(frame, textvar=self.var, width=50, bg='lightgrey', justify=LEFT)
entry.grid(padx=16, ipady=20)
# this button should print the contents of the entry widget
button = Button(frame, text='print', command=lambda: self.print_var())
button.grid()
def print_var(self):
# prints value of self.var
print(self.var.get())
The problem I get is that when I press the button in the second window, it doesn't print the actual contents of the entry widget. I think this means that the StringVar() isn't working properly so doesn't link to the entry widget, but I'm not sure why this is happening.
i'm trying to make some sample GUI tkinter thats on one button try server status,and if it works,after click on button start it destroy gui and start some function that sends me file text. I've made gui,and server connection test button, but don't know how to destroy gui and start function.Thanks a lot :)
from tkinter import *
import requests, os
class form():
root = Tk()
wel = Label(root,text="Welcome")
serv = Entry(root,width=40)
def checkConn():
if(requests.get(serv.get()).status_code==200):
print("Succesfull")
def start(self):
root.destroy()
prov = Button(root,text="Proveri",width=35, command = checkConn)
zap = Button(root, text ="Zapocni",width=35,command =start)
wel.pack()
serv.pack()
prov.pack()
zap.pack()
root.mainloop()
form()
I would recommend using an __init__() function for setting up the GUI as well as class variables. I rewrote your code in the way I would write it but without the requests bit, just to show GUI function.
from tkinter import *
import requests
class form():
def __init__(self, master):
self.master = master
self.serv_text = StringVar() # StringVar to hold entry value
wel = Label(root, text="Welcome")
serv = Entry(root, width=40, textvariable=self.serv_text)
prov = Button(self.master, text="Proveri", width=35,
command=self.checkConn)
zap = Button(self.master, text ="Zapocni", width=35,
command=self.start)
wel.pack()
serv.pack()
prov.pack()
zap.pack()
def checkConn(self):
if(requests.get(self.serv_text.get()).status_code==200):
print("Succesfull")
else:
print('miss')
def start(self):
self.master.destroy()
root = Tk()
form(root)
root.mainloop()
You will find more examples and discussion in Best way to structure a tkinter application.
I have a Raspberry Pi with the Piface adaptor board. I have made a GUI which controls the LED's on the Piface board.
One button on the GUI opens a new window which, on the press of a button, starts and stops running a small piece of code to make the LED's run up and down continuously, like Knight Riders car, using a While loop in a thread.
In this new window I have added a EXIT button. I want to add a piece of code that will close the new window when I click the EXIT button, and then return to the main window.
I have looked up many examples but just can't quite see what I should put or where. I have tried the 'quit' but it closed the whole program.
Having looked at many examples I maybe creating my new window in not quite the right way so feel free to tell me if there are better ways.
So is there a better way of doing it? Any pointers would be appreciated.
Thanks in advance.
Heres a piece of the code....
def new_window(self):
print('New Window')
self.newWindow = tk.Toplevel(self.master)
self.app = App2(self.newWindow)
self.newWindow.grab_set() # I added this line to stop opening multiple new windows
class App2:
def __init__(self, master):
frame = Frame(master)
frame.pack()
Label(frame, text='Turn LED ON').grid(row=0, column=0)
Label(frame, text='Turn LED OFF').grid(row=0, column=1)
self.button0 = Button(frame, text='Knight Rider OFF', command=self.convert0)
self.button0.grid(row=2, column=0)
self.LED0 = Label(frame, image=logo2)
self.LED0.grid(row=2, column=1)
self.button9 = Button(frame, text='Exit', command=self.close_window)
self.button9.grid(row=3, column=0)
def convert0(self, tog=[0]):
tog[0] = not tog[0]
if tog[0]:
print('Knight Rider ON')
self.button0.config(text='Knight Rider ON')
t=threading.Thread(target=self.LED)
t.start()
self.signal = True #added to stop thread
self.LED0.config(image = logo)
else:
print('Knight Rider OFF')
self.button0.config(text='Knight Rider OFF')
self.signal = False #added to stop thread
self.LED0.config(image = logo2)
def LED(self):
while self.signal: #added to stop thread
a=0
while self.signal: #added to stop thread
pfio.digital_write(a,1) #turn on
sleep(0.05)
pfio.digital_write(a,0) #turn off
sleep(0.05)
a=a+1
if a==7:
break
while self.signal: #added to stop thread
pfio.digital_write(a,1) #turn on
sleep(0.05)
pfio.digital_write(a,0) #turn off
sleep(0.05)
a=a-1
if a==0:
break
def close_window(self):
print('Close Child window')
#self.newWindow.destroy() Not sure what to put here?
If you put the new_window into your App2 then you should be fine.
self.newWindow.destroy()
Destroys the window. This is the right call. The window is closed then and all widgets in the window also get destroyed.
quit() will stop the mainloop() In this case the the program ends at the last line and also destroys everything.
You definitely want to use destroy.
class App2:
newWindow = None
def close_window(self):
print('Close Child window')
if self.newWindow:
try: self.newWindow.destroy()
except (): pass # fill in the error here
self.newWindow = None
def new_window(self):
print('New Window')
self.close_window()
self.newWindow = tk.Toplevel(self.master)
self.app = App2(self.newWindow)
self.newWindow.grab_set()
#classmethod
def start_app(cls):
window = tk.Tk(self.master)
app = App2(window)
return app
You should not acces Tkinter from threads. Have a look at alternatives
I was confused by quit and destroy, too, when I was starting with Tkinter.
In Tk windows are destroyed using the destroy method. So if you have a dialog toplevel and you want to get rid of it you call its destroy() method. Or you can withdraw it in which case the object continues to exist but is no longer on-screen and to re-show it you deiconify() the toplevel frame. It's more common to destroy them though. Here is a simple example creating and destroying a child dialog:
import sys
from Tkinter import *
class App(Frame):
def __init__(self, parent = None):
Frame.__init__(self, parent)
self.grid()
self.button = Button(self, text = "Create Dialog", command=self.CreateDialog)
self.button.grid()
def CreateDialog(self):
dialog = Toplevel(self)
dialog.wm_title("Dialog window")
dialog.wm_transient(self)
dialog.wm_protocol("WM_DELETE_WINDOW", lambda: self.onDeleteChild(dialog))
button = Button(dialog, text="Close", command=lambda: self.onDeleteChild(dialog))
button.grid()
def onDeleteChild(self, w):
w.destroy()
def main():
app = App()
app.mainloop()
if __name__ == "__main__":
sys.exit(main())
You should also consider looking at using a timer in the code to drive the LED loop rather than a while loop. Take a look at this answer using Tk's after function to run code after an interval. If you re-schedule another after call in the handler, then you can arrange a function to be run at regular intervals and avoid blocking the event handling without requiring additional threads.
I have code for selecting backup that should be loaded. It opens Toplevel window and lets you select one of the backups to load. When this window opens, I would like to block input to original window, so the only way to get back to original window is by closing the new Toplevel window.
The part of code that I hoped would work:
from tkinter import *
class BackupsGui:
def __init__(self, parent):
top = Toplevel()
self.top = top
Some more code and __init__ ending with:
top.update_idletasks()
top.overrideredirect(True)
top.mainloop()
or:
top.transient(parent)
top.mainloop()
Niether code part appears to change Toplevel interaction in any way, nor does changing if top.mainloop() precedes top.transient() or top.update_idletasks().
What do I miss?
transient and overrideredirect have nothing to do with event handling. If you want to block all input except for the toplevel, you need to call grab_set on the toplevel window. This will cause all events to be sent to that window.
Run the following code, and notice that if you don't check the box, you can continue to create new windows and change the value of the checkbox. Once checked, the next window grabs all events, preventing you from interacting with the other windows.
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.do_grab = tk.BooleanVar()
cb = tk.Checkbutton(self, text="New window grabs all events",
variable=self.do_grab, onvalue=True, offvalue=False)
cb.pack()
new_button = tk.Button(self, text="New window", command=self.on_click)
new_button.pack()
def on_click(self):
self.top = tk.Toplevel(self)
button = tk.Button(self.top, text="dismiss", command=self.top.destroy)
do_grab = self.do_grab.get()
if do_grab:
label = tk.Label(self.top, wraplength=200,
text="This window grabs all events")
else:
label = tk.Label(self.top, wraplength = 200,
text="This window does NOT grab all events")
label.pack(fill="x")
button.pack()
if do_grab:
self.top.grab_set()
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()