开发者

How to run a function in the background of tkinter [duplicate]

This question already has an answer here: Tkinter locks Python when an icon is loaded and tk.mainloop is in a thread (1 answer) Closed 6 months ago.

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