Как сделать свой слот qt

Обновлено: 04.07.2024

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

Введение

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

Обратите внимание на то, что функция setValue() устанавливает значение и испускается только в том случае, если value != m_value. Это уберегает от зацикливаний при циклических соединениях (например, если бы b.valueChanged() был соединен с a.setValue()).

Сигнал испускается для каждого соединения, которое было создано; если сигнал соединен с двумя слотами, то он будет испущен дважды. Также Вы можете разорвать соединение с помощью QObject::disconnect().

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

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

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

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

Сигналы

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

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

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

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

Слоты

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

Так как слоты являются обычными функциями-членами лишь с небольшой особенностью, они имеют права доступа, подобные обычным функциям-членам. Права доступа слота определяют, кто может с ним быть соединен:

  • Секция public slots содержит слоты, с которыми может быть соединен любой сигнал. Это очень удобно для компонентного программирования: Вы создаете объекты, которые ничего не знают друг о друге, соединяете их сигналы с слоты так, чтобы информация правильно проходила между ними, и, подобно модели железной дороги, запускаете и оставляете ее работать.
  • Секция protected slots содержит слоты, с которыми могут быть соединены сигналы этого класса и его подклассов. Эта секция предназначена для сигналов, которые скорее являются частью реализации класса, чем его интерфейсов с внешним миром.
  • Секция private slots содержит слоты, с которыми могут быть соединены только сигналы этого класса. Эта секция предназначена для очень строгих классов, которые не доверяют связь с некоторыми слотами даже своим потомкам.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Теперь, когда добавлены все необходимые виджеты и создан макет формы, остается создать соединения сигналов и слотов. Это можно сделать вручную при помощи функции connect(), но в Qt Designer есть простой и эффективный способ — утилита соединения (Connection Tool). Для ее запуска либо нажмите кнопку на панели инструментов (на ней нарисована красная стрелка и зеленый прямоугольник), либо выберите пункт меню Tools => Connect Signals/Slots, либо просто нажмите F3. Чтобы создать соединение, щелкните на виджете, с которым должен взаимодействовать слот, перетащите появившуюся линию на форму и отпустите кнопку мыши.

Сначала займемся кнопкой Create!. Щелкните на значке Connect Signal/Slot, выберите соответствующий пункт из меню Tools или нажмите F3. Затем щелкните на кнопке Create! и перетащите линию за пределы формы. Когда вы отпустите кнопку мыши, на экране появится диалог, показанный на рис. 17.

Нам нужно создать слот (не забывайте, что слот — это функция), который будет выводить подпись при нажатии кнопки Create!. Сигнал, который будет выдан при нажатии кнопки, называется clicked() (для виджета QPushButton существует пять сигналов), и нам нужно создать для него слот и соединение.

Чтобы создать слот, щелкните на кнопке "Edit Slots". Появится окно создания слота (см. рис. 16). Щелкните на кнопке New Slot и в списке появится новый слот. Вместо new_slot() введите slotCreateSig() . В выпадающем списке Access должно стоять значение public. Когда вы нажмете ОК, вы вернетесь к диалогу View and Edit Connections, где в списке слотов увидите только что созданный.

Рис. 16: Создание слота

Рис. 17: Диалог просмотра и редактирования соединений

Чтобы создать соединение, нужно просто выбрать нужный сигнал (в нашем случае — clicked() ) и слот (созданный нами slotCreateSig() ). Когда вы выбрали сигнал и слот, вы увидите, что слева появится зеленая галочка. Теперь нажмите ОК.

Повторите ту же операцию для кнопки Cancel, используя сигнал clicked() и слот close() . На этом создание сигналов и слотов завершено.

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

Сигналы и слоты: отличительные особенности

Вам будет интересно: SQL Profiler: примеры настройки, как пользоваться

Вам будет интересно: Чем открыть FRW-файлы? Программы

Сигналы и слоты: отличительные особенности

Четыре фразы в кавычках в примере являются аргументами вызова функции в псевдокоде. Одним из типичных способов написания оператора connect выполняется выравниванием аргументов. Это имеет преимущество по сравнению с типичным механизмом, используемым в стандартном C или C ++, таким как обратные вызовы с указателями на функции, заключенными в std :: function.

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

Новый механизм связи

Qt предоставляет некоторые языковые дополнения через макросы, которые обрабатываются инструментом генератора кода Meta-object Compiler (MOC). Это приводит к более продвинутой поддержке самоанализа со специальными механизмами в классах в Qt. К ним относится подход сигналов и слотов объекта связи.

Вам будет интересно: Как установить и настроить Tor?

Новый механизм связи объекта

Эти специальные макросы выбираются инструментом генератора кода MOC, который генерирует соответствующие классы и функции, реализующие все механизмы, специфичные для Qt. Они должны быть выполнены пользователем приложения. Ниже приведена возможная форма слота Counter :: setValue ().

Возможная форма слота

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

Обновленные функциональные возможности

Начиная с Qt 5.0, существует новая перегрузка QObject::connect, которая поддерживает передачу данных через сигналы Qt в качестве второго и четвертого аргументов указателя на функцию, указывающую ту, которую нужно вызывать.

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

Обновленные функциональные возможности

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

Симметрия и безопасность

Симметрия и безопасность

Вам будет интересно: Как перевести аудио в текст быстро и качественно

В Qt есть служебные классы, которые QObject автоматически обрабатывают, например, QScopedPointer QObjectCleanupHandler. Если у пользователя есть какая-то часть приложения, использующая классы Qt, но нет тесно связанного с этим пользовательского интерфейса, он сможет найти способ использовать их в качестве членов класса, не основанного на QObject.

Комплексная библиотека

Комплексная библиотека

У Qt есть набор библиотек под названием QtCore, охватывающий, набор основных функций:

Кроме того, есть несколько других пакетов, большинство из которых являются зрелыми и многофункциональными в областях: обработки XML, мультимедийных возможностей, поддержки баз данных SQL, модульного тестирования, OpenGL, интеграцию WebKit, библиотек, инструментов интернационализации, манипуляции SVG, связи по шине D-Bus, элементов управления ActiveX и других.

Управление ресурсами

Два механизма управления ресурсами в Qt - это иерархия владения и неявного совместного использования. Иерархия владения состоит из дерева объектов, которое обрабатывает уничтожение потомков. Всякий раз, когда новый объект на основе QObject создается в куче, используя new, ему назначают родительский объект QObject, что в итоге приводит к созданию иерархического дерева объектов. Когда объект уничтожается в дереве, все его потомки также уничтожаются.

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

Создание собственных сигналов и слотов

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

• from PySide.QtCore import Signal;

Затем, когда условия для объекта, к которому подключаются, удовлетворяются, вызывают метод сигнала, и он излучается, вызывая любые Qt слоты, к которым он подключен: thing.tapped.emit().

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

Простой пример генерации

Простой пример генерации

В Punching Bag унаследован признак от QObject так, что может излучать сигналы, у него есть вызванный сигнал punched, который не несет никаких данных и punch метод, который делает только излучение punched сигнала Qt. Чтобы сделать Punching Bag полезным, нужно подключить его punched сигнал к слоту. Определяют простую функцию, которая печатает на консоли, создают экземпляр Punching Bag и подключают его punched сигнал к слоту.

Если поместите все это в скрипт и запустите его, он обеспечит реализацию реакции на перфорацию использующего кода.

Одна из самых интересных вещей, которые можно сделать при использовании Qt creator - создание сигналов, чтобы заставить их переносить данные. Например, можно заставить сигнал нести целое число, таким образом:

  • 1 updated = Signal(int);
  • 1 updated = Signal(str).

Тип данных может быть любым именем типа Python или строкой, идентифицирующей тип данных C ++.

Круг отправки PySide/PyQt

Circle унаследован от QObject так, что может излучать сигналы. Сигналы создаются с подписью слота, к которому они будут подключены. Один и тот же сигнал может излучаться в нескольких местах.

Круг отправки PySide/PyQt

Теперь определяют некоторые слоты, которые можно подключить к сигналам Круга. Для этого существуют сигналы, которые переносят данные. Чтобы слот принимал данные от сигнала, его определяют с той же сигнатурой, что и у сигнала.

Вам будет интересно: Переменные окружения Linux: описание, особенности

Код очень простой и интуитивно понятный. Далее создают экземпляр Circle, подключив сигналы к слотам, переместим и изменим его размер.

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

  • Circle was moved to (6, 5).
  • Circle was resized to radius 5.

Преимущества платформы

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

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

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

Система используют точки останова или qDebug, чтобы проверить, что сигнал и код слота определенно достигнуты:

  • оператор connect;
  • код, где подается сигнал;
  • код слота.

Секреты и уловки

Можно подключить сигнал непосредственно к сигналу другого объекта. Что применяют, например, для пересылки сигналов.

Секреты и уловки

Ниже приведены некоторые рекомендации по устранению неисправностей сигналов и слотов в библиотеке Qt C ++:

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

В чем же суть. В вашем коде любой объект (кнопка, класс, виджет, форма, таймер и т. д.) может испускать сигналы. Когда и почему испускать сигналы решаете вы, ну или иногда (для классов из библиотеки) эти сигналы уже определены. Посмотрим, какие же классы предопределены скажем для QTextEdit (поле для ввода текста). Для этого открываем справку (красным цветом), проверяем, чтобы у нас отображалась справка по указателю (оранжевым цветом) и ищем QTextEdit. Листаем открывшуюся справку до поля Signals (зеленым цветом).


Как вы можете видеть, у QTextEdit есть 7 собственных сигналов и по одному сигналу от QWidget и от QObject.
Например, сигнал void cursorPositionChanged() испускается каждый раз, когда внутри текстового поля меняется позиция курсора, а сигнал textChanged() испускается при изменении текста внутри текстового поля.
Что же делать с сигналами. Как и сигналы от девушек, эти сигналы надо ловить :)))
В нашем случае сигналы ловятся слотами. Для того, чтобы слот "поймал" сигнал, их надо подключить друг к другу. делается это функцией QObject::connect().

Ради интереса посмотрим, какие же слоты есть у нашего QTextEdit.


Рассмотрим первый слот void append (const QString & text). Мы можем подключить к этому слоту какой-то сигнал, который также будет передавать в качестве параметра строку QString, и тогда к тексту в QTextEdit будет присоединяться переданная нами строка.

Слот void clear() отчищает текст в QTextEdit.

Простой пример кода:


Логика простая. Мы создали объект text класса QTextEdit. В данном случае нам не надо писать QObject (знатоки догадаются, а новеньким вдаваться пока не стоит).
В данном случае формат функции connect следующий:

connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection )т.е. в функцию нам необходимо передать:
*sender - указатель на объект-отправитель (в нашем случае это text)
*signal - какой сигнал мы подключаем
*receiver - указатель на объект-получатель (в нашем случае это тот же самый text)
type - способ подключение (лично я им ещё не разу не пользовался )

т.е. мы подлючаем сигнал textChanged() к сигналу clear(), и если человек пытается ввести в наше текстовое поле какой-то текст, поле автоматически отчищается.
Проще говоря мы издеваемся над пользователем :))

Читайте также: