Что такое сигналы и слоты в qt

Сигналы и слоты в Qt

Введение

В программировании графического интерфейса, когда мы меняем один виджет, мы часто хотим что бы другой виджет получил об этом уведомление. В общем случае, мы хотим что бы объекты любого типа могла общаться с другими. Например, если пользователь нажимает кнопку Закрыть, мы вероятно хотим что бы была вызвана функция окна close().
Другие библиотеки добиваются такого рода общения используя обратный вызов. Обратный вызов это указатель на функцию, таким образом, если мы хотим что бы функция уведомила нас о каких-нибудь событиях, мы передаем указатель на другую функцию (обратновызываемую) этой функции. Функция в таком случае делает обратный вызов когда необходимо. Обратный вызов имеет два основных недостатка. Во-первых, он не является типобезопасным. Мы никогда не можем быть уверены что функция делает обратный вызов с корректными аргументами. Во-вторых, обратный вызов жестко связан с вызывающей его функцией, так как эта функция должна точно знать какой обратный вызов надо делать.

Сигналы и слоты

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

Механизм сигналов и слотов типобезопасен. Сигнатура сигнала должна совпадать с сигнатурой слота-получателя. (Фактически слот может иметь более короткую сигнатуру чем сигнал который он получает, так как он может игнорировать дополнительные аргументы). Так как сигнатуры сравнимы, компилятор может помочь нам обнаружить несовпадение типов. Сигналы и слоты слабо связаны. Класс, который вырабатывает сигнал не знает и не заботится о том, какие слоты его получат. Механизм сигналов и слотов Qt гарантирует, что если мы подключим сигнал к слоту, слот будет вызван с параметрами сигнала в нужное время. Сигналы и слоты могут принимать любое число аргументов любого типа. Они полностью типобезопасны.
Все классы, наследуемые от QObject или его дочерних классов (например, QWidget) могут содержать сигналы и слоты. Сигналы вырабатываются объектами когда они изменяют свое состояние так, что это может заинтересовать другие объекты. При этом он на знает и не заботится о том что у его сигнала может не быть получателя.
Слоты могут быть использованы для получения сигналов, но они так же нормальные функции-члены. Так же как объект не знает ничего о получателях своих сигналов, слот ничего не знает о сигналах, которые к нему подключены. Это гарантирует что полностью независимые компоненты могут быть созданы с помощью Qt.
Мы можем подключать к одному слоту столько сигналов, сколько захотим, также один сигнал может быть подключен к стольким слотам, сколько необходимо. Так же возможно подключать сигнал к другому сигналу (это вызовет выработку второго сигнала немедленно после появления первого).
Сигналы и слоты вместе составляют мощный механизм создания компонентов.

Небольшой пример

Компилирование примера

Мета-объектный компилятор (meta-object compiler, moc) просматривает описание классов в файлах исходных кодов и генерирует код на C++, который инициализирует мета-объекты. Мета-объекты содержат имена все сигналов и слотов, так же как и указатели на эти функции.
Запуская программу moc для описания класса, содержащего сигналы и слоты, мы получаем файл исходных кодов, который должен быть скомпилирован и слинкован с другими объектными файлами приложения. При использовании qmake, правила для автоматического вызова moc будут добавлены в Makefile проекта.

Сигналы

Сигналы вырабатываются объектами когда они изменяют свое состояние так, что это может заинтересовать другие объекты. Только класс, который определяет сигнал или его потомки могут вырабатывать сигнал.
Когда сигнал вырабатывается, слот, к которому он подключен обычно выполняется немедленно, так же как и нормальный вызов процедуры. Когда это происходит, механизм сигналов и сигналов и слотов полностью независим от любого цикла событий графического интерфейса. Выполнение кода, следующего за выпуском сигнала произойдет сразу после выхода из всех слотов. Ситуация слегка отличается когда используются отложенные соединения (queued connections); в этом случае код после ключевого слова emit продолжает выполнение немедленно, а слоты будут выполнены позже.
Если несколько слотов подключены к одному сигналу, слоты будут выполнены один за другим в произвольном порядке после выработки сигнала.
Сигналы автоматически генерируются программой moc и не должны быть реализованы в исходном коде. Они могут не возвращать значение (т. е., используем тип void).
Замечание по поводу аргументов: опыт показывает, что сигналы и слоты легче повторно использовать при написании программ, если они не используют специальных типов. Например, если бы сигнал QScrollBar::valueChanged() использовал бы специальный тип вроде гипотетического QScrollBar::Range, он мог бы быть подключенным только к слотам, спроектированным специально для него.

