Qt 小部件不是线程安全的,因此通常不能(或不应该)在另一个线程中创建和修改任何小部件(这涉及到 matplotlib 和 pyqtgraph 的 Qt 后端)。
考虑到上述情况,一个可能的替代方案是,Qt 事件循环在主线程中存在,cmd.Cmd 事件循环在辅助线程中,然后通过线程安全的信号发送信息。
import cmd
import signal
import sys
import threading
from PyQt5 import QtCore, QtWidgets
import numpy as np
from matplotlib.backends.backend_qt5agg import (
FigureCanvas,
NavigationToolbar2QT as NavigationToolbar,
)
from matplotlib.figure import Figure
class QtSignaller(QtCore.QObject):
commandChanged = QtCore.pyqtSignal(str, object)
class QtShell(cmd.Cmd):
def __init__(self, qobject):
super().__init__()
self._qobject = qobject
@property
def qobject(self):
return self._qobject
def do_plot(self, arg):
self.qobject.commandChanged.emit("plot", parse(arg))
class QtManager(QtCore.QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._window = None
@QtCore.pyqtSlot(str, object)
def run_command(self, command, parameters):
x, y = parameters, np.sin(parameters)
if self._window is None:
self._window = FigureCanvas(Figure(figsize=(5, 3)))
self._ax = self._window.figure.add_subplot(111)
self._ax.plot(x, y)
else:
self._ax.clear()
self._ax.plot(x, y)
self._ax.figure.canvas.draw()
self._window.show()
def run_command(signaller):
shell = QtShell(signaller)
shell.cmdloop()
def parse(arg):
return tuple(map(float, arg.split()))
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
signaller = QtSignaller()
manager = QtManager()
signaller.commandChanged.connect(manager.run_command)
threading.Thread(target=run_command, args=(signaller,), daemon=True).start()
app.exec_()
if __name__ == "__main__":
main()
注意:虽然我的示例使用 matplotlib,但 pyqtgraph 应该使用相同的逻辑