我的应用包含一些由按钮单击触发的长时间运行的任务。我想在单击按钮时将光标设置为'wait'
,并在任务完成后重置它。这似乎相当简单,但我无法让它工作。
一个示例(不是实际的应用程序(来说明我的问题:
import tkinter as tk
import tkinter.ttk as ttk
import time
class App(ttk.Frame):
def __init__(self, master):
super().__init__(master)
self.button = ttk.Button(master=self.master, text='Run task', command=self.onclick_button)
self.button.grid(padx=25, pady=25)
def set_cursor_busy(self):
self.config(cursor='wait')
def reset_cursor(self):
self.config(cursor='')
def onclick_button(self):
self.set_cursor_busy()
time.sleep(5) # Simulate a long running task.
self.reset_cursor()
root = tk.Tk()
app = App(master=root)
app.mainloop()
根据我从搜索互联网和阅读大量帖子中学到的知识,我知道在执行返回到主循环之前,光标不会更新。但到那时,任务已经完成,光标已经重置为正常,因此看不到光标更改。我看到的选项是,使用:
update_idletasks()
update()
after()
- 线程
- 您自己的主环
除了 5 个,我尝试实现所有选项,但我无法让其中任何一个工作。
让我的代码工作的最简单方法是什么?
编辑:这篇文章中提到的解决方案实际上是上面提到的选项2,但是在self.config(.)
行之后添加self.update()
或self.master.update()
并不能解决我的问题。添加self.master.update_idletasks()
也是如此(选项 1(。
有关我的设置的其他信息:Windows 10 企业版上的 Python 3.7.3
编辑2:因此,为了绝对清晰起见,以下方法均无效:
def set_cursor_busy(self):
self.master.config(cursor='wait')
self.master.update_idletasks()
# or: self.update_idletasks()
# or: self.update()
# or: self.master.update()
def reset_cursor(self):
self.master.config(cursor='')
self.master.update_idletasks()
# or: self.update_idletasks()
# or: self.update()
# or: self.master.update()
编辑3:也没有:
def set_cursor_busy(self):
self.master.config(cursor='watch') # 'watch' instead of 'wait'
self.master.update_idletasks()
编辑4:使用after()
延迟长时间运行的任务(如本文所示(也无济于事:
def set_cursor_busy(self):
self.master.config(cursor='watch')
def reset_cursor(self):
self.master.config(cursor='')
def onclick_button(self):
self.set_cursor_busy()
self.after(500, lambda: time.sleep(5))
# Simulate a long running task with time.sleep(5)
self.reset_cursor()
编辑5:虽然它不能解决问题,但在许多情况下,状态栏至少可以作为替代方案,或者甚至可以首选,因为可以添加定制的状态消息。与光标(在 Windows 上的 TKInter 中(相反,状态栏可以在长时间运行的任务期间更新。下面是一个适用于 Windows 的示例:
class App(ttk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.button = ttk.Button(master=self.master, text='Run task', command=self.onclick_button)
self.button.grid(padx=25, pady=25)
self.statusbar = ttk.Label(master=self.master, text='', background='white')
self.statusbar.grid(row=1000, padx=1, sticky=tk.EW)
# Use a high row number to force the statusbar to be the last item on the grid.
def onclick_button(self):
self.update_statusbar()
time.sleep(3) # A long-running task.
self.update_statusbar()
def update_statusbar(self):
if not self.statusbar['text']:
self.statusbar['text'] = 'Busy...'
else:
self.statusbar['text'] = ''
self.master.update()
问题:如何在长时间运行的任务期间更改
cursor=
参考
- TkCmd - 游标
- 尽管有 update_idletasks(( 操作后,光标才会更改
- 如何获取光标以显示忙碌状态
注意:它没有区别,使用
.update(...
、update_idletasks(...
或.after(500, ...
强制将时间片强制到.mainloop()
。
-
工作、使用
root.config(...
或self.master.config(...
root.config(cursor='watch') root.update_idletasks() ...
-
工作,使用
self.button.config(...
self.button.config(cursor='watch') self.update_idletasks()
-
不工作:使用
self.config(...
似乎,人类无法将cursor=
设置为Frame
对象,没有显示错误。self.config(cursor='watch') self.update_idletasks()
延迟变体,使用.after(...
,结果与上面相同:
def long_running_task(self):
time.sleep(5) # Simulate a long running task.
self.button.config(cursor='no')
def onclick_button(self):
self.button.config(cursor='watch')
self.after(500, self.long_running_task)
用Linux测试 - Python:3.5 - "Tcl版本":8.6 "Tk版本":8.6