События Qt и сигнал/слот


в мире Qt, в чем разница событий и сигналов/слотов?

одно заменяет другое? Являются ли события абстракцией сигнала / слотов?

9 76

9 ответов:

на документации Qt вероятно, объясняет это лучше всего:

в Qt события являются объектами, производными из абстрактного класса QEvent, что представляйте вещи, которые произошли либо в рамках приложения, либо в качестве результат внешней деятельности, что приложение должно знать об этом. События могут быть получены и обработаны любой экземпляр подкласса QObject, но они особенно актуальны для штучки. Этот документ описывает, как события доставлен и обработан в типичное применение.

таким образом, события и сигнал/слоты-это два параллельных механизма, выполняющих одни и те же вещи. Как правило, событие генерируется внешним объектом (например, клавиатурой или колесом мыши) и передается через цикл событий в QApplication. В общем случае, если вы не настроили код, вы не будете генерировать события. Вы можете фильтровать их через QObject::installEventFilter() или обрабатывать события в подклассе объекта путем переопределения соответствующая функция.

сигналы и слоты гораздо проще генерировать и получать, и вы можете подключить любые два подкласса QObject. Они обрабатываются через метакласс(посмотрите на свое имя moc_class.cpp file for more), но большая часть межклассовой связи, которую вы будете производить, вероятно, будет использовать сигналы и слоты. Сигналы могут быть доставлены немедленно или отложены через очередь (если вы используете потоки).

сигнал может быть сгенерирован.

в Qt сигналы и события являются реализациями шаблон Observer. Они используются в разных ситуациях, потому что у них разные сильные и слабые стороны.

прежде всего, давайте точно определим, что мы подразумеваем под "событием Qt": виртуальная функция в классе Qt, которую вы должны переопределить в своем базовом классе, если хотите обработать событие. Это связано с шаблон метода pattern.

обратите внимание, как я использовал слово "дескриптор". Действительно, вот основное различие между намерением сигналов и событий:

  • вы "дескриптор" события
  • вы "получить уведомление о" выбросы сигнала

разница в том, что когда вы "обрабатываете" событие, вы берете на себя ответственность "отвечать" поведением, которое полезно вне класса. Например, рассмотрим приложение, которое имеет кнопку с номером оно. Приложение должно позволить пользователю сфокусировать кнопку и изменить номер, нажав клавиши клавиатуры "вверх" и "вниз". В противном случае кнопка должна функционировать как обычная QPushButton (ее можно щелкнуть и т. д.). В Qt это делается путем создания собственного маленького многоразового "компонента" (подкласса QPushButton), который переопределяет QWidget::keyPressEvent. Псевдокод:

class NumericButton extends QPushButton
    private void addToNumber(int value):
        // ...

    reimplement base.keyPressEvent(QKeyEvent event):
        if(event.key == up)
            this.addToNumber(1)
        else if(event.key == down)
            this.addToNumber(-1)
        else
            base.keyPressEvent(event)

посмотреть? Этот код представляет новую абстракцию: виджет, который действует как кнопка, но с некоторыми дополнительными функциями. Мы добавили эту функциональность очень удобно:

  • так как мы переопределили виртуальный, наша реализация автоматически стала инкапсулирована в нашем классе. Если бы дизайнеры Qt сделали keyPressEvent сигналом, нам нужно было бы решить, следует ли наследовать QPushButton или просто внешне подключиться к сигналу. Но это было бы глупо, так как в Qt ты всегда ожидается наследование при написании виджета с пользовательским поведением (по уважительной причине - многоразовость / модульность). Таким образом, делая keyPressEvent событием, они передают свое намерение, что keyPressEvent-это всего лишь базовый строительный блок функциональности. Если бы это был сигнал, он выглядел бы как пользовательская вещь, когда она не предназначена.
  • так как базовый класс-реализация функции доступна, мы легко реализуем цепочка ответственности шаблон путем обработки наших специальных случаев (up&down ключи) и оставляя остальное в базовый класс. Вы можете видеть это было бы почти невозможно, если бы keyPressEvent был сигналом.

дизайн Qt хорошо продуман - они сделали нас упасть в яму успеха делая его легко сделать правильную вещь и трудно сделать неправильную вещь (сделав keyPressEvent событие).

С другой стороны, рассмотрим самое простое использование QPushButton - просто создать экземпляр и получать уведомления, когда он нажал:

button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())

это очевидно, что это должно быть сделано пользователей класс:

  • если бы нам приходилось подкласс QPushButton каждый раз, когда мы хотим, чтобы какая-то кнопка уведомляла нас о щелчке, это потребовало бы много подклассов без уважительной причины! виджет, который всегда показывает" Hello world " messagebox при нажатии полезен только в одном случае - так что это совершенно не многоразовые. Опять же, у нас нет выбора, кроме как поступить правильно-подключившись к нему внешне.
  • мы можем подключить несколько слотов к clicked() - или подключите несколько сигналов к sayHello(). С сигналами нет никакой суеты. С подклассами вам придется сесть и обдумать некоторые диаграммы классов, пока вы не решите соответствующий дизайн.

обратите внимание, что одно из мест QPushButton излучает clicked() в своем mousePressEvent() реализация. Это не значит clicked() и mousePressEvent() взаимозаменяемы-только то, что они связаны.

так сигналы и события имеют разные цели (но связаны тем, что оба позволяют "подписаться" на уведомление о чем-то происходящем).

Мне не нравятся ответы до сих пор. – Позвольте мне сосредоточиться на этой части вопроса:

являются ли события абстракцией сигнала / слотов?

короткий ответ: нет. длинный ответ поднимает "лучший" вопрос: Как связаны сигналы и события?

основной цикл простоя (например, Qt) обычно "застревает" в вызове select () операционной системы. Этот вызов делает приложение "спящим", в то время как он передает кучу сокетов или файлов или чего-то еще к ядру, запрашивающему: если что-то изменится на них, пусть вызов select() возвращается. – И ядро, как хозяин мира, знает, когда это произойдет.

результатом этого вызова select () может быть: новые данные на сокете подключаются к X11, пакет к порту UDP, который мы слушаем, и т. д. - этот материал не является ни сигналом Qt, ни событием Qt, и основной цикл Qt решает сам, превращает ли он свежие данные в один, другой или игнорирует оно.

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

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

событие может запускаться или полностью превращаться в сигнал (просто излучайте его и не вызывайте "super()"). Сигнал можно превратить в событие (вызвать обработчик событий).

что абстрагирует то, что зависит от случая: щелчок()-сигнал абстрагирует события мыши (кнопка снова опускается и поднимается, не слишком много перемещаясь). События клавиатуры-это абстракции с более низких уровней (например, 果 или é несколько ключевые штрихи в моей системе).

возможно, focusInEvent () является примером противоположного: он может использовать (и, следовательно, абстрактный) сигнал clicked (), но я не знаю, действительно ли это так.

события отправляются циклом событий. Каждая программа GUI нуждается в цикле событий, независимо от того, что вы пишете в Windows или Linux, используя Qt, Win32 или любую другую библиотеку GUI. Также каждый поток имеет свой собственный цикл обработки событий. В Qt" GUI Event Loop " (который является основным циклом всех приложений Qt) скрыт, но вы начинаете его вызывать:

QApplication a(argc, argv);
return a.exec();

сообщения ОС и других приложений, отправляемых в вашу программу, отправляются как события.

сигналы и слоты в Qt механизмов. В процесс компиляции с использованием moc (meta-object compiler), они изменяются на функции обратного вызова.

событие должно иметь один приемник, который должен направить его. Никто другой не должен получить это событие.

будут выполнены все слоты, подключенные к излучаемому сигналу.

вы не должны думать о сигналах как о событиях, потому что, как вы можете прочитать в документации Qt:

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

когда вы отправляете событие, оно должно подождать некоторое время, пока цикл событий не отправит все события, которые пришли ранее. Из-за этого выполнение кода после отправки события или сигнала отличается. Код после отправки события будет запущен немедленно. С сигналами и механизмами слотов это зависит от соединения тип. Обычно он будет выполняться после всех слотов. Используя Qt:: QueuedConnection, он будет выполнен немедленно, как и события. Проверьте все типы соединений в документации Qt.

есть статья, которая обсуждает обработку событий в некоторых деталях:http://www.packtpub.com/article/events-and-signals

здесь обсуждается разница между событиями и сигналами:

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

Это, кажется, распространенный способ говорить об этом, так как принятый ответ использует некоторые из них фразы.


обратите внимание, пожалуйста, смотрите полезные комментарии ниже на этот ответ от Kuba Ober, которые заставляют меня задаться вопросом, Может ли он быть немного упрощенным.

TL; DR: сигналы и слоты являются косвенными вызовами метода. События-это структуры данных. Так что это совершенно разные животные.

единственное время, когда они собираются вместе, - это когда вызовы слотов выполняются через границы потоков. Аргументы вызова слота упаковываются в структуру данных и отправляются как событие в очередь событий принимающего потока. В принимающем потоке,QObject::event метод распаковывает аргументы, выполняет вызов и, возможно, возвращает результат, если это была блокировка соединение.

если мы хотим обобщить до забвения, можно было бы думать о событиях как о способе вызова целевого объекта event метод. Это косвенный вызов метода, в некотором роде - но я не думаю, что это полезный способ думать об этом, даже если это истинное утверждение.

события (в общем смысле взаимодействия пользователя/сети) обычно обрабатываются в Qt с помощью сигналов/слотов, но сигналы/слоты Могут делать много других вещей.

QEvent и его подклассы в основном представляют собой небольшие стандартизированные пакеты данных для фреймворка для связи с вашим кодом. Если вы хотите каким-то образом обратить внимание на мышь, вам нужно только посмотреть на API QMouseEvent, и дизайнерам библиотеки не нужно изобретать колесо каждый раз, когда вам нужно понять из того, что мышь сделала в каком-то углу Qt API.

Это правда, что если вы ждете событий (опять же в общем случае) какого-то рода, ваш слот почти наверняка примет подкласс QEvent в качестве аргумента.

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

еще одно незначительное прагматическое соображение: излучение или прием сигналов требует наследования QObject, тогда как объект любого наследования может отправлять или отправлять событие (поскольку вы вызываете QCoreApplication.sendEvent () или postEvent ().) Обычно это не проблема, но: для использования сигналов PyQt странно требует, чтобы QObject был первым суперклассом, и вы, возможно, не захотите изменить порядок наследования, чтобы иметь возможность отправлять сигналы.)

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

connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});

заменит функцию удобства mouseMoveEvent (), найденную в QWidget (но не в QQuickItem больше) и будет обрабатывать mouseMove сигналы, что сцена менеджер будет излучать для элемента. Тот факт, что сигнал излучается от имени элемента какой-то внешней сущностью, не имеет значения и происходит довольно часто в мире компонентов Qt, хотя предполагается, что это не допускается. Но Qt-это конгломерат многих различных дизайнерских решений и в значительной степени брошен в камень из-за страха сломать старый код (что происходит достаточно часто в любом случае).