如何让 Qt 异步运行以进行交互使用,如 Matplotlib 离子模式?

Posted

技术标签:

【中文标题】如何让 Qt 异步运行以进行交互使用,如 Matplotlib 离子模式?【英文标题】:How to have Qt run asynchroneously for interactive use like Matplotlib's ion mode? 【发布时间】:2020-03-07 10:08:35 【问题描述】:

我希望能够从 python 解释器启动一个 Qt 接口,并立即返回命令行,这样我就可以在能够使用该接口的同时继续使用 python。基本上我希望能够与解释器的 GUI 进行交互,就像使用 matplotlib 的 ion 交互模式、jupyter notebook 或 Matlab 一样。

我天真地尝试将应用程序的执行放在一个线程中:

from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QApplication, QGraphicsRectItem, QGraphicsScene, QGraphicsView, QMainWindow

class Rect(QGraphicsRectItem):
  def mousePressEvent(self, event):
    print("foo")

app = QApplication([])

class AppThread(QThread):
  def run(self):
    app.exec()
    print('bar')


window = QMainWindow()
window.setGeometry(100, 100, 400, 400)
view = QGraphicsView()
scene = QGraphicsScene()
rect = Rect(0, 0, 150, 150)
scene.addItem(rect)
view.setScene(scene)
window.setCentralWidget(view)
window.show()

thread = AppThread()
thread.start()

...但这不起作用,因为生成的 GUI 已冻结,因此无法使用。

matplotlib 的一个后端是Qt5Agg,我天真地认为这意味着我可以使用 PyQt5 或 PySide2 来实现类似的效果。

真的可以做到吗?

【问题讨论】:

【参考方案1】:

不需要使用线程或其他库的补充,你只需要执行命令但你不应该调用 QApplication 的 exec_() 方法,因为它使用 python 交互式控制台事件循环。

$ python
Python 3.8.2 (default, Feb 26 2020, 22:21:03) 
[GCC 9.2.1 20200130] on linux
Type "help", "copyright", "credits" or "license" for more information
>>> from PyQt5.QtWidgets import QApplication, QGraphicsRectItem, QGraphicsScene, QGraphicsView, QMainWindow
>>> class Rect(QGraphicsRectItem):
...   def mousePressEvent(self, event):
...     print("foo")
... 
>>> app = QApplication([])
>>> window = QMainWindow()
>>> window.setGeometry(100, 100, 400, 400)
>>> view = QGraphicsView()
>>> scene = QGraphicsScene()
>>> rect = Rect(0, 0, 150, 150)
>>> scene.addItem(rect)
>>> view.setScene(scene)
>>> window.setCentralWidget(view)
>>> window.show()

IPython

正如IPython docs 指出的那样,%gui backend 必须用于启用 GUI 事件循环。在 PyQt5/PySide2 的情况下,必须在开头使用%gui qt5

$ ipython
Python 3.8.2 (default, Feb 26 2020, 22:21:03) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.13.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: %gui qt5                                                                                                                                                                                

In [2]: from PyQt5.QtWidgets import QApplication, QGraphicsRectItem, QGraphicsScene, QGraphicsView, QMainWindow                                                                                 

In [3]: class Rect(QGraphicsRectItem): 
   ...:   def mousePressEvent(self, event): 
   ...:     print("foo") 
   ...:                                                                                                                                                                                         

In [4]: app = QApplication([])                                                                                                                                                                  

In [5]: window = QMainWindow()                                                                                                                                                                  

In [6]: window.setGeometry(100, 100, 400, 400)                                                                                                                                                  

In [7]: view = QGraphicsView()                                                                                                                                                                  

In [8]: scene = QGraphicsScene()                                                                                                                                                                

In [9]: rect = Rect(0, 0, 150, 150)                                                                                                                                                             

In [10]: scene.addItem(rect)                                                                                                                                                                    

In [11]: view.setScene(scene)                                                                                                                                                                   

In [12]: window.setCentralWidget(view)                                                                                                                                                          

In [13]: window.show()  

【讨论】:

不幸的是,这不起作用。我在 Windows 和 Linux 上进行了测试——在这两个平台上,GUI 仍然冻结。 @user209974 好奇怪,你是怎么打开交互式python控制台的?我使用 Linux,只需在控制台中键入 python 并按 ENTER,然后编写我在答案中显示的命令(不应使用app.exec() 我使用了 ipython -- 这会有所不同吗? 哦,是的——只是在一个普通的 python 解释器中尝试过,它可以工作 知道如何让它在 ipython 中工作吗?或者这是一个完全不同的话题?

以上是关于如何让 Qt 异步运行以进行交互使用,如 Matplotlib 离子模式?的主要内容,如果未能解决你的问题,请参考以下文章

请问如何让QT程序支持XP-WIN10系统

python爬虫之Splash使用初体验

技术分享 | 让ROS人机交互更加有趣--基于Qt+ROS开发

如何安装 Thrift 以使用 Qt-Windows

Python - Qt。如何为 Telnet 或 ssh 服务器交互制作终端窗口

Qt例子,线程间通信,如何在线程外部对线程进行控制,问题请看问题补充,多谢了先