Matplotlib交互式事件循环

时间:2017-08-13 20:23:27

标签: python matplotlib pyqt

Matplotlib如何为后端库(如Qt)设置事件循环,同时仍然允许通过python REPL进行交互?至少对于Qt来说,主事件循环必须在主线程中运行,但是REPL所在的位置是正确的,所以我很难看到两者如何共存。

我目前的尝试是在单独的Python中启动QApplication threading.Thread

def mainloop():
    app = QtWidgets.QApplication([])
    while True:
        app.processEvents()
        time.sleep(0.01)
t = threading.Thread(target=mainloop)
t.daemon = True
t.start()

哪种方式有效,但我收到此警告,有时会崩溃:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QApplication(0x7fc5cc001820), parent's thread is QThread(0x7fc5cc001a40), current thread is QThread(0x2702160)

更新1

此处尝试使用QThread

from PyQt5 import QtGui, QtCore, QtWidgets
import time

class Mainloop(QtCore.QObject):
    def __init__(self):
        super().__init__()
        self.app = QtWidgets.QApplication([])

    def run(self):
        while True:
            self.app.processEvents()
            time.sleep(1)

t = QtCore.QThread()
m = Mainloop()
m.moveToThread(t)
t.started.connect(m.run)
t.start()

# Essentially I want to be able to interactively build a GUI here
dialog = QtWidgets.QDialog()
dialog.show()

更新2

基本上,我想模拟以下交互式 python会话,即不将其作为脚本运行以呈现现成的GUI。保持图形窗口外观阻止python解释器的神奇之处是什么?

Python 2.7.13 (default, Jan 19 2017, 14:48:08) 
Type "copyright", "credits" or "license" for more information.

IPython 5.2.2 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import matplotlib.pyplot as plt

In [2]: plt.ion()

In [3]: fig = plt.figure()

In [4]: # The last command opens a figure window which remains responsive.
   ...:  I can resize it and the window redraws, yet I can still interact 
   ...: with the python interpreter and interactively add new content to t
   ...: he figure

In [5]: ax = fig.add_subplot(111)

In [6]: # The last command updated the figure window and I can still inter
   ...: act with the interpreter

In [7]: 

1 个答案:

答案 0 :(得分:2)

神奇不是由Matplotlib完成的,而是由IPython完成的,实际上是名为magic command

如果你用ipython --gui=qt启动IPython,或者输入%gui qt,Qt事件循环将被启动并集成到IPython中(--matplotlib选项也会这样做但是对于默认的后端)。之后,您只需在命令行上创建小部件,而无需启动事件循环。

~> ipython
Python 3.5.3 |Continuum Analytics, Inc.| (default, Mar  6 2017, 12:15:08)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: %gui qt

In [2]: from PyQt5 import QtWidgets

In [3]: win = QtWidgets.QPushButton("click me")

In [4]: win.show()

In [5]: win.raise_()

Integrating with GUI event loop上有一个简短的部分,解释了这是如何工作的。但为了清楚起见,您不必遵循这些说明,因为已经为Qt实现了事件循环集成。只需使用%gui qt magic-command。

<强>更新

所以你确实可以在没有IPython的情况下做到这一点。 PyQt使得同时拥有常规Python shell和运行事件循环成为可能,如PyQt参考指南中Using PyQt5 from the Python Shell部分所述。

差别在于您需要自己明确创建QApplication。例如类型:

~> python
Python 3.6.0 |Continuum Analytics, Inc.| (default, Dec 23 2016, 13:19:00)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from PyQt5.QtWidgets import QApplication, QWidget
>>> a = QApplication([])
>>> w = QWidget()
>>> w.show()
>>> w.raise_()

我想知道它如何与Matplotlib一起工作并在the source code of pyplot中查看了一下。在我看来,Matplotlib可以处理这两种情况。执行plt.ion()时,会调用install_repl_displayhook函数。它的文档字符串说:

  

安装一个repl显示挂钩,以便自动显示任何陈旧的图形   当控件返回到repl时重绘。这适用于IPython   终端和内核,以及香草蟒蛇壳。

因此,尽管IPython不是Matplotlib的依赖项,但Matplotlib知道IPython并且可以检测它是否在IPython shell或常规Python shell中。