Перехватчики событий, сигналы и слоты


Содержание

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Перехватчики событий, сигналы и слоты

Этот «классический» слегка доработанный пример на сигналы и слоты в QT показывает, как их соединять, как разрывать и возобновлять соединение. Сначала немного теории.

В QT реализована концепция функций обратного вызова (callback functions) — в результате действий пользователя вызываются обычные методы класса типа void . Чтобы сопоставить код с кнопкой, необходимо передать в функцию указатель на кнопку. Элементы графического интерфейса пользователя оказываются тесно связаны с функциональными частями программы. Для обеспечения связей сообщения и методов обработки используются макросы — карты сообщений. Примеры интерфейсов, где так сделано — Windows API, MFC.

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

Механизм сигналов и слотов основан на следующих принципах:

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

Особенности работы механизма сигналов и слотов следующие:

  • сигналы и слоты не являются частью языка C++, поэтому требуется запуск дополнительного препроцессора перед компиляцией программы;
  • отправка сигналов происходит медленнее, чем обычный вызов функции, который производится при использовании механизма функций обратного вызова;
  • существует необходимость в наследовании класса QObject ;
  • в процессе компиляции не производится никаких проверок: имеется ли сигнал или слот в соответствующих классах или нет; совместимы ли сигнал и слот друг с другом и могут ли они быть соединены вместе. Об ошибке можно будет узнать лишь тогда, когда приложение будет запущено. Вся эта информация выводится на консоль, поэтому, для того чтобы увидеть ее в Windows, в проектном файле необходимо в секции CONFIG добавить опцию console .

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


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

Препроцессор обеспечит примерно такую реализацию сигнала:

Выслать сигнал можно при помощи ключевого слова emit . Ввиду того, что сигналы играют роль вызывающих методов, конструкция отправки сигнала emit doIt() приведет к обычному вызову метода doIt() . Сигналы могут отправляться из классов, которые их содержат. Например, в листинге выше сигнал doIt() может отсылаться только объектами класса MySignal , и никакими другими. Чтобы иметь возможность отослать сигнал программно из объекта этого класса, следует добавить метод sendSignal() , вызов которого заставит объект класса MySignal отправлять сигнал doIt() :

Сигналы также имеют возможность высылать информацию, передаваемую в параметре.

Обратите внимание, что в прототипе функции-сигнала не указываются имена параметров, а только типы.

Слоты (slots) — это методы, которые присоединяются к сигналам. По сути, они являются обычными методами. Основное их отличие состоит в возможности принимать сигналы. Как и обычные методы, они определяются в классе как public , private или protected . Соответственно, перед каждой группой слотов должно стоять одно из ключевых слов private slots: , protected slots: или public slots:

В слотах нельзя использовать параметры по умолчанию, например slotMethod (int n = 8) , или определять слоты как static . Классы библиотеки содержат целый ряд уже реализованных слотов. Но определение слотов для своих классов — это частая процедура.

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

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

Ему передаются пять следующих параметров:

  1. sender — указатель на объект, отправляющий сигнал;
  2. signal — это сигнал, с которым осуществляется соединение. Прототип (имя и аргументы) метода сигнала должен быть заключен в специальный макрос SIGNAL(method()) ;
  3. receiver — указатель на объект, который имеет слот для обработки сигнала;
  4. slot — слот, который вызывается при получении сигнала. Прототип слота должен быть заключен в специальном макросе SLOT(method()) ;
  5. type — управляет режимом обработки. Имеется три возможных значения:
    • Qt::DirectConnection — сигнал обрабатывается сразу вызовом соответствующего метода слота
    • Qt::QueuedConnection — сигнал преобразуется в событие и ставится в общую очередь для обработки
    • Qt::AutoConnection — это автоматический режим, который действует следующим образом: если отсылающий сигнал объект находится в одном потоке с принимающим его объектом, то устанавливается режим Qt::DirectConnection , в противном случае — режим Qt::QueuedConnection . Этот режим ( Qt::AutoConnection ) определен в методе connection() по умолчанию.

Как может быть осуществлено соединение объектов в программе:

Если вызов происходит из класса, унаследованного от QObject , тогда указание QObject:: можно опустить:

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

Иногда возникают ситуации, когда объект не обрабатывает сигнал, а просто передает его дальше. Для этого необязательно определять слот, который в ответ на получение сигнала (при помощи emit ) отсылает свой собственный. Можно просто соединить сигналы друг с другом. Отправляемый сигнал должен содержаться в определении класса:

Отправку сигналов заблокировать можно на некоторое время, вызвав метод blockSignals() с параметром true . Объект будет «молчать», пока блокировка не будет снята тем же методом blockSignals() с параметром false . При помощи метода signalsBlocked() можно узнать текущее состояние блокировки сигналов.

Параметры в слот передаются из сигнала, если количество, порядок и типы этих параметров в сигнале и слоте совпадают (или в слоте их может быть меньше).

В качестве законченного примера приведём проект Counter. Он умеет увеличивать счётчик на QLabel по нажатию кнопки «Add», а также разрывать и восстанавливать обработку сигналов по нажатию кнопки «Connect»/»Disconnect». Когда соединение отсутствует, счётчик не увеличивается. После 10 увеличений счётчика приложение в любом случае завершается.

Файл counter.h
Файл counter.cpp
Файл main.cpp

Соединять сигналы со слотами, разумеется, не обязательно программно. В режиме дизайна формы нажмите клавишу F4 для доступа к интерфейсу управления сигналами и слотами. Там же можно добавить в список новые, заданные программистом слоты.

Проиллюстрируем также «визуальное» соединение сигналов со слотами на примере обработки текстового поля QLineEdit и кнопки QPushButton , размещённых на форме виджета:

Нажмём в режиме дизайна формы клавишу F4 или обведённую на рисунке кнопку «Изменение сигналов/слотов», затем зажмём левую кнопку мыши на поверхности PushButton и протянем красную линию в окно виджета:

После отпускания кнопки мыши появилось окно «Настройка соединения», слева выберем сигнал clicked() , а справа нажмём кнопку Изменить, затем в новом окне Сигналы/Слоты кнопку «+» под списком слотов. К виджету добавился слот slot1() , после нажатия OK он появился в окне настройки соединения:

После нажатия OK связь создана и отображена на форме, вернуться к обычном виду можно нажатием клавиши F3 .

Если мы хотим просто автоматически создать пустую функцию-слот, достаточно нажать правой кнопкой мыши на PushButton и выбрать пункт меню «Перейти к слоту. «, а затем сигнал clicked() , для которого создаётся слот.

В добавленной таким способом в модуль функции можно писать код, например:

Приложение готово к работе, по нажатию кнопки выполняется этот код:

Существенно также то, что в пятой версии QT стало можно применять запись соединения, основанную на указателях.

Запись с макросами:

Запись на основе указателей:

Преимущество второго варианта заключается в том, что имеется возможность определить несоответствие сигнатур и неверное наименование слота или сигнала ещё на стадии компиляции проекта, а не в процессе тестирования приложения.


28.04.2015, 17:25; рейтинг: 29871

Создание собственных виджетов Qt. Сигналы, слоты и события.

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

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

В результате у нас получится создать свой собственный виджет и связать его со стандартными элементами управления.

Собственный виджет Qt

Класс QObject

В библиотеке Qt есть множество самых различных классов, многие из них являются потомками класса QObject, который позволяет:

  • использовать механизм сигналов и слотов Qt;
  • организовывать объекты в древовидную структуру;
  • дополняет объект информацией о типе (мета-объектной информацией);
  • перемещать объект в отдельный поток [1];
  • обрабатывать события (QEvent);
  • использовать встроенный таймер QObject;

Механизм сигналов и слотов Qt

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

В приведенном примере при генерацией сигнала valueChanged объекта speedSpinBox активируется слот setSpeed объекта runline.

Один сигнал может быть соединен сразу с несколькими слотами. Механизм сигналов и слотов позволяет общаться объектам, находящихся в различных потоках. Последним аргументом метода connect может быть тип соединения, позволяющий переключать асинхронную и синхронную обработку сигналов. По умолчанию используется соединение типа Qt::AutoConnection, означающее асинхронную обработку в случае если источник и обработчик сигнала находятся в одном потоке и синхронную (сигналы накапливаются в очереди потока), в противном случае. Для использования данного механизма в начале описания класса должен стоять макрос Q_OBJECT.

Из листинга видно, что наша бегущая строка имеет 2 слота, для изменения текста и скорости движения строки, но не генерирует ни одного сигнала.

Автоматическая сборка мусора в Qt

При использовании библиотеки Qt объекты часто формируют древовидную структуру с отношениями «родитель-потомок». Для любого объекта можно назначить родителя вызовом метода setParent, получить список дочерних объектов методом children или, допустим, выполнять поиск среди дочерних объектов. Обычно родительский объект передается в качестве параметра конструктора дочернему объекту.

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

В нашем примере создается главное окно (класс MainWidget ), которое включает в себя объекты классов QSpinBox , QLineEdit и RunLine . Очевидно, что вложенный QLineEdit не имеет смысла «оставлять живым» после того, как главное окно будет закрыто, т.е. время жизни поля ввода не больше чем время жизни главного окна, поэтому главное окно может быть родителем для вложенных в него объектов. Не всегда ситуация так очевидна, при установлении отношений «родитель-потомок» в Qt надо руководствоваться именно временем жизни объектов.

Диаграмма используемых классов Qt

Методы findChild и findChildren позволяют находить среди дочерних объектов, такие, которые приводятся к требуемому типу и имеют заданное имя (имя может быть задано методом setObjectName ). Информация о типе и имени объекта называется мета-объектной.

Мета-объектная информация используется также, например при вызове функции qobject_cast , проверяющий принадлежность объекта к определенному типу.

Компилятор c++ ничего не знает об именах объектов (классы в c++ устроены проще), сигналах, слотах, событиях и т.п. В связи с этим, всякий раз, когда вы отправляете свою Qt-программу на компиляцию, сначала она подается на вход мета-объектного компилятора (MOC), который инициирует мета-объекты и генерирует код, понятный обычному компилятору.

Обработка событий в Qt

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

  • если мы нажимаем на кнопку, то кнопка обрабатывает соответствующее событие ( QKeyEvent/QMouseEvent,QTouchEvent , смотря чем нажали), при этом кнопка генерирует сигнал;
  • если мы провели мышью над окном, то элементы под мышью получали об этом событие QHoverEvent , кроме того, обрабатывалось событие QPaintEvent , т.к. область окна под мышью перерисовывалась.

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

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

Например, мы щелкнули мышью над кнопкой спинбокса, при этом событие получает сначала MainWidget , но т.к. обработать он его не может, то передает его тому дочернему элементу, над которым был произведен щелчок (по спинбоксу), спинбокс передает событие кнопке, которая его обрабатывает и вызывает метод accept , генерируя сигнал clicked .


Пример отражает суть механизма обработки событий, однако, любое из промежуточных звеньев тоже могло обработать событие (при этом как выполнив для него accept , прервав обработку, так и передать событие обработанное дальше). Для реализации такого поведения может быть использован виртуальный метод event . Кроме того, объект может генерировать события методами sendEvent и postEvent .

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

Обработка событий таймера

В библиотеке Qt есть 3 вида таймера — встроенный в QObject, QTimer и QBasicTimer. Все эти таймеры по умолчанию запускаются в том потоке, в котором были созданы, т.е. они не могут сработать пока выполняется какой-либо другой код в текущем потоке, а значит, их точность зависит от загруженности и зернистости (granularity, трудоемкости функций) потока. Если вам нужна точность, таймер стоит перенести в отдельный поток.

Наиболее удобным из них является QTimer, именно его рекомендует использовать официальная документация — он с заданной периодичностью генерирует сигналы. Более подробное описание и пример использования QTimer можно найти в соседней статье [2].

QBasicTimer является низкоуровневым, периодическим (генерирует события до тех пор, пока не будет остановлен) таймером. Весь интерфейс таймера составляют 4 метода — isActive возвращает true если таймер работает, timerId возвращает идентификатор таймера (идентификатор нужен в том случае, если объект-обработчик принимает события от нескольких таймеров и должен их различать). Методы stop и start останавливают и запускают таймер соответственно. Метод start в качестве аргументов принимает период в миллисекундах, с которым должны генерироваться события и указатель на объект-обработчик событий, для которого должен быть перегружен метод timerEvent.

Интерфейс таймеров, встроенных в QObject очень поход на QBasicTimer. Они тоже являются периодическими и порождают события, а не сигналы. Запуск таймера осуществляется методом startTimer, который принимает периодичность и возвращает идентификатор таймера. Таймер работает и порождает события до тех пор, пока не будет уничтожен методом killTimer (принимающий идентификатор таймера в качестве аргумента).

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

Мы не используем объект QTimerEvent в методе timerEvent, т.к. у нас работает лишь один таймер (нам не требуется идентифицировать источник события). Скорость движения строки у нас задается методом setSpeed, однако мы могли избавить от этого метода при использовании внешнего QBasicTimer — достаточно было бы указать объект RunLine в качестве адресата событий.

Создание пользовательского интерфейса Qt

Класс QWidget является базовым для всех виджетов (элементов управления). Этот класс содержит множество полей и методов, например, методы изменения размера или перемещения объекта. Виджеты могут вкладываться друг в друга (визуально, а не с установкой отношения «родитель-потомок», рассмотренного выше), при этом виджет-контейнер может использовать менеджер размещения (QLayout).

В листинге конструктора MainWidget представленного выше уже использовали менеджер QGridLayout, позволяющий размещать вложенные виджеты по сетке, кроме него может использоваться QVBoxLayout или QHBoxLayout (для размещения виджетов в линию по вертикали или горизонтали соответственно). Менеджер размещения управляет размерами и положением вложенных виджетов при изменении размера виджета-контейнера. Кроме того, менеджеры размещения могут вкладываться друг в друга. Размещать виджеты удобно визуально, мышью с использованием QtDesigner [2], но в этой статье мы вызываем addWidget явно. Во многих случаях явное использование addWidget оказывается единственным возможным, например если бы мы разрабатывали игру «Сапер» и размер игрового поля был бы заранее не известен.

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

На приведенных выше листингах вы могли заметить, что класс бегущей строки наследует QLabel и вызывает его метод setText. Класс QLabel предназначен для вывода текста или изображений, является наследником класса QFrame. QLabel позволяет использовать HTML теги для оформления содержимого, однако, в текущей статье используется лишь метод setText() для задания выводимого текста.

Класс QFrame расширяет возможности QWidget в плане отображения рамки вокруг виджета и является базовым для элементов управления, нуждающихся в особой рамке. Наследниками класса QFrame являются, например, виджет панели инструментов (QToolBox), виджет видовой прокрутки (QAbstractScrollArea) или виджет вывода текста/изображений (QLabel).

Рекомендуемая литература:

Что лучше слоты или события?

Так и не понил, зачем придумали эти СЛОТЫ. Во всех остальных библиотеках используется понятие СОБЫТИЕ — всё чётко и понятно. А слоты вызывают пока что одно только недопонимание.

Всё-таки, что лучше, слоты или события?

10.01.2013, 14:08

QSslSocket vs QTcpSocket или что лучше использовать?
Добрый день. Такой вопрос возник: Для своего приложения я использую QTcpSocket. А тут, чисто.

Что лучше для конкретного случая QWidget или QMainWindow?
Всем привет. Посоветуйте, что лучше выбрать для создания программы с одним главным окном и парой.

Что лучше для рисования линий и кругов — QPainter или QGraphicsScene?
Вот реально, что удобней и производительней? Слышал, что QGraphicsScene может быстро рисовать.

Как вызываются слоты? Синхронно или асинхронно?
Здравствуйте, товарищи! Возник такой вопрос: Например, у меня в файле есть 2 слота. Они будут.

Что лучше брать? sony xba c10 или ex450 или xiaomi mi hybrid
что лучше брать? sony xba c10 или ex450 или xiaomi mi hybrid понимаю что вопрос смешно звучит.

10.01.2013, 14:27 2

А я вот поняла, что это тоже самое что и событие, просто в QT решили это так назвать)
Хотя, не слот это не событие, а как лично я поняла, просто функция.
Т.е. например есть кнопка для копирования, и функция копирования(это и есть слот).
Т.е. ты нажимаешь на кнопку происходит событие(а в Qt сигнал clicked()), который вызывает функцию copy.
Как то так)

Сложнее с понятие триггер. я вот до сих пор не поняла, что это такое)
Знаю, что такое триггер в электронике. а вот в QT ума не приложу)

10.01.2013, 14:56 3

Это в каких же?
Как видишь сигнал-слот полезный и распространенный паттерн.

10.01.2013, 16:05 [ТС] 4

Дельфи/С++ билдер, Си шарп

Кстати, в тойже википедии http://ru.wikipedia.org/wiki/%D1%E8%E3%ED%E0%EB%FB_%E8_%F1%EB%EE%F2%FB
читаю «В языке программирования C# есть похожая конструкция с другой терминологией и синтаксисом: события играют роль сигналов, а делегаты — роль слотов.»


Получается, что это тоже самое, только другая терминология? Или что-то особенное?

10.01.2013, 16:05
10.01.2013, 16:07 5

Нелепый ответ на нелепый вопрос:
Технология слотов и сигналов позволяет повысить повторное использование классов. Это достигается за счет того, что класс отправляющий сигналы ничего не знает о классе, который принимает (или повторно отправляет) сигналы. При чем зачастую обратное (принимающий ничего не знает об отправляющем) тоже верно. В итоге имеется возможность получить практически бесконечное количество вариантов связок, всего лишь написав нужные connect’ы.

И, кстати, события (events) в Qt тоже есть, но это уже совсем другая история.

Делегаты, сигналы и слоты.

Как известно, в C++ делегатов нет. А жаль.
Впринципе, в интернете много делегатов. И слотов много. Но, я зачем-то решил написать свои.

Благодаря механикам: официальной — variadic templates и неофициальной — horrible_cast реализация делегатов оказалось компактной, хотя, у нее, возможно и будут проблемы с переносимостью (не зря же у нормальных людей в реализациях трёхэтажная шаблонная магия и двухэтажная препроцессорная).

Платформа на которой всё запустилось — avr2560 в виде Ардуино. К статье прилагаю сию работу в виде Ардуино библиотеки.

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

Механика сохранения информации делегата у нас следующая следующая.
1. Делегат инициализируется парой указатель на объект и указатель на метод класса.
2. Указатель на объект приводится к указателю на тип Abstract.
3. Указатель на метод класса приводится к указателю на метод класса Abstract (спасибо horrible_cast)
4. Полученные указатели сохраняются в отведённых полях.

В этом процессе мы теряем информацю о типе объекта, но оказывается, что для корректного вызова метода тип знать совсем не обязательно.

Механика вызова делегата и того проще. Мы просто вызываем с передаваемыми оператору вызова параметрами метод, якобы, Abstract класса по сохранённому указателю на метод и сохранённому указателю на объект. Удивительно, но это работает.

Собственно, вот что из себя представляет основа делегата:

Вот типичный конструктор:

Всё… Если вы откроете файл delegate.h в выложенной мной библиотеке, вы увидите, что реализация немного сложнее из-за некоторых дополнительных механизмов инициализации и из-за добавления возможности вызова обычных, не являющихся членами класса функций. У них точно такая же сигнатура, так что проблем с этим не возникает.

Первый параметр шаблона — возвращаемое значение. Дальше типы параметров в любом количестве.
Макрос method введён специально для передачи значений в конструктор через знак присваивания.
(операторы присваивания неопределены, а потому присваивание вызывает конструктор).

Пожалуй, здесь всё.

На основе данной реализации делегатов и механики monolist строится sigslot библиотека. Класс sigslot — класс сигнала. В качестве слота выступает любая функция или метод с такой же сигнатурой.
Вызов sigslot через operator() приводит к последовательному выполнению всех находящихся в списке делегатов (с переданными параметрами, разумеется).

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

Библиотека перегружает для класса sigslot операторы

+= — добавление метода или функции.
!= — приоритетное добавление метода.
-= — удаление метода.

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

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

Если вы до сюда дочитали, спасибо.

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

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

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

  1. A-аминокислоты. Петиды
  2. Аминокислоты
  3. Аминокислоты с положительно заряженными полярными R-группами
  4. БЕЛКИ, ПЕПТИДЫ И АМИНОКИСЛОТЫ МЯСА
  5. Величина рН может служить критерием силы кислоты или основания.
  6. Вопрос 1. Физико-химические свойства фосгена и дифосгена, хлора, хлорпикрина, азотной кислоты и ее окислов, треххлористого фосфора, пятифтористой серы, аммиака, гидразина и др.
  7. Желчные кислоты образуются в печени из эфиров холестерола
  8. Жирные кислоты — это алифатические карбоновые кислоты.
  9. Импульсные сигналы. Параметры, спектры.
  10. Кислоты
  11. Кислоты и основания по Брёнстеду-Лоури
  12. Кислоты и основания по Льюису

Введение

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

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

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

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


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

Пример 4.Простой сигнало-слотный.

Рассмотрим простой пример, являющийся некоторой переработкой стандартного «Hello, World!» направленный на ознакомление с сигналами-слотами и так же некоторыми возможностями Qt.

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

Данное приложение состоит из нескольких виджетов: QWidget, QLabel, QPushButton, QSpinBox, QSlider.

QWidget является главным окном приложения. Все виджеты помещены внутрь QWidget, и является дочерними по отношению к нему.

1. int main(int argc, char *argv[])

3. QApplication app(argc, argv);

6. QLabel *label = new QLabel(«

Hello» » Student!

В строке 6 мы описываем новый label, который будет содержать нужную нам информацию. Как можно заметить, можно пользоваться простыми средствами форматирования документов HTML

7. QPushButton *button = new QPushButton(«Quit»);

8. QObject::connect(button, SIGNAL(clicked()),

10.QSpinBox *spinBox = new QSpinBox;

Мы объявляем два виджета, наборный счетчик и ползунок. Именно их мы и соединим в последствии сигналом и слотом.

Вызов функции QObject::connect() обеспечивает синхронизацию их работы. Если один из виджетов изменит значение, то сгенерируется сигнал valueChanged(int) и вызовется слот setValue(int) другого виджета.

Qt имеет в своём распоряжении Менеждер компоновки – это объект, который устанавливает размер и положение виджетов в зоне его действия.

Их три – QHBoxLayout, QVBoxLayout, QGridLayout. Они позволяют не заморачиваться над положением и размером виджетов и знать,что они всё равно будут аккуратно расположены в ряд.

19.QHBoxLayout *layout1 = new QHBoxLayout;

22.QHBoxLayout *layout2 = new QHBoxLayout;

25.QVBoxLayout *vlayout = new QVBoxLayout;

Дата добавления: 2014-01-05 ; Просмотров: 867 ; Нарушение авторских прав? ;

Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет

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

Сигналы и слоты — это то, как в Qt взаимодействуют между собой объекты разных классов.

Содержание

Как это работает в теории

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

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

Таким образом, чтобы соединить два объекта, нужно:

  1. создать у одного сигнал, а у второго слот;
  2. соединить сигнал первого и слот второго.

Это можно изобразить вот так:

а) два изначальных объекта, ничем ни с кем не соединены; б) у первого объекта появился сигнал, а у второго — слот. Теперь им есть чем соединяться, но они всё ещё ни с кем не соединены; в) сигнал первого объекта соединён со слотом второго.


Каждый объект может иметь больше одного сигнала и больше одного слота. Соединяться могут также более двух объектов:

Как видно, кроме очевидных соединений, при отправке Объектом 4 сигнала выполнятся слот Объекта 3 и слот Объекта 1.

Примеры

Немного поясняющих картинок:

Один сигнал соединён с одинаковыми слотами разных объектов (наследников одного класса)

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

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

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

Объявление connect() выглядит следующим образом:

где sender и receiver — это указатели на экземпляры класса QObject (или его потомки), а signal и slot — это сигнатуры функций. Макросы SIGNAL() и SLOT() по сути преобразуют свои аргументы в строки. В наших примерах мы до сих пор подключали к каждому из сигналов только один слот. Однако это не единственный способ.

Один сигнал может быть подключен к нескольким слотам:

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

К одному слоту может быть подключено несколько сигналов:

Когда посылается какой-либо из сигналов — вызывается функция-слот.

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

Когда посылается первый сигнал, то вслед за ним подается и второй. С точки зрения программы, соединение типа сигнал-сигнал ничем не отличается от соединения типа сигнал-слот.

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

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

Соединяемые сигналы и слоты должны иметь идентичные сигнатуры (т.е. количество и типы входных аргументов): Исключение составляет случай, когда сигнал имеет большее число аргументов, чем слот. В этом случае «лишние» аргументы просто не передаются в слот.

Если типы входных аргументов не совместимы, или сигнал или слот не определены, Qt выдаст предупреждение во время исполнения. Точно так же Qt выдаст предупреждение, если в сигнатуры сигналов или слотов включены имена аргументов (в методе connect()).

Метаобъектная Система в библиотеке Qt

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

Этот механизм получил название Meta Object System и предоставляет два ключевых сервиса: сигналы-слоты и интроспекцию. Интроспекция позволяет получать метаинформацию о потомках класса QObject во время исполнения, включая список поддерживаемых сигналов, слотов и имя класса объекта. Этот механизм также реализует поддержку свойств объектов (используются в Qt Designer ) и перевод текста (для нужд интернационализации).

Стандарт C++ не обеспечивает возможность получения динамической метаинформации, которая необходима метаобъектной системе Qt. Поэтому данная проблема была решена созданием дополнительного инструмента moc (метаобъектный компилятор). Он собирает всю необходимую информацию из классов Q_OBJECT и делает ее доступной через вызовы обычных функций языка C++, что позволяет метаобъектной системе работать с любым компилятором C++.

Механизм работает следующим образом:

Макрос Q_OBJECT объявляет ряд функций, который должны присутствовать в каждом потомке QObject: metaObject(), >tr() и ряд других.

Утилита moc генерирует реализацию сигналов и функций, объявленных макросом Q_OBJECT.

Эти функции используются методами connect() и disconnect().

Все действия выполняются автоматически, утилитами qmake и moc, так что вы довольно редко будете вспоминать об этом. Но если вас одолевает любопытство — загляните в исходные файлы, созданные moc, и посмотрите — что да как.

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


Обратите внимание на реализацию слота setSalary(). Сигнал salaryChanged() посылается только в том случае, когда newSalary != mySalary. Такой способ предотвращает попадание в бесконечный цикл при наличии обратной связи с другим объектом.

Сигналы и слоты между объектами в разных потоках в Qt

По сути, я имею следующий код:

Проблема в том, что соединения между объектами ‘server’ и ‘collect’ не работают. Объект ‘сервер’ находится в том же потоке, что и функция ‘main’, но объекты ‘собираются’ перемещены в другие потоки.

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

Моя цель заключается в том, чтобы «сервер» мог отправлять сигнал всем существующим объектам. В каждом из объектов «собирать» необходимо выполнить слот и вернуть ответ «сервер», если таковые имеются.

Если я настроил соединение, чтобы быть типом Qt::DirectConnection слоты, работающие в том же потоке, что и «главная» функция и объект «сервер», и это меня не интересует.

Любая помощь или предложения будут оценены.

Решение

Все есть в документации Qt.
Сначала прочитайте тот .
Тогда, если вы не удовлетворены, вы можете использовать QCoreApplication::postEvent() (для получения дополнительной информации вам нужно: Вот )

Оба сигнала / слоты ( QObject:connect() ) а также QCoreApplication::postEvent() являются потокобезопасной и может решить вашу проблему.

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

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

Другие решения

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

Кстати, после прочтения ваших интересов, я бы порекомендовал вам модуль QtConcurrent, который, кажется, больше соответствует вашим интересам.

Ха … Ваш код не в порядке. Это источник всех ваших неприятностей

Если вы хотите, чтобы слоты newInstructions а также responseObtained Слоты, чтобы когда-либо запускаться, myprocess не должен быть бесконечным циклом. Вам нужно :

  • изменять myprocess такой, что когда-то это делается написание и чтение доступные на данный момент данные, это завершает
  • Имейте механизм, чтобы знать, что новая обработка должна быть сделана. Например, если вы используете QIODevice подкласс (сокет, входной поток и т. д.) у вас есть сигнал QIODevice::readyRead() который сообщит вам, что есть новые данные для чтения с устройства. Я подозреваю, что ваш newInstructions должен сделать именно это.
  • подключить этот механизм к другому вызову myprocess чтобы начать обработку заново

Редактировать : Учитывая ваш комментарий, это способ изменить бесконечный цикл while без особых изменений.

QMetaObject :: InvokeMethod запланирует выполнение этого метода в конце потока очереди QEventLoop. Что означает, что другие слоты могут выполняться.

Я могу решить мою проблему, добавив следующую строку в конце метода «myprocess»:

Окончательный код этого метода:

С помощью этой строки я получаю, что события будут обработаны, если таковые имеются. Я не знаю, является ли это лучшим решением, но оно работает как хотелось.

Информационный портал по безопасности

Как работают сигналы и слоты в Qt

Автор: admin от 2-03-2014, 03:08, посмотрело: 3903

Qt хорошо известен своим механизмом сигналов и слотов. Но как это работает? В этом посте мы исследуем внутренности QObject и QMetaObject и раскроем их работу за кадром. Я буду давать примеры Qt5 кода, иногда отредактированные для краткости и добавления форматирования.

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

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

Где-то, в .cpp файле, мы реализуем setValue():

Затем, можем использовать объект Counter таким образом:


Это оригинальный синаксис, который почти не изменялся с начала Qt в 1992 году. Но даже если базовое API не было изменено, реализация же менялась несколько раз. Под капотом добавлялись новые возможности и происходили другие вещи. Тут нет никакой магии и я покажу как это работает.

MOC или метаобъектный компилятор

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

C++ не предоставляет родной поддержки самоанализа, поэтому Qt поставляется с инструментом, который это обеспечивает. Этот инструмент называется MOC. Это кодогенератор (но не препроцессор, как думают некоторые люди).

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

Магические макросы

Сможете ли вы заметить ключевые слова, которые не являются ключевыми словами C++? signals, slots, Q_OBJECT, emit, SIGNAL, SLOT. Они известны как Qt-расширение для C++. На самом деле это простые макросы, которые определены в qobjectdefs.h.

Это правда, сигналы и слоты являются простыми функциями: компилятор обрабатывает их как и любые другие функции. Макросы еще служат определённой цели: MOC видит их. Сигналы были в секции protected в Qt4 и ранее. Но в Qt5 они уже открыты, для поддержки нового синтаксиса .

Q_OBJECT определяет связку функций и статический QMetaObject. Эти функции реализованы в файле, сгенерированном MOC.

emit – пустой макрос. Он даже не парсится MOC. Другими словами, emit опционален и ничего не значит (за исключением подсказки для разработчика).

Эти макросы просто используются препроцессором для конвертации параметра в строку и добавления кода в начале. В режиме отладки мы также дополняем строку с расположением файла предупреждением, если соединение с сигналом не работает. Это было добавлено в Qt 4.5 для совместимости. Для того, чтобы узнать, какие строки содержат информацию о строке, мы используем qFlagLocation, которая регистрирует адрес строки в таблице, с двумя включениями.

Теперь перейдём к коду, сгенерированному MOC.

QMetaObject

Тут мы видим реализацию Counter::metaObject() и Counter::staticMetaObject. Они объявленый в макросе Q_OBJECT. QObject::d_ptr->metaObject используется только для динамических метаобъектов (QML объекты), поэтому, в общем случае, виртуальная функция metaObject() просто возращает staticMetaObject класса. staticMetaObject построен с данными только для чтения. QMetaObject определён в qobjectdefs.h в виде:

d косвенно символизирует, что все члены должны быть сокрыты, но они не сокрыты для сохранение POD и возможности статической инициализации.

QMetaObject инициализируется с помощью метаобъекта родительского класса superdata (QObject::staticMetaObject в данном случае). stringdata и data инициализируются некоторыми данными, которые будут расмотрены далее. static_metacall это указатель на функцию, инициализируемый Counter::qt_static_metacall.

Таблицы самоанализа

Во-первых, давайте посмотрим на основные данные QMetaObject.

Первые 13 int составляют заголовок. Он предоставляет собой две колонки, первая колонка – это количество, а вторая – индекс массива, где начинается описание. В текущем случае мы имеем два метода, и описание методов начинается с индекса 14.
Описание метода состоит из 5 int. Первый – это имя, индекс в таблице строк (мы детально рассмотрим её позднее). Второе целое – количество параметров, вслед за которым идёт индекс, где мы может найти их описание. Сейчас мы будет игнорировать тег и флаги. Для каждой функции MOC также сохраняет возращаемый тип каждого параметра, их тип и индекс имени.

Таблица строк

В основном, это статический массив QByteArray (создаваемый макросом QT_MOC_LITERAL), который ссылается на конкретный индекс в строке ниже.

Сигналы

MOC также реализует сигналы. Они являются функциями, которые просто создают массив указателей на аргументы и передают их QMetaObject::activate. Первый элемент массива это возращаемое значение. В нашем примере это 0, потому что возращаемое значение void. Третий аргумент, передаваемый функции для активации, это индекс сигнала (0 в данном случае).

Вызов слота

Также возможно вызвать слот по его индексу, используя функцию qt_static_metacall:

Массив указателей на аргументы в таком же формате, как и в сигналах. _a[0] не тронут, потому что везде тут возращается void.

Примечение по поводу индексов

Для каждого QMetaObject, сигналам, слотам и прочим вызываемым методам объекта, даются индексы, начинающиеся с 0. Они упорядочены так, что на первом месте сигналы, затем слоты и затем уже прочие методы. Эти индексы внутри называется относительными индексами. Они не включают индексы родителей. Но в общем, мы не хотим знать более глобальный индекс, который не относится к конкретному классу, но включает все прочие методы в цепочке наследования. Поэтому, мы просто добавляем смещение к относительному индексу и получаем абсолютный индекс. Этот индекс, используемый в публичном API, возращается функциями вида QMetaObject::indexOf.

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

Как работает соединение

Первое, что делает Qt при соединении, это ищет индексы сигнала и слота. Qt будет просматривать таблицы строк метаобъекта в поисках соответствующих индексов. Затем, создается и добавляется во внутренние списки объект QObjectPrivate::Connection.

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

Вот QObjectPrivate::Connection, определённый в qobject_p.h:

Каждый объект имеет массив соединений: это массив, который связывает каждого сигнала списки QObjectPrivate::Connection. Каждый объект также имеет обратные списки соединений объектов, подключённых для автоматического удаления. Это двусвязный список.

Связные списки используются для возможности быстрого добавления и удаления объектов. Они реализованы с наличием указателей на следующий/предыдущий узел внутри QObjectPrivate::Connection. Заметьте, что указатель prev из senderList это указатель на указатель. Это потому что мы действительно не указываем на предыдущий узел, а, скорее, на следующий, в предыдущем узле. Этот указатель используется только когда соединение разрушается. Это позволяет не иметь специальный случай для первого элемента.

Эмиссия сигнала

Когда мы вызываем сигнал, мы видели, что он вызывает код, сгенерированный MOC, который уже вызывает QMetaObject::activate. Вот реализация (с примечаниями) этого метода в qobject.cpp:

От переводчика: это была первая часть и традиционным будет вопрос о необходимости перевода второй части.

Илон Маск рекомендует:  Серверы omnihttpd server
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL