pyqtgraph ImageView多线程时冻结

时间:2019-07-12 18:56:56

标签: python pyqt5 python-multithreading qthread pyqtgraph

我有多个通过wifi无线连接的摄像机,我正在尝试将数据流传输到客户端,客户端在GUI上显示流。

我的问题是pyqtgraph ImageItems似乎在大约30秒后停止了重新绘制,或者单击了窗口之外的窗口,或者调整了其中一张图像的控件。之后,我可以通过调整窗口大小来获取要重绘的图像,但这有点乏味。

我以为pyqtgraph不是线程安全的,但是我什至不知道我是否在使用真正的线程,因为我必须使所有程序都通过Qt(QThread)运行才能使工作正常。

我发现一些论坛帖子显示了与此类似的问题。重定向到The first onehere解释说您可以使用名为NSAppSleepDisabled的东西,但这似乎只在OSX上并且我正在运行Windows 10。The second one解释了它们的全部内容。 GUI冻结,并且我没有这个问题,仅ImageItem冻结,GUI的其余部分均响应。

这些是我的进口货

import time
from multiprocessing import Queue
from threading import Lock
from queue import Empty

import pyqtgraph as pg
from PyQt5.QtCore import QThread

为了保持软件的可扩展性,我使用了工作线程来管理传入的图像数据,为了保持ImageView的线程安全,我添加了一个锁:

graph_lock = Lock()

class WindowUpdater(QThread):

    stop_q = Queue()

    def __init__(self, cam_q: Queue, img_control: pg.ImageView, **kwargs):
        super().__init__(**kwargs)
        self.q = cam_q
        self.c = img_control

    def run(self) -> None:
        while self.stop_q.empty():
            try:
                timg = self.q.get_nowait()
                graph_lock.acquire(True)
                self.c.setImage(timg.frame)
                self.c.invalidate()
                graph_lock.release()
            except Empty:
                time.sleep(0.1)

然后,主应用程序处理将这些线程链接到传入的数据

app = QApplication(sys.argv)
window = QMainWindow()

grid = QGridLayout()
grid.setSpacing(10)
widg = QWidget()
widg.setLayout(grid)
window.setCentralWidget(widg)
window.show()

window.setGeometry(300, 300, 500, 400)
window.show()

threads = []

for i, h in enumerate(hosts):
    img = pg.ImageView(window)
    grid.addWidget(img, i, 0)

    img_item = pg.ImageItem()
    img.addItem(img_item)

    threads.append(WindowUpdater(img_queue, img)

for t in threads:
    t.start()

sys.exit(app.exec_())

其中hosts是用于连接的主机名列表,而img_queue是特定于该主机的摄像机流的多处理队列。

有人知道为什么同时运行多个pyqtgraph ImageView或ImageItem实例会导致问题吗?

1 个答案:

答案 0 :(得分:2)

由于Qt禁止(1),因此您不应从其他线程更新GUI。因此,您必须使用信号来传输信息。

?data.table
class WindowUpdater(QThread):
    imageChanged = pyqtSignal(np.ndarray)
    stop_q = Queue()

    def __init__(self, cam_q: Queue, **kwargs):
        super().__init__(**kwargs)
        self.q = cam_q

    def run(self) -> None:
        while self.stop_q.empty():
            try:
                timg = self.q.get_nowait()
                graph_lock.acquire(True)
                self.imageChanged.emit(timg.frame)
                graph_lock.release()
            except Empty:
                time.sleep(0.1)

(1)GUI Thread and Worker Thread