so_4: Диспетчер для GUI-приложений с использованием Qt

Назначение

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

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

Диспетчер so_4::disp::qt_ui предназначен для того, чтобы объекты-окна можно было делать агентами. Вызов обработчиков событий этих агентов будет осуществляться на контексте главной нити. Что позволит агентам-окнам свободно обращаться к GUI примитивам не опасаясь проблем, которые возникают в этих случаях в многопоточных приложениях.

Способ работы

Все агенты делятся на два типа: агенты главной нити и обычные агенты. Агент считается агентом главной нити, если для него установлено свойство (trais) возвращаемое функцией so_4::disp::qt_ui::query_gui_thread_traits().

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

Диспетчер so_4::disp::qt_ui использует механизм обработки событий Qt (основанный на методах postEvent и QObject::event()). При своем старте so_4::disp::qt_ui создает специальный Qt-объект типа so_4::disp::qt_ui::qt_event_processor_t. Этот объект в своем методе event() обрабатывает все Qt-события, которые имеют специальный идентификатор. Отсылает эти события сам диспетчер.

При диспетчерезации события диспетчер so_4::disp::qt_ui определяет, является ли агент-владелец события агентом главной нити. Если нет, то событие диспетчеризируется дополнительным диспетчером. Если же событие относится к агенту главной нити, то событие помещается в отдельную очередь заявок, а объекту qt_event_processor отсылается специальное событие. Объект qt_event_processor благодоря механизмам Qt получает это событие на контексте главной нити. Получив очередное событие qt_event_processor извлекает из отдельной очереди заявок заявку и запускает ее обработчик. Т.о. GUI агент обрабатывает свои события только на контексте главной нити.

Особенности

Один диспетчер и старт SObjectizer RunTime

Объект диспетчер so_4::disp::qt_ui должен быть в программе один.

Вызов so_4::api::start() нужно осуществлять на контексте любой нити, отличной от главной нити приложения, т.к. возврат из so_4::api::start() осуществляется только при завершении работы run-time SObjectizer. Поэтому, если вызвать so_4::api::start() на контексте главной нити, то приложение остановится в месте вызова so_4::api::start().

Специальная динамическая кооперация для GUI агентов

Агенты, производные от QObject (например, агенты-окна), не могут быть уничтожены в произвольный момент времени. Для их корректного уничтожения следует использовать метод QObject::deleteLater. Поэтому, производные от QObject агенты не могут регистрироваться в SObjectizer-е через стандартную динамическую кооперацию. Для таких агентов предназначена кооперация so_4::disp::qt_ui::dyn_coop_t. В своем методе deregistered() она проверят, производен ли агент от QObject. Если да, то для агента используется отложенное уничтожение через QObject::deleteLater. Если же агент не производен от QObject, то он уничтожается через обычный механизм уничтожения агентов динамической кооперацией.

Диспетчер не является частью so_4 DLL

Диспетчер so_4::disp::qt_ui располагается в отдельной DLL библиотеке. Для линковки к этой библиотеке в проекте нужно указать:

required_prj 'so_4/disp/qt_ui/prj.rb'

Важная особенность реализации: метод dispatcher_t::wait()

Метод so_4::rt::dispatcher_t::wait() должен гарантировать, что после его возврата не останется запущенных обработчиков событий. Если диспетчер контролирует все свои рабочие нити (т.е. нити, на которых работают обработчики событий агентов), то это обеспечивается без особых проблем. В случае же GUI диспетчера возникает сложность -- диспетчер не контролирует главную нить приложения. Поэтому qt_event_processor при запуске обработчика агента на главной нити уведомляет диспетчера о том, что главная нить занята. Если в этот момент диспетчер войдет в метод wait() он должен будет уснуть в нем пока агент на главной нити не завершит своей работы. Для этого диспетчер и qt_event_processor используют объект so_4::disp::reuse::gui_work_indicator::indicator_t для информирования друг друга о том, что происходит на контексте главной нити.

Документация по SObjectizer v.4.4 'Тебуломста'. Последние изменения: Thu Sep 18 10:26:48 2008. Создано системой  doxygen1.5.6 Intervale SourceForge.net Logo