Слоты

Слот вызывается когда вырабатывается сигнал, с которым он связан. Слот это обычная функция в C++ и может вызываться обычным способом; единственная его особенность, что с ним можно соединсять сигналы.
Так как слоты это нормальные функции-члены, они следуют обычным правилам C++ при прямом вызове. Тем не менее, как слоты, они могут быть вызваны любым компонентом, независимо от их уровней доступа, через соединение сигнал-слот. Это значит, что сигнал, выработаный объектом произвольного класса может вызвать защищенный (private) слот объекта несвязанного с ним класса.
Слоты так же можно объявлять виртуальными, что иногда бывает довольно удобно.
По сравнению с обратными вызовами, сигналы и слоты слегка медленнее из-за увеличенной гибкости, которую они обеспечивают, хотя разница для реальных приложений незаметна. В общем, выработка сигнала, который подключен к некоторым слотам, в среднем в 10 раз медленнее, чем вызов получателя напрямую, при вызове не виртуальной функции. Эти накладные расходы требуются для нахождения объекта, для безопасного перебора всех его соединений (т. е. проверка что последующий получатель не был уничтожен во время выпуска сигнала) и передачи любых параметров в общем виде. Хотя вызов десяти невиртуальных процедур может показаться дорогим, это менее затратно, чем, например, операция создания или удаления объекта. Пока мы создаем строку, вектор или список, что неявно требует создание объекта, затраты сигналов и слотов отвечают за очень маленькую долю в затратах среди всех вызовов процедур.
То же самое верно делаете ли вы системный вызов в слот или косвенно вызываете более десяти функций. На i586-500, мы можем вырабатывать около 2,000,000 сигналов в секунду, соединенных с одним слотом или 1,200,000 в секунду, при соединении в двумя слотами. Простота и гибкость механизма сигналов и слотов окупает дополнительные затраты, которые пользователь программы даже не заметит.
Следует заметить, что библиотеки, которые определяют переменные с именами signal или slot, могут вызывать предупреждения или ошибки компилятора при компиляции вместе с программой, написанной на Qt. Что бы решить данную проблему, необходимо убрать определение мешающегося символа препроцессора с помощью директивы #undef.

Метаобъектная информация

Реальный пример

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

Продвинутое использование сигналов и слотов

Источник

Оглавление

Введение

При программировании графического интерфейса пользователя мы часто хотим сообщать одним элементам об изменении других элементов управления. Более обобщенно можно сказать, что мы хотим обеспечить связь между объектами любых видов. Например, если пользователь нажимает кнопку Close мы, вероятно, хотим, чтобы была вызвана функция окна close().

Сигналы и слоты

Этот механизм типобезопасен: сигнатура сигнала должна соответствовать сигнатуре принимающего слота. (Фактически, слот может иметь более короткую сигнатуру, чем сигнал, который он получает, поскольку может игнорировать лишние аргументы.) Сигналы и слоты связаны не жёстко: Класс, испускающий сигналы, не знает и не интересуется, который из слотов получит сигнал. Механизм сигналов и слотов Qt гарантирует, что, если Вы соединили сигнал со слотом, слот будет вызываться с параметрами сигнала в нужный момент. Сигналы и слоты могут иметь любое количество аргументов любых типов. Они полностью типобезопасны.

Читайте также:  Что такое реестр операций по брокерскому счету

Все классы, наследуемые от QObject или одного из его подклассов (например, QWidget) могут содержать сигналы и слоты. Сигналы испускаются при изменении объектом своего состояния, если это изменение может быть интересно другим объектам. Все объекты делают это для связи с другими объектами. Их не заботит, получает ли кто-нибудь испускаемые ими сигналы. Это является истинной инкапсуляцией информации, и она гарантирует, что объекты могут использоваться как отдельные компоненты программного обеспечения.

Слоты могут получать сигнал, но они также являются обыкновенными функциями-членами. Также, как объект не знает, получает ли кто-нибудь сигналы, испускаемые им, слоты не знают, существуют ли сигналы, с ними связанные. Это гарантирует, что можно создать полностью независимые Qt компоненты.

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

Вместе сигналы и слоты представляют собой мощный механизм компонентного программирования.

Небольшой пример

Минимальная декларация класса C++ может выглядеть следующим образом:

Небольшой класс, основанный на QObject, может выглядеть так:

Версия класса, основанная на QObject, имеет то же самое внутреннее состояние и предоставляет открытые методы для доступа к нему, но в дополнение к этому она поддерживает компонентное программирование с использованием сигналов и слотов. Этот класс, испустив сигнал valueChanged(), может сообщать вовне, что его состояние изменилось, и имеет слот, которому другие объекты могут посылать сигналы.

Все классы, содержащие сигналы и слоты, должны упомянуть макрос Q_OBJECT в начале своей декларации. Также они должны происходить (прямо или косвенно) от QObject.

Слоты реализуются программистом. Вот возможная реализация слота Counter::setValue():

Строка, содержащая emit, заставляет объект испустить сигнал valueChanged() с новым значением, переданным в аргументе.

В следующем фрагменте кода мы создаем два объекта Counter и соединяем сигнал первого объекта valueChanged() со слотом второго объекта setValue(), используя QObject::connect():

Вызов a.setValue(12) заставляет a испускать сигнал valueChanged(12), который будет получен объектом b через слот setValue(), т.е. будет вызвана функция b.setValue(12). Затем b сам испустит сигнал valueChanged(), но так как никто не связан с объектом b через сигнал valueChanged(), он будет проигнорирован.

По умолчанию, для каждого сделанного соединения испускается сигнал; два сигнала испускаются для дублей соединений. Вы можете разорвать все эти соединения с помощью единственного вызова disconnect(). Если вы передадите Qt::UniqueConnection type, соединение будет установлено только если оно не является дублирующим. Если уже есть дубль (тот же сигнал к тому же слоту тех же самых объектов), соединение завершится неудачно и connect вернет false.

Сборка примера

Препроцессор C++ заменяет или удаляет ключевые слова signals, slots и emit для того, чтобы компилятору был предоставлен код, соответствующий стандарту C++.

С помощью moc обрабатываются определения классов, содержащих сигналы и слоты, и генерируются файлы реализации C++, которые будут скомпилированы и связаны с другими объектными файлами приложения. Если вы используете qmake, то в make-файл будет автоматически добавлен вызов moc.

Сигналы

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

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

Если несколько слотов связаны с одним сигналом, то при испускании сигнала они будут выполнены один за другим в том порядке, в котором они были соединены.

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

Замечание про аргументы: наш опыт показывает, что сигналы и слоты более пригодны для повторного применения, если они не используют специальных типов. Если сигнал QScrollBar::valueChanged() должен использовать специальный тип, такой как гипотетический QScrollBar::Range, он может быть соединен только со слотами, спроектированными специально для QScrollBar. Соединение разных входных виджетов вместе не возможно.

Слоты

Так как слоты являются обычными функциями-членами, они имеют права доступа, подобные обычным функциям-членам. Вместе с тем, в качестве слотов они могут быть вызваны любым компонентом независимо от уровня доступа посредством соединения сигнал-слот. Это означает, что сигнал, испускаемый объектом произвольного класса, может быть связан с закрытым (private) слотом и быть вызван в совершенно постороннем классе.

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

По сравнению с обратными вызовами, сигналы и слоты немного медленнее в связи с большей гибкостью, которую они предоставляют, но для реальных приложений это различие несущественно. Вообще, испускание сигнала, связанного с некоторыми слотами, примерно в десять раз медленнее, чем вызов невиртуальной функции приемника непосредственно. Так происходит, потому что требуется безопасно перебрать все соединения (т.е. проверить, чтобы последующие приемники не были разрушены во время испускания сигнала) и передать параметры положенным образом. Хотя «десять вызовов невиртуальных функций» кажется долгим, это меньше чем, например, операция new или delete. Если вы обрабатываете строку, вектор или список, то есть операции, которые требуют вызова new или delete, обработка сигналов и слотов становятся не самыми активными потребителями времени.

То же самое происходит, когда система вызывает слот или косвенно вызываются более десятка функций. На i586-500 вы можете генерировать около 2,000,000 сигналов, связанных с одним слотом, в секунду, или около 1,200,000 сигналов, связанных с двумя слотами, в секунду. Простой и гибкий механизм сигналов и слотов является хорошей оболочкой для внутренней реализации, которую пользователи даже не будут замечать.

Обратите внимание на то, что другие библиотеки, определяющие переменные с именем signals или slots, могут вызвать предупреждения и ошибки при компиляции с приложением, созданным на основе Qt. Решить эту проблему может директива препроцессора #undef.

Мета-объектная информация

Мета-объектный компилятор (moc) просматривает декларацию класса в файле C++ и генерирует код, инициализирующий мета-объект. Мета-объект содержит имена всех сигналов и слотов и указатели на их функции.

Мета-объект содержит дополнительную информацию, такую как имя класса объекта. Также вы можете проверить, является ли объект наследником определённого класса, например:

Информация мета-объекта также используется в qobject_cast (), который подобен QObject::inherits(), но менее подвержен ошибкам:

Для получения более подробной информации смотрите Система мета-объектов.

Реальный пример

Далее приведен простой пример виджета.

LcdNumber наследует QObject, который использует сигналы и слоты через QFrame и QWidget. Он немного похож на встроенный виджет QLCDNumber.

При расширении препроцессором, макрос Q_OBJECT декларирует несколько функций-членов, которые реализуются moc; если вы получили сообщения об ошибках компилятора, подобные «undefined reference to vtable for LcdNumber«, вы, вероятно, забыли запустить moc или включить вывод moc в команду link.

moc явно не требует этого, но если вы наследуете QWidget, почти наверняка захотите иметь аргумент parent в вашем конструкторе и передать его в конструктор базового класса.

Некоторые деструкторы и функции-члены здесь опущены; moc игнорирует их.

LcdNumber испускает сигнал, когда его просят показать неверное значение.

Читайте также:  Что такое ода и каковы ее жанровые признаки

Если вы не заботитесь о том, выходит ли значение за установленные пределы, или считаете, что оно не может за них выйти, можете игнорировать сигнал overflow(), т.е. не соединять с ним ни какой слот.

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

Слот обычно используется для получения информации об изменении состояния других виджетов. LcdNumber использует эту возможность, код, приведенный выше, показывает, как отобразить измененное значение. Так как display() является частью интерфейса между классом и остальной частью программы, то он является открытым (public) слотом.

В некоторых из примеров программ сигнал valueChanged() объекта QScrollBar соединяется со слотом display(), в результате LCD-номер непрерывно отображает значение полосы прокрутки.

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

Некоторые несущественные функции-члены в данном примере были опущены.

Сигналы и слоты с аргументами по умолчанию

Сигнатуры сигналов и слотов могут содержать аргументы, а аргументы могут иметь значения по умолчанию. Рассмотрим QObject::destroyed():

При удалении QObject, он отправит сигнал QObject::destroyed(). Мы хотим перехватить этот сигнал, где бы у нас не была повисшая ссылка, чтобы удалить QObject, таким образом мы сможем привести её в порядок. Соответствующая сигнатура слота может быть такой:

Чтобы соединить сигнал со слотом используем QObject::connect() и макросы SIGNAL() и SLOT(). Правило о том включать ли аргументы или нет в макросы SIGNAL() и SLOT(), если аргументы имеются значения по умолчанию, то сигнатура, переданная в макрос SIGNAL(), не должна иметь аргументов меньше, чем сигнатура, переданная в макрос SLOT().

Все это будет работать:

А это работать не будет:

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

Расширенное использование сигналов и слотов

Для случаев, когда вам может понадобиться информация об отправителе сигнала, Qt предоставляет функцию QObject::sender(), возвращающую указатель на объект, который отправил сигнал.

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

Допустим, вы имеете три кнопки, которые определяют какой файл будет открыт: «Tax File», «Accounts File» или «Report File».

Для того чтобы открыть правильный файл, используйте QSignalMapper::setMapping() чтобы отобразить все сигналы clicked() на объект QSignalMapper. Затем вы присоединяете сигнал файла QPushButton::clicked() к слоту QSignalMapper::map().

Далее, соединяем сигнал mapped() со слотом readFile(), где в зависимости от того какая кнопка нажата будут открываться разные файлы.

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

Использование Qt со сторонними сигналами и слотами

Возможно использование Qt со сторонним механизмом сигналов/слотов. Вы даже можете использовать оба механизма в одном проекте. Только добавьте в свой файл проекта qmake (.pro) следующую строку.

Это сообщит Qt не определять ключевые слова moc signals, slots и emit, поскольку эти имена будут использоваться сторонней библиотекой, например, Boost. Затем, для продолжения использования сигналов и слотов Qt вместе с флагом no_keywords, просто замените все использования ключевых слов moc Qt в ваших исходных кодах на соответствующие макросы Qt Q_SIGNALS (или Q_SIGNAL), Q_SLOTS (или Q_SLOT) и Q_EMIT.

Все остальные торговые марки являются собственностью их владельцев. Политика конфиденциальности

Лицензиаты, имеющие действительные коммерческие лицензии Qt, могут использовать этот документ в соответствии с соглашениями коммерческой лицензии Qt, поставляемой с программным обеспечением, либо, альтернативно, в соответствии с условиями, содержащимися в письменном соглашении между вами и Nokia.

Кроме того, этот документ может быть использован в соответствии с условиями GNU Free Documentation License version 1.3, опубликованной фондом Free Software Foundation.

Источник

Введение

При программировании графического интерфейса пользователя мы часто хотим сообщать одним элементам об изменении других элементов управления. Более обобщенно можно сказать, что мы хотим обеспечить связь между объектами любых видов. Например, если пользователь нажимает кнопку Close мы, вероятно, хотим, чтобы была вызвана функция окна close().

Сигналы и слоты

Этот механизм типобезопасен: сигнатура сигнала должна соответствовать сигнатуре принимающего слота. (Фактически, слот может иметь более короткую сигнатуру, чем сигнал, который он получает, поскольку может игнорировать лишние аргументы.) Сигналы и слоты связаны не жёстко: Класс, испускающий сигналы, не знает и не интересуется, который из слотов получит сигнал. Механизм сигналов и слотов Qt гарантирует, что, если Вы соединили сигнал со слотом, слот будет вызываться с параметрами сигнала в нужный момент. Сигналы и слоты могут иметь любое количество аргументов любых типов. Они полностью типобезопасны.

Все классы, наследуемые от QObject или одного из его подклассов (например, QWidget) могут содержать сигналы и слоты. Сигналы испускаются при изменении объектом своего состояния, если это изменение может быть интересно другим объектам. Все объекты делают это для связи с другими объектами. Их не заботит, получает ли кто-нибудь испускаемые ими сигналы. Это является истинной инкапсуляцией информации, и она гарантирует, что объекты могут использоваться как отдельные компоненты программного обеспечения.

Слоты могут получать сигнал, но они также являются обыкновенными функциями-членами. Также, как объект не знает, получает ли кто-нибудь сигналы, испускаемые им, слоты не знают, существуют ли сигналы, с ними связанные. Это гарантирует, что можно создать полностью независимые Qt компоненты.

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

Вместе сигналы и слоты представляют собой мощный механизм компонентного программирования.

Небольшой пример

Минимальная декларация класса C++ может выглядеть следующим образом:

Небольшой класс, основанный на QObject, может выглядеть так:

Версия класса, основанная на QObject, имеет то же самое внутреннее состояние и предоставляет открытые методы для доступа к нему, но в дополнение к этому она поддерживает компонентное программирование с использованием сигналов и слотов. Этот класс, испустив сигнал valueChanged(), может сообщать вовне, что его состояние изменилось, и имеет слот, которому другие объекты могут посылать сигналы.

Все классы, содержащие сигналы и слоты, должны упомянуть макрос Q_OBJECT в начале своей декларации. Также они должны происходить (прямо или косвенно) от QObject.

Слоты реализуются программистом. Вот возможная реализация слота Counter::setValue():

Строка, содержащая emit, заставляет объект испустить сигнал valueChanged() с новым значением, переданным в аргументе.

В следующем фрагменте кода мы создаем два объекта Counter и соединяем сигнал первого объекта valueChanged() со слотом второго объекта setValue(), используя QObject::connect():

Вызов a.setValue(12) заставляет a испускать сигнал valueChanged(12), который будет получен объектом b через слот setValue(), т.е. будет вызвана функция b.setValue(12). Затем b сам испустит сигнал valueChanged(), но так как никто не связан с объектом b через сигнал valueChanged(), он будет проигнорирован.

По умолчанию, для каждого сделанного соединения испускается сигнал; два сигнала испускаются для дублей соединений. Вы можете разорвать все эти соединения с помощью единственного вызова disconnect(). Если вы передадите Qt::UniqueConnection type, соединение будет установлено только если оно не является дублирующим. Если уже есть дубль (тот же сигнал к тому же слоту тех же самых объектов), соединение завершится неудачно и connect вернет false.

Сборка примера

Препроцессор C++ заменяет или удаляет ключевые слова signals, slots и emit для того, чтобы компилятору был предоставлен код, соответствующий стандарту C++.

С помощью moc обрабатываются определения классов, содержащих сигналы и слоты, и генерируются файлы реализации C++, которые будут скомпилированы и связаны с другими объектными файлами приложения. Если вы используете qmake, то в make-файл будет автоматически добавлен вызов moc.

Читайте также:  Что такое опция бпл по карте мтс деньги отключена это значит

Сигналы

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

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

Если несколько слотов связаны с одним сигналом, то при испускании сигнала они будут выполнены один за другим в том порядке, в котором они были соединены.

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

Замечание про аргументы: наш опыт показывает, что сигналы и слоты более пригодны для повторного применения, если они не используют специальных типов. Если сигнал QScrollBar::valueChanged() должен использовать специальный тип, такой как гипотетический QScrollBar::Range, он может быть соединен только со слотами, спроектированными специально для QScrollBar. Соединение разных входных виджетов вместе не возможно.

Слоты

Так как слоты являются обычными функциями-членами, они имеют права доступа, подобные обычным функциям-членам. Вместе с тем, в качестве слотов они могут быть вызваны любым компонентом независимо от уровня доступа посредством соединения сигнал-слот. Это означает, что сигнал, испускаемый объектом произвольного класса, может быть связан с закрытым (private) слотом и быть вызван в совершенно постороннем классе.

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

По сравнению с обратными вызовами, сигналы и слоты немного медленнее в связи с большей гибкостью, которую они предоставляют, но для реальных приложений это различие несущественно. Вообще, испускание сигнала, связанного с некоторыми слотами, примерно в десять раз медленнее, чем вызов невиртуальной функции приемника непосредственно. Так происходит, потому что требуется безопасно перебрать все соединения (т.е. проверить, чтобы последующие приемники не были разрушены во время испускания сигнала) и передать параметры положенным образом. Хотя «десять вызовов невиртуальных функций» кажется долгим, это меньше чем, например, операция new или delete. Если вы обрабатываете строку, вектор или список, то есть операции, которые требуют вызова new или delete, обработка сигналов и слотов становятся не самыми активными потребителями времени.

То же самое происходит, когда система вызывает слот или косвенно вызываются более десятка функций. На i586-500 вы можете генерировать около 2,000,000 сигналов, связанных с одним слотом, в секунду, или около 1,200,000 сигналов, связанных с двумя слотами, в секунду. Простой и гибкий механизм сигналов и слотов является хорошей оболочкой для внутренней реализации, которую пользователи даже не будут замечать.

Обратите внимание на то, что другие библиотеки, определяющие переменные с именем signals или slots, могут вызвать предупреждения и ошибки при компиляции с приложением, созданным на основе Qt. Решить эту проблему может директива препроцессора #undef.

Мета-объектная информация

Мета-объектный компилятор (moc) просматривает декларацию класса в файле C++ и генерирует код, инициализирующий мета-объект. Мета-объект содержит имена всех сигналов и слотов и указатели на их функции.

Мета-объект содержит дополнительную информацию, такую как имя класса объекта. Также вы можете проверить, является ли объект наследником определённого класса, например:

Информация мета-объекта также используется в qobject_cast (), который подобен QObject::inherits(), но менее подвержен ошибкам:

Для получения более подробной информации смотрите Система мета-объектов.

Реальный пример

Далее приведен простой пример виджета.

LcdNumber наследует QObject, который использует сигналы и слоты через QFrame и QWidget. Он немного похож на встроенный виджет QLCDNumber.

При расширении препроцессором, макрос Q_OBJECT декларирует несколько функций-членов, которые реализуются moc; если вы получили сообщения об ошибках компилятора, подобные «undefined reference to vtable for LcdNumber«, вы, вероятно, забыли запустить moc или включить вывод moc в команду link.

moc явно не требует этого, но если вы наследуете QWidget, почти наверняка захотите иметь аргумент parent в вашем конструкторе и передать его в конструктор базового класса.

Некоторые деструкторы и функции-члены здесь опущены; moc игнорирует их.

LcdNumber испускает сигнал, когда его просят показать неверное значение.

Если вы не заботитесь о том, выходит ли значение за установленные пределы, или считаете, что оно не может за них выйти, можете игнорировать сигнал overflow(), т.е. не соединять с ним ни какой слот.

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

Слот обычно используется для получения информации об изменении состояния других виджетов. LcdNumber использует эту возможность, код, приведенный выше, показывает, как отобразить измененное значение. Так как display() является частью интерфейса между классом и остальной частью программы, то он является открытым (public) слотом.

В некоторых из примеров программ сигнал valueChanged() объекта QScrollBar соединяется со слотом display(), в результате LCD-номер непрерывно отображает значение полосы прокрутки.

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

Некоторые несущественные функции-члены в данном примере были опущены.

Сигналы и слоты с аргументами по умолчанию

Сигнатуры сигналов и слотов могут содержать аргументы, а аргументы могут иметь значения по умолчанию. Рассмотрим QObject::destroyed():

При удалении QObject, он отправит сигнал QObject::destroyed(). Мы хотим перехватить этот сигнал, где бы у нас не была повисшая ссылка, чтобы удалить QObject, таким образом мы сможем привести её в порядок. Соответствующая сигнатура слота может быть такой:

Чтобы соединить сигнал со слотом используем QObject::connect() и макросы SIGNAL() и SLOT(). Правило о том включать ли аргументы или нет в макросы SIGNAL() и SLOT(), если аргументы имеются значения по умолчанию, то сигнатура, переданная в макрос SIGNAL(), не должна иметь аргументов меньше, чем сигнатура, переданная в макрос SLOT().

Все это будет работать:

А это работать не будет:

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

Расширенное использование сигналов и слотов

Для случаев, когда вам может понадобиться информация об отправителе сигнала, Qt предоставляет функцию QObject::sender(), возвращающую указатель на объект, который отправил сигнал.

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

Допустим, вы имеете три кнопки, которые определяют какой файл будет открыт: «Tax File», «Accounts File» или «Report File».

Для того чтобы открыть правильный файл, используйте QSignalMapper::setMapping() чтобы отобразить все сигналы clicked() на объект QSignalMapper. Затем вы присоединяете сигнал файла QPushButton::clicked() к слоту QSignalMapper::map().

Далее, соединяем сигнал mapped() со слотом readFile(), где в зависимости от того какая кнопка нажата будут открываться разные файлы.

Использование Qt со сторонними сигналами и слотами

Возможно использование Qt со сторонним механизмом сигналов/слотов. Вы даже можете использовать оба механизма в одном проекте. Только добавьте в свой файл проекта qmake (.pro) следующую строку.

Это сообщит Qt не определять ключевые слова moc signals, slots и emit, поскольку эти имена будут использоваться сторонней библиотекой, например, Boost. Затем, для продолжения использования сигналов и слотов Qt вместе с флагом no_keywords, просто замените все использования ключевых слов moc Qt в ваших исходных кодах на соответствующие макросы Qt Q_SIGNALS (или Q_SIGNAL), Q_SLOTS (или Q_SLOT) и Q_EMIT.

Источник

Информационный сайт