Mat*_*aws 5 tkinter matplotlib python-multithreading python-3.x
我似乎tkinter通过使用一些多线程来破坏linux。据我所知,我设法在不是主 GUI 线程的线程上触发垃圾回收。这导致__del__在一个tk.StringVar实例上运行,该实例试图tcl从错误的线程调用堆栈,从而导致 linux 上的混乱。
下面的代码是我能想出的最小例子。请注意,我没有用 做任何实际工作matplotlib,但否则我无法触发问题。在__del__对方法Widget验证的Widget情况下被从其他线程删除。典型的输出是:
Running off thread on 140653207140096
Being deleted... <__main__.Widget object .!widget2> 140653210118576
Thread is 140653207140096
... (omitted stack from from `matplotlib`
File "/nfs/see-fs-02_users/matmdpd/anaconda3/lib/python3.6/site-packages/matplotlib/text.py", line 218, in __init__
elif is_string_like(fontproperties):
File "/nfs/see-fs-02_users/matmdpd/anaconda3/lib/python3.6/site-packages/matplotlib/cbook.py", line 693, in is_string_like
obj + ''
File "tk_threading.py", line 27, in __del__
traceback.print_stack()
...
Exception ignored in: <bound method Variable.__del__ of <tkinter.StringVar object at 0x7fec60a02ac8>>
Traceback (most recent call last):
File "/nfs/see-fs-02_users/matmdpd/anaconda3/lib/python3.6/tkinter/__init__.py", line 335, in __del__
if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
_tkinter.TclError: out of stack space (infinite loop?)
Run Code Online (Sandbox Code Playgroud)
通过修改tkinter库代码,我可以验证__del__从与Widget.__del__.
我这里的结论正确吗?我怎样才能阻止这种情况发生?
我真的,真的想matplotlib从单独的线程调用代码,因为我需要生成一些渲染缓慢的复杂图,因此使它们脱离线程,生成图像,然后在tk.Canvas小部件中显示图像看起来很优雅解决方案。
最小的例子:
import tkinter as tk
import traceback
import threading
import matplotlib
matplotlib.use('Agg')
import matplotlib.figure as figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
class Widget(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.var = tk.StringVar()
#tk.Entry(self, textvariable=self.var).grid()
self._thing = tk.Frame(self)
def task():
print("Running off thread on", threading.get_ident())
fig = figure.Figure(figsize=(5,5))
FigureCanvas(fig)
fig.add_subplot(1,1,1)
print("All done off thread...")
#import gc
#gc.collect()
threading.Thread(target=task).start()
def __del__(self):
print("Being deleted...", self.__repr__(), id(self))
print("Thread is", threading.get_ident())
traceback.print_stack()
root = tk.Tk()
frame = Widget(root)
frame.grid(row=1, column=0)
def click():
global frame
frame.destroy()
frame = Widget(root)
frame.grid(row=1, column=0)
tk.Button(root, text="Click me", command=click).grid(row=0, column=0)
root.mainloop()
Run Code Online (Sandbox Code Playgroud)
请注意,在示例中,我不需要tk.Entry小部件。 但是,如果我注释掉该行,self._thing = tk.Frame(self)则无法重现该问题!我不明白这个...
如果我取消注释 thengc行,那么问题也会消失(这符合我的结论......)
更新:这在 Windows 上的工作方式似乎相同。 tkinter在 Windows 上似乎更容忍在“错误”线程上被调用,所以我没有得到_tkinter.TclError异常。但是我可以看到在__del__非主线程上调用了析构函数。
Jus*_*ues -1
Tkinter 不是线程安全的。在线程中调用 Tkinter 对象可能会导致诸如“ Widget 上的del方法验证 Widget 实例正在从其他线程中删除”之类的情况。
您可以使用锁定和队列来正确完成它。
检查此示例: Tkinter:如何使用线程来防止主事件循环“冻结”
和这个示例(您可以找到许多其他示例): Mutli-threading python with Tkinter
希望这能让您朝着正确的方向前进。
| 归档时间: |
|
| 查看次数: |
1363 次 |
| 最近记录: |