PyQt: передача информации между окнами

Вопрос задавался раньше, и я хочу предложить свое решение и задаться вопросом, является ли это приемлемым шаблоном или есть лучшая альтернатива.

Работая с QGIS, я начал работать с PyQt как с графическим интерфейсом (ранее в основном с tkinter), и у меня была ситуация, когда я делаю выбор на холсте, который затем открывает другое окно для предоставления информации о функциях. На холсте я хочу отметить выбранную функцию, поэтому требуется информация из другого окна.

Я сделал минимальный рабочий пример в PyQt ниже (требуется импорт PyQt5 с Python 3.8), где у меня есть два окна, одно из которых называется Button, а другое Display, где Display показывает, сколько раз была нажата кнопка.

Чтобы установить связь, я определил QTimer в окне Display, которое опрашивает окно Button (в данном случае каждые 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()

person Bruno Vermeulen    schedule 25.09.2020    source источник


Ответы (1)


Нет, это не очень хорошее решение, по крайней мере, в этом сценарии. Кроме того, использование таймера для опроса редко является хорошим решением при работе с интерактивными элементами: это ненадежно, не позволяет немедленно реагировать между нажатой кнопкой и результатом и делает код излишне сложным.

Сигналы и слоты существуют именно для этой цели: общий интерфейс для связи между объектами; поэтому реализуйте собственный сигнал в исходном классе и подключите его к цели.

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, должно быть удалено.

Обратите внимание, что если вы используете другое окно для получения функций, которые должны изменить поведение основного интерфейса, вам следует вместо этого рассмотреть возможность использования QDialog и в конечном итоге решить, использовать ли его как модальное (обычно с помощью exec_()), что предотвращает взаимодействие с другие окна, пока диалоговое окно не будет закрыто, и (возможно) вернет необходимые значения.

person musicamante    schedule 25.09.2020
comment
Отличный ответ, прямо в точку, представил правильную концепцию и простой пример :) - person geckos; 25.09.2020
comment
Отлично, большое спасибо. У меня было ощущение, что будет лучшее решение. - person Bruno Vermeulen; 26.09.2020