首先,我不得不说我不是Python的专家。我从示例中收集了大部分代码。
我做了一些研究,并且无法找到类似于我想要做的代码配置:从自定义窗口小部件类中启动一个线程。我知道tkinter有多个线程尝试与单个小部件通信时出现问题,但我不会在这里看到这种情况。
我尝试这样做的原因是每个小部件都可以启动/停止并在其自身上进行更新。通过这种方式,我可以在同一个根窗口中查看来自多个源的数据,并且可以随意添加/删除每个源。我知道我可以用不同的方式编码(所有在一个类中),但我想以这种方式尝试。我认为这会让事情更清洁。
我的思考过程如下:
我的代码示例显示了我要完成的基础知识。在完成的程序中,每个小部件都有一个文本框,用于定义从何处获取数据。
此测试代码在WindowsXP中使用Python3正常工作。每个小部件都可以添加和删除,并且可以独立于其他小部件进行自我更新。
当我在Fedora20中使用Python3运行相同的代码时,它会在按下启动按钮时崩溃。例外情况如下:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.3/threading.py", line 637, in _bootstrap_inner
self.run()
File "/usr/lib/python3.3/threading.py", line 594, in run
self._target(*self._args, **self._kwargs)
File "dummy.py", line 44, in updlabel
self.label.config(text=number)
File "/usr/lib/python3.3/tkinter/__init__.py", line 1263, in configure
return self._configure('configure', cnf, kw)
File "/usr/lib/python3.3/tkinter/__init__.py", line 1254, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: None
所以最终,我有两个问题:
代码示例:
from threading import Thread
import tkinter as tk
import numpy as np
import time
class Widget(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.running = False
self.abort = True
labelfont = ('times', 20, 'bold')
self.label = tk.Label(self, text="---", font=labelfont)
self.startb = tk.Button(self, text="START", command=lambda: self.sbpressed())
self.remove = tk.Button(self, text="-", command=lambda: self.rbpressed())
self.startb.grid(row=0, column=0)
self.label.grid(row=0, column=1)
self.remove.grid(row=0, column=2)
def rbpressed(self):
self.abort = True
while self.running:
self.update()
self.destroy()
def sbpressed(self):
if self.running:
self.abort = True
self.update()
else:
self.startb["text"] = "ABORT"
self.running = True
self.abort = False
self.update()
self.t = Thread(target=self.updlabel, args=())
self.t.start()
def updlabel(self):
while self.abort == False:
number = str(np.random.random_integers(100))
self.label.config(text=number)
time.sleep(1)
self.startb["text"] = "START"
self.running = False
self.abort = False
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.addb = tk.Button(self, text="+", command=lambda: self.addwidget())
self.addb.pack()
Widget(self).pack()
def addwidget(self):
Widget(self).pack()
root = tk.Tk()
app = Application(master=root)
app.mainloop()
答案 0 :(得分:2)
我认为你的基本设计存在缺陷。正如您所提到的,tkinter并未设计为在多个线程中工作。永远不应该在创建根窗口的线程之外的线程中调用tkinter函数。这几乎肯定是你问题的根源。 可能工作,或者可能不工作 - 这是非线程安全的本质。
另外,作为一般规则,你不应该打电话给update
- 它比你意识到的要多,而且通常是完全没必要的。
普遍接受的解决方案是您的线程需要将信息放入队列,并且您的主线程可以从队列中提取数据并对其进行操作。例如,您可以放置由窗口小部件和应显示的字符串组成的元组。然后,您的主程序可以轮询队列,关闭项目并使用新文本配置小部件。