Как создавать пользовательские события в цикле событий в PyQt

Я пытаюсь создавать пользовательские события в PyQt. Один виджет будет излучать, а другой прослушивать события, но два виджета не должны быть связаны.

В JavaScript я бы добился этого, выполнив

// Component 1
document.addEventListener('Hello', () => console.info('Got it'))

// Component 2
document.dispatchEvent(new Event("Hello"))

Редактировать: я знаю о сигналах и слотах, но знаю только, как использовать их между родителем и дочерним элементом. Как бы мне этот механизм (или другой механизм) между произвольными несвязанными виджетами?

Я знаю о сигналах и слотах, но знаю только, как использовать их между родителем и потомком. Как бы я использовал их между произвольными, несвязанными виджетами?

Andrei Cioara 06.04.2019 23:35

Чего вы на самом деле пытаетесь достичь, делая это? Какие конкретные проблемы вы пытаетесь решить? Отправка пользовательских событий возможна в qt, но такая функциональность требуется очень редко. Также трудно понять, как вы могли знать только, как использовать сигналы и слоты между родителем и потомком.

ekhumoro 07.04.2019 00:08

У меня есть виджет A, который содержит кнопку. Когда я нажимаю эту кнопку, я хочу, чтобы свет стал зеленым. Свет очень далеко от кнопки (иерархически). Я должен был бы пойти Кнопка -> Виджет A -> Родитель -> Родитель -> Дочерний -> Дочерний -> Дочерний -> Дочерний -> Свет.

Andrei Cioara 07.04.2019 00:15

Может быть, я ошибаюсь, пытаясь соотнести знания, которые у меня уже есть. В React я бы просто обновлял Redux Store из Button и подписывался на изменения состояния от света. Как я должен думать об этом в Qt?

Andrei Cioara 07.04.2019 00:16

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

ekhumoro 07.04.2019 01:44

Для меня иерархия классов имитирует иерархию виджетов. Итак, для моего примера выше мне пришлось бы перепрыгнуть через 9 классов, чтобы перейти от дочернего к родительскому. Я не уверен, что вы имеете в виду, когда упоминаете, что иерархия виджетов не имеет значения?

Andrei Cioara 07.04.2019 02:09

Если вам приходится «прыгать через 9 классов», чтобы получить доступ к виджетам, это довольно очевидный запах кода. Похоже, вы пытались создать сложную DOM-подобную структуру с множеством небольших вложенных классов. Но многие (большинство?) приложений pyqt написаны с использованием одного центрального класса вместе с несколькими дочерними классами, которые доступны как атрибуты экземпляра, поэтому реальной иерархии нет вообще. Это позволяет очень легко связать элементы графического интерфейса с помощью сигналов, слотов и/или фильтров событий. Как говорит дзен: плоский лучше, чем вложенный.

ekhumoro 07.04.2019 06:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
7
3 594
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В PyQt следующая инструкция:

document.addEventListener('Hello', () => console.info('Got it'))

эквивалентен

document.hello_signal.connect(lambda: print('Got it'))

Подобным образом:

document.dispatchEvent(new Event("Hello"))

эквивалентен

document.hello_signal.emit()

Но большая разница заключается в области действия объекта «документ», поскольку связь осуществляется между глобальным элементом. Но в PyQt этого элемента нет.

Один из способов эмулировать указанное вами поведение — создать глобальный объект:

глобальный объект.py

from PyQt5 import QtCore
import functools

@functools.lru_cache()
class GlobalObject(QtCore.QObject):
    def __init__(self):
        super().__init__()
        self._events = {}

    def addEventListener(self, name, func):
        if name not in self._events:
            self._events[name] = [func]
        else:
            self._events[name].append(func)

    def dispatchEvent(self, name):
        functions = self._events.get(name, [])
        for func in functions:
            QtCore.QTimer.singleShot(0, func)

main.py

from PyQt5 import QtCore, QtWidgets
from globalobject import GlobalObject


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        button = QtWidgets.QPushButton(text = "Press me", clicked=self.on_clicked)
        self.setCentralWidget(button)

    @QtCore.pyqtSlot()
    def on_clicked(self):
        GlobalObject().dispatchEvent("hello")


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        GlobalObject().addEventListener("hello", self.foo)
        self._label = QtWidgets.QLabel()
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self._label)

    @QtCore.pyqtSlot()
    def foo(self):
        self._label.setText("foo")


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w1 = MainWindow()
    w2 = Widget()
    w1.show()
    w2.show()
    sys.exit(app.exec_())

Сработало как по волшебству. Не могу проголосовать достаточно! Тоже четкое решение, все понял, спасибо!

Andrei Cioara 07.04.2019 19:20

@eyllanesc, как мы можем передать параметры функции, используя этот подход?

Voldemort 28.07.2020 13:29

Как насчет использования QApplication в качестве глобального объекта? Вы можете получить к нему доступ в любом месте, перейдя QApplication.instance()...

mike rodent 22.11.2020 19:40

Другие вопросы по теме