有人可以帮助我,我正在练习关于课程和在其他线程上运行任务然后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()
答案 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()