PyQt:在 windows 之间传递信息

PyQt: passing information between windows

之前有人问过这个问题,我想提出我的解决方案,想知道这是一个可以接受的模式还是有更好的选择。

使用 QGIS 我开始使用 PyQt 作为 GUI(以前主要是 tkinter),我有一种情况,我在 canvas 上进行选择,然后打开另一个 window 给有关功能的信息。在canvas上我要标记选择的特征,所以需要其他window的信息。

我在下面的 PyQt 中做了一个最小的工作示例(需要使用 Python 3.8 导入 PyQt5),其中我有两个 windows,一个叫 Button,另一个叫 Display其中 Display 显示按钮被按下的次数。

为了进行通信,我在 Display window 中定义了一个 QTimer 来轮询 Button window (在这种情况下每 250 毫秒)以获得所需的信息。问题是在 PyQt 中有更好的方法吗?

import sys
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QApplication, QPushButton
from PyQt5.QtCore import QTimer

POLL_INTERVAL = 250  # in ms
WINSIZE = (200, 50)  # w, h
BUTTON_WIN_POS = (200, 200)  # w, h
DISPLAY_WIN_POS = (450, 200)  # w, h

class ButtonWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.button_pressed_counter = 0
        self.initUI()

    def initUI(self):
        vbox = QVBoxLayout()
        button = QPushButton('Press me')
        button.clicked.connect(self.press_button)
        vbox.addWidget(button)

        self.setLayout(vbox)
        self.move(*BUTTON_WIN_POS)
        self.setWindowTitle('Button ... ')
        self.resize(*WINSIZE)
        self.show()

    def press_button(self):
        self.button_pressed_counter += 1

    @property
    def button_count(self):
        return self.button_pressed_counter

class DisplayWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.button_window = ButtonWindow()

        self.button_count = 0
        self.button_counter = QTimer()
        self.button_counter.timeout.connect(self.get_button_count)

        self.initUI()

    def initUI(self):
        vbox = QVBoxLayout()
        self.text_lbl = QLabel()
        vbox.addWidget(self.text_lbl)
        self.text_lbl.setText(f'button count: {self.button_count}')

        self.setLayout(vbox)
        self.move(*DISPLAY_WIN_POS)
        self.setWindowTitle('Display ... ')
        self.resize(*WINSIZE)
        self.show()

        self.button_counter.start(POLL_INTERVAL)

    def get_button_count(self):
        new_button_count = self.button_window.button_count
        if self.button_count == new_button_count:
            pass

        else:
            self.button_count = new_button_count
            self.text_lbl.setText(f'button count: {self.button_count}')


def main():
    app = QApplication([])
    _ = DisplayWindow()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

不,这不是一个好的解决方案,至少在这种情况下是这样。此外,在处理交互式元素时,依靠计时器“轮询”很少是一个好的解决方案:它不可靠,不允许在单击的按钮和结果之间立即响应,并且使代码不必要地复杂。

信号和槽的存在正是为了这个目的:允许对象之间通信的通用接口;所以在“源”class 中实现自定义信号,并在“目标”中连接它。

class ButtonWindow(QWidget):
    pressCountChanged = pyqtSignal(int)
    # ...

    def press_button(self):
        self.button_pressed_counter += 1
        self.pressCountChanged.emit(self.button_pressed_counter)


class DisplayWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.button_window = ButtonWindow()
        self.button_window.pressCountChanged.connect(self.get_button_count)

        self.button_count = 0

        self.initUI()

显然,必须删除与 QTimer 相关的所有内容。

请注意,如果您使用另一个 window 来获得应该改变主界面行为的“功能”,您应该考虑改用 QDialog,并最终决定是否将其用作模态 (通常通过 exec_()),这会阻止与其他 windows 的交互,直到对话框关闭,并且(可能)returns 所需的值。