Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

Разница в том, что когда вы "обрабатываете" событие, вы берете на себя ответственность "отвечать" поведением, которое полезно вне класса. Например, рассмотрим приложение, в котором есть кнопка с номером. Приложение должно позволить пользователю сфокусировать кнопку и изменить номер, нажимая клавиши "вверх" и "вниз" на клавиатуре. В противном случае кнопка должна функционировать как обычный 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 - это просто базовый строительный блок функциональности. Если бы это был сигнал, он выглядел бы как вещь, обращенная к пользователю, когда это не было задумано.
  • Поскольку реализация функции в базовом классе доступна, мы легко реализуем шаблон цепочки ответственности, обрабатывая наши особые случаи (клавиши вверх и вниз) и оставляя остальное базовому классу. Вы можете видеть, что это было бы почти невозможно, если бы keyPressEvent был сигналом.

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

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

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

Это явно предназначено для пользователя класса:

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

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

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

Ответ 3

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

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

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

Сигналы и слоты - это Qt-механизмы. В процессе компиляции с использованием moc (метаобъектный компилятор) они меняются на функции обратного вызова.

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

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

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

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

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

Ответ 5

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

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

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

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


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

Ответ 6

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

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

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

Ответ 7

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

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

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

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

Ответ 8

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

Ответ 9

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

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

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