I am new at python. I am using python 2.7. I want to have messagebox to notify me on every 50 seconds.
when I write this:
import sys
import time
import threading
if sys.version_info < (3,0):
import Tkinter as tkinter
import tkMessageBox as mbox
else:
import tkinter
import tkinter.messagebox as mbox
window = tkinter.Tk()
window.wm_withdraw()
def loop():
threading.Timer(20.0, loop).start()
mbox.showinfo('my app',time.ctime())
loop()
But when I press OK application freeze. What I am doing wrong?
You forgot to call window.mainloop().
This method triggers the main loop of the widget, which processes the events, and allows interaction with the widget and its children.
In addition, you should use the after widget method rather than other timers.
This method allows you to schedule a call to a method.
You might wan to have a look at this post for a more complete explanation of the after method.
The following code implements the loop function with the after method, and runs the main loop by calling window.mainloop().
def loop(root):
mbox.showinfo('my app',time.ctime())
root.after(50000, lambda: loop(root))
window = tkinter.Tk()
window.wm_withdraw()
loop(window)
window.mainloop()
Note that the loop function takes a widget as parameters, that will be your window.
This is needed, because the after method needs to be called on a widget.
Additionally, the after method takes a callback as second parameter, and I found it easier to pass it as a lambda function.
The latter is tantamount to calling root.after(50000, f) where f has been defined by def f(): return loop(root).
Related
I have a windows application with a python API (supports python 3). You can provide a class that has a main function. On program startup, an instance of this class is created, and the main function is called periodically. I can configure the frequency, but nothing else. (I cannot change the way the API works)
Now, I'd like to use tkinter for some visualizations, charts, and so on. Here is an example of my current class structure:
import tkinter as tk
class Script:
def __init__(self):
self.root = tk.Tk()
self.count = 0
def main(self):
# do some stuff
print(str(self.count))
self.count += 1
# update gui
self.root.update()
# This is to simulate the software behavior.
import time
scriptInstance = Script()
for i in range(0, 20 * 10):
scriptInstance.main()
time.sleep(0.1)
The main issue I have is, that the root.update() method freezes the main-function as long as you interact with Tkinter. You can see that with the example above, that the number is not counting up as long as you are moving the window. Is there a way to prevent that?
I tried to call the root.update() form another thread, which did not work because Tkinter does not allow that.
Thank you for any help.
With tkinter, how can I use the after method to make a function run periodically?
For instance, I have a speak function that only prints something in the console:
def speak():
print("Hello, world!")
How can I use the after method to call the speak function every second?
Note: the following code is written and tested in Python 3.5. Minor changes might be needed, for instance, when calling super.
The documentation describes the Widget.after method as follows:
after(delay_ms, callback=None, *args)
Registers an alarm callback that is called after a given time.
Scheduling a function
The after method is primarily used to schedule a function call after a given delay. For instance, the following code schedules a call to a function after one second:
import tkinter as tk
def speak():
print("Hello world!")
root = tk.Tk()
root.after(1000, speak)
# Output
Hello world!
Making a function run periodically
In order to make a function run periodically, one can make it call itself at the end of its own body. However, after is a method from the Widget class, so a widget is needed. Therefore, the best choice is generally to put the scheduled function inside of a class extending Widget.
The following code prints "Hello world!" every other second in the console.
import tkinter as tk
class Foo(tk.Tk):
def periodically_speak(self):
print("Hello world!")
self.after(2000, self.periodically_speak)
foo = Foo()
foo.periodically_speak()
Using parameters
One might want to pass parameters to a method that runs periodically. For this purpose, the after method unpacks every parameter after the callback as the parameters to pass to the callback. For instance, root.after(1000, foo, a, b, c) will schedule a call to foo(a, b, c). The following example shows a use of this feature to determine the behaviour of the function.
import tkinter as tk
class Foo(tk.Tk):
def periodically_speak(self, text):
print(text)
self.after(2000, self.periodically_speak, text)
foo = Foo()
foo.periodically_speak("Good night world!")
Canceling a call
The after methods returns a string, that corresponds to the call's id. It can be passed to the after_cancel method, in order to cancel a call that was scheduled.
The following example will start printing "Hello world!" every second, but will stop when pressing the button.
import tkinter as tk
class Foo(tk.Tk):
def __init__(self):
super().__init__()
self.callId = None
self.button = tk.Button(self, text="Stop", command=self.stop)
self.button.pack()
def periodically_speak(self):
print("Hello world!")
self.callId = self.after(2000, self.periodically_speak)
def stop(self):
if self.callId is not None:
self.after_cancel(self.callId)
foo = Foo()
foo.periodically_speak()
Side notes
The following points should be kept in mind.
The after method does not guarantee that the callback will be called *exactly* after the given delay, but *at least* after it. As a consequence, after should not be used where precision is required.
It might be tempting to use time.sleep in order to schedule or periodically run a function. This must be avoided when working on a GUI, because `sleep` will pause the current thread, which most of the time is the main thread. For example, this could halt the refresh of the widgets, the program would stop responding.
The StringVar.get() method returns a blank value when the function c() is called. However, it works perfectly fine when I call only the new_db () function.
I really cannot understand the problem. Could somebody explain it to me?
#modules
import os
from Tkinter import *
chance=3
def cr():
print data.get()
#new_db
def new_db():
global data
m.destroy()
new=Tk()
data=StringVar()
Entry(new,font='BRITANIC 16',textvariable=data).grid(column=1,row=2)
Button(new,text='Create New Database',command=cr).place(x=175,y=75)
new.geometry('500x100+400+250')
new.mainloop()
def c():
global m
m=Tk()
Button(m,text='erferf',command=new_db).pack()
m.mainloop()
c()
Look at this answer When do I need to call mainloop in a Tkinter application?. It tells that the mainloop() must be called once and only once.
Also, the Tk object m should still exist when new_db() is executed on the click of the Button.
For what you try to accomplish, you should create the Tk() only once, and call mainloop() only once. Then you shoud place code to hide/show the appropriate widgets. Look at In Tkinter is there any way to make a widget not visible? to know how to show/hide widgets.
In my Program I want to use forget() on a button. Now if I try that, the program crashes. I know that it has something to do with threading but I couldn't find a soultion yet. Thanks in advance. Here is my examplecode:
import Tkinter as tk
import thread
window = tk.Tk()
def ok():
pass
def voice():
button1.forget()
print("If you see this, it works!")
thread.start_new_thread(voice,())
button1=tk.Button(command=ok, text="PRESS")
button1.pack()
window.mainloop()
You can't access tkinter objects from any thread but the thread that creates the object. In other words, you can't call button1.forget() from a thread and expect it to work reliably.
The generally accepted solution is to have your thread(s) write information to a thread-safe queue, and have your GUI thread poll that queue perioducally, pull an item off, and do whatever that item is requesting.
So I solved this problem simply by using the module mtTkinter , which you can find here :
http://tkinter.unpythonic.net/wiki/mtTkinter To use it you only have to write import mtTkinter as Tkinter at the beginning. After that you can use your Tkinter normally. This module changes nothing in Tkinter, it only makes it thread-friendly.
Tkinter is notorious for the fact that its lack of thread safety means that code you have written can sometimes work, and sometimes cause the entire program to hang with no errors produced, which is a pain.
Luckily, Tkinter does have its own measure for dealing with the problem, so to start a a thread with voice in it, just call voice. However, at theend of voice make sure you have made use of the window.after method to call it again later down the line. For example:
import Tkinter as tk
import thread
window = tk.Tk()
def ok():
pass
def voice():
button1.forget()
print("If you see this, it works!")
window.after(10, voice())
voice()
button1=tk.Button(command=ok, text="PRESS")
button1.pack()
window.mainloop(
I'm having some issues with a Tkinter-based GUI. Basically the GUI creates lots of threads and run them. When each thread has finished, I'd like it to update a label to inform the user of this specific thread completion.
I know Tkinter widgets are not thread-safe and that it is a bad practice to allow subthreads to update the view. So I'm trying to trigger an event on the main thread so it can update the view itself.
I'm running the simplified code sample below:
from Tkinter import *
from threading import *
def myClass(root):
def __init__(self, root):
self.root = root
# Other stuff populating the GUI
# Other methods creating new 'threading.Thread'
# objects which will call 'trigger_Event'
# Called by child threads
def trigger_Event(self):
self.root.event_generate("<<myEvent>>", when="tail")
# Called by main thread
def processEvent(self):
# Update GUI label here
if __name__ == '__main__':
root = Tk()
root.geometry("500x655+300+300")
app = myClass(root)
root.bind("<<myEvent>>", app.processEvent())
root.mainloop()
Unfortunately, this does not work: processEvent is never called. What am I missing ?
root.bind("<<myEvent>>", app.processEvent())
Here, you're binding myEvent to the return value of app.processEvent, because you're calling the function rather than just referring to it. Try removing the parentheses.
root.bind("<<myEvent>>", app.processEvent)