在其他类中更改tkinter标签?

时间:2017-07-30 18:41:33

标签: python multithreading tkinter

有人可以帮助我,我正在练习关于课程和在其他线程上运行任务然后tkinter。我想在另一个类中更改标签。无法让我的脚本运行。

我尝试了不同的东西,但是我在理解继承类和线程方面遇到了一些麻烦,所以这只是一个了解它的例子。

from tkinter import *
import tkinter as tk
from tkinter import ttk
import threading

#Gloabl for stopping the run task
running = True

#class 1 with window
class App():

     def __init__(self):
          #making the window
          self.root = tk.Tk()
          self.root.geometry("400x400+300+300")
          self.root.protocol("WM_DELETE_WINDOW", self.callback)
          self.widgets()
          self.root.mainloop()

     # stop task and close window
     def callback(self):
          global running
          running = False
          self.root.destroy()

     # all the widgets of the window
     def widgets(self):
          global labelvar
          #startbutton
          self.start_button = tk.Button(self.root, text="Start",     command=lambda:App2())
          self.start_button.pack()

          #stopbutton
          self.stop_button = tk.Button(self.root, text="Stop", command=lambda:self.stop())
          self.stop_button.pack()

          #Defining variable for text for label
          labelvar = "Press start to start running"
          self.label = tk.Label(self.root, text=labelvar)
          self.label.pack()

     #stop the task
     def stop(self):
          global running
          running = False


#class 2 with task in other thread
class App2(threading.Thread):

     def __init__(self):
          global running
          #check if task can be run
          running = True
          threading.Thread.__init__(self)
          self.start()

     def run(self):
               #starting random work
               for i in range(10000):
                    print(i)
                    labelvar = "running"
                    App.label.pack()
                    #checking if task can still be running else stop task
                    if running == False:
                         break
                         labelvar = "stopped"
                         App.label.pack()

#initiate main app
app = App()

1 个答案:

答案 0 :(得分:0)

正如我在评论中所说,tkinter本身并不支持多线程,但只要只有一个线程(通常是主线程)使用(或“就可以执行它。谈论“)它。

如果您想影响GUI显示的内容,其他线程必须以某种方式与GUI线程进行通信。这通常是通过queue.Queue完成的,但在这个相对简单的情况下,可以通过global变量来完成,前提是通过某种方式共享来控制对它的并发访问内存空间(即全局变量)是多线程与多任务处理的优势之一,但必须正确完成并完成。

分享此类资源的简便方法是使用专用于此目的的threading.Lock。 (有关详细信息,请参阅维基百科文章Lock (computer science)。) 所有对此共享资源的引用(running标志)都应该在“获取”Lock并在之后“释放”之后完成。幸运的是,使用Python with语句(如下所示)这样做是微不足道的。

多线程问题的另一个重要方面是如何处理两个线程之间交换的任何信息。在这种情况下,我选择将tkinter主题轮询作为运行标志,注意更改,并相应地更新任何受影响的小部件。这可以通过使用通用窗口小部件方法after()来完成,该方法告诉tkinter将未来的调用(在'mainloop'内)安排到用户提供的函数或方法,并将其传递给某些参数。为了使这种情况反复发生,被调用函数可以在完成之前调用after()来重新安排本身再次运行。

以下是执行这些操作的代码的修改版本。请注意App2从不调用tkinter或触及任何小部件,这就是它的工作原理。

import threading
from time import sleep
from tkinter import *
import tkinter as tk
from tkinter import ttk

DELAY = 100  # millisecs between status label updates

# global flag and a Lock to control concurrent access to it
run_flag_lock = threading.Lock()
running = False


# class 1 with window
class App():
    def __init__(self):
        global running
        self.root = tk.Tk()
        self.root.geometry("400x400+300+300")
        self.root.protocol("WM_DELETE_WINDOW", self.quit)
        self.create_widgets()
        with run_flag_lock:
            running = False
        self.root.after(DELAY, self.update_status, None)  # start status widget updating
        self.root.mainloop()

    # create all window widgets
    def create_widgets(self):
        self.start_button = tk.Button(self.root, text="Start", command=self.start)
        self.start_button.pack()

        self.stop_button = tk.Button(self.root, text="Stop", command=self.stop)
        self.stop_button.pack()

        self.status_label = tk.Label(self.root, text='')
        self.status_label.pack()

    def update_status(self, run_state):
        """ Update status label text and state of buttons to match running flag. """
        # no need to declare run_flag_lock global since it's not being assigned a value
        with run_flag_lock:
            if running != run_state:  # status change?
                if running:
                    status_text = 'Press Stop button to stop task'
                    run_state = True
                else:
                    status_text = 'Press Start button to start task'
                    run_state = False
                self.status_label.config(text=status_text)
                # also update status of buttons
                if run_state:
                    self.start_button.config(state=DISABLED)
                    self.stop_button.config(state=ACTIVE)
                else:
                    self.start_button.config(state=ACTIVE)
                    self.stop_button.config(state=DISABLED)

        # run again after a delay to repeat status check
        self.root.after(DELAY, self.update_status, run_state)

    # start the task
    def start(self):
        global running
        with run_flag_lock:
            if not running:
                app2 = App2()  # create task thread
                app2.start()
                running = True

    # stop the task
    def stop(self):
        global running
        with run_flag_lock:
            if running:
                running = False

    # teminate GUI and stop task if it's running
    def quit(self):
        global running
        with run_flag_lock:
            if running:
                running = False
        self.root.destroy()


# class 2 with task in another thread
class App2(threading.Thread):
    def __init__(self):
        super(App2, self).__init__()  # base class initialization
        self.daemon = True  # allow main thread to terminate even if this one is running

    def run(self):
        global running
        # random work
        for i in range(10000):
            print(i)
            # Normally you shouldn't use sleep() in a tkinter app, but since this is in
            # a separate thread, it's OK to do so.
            sleep(.25)  # slow printing down a little
            # stop running if running flag is set to false
            with run_flag_lock:
                if not running:
                    break  # stop early

        with run_flag_lock:
            running = False  # task finished

# create (and start) main GUI app
app = App()