I am new to GUI programming a开发者_JAVA技巧nd I want to write a Python program with tkinter. All I want it to do is run a simple function in the background that can be influenced through the GUI.
The function counts from 0 to infinity until a button is pressed. At least that is what I want it to do. But I have no idea how I can run this function in the background, because the mainloop() of tkinter has control all the time. And if I start the function in an endless loop, the mainloop() cannot be executed and the GUI is dead.
I would like to return control back to the mainloop() after each cycle, but how can I get the control back from the mainloop() to the runapp-function without a user-triggered event?
Here is some sample code that kills the GUI:
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="START", command=self.runapp)
self.button.pack(side=LEFT)
self.hi_there = Button(frame, text="RESTART", command=self.restart)
self.hi_there.pack(side=LEFT)
self.runapp()
def restart(self):
print "Now we are restarting..."
def runapp(self):
counter = 0
while (1):
counter =+ 1
time.sleep(0.1)
Event based programming is conceptually simple. Just imagine that at the end of your program file is a simple infinite loop:
while <we have not been told to exit>:
<pull an event off of the queue>
<process the event>
So, all you need to do to run some small task continually is break it down into bite-sized pieces and place those pieces on the event queue. Each time through the loop the next iteration of your calculation will be performed automatically.
You can place objects on the event queue with the after method. So, create a method that increments the number, then reschedules itself to run a few milliseconds later. It would look something like:
def add_one(self):
self.counter += 1
self.after(1000, self.add_one)
The above will update the counter once a second. When your program initializes you call it once, and from then after it causes itself to be called again and again, etc.
This method only works if you can break your large problem (in your case "count forever") into small steps ("add one"). If you are doing something like a slow database query or huge computation this technique won't necessarily work.
You will find the answer in this other question Tkinter locks python when Icon loaded and tk.mainloop in a thread.
In a nutshell, you need to have two threads, one for tkinter and one for the background task.
Try to understand this example : clock updating in backgroud, and updating GUI ( no need for 2 threads ).
# use Tkinter to show a digital clock
# tested with Python24 vegaseat 10sep2006
from Tkinter import *
import time
root = Tk()
time1 = ''
clock = Label(root, font=('times', 20, 'bold'), bg='green')
clock.pack(fill=BOTH, expand=1)
def tick():
global time1
# get the current local time from the PC
time2 = time.strftime('%H:%M:%S')
# if time string has changed, update it
if time2 != time1:
time1 = time2
clock.config(text=time2)
# calls itself every 200 milliseconds
# to update the time display as needed
# could use >200 ms, but display gets jerky
clock.after(200, tick)
tick()
root.mainloop( )
credits: link to site
I don't have sufficient reputation to comment on Bryan Oakley's answer (which I found to be very effective in my program), so I'll add my experience here. I've found that depending on how long your background function takes to run, and how precise you want the time interval to be, it can be better to put self.after
call at the beginning of the recurring function. In Bryan's example, that would look like
def add_one(self):
self.after(1000, self.add_one)
self.counter += 1
Doing it this way ensures that the interval of time is respected exactly, negating any interval drift that might occur if your function takes a long time.
If you don't want to be away from those threads, I would like to give one suggestion for your GUI- Place the function for your GUI just before the root.mainloop() statement.
Example-
root = tk.Tk()
.
.
graphicsfunction() #function for triggering the graphics or any other background
#function
root.mainloop()
Please up vote if you like.
精彩评论