====== Qt → Application Framework ====== A TerraLib 5 fornece um framework para construção de aplicações GIS baseadas no [[http://qt.nokia.com|Qt]]. A figura abaixo ilustra a concepção geral deste framework. {{:wiki:designimplementation:qt:qt_af_architecture.png?430|Architecture of TerraLib Application Framework for Qt}} A idéia central deste framework é fornecer: * um ponto central para notificação de eventos da aplicação usando o próprio mecanismo de //sinal/slot// do Qt. Este ponto de entrada é útil para comunicação entre partes da aplicação, principalmente, as que forem introduzidas por plugins. * métodos para facilitar a localização de objetos comuns das aplicações (menus, botões, actions) através de um sistema de identificação simples. * uma forma fácil de registrar e gerenciar plugins instalados. * um relacionamento forte com o conceito de projeto. * configurações básicas das aplicações. * configurações básicas aplicadas por usuário do sistema operacional. * janela de entrada usada para relatar o andamento da carga da aplicação. * inicialização de recursos do Qt. * inicialização dos temas de ícones. É importantante ressaltar que este framework não impõe dependências aos widgets Qt do módulo [[:wiki:designimplementation:qt:widgets|te::qt::widgets]] nem aos componentes da TerraLib presents no Qt Designer que se encontram no módulo [[:wiki:designimplementation:qt:designer|te::qt::designer]]. De toda a TerraLib, a dependência deste framework ocorre apenas nas classes que realizam o elo de ligação entre os vários módulos da TerraLib (geometry, raster, data access, qt::widgets entre outros) e o framework, os chamados plugins. Esta seção descreve em maiores detalhes este framework e como ele pode ser usado para criação de novas aplicações. ===== Decisões de Projeto ===== {{:wiki:designimplementation:qt:af:qt_af_overview.png|Visão Geral das Classes do Framework de Aplicação}} ==== Projeto ==== O framework de aplicação baseia-se no conceito de projeto. O documento XML mostrado abaixo apresenta as informações que são mantidas em um projeto. Minas Gerais Gilberto Ribeiro de Queiroz ... ==== Configuração da Aplicação ==== O framework é reponsável por realizar uma série de inicializações, entre elas: * Informações básicas da aplicação, que podem ser usadas para editar o registro do Windows ou recursos semelhantes nos demais sistemas operacionais. * A carga da lista de plugins da aplicação, considerando a lista particular de cada usuário * A carga do sistemas de help * A carga das bibliotecas de ícones * Incialização dos módulos da TerraLib O arquivo XML abaixo mostra as informações básicas que o framework manipula. INPE TerraView TerraView terraview user_settings.xml application_plugins.xml datasources.xml tango 24 Caso o framework seja carregado a partir de arquivos XML, ele irá procurar por um arquivo contendo a lista de plugins da aplicação. Esta lista possui o formato ilustrado no documento XML abaixo. te.da.ogr te.da.pgis te.plugin.builder Algumas das preferência do usuário conhecidas pelo framework são mostradas no documento XML abaixo. tango 24 te.da.gdal te.plugin.exchanger te.cellspace.builder te.da.ogr te.da.gdal te.da.pgis te.plugin.builder datasources.xml BaseApplicationController: responsável pela inicialização da TerraLib e das configurações da aplicação: biblioteca de icones, lista de plugins, lista de fontes de dados, .... BaseApplication: é um diálogo que contém menus, barra de botões etc que depende da inicialização realizada pelo BaseApplicationController ==== Eventos ==== A lista dos eventos pode [[:wiki:designimplementation:qt:af:events|ser acessada aqui]]. ===== BaseApplication ===== ==== Menus ==== Lista de menus: * File * New Project * Open Project... * Save Project... * Save Project As... * Recent Projects * p1 * ... * pn * Exit * View * Toolbars * t1 * tn * Layer Explorer * Map Display * Data Table * Style Explorer * Full Screen * Project * Properties... * Layer * Add * Vector File... * Raster File... * Query... * Dataset... * Remove * Rename * Export... * Properties * Raise * Lower * To Top * To Bottom * Tools * Data Source Management... * Data Exchanger... * Customize * Plugins * Management... * Build a new plugin... * Help * View Help * Update * About ====== OLD!!!! Qt → Application Framework ====== * [[:wiki:designimplementation:qt:af:systemsettings|Application Settings]]: a widget to customize application settings. * [[:wiki:designimplementation:qt:widgets:plugins|Plugins]]: a set of plugins used to bind some components in widgets module to any application based on //TerraLib Application Framework//. A TerraLib 5 fornece um framework para construção de aplicações GIS baseadas no [[http://qt.nokia.com|Qt]]. {{:wiki:designimplementation:qt:qt_af_architecture.png|Architecture of TerraLib Application Framework for Qt}} A idéia central deste framework é fornecer: * um ponto central para notificação de eventos da aplicação usando sinal/slot, útil na comunicação entre partes da aplicação, especialmente por plugins. * facilidades para registrar novos itens da aplicação, principalmente as que precisem de interação com outras partes do sistema. * um diálogo principal com barra de menus e botões pré-definidos. * métodos para facilitar a localização dos objetos da aplicação (menus, botões, actions) através de um sistema de identificação simples. * uma forma fácil de registrar e gerenciar plugins instalados. * um relacionamento forte com o conceito de projeto e projetos recentes. * configurações básicas das aplicações. * configurações básicas aplicadas por usuário do sistema operacional. * janela de entrada usada para relatar o andamento da carga da aplicação. * inicialização de recursos do Qt. * inicialização dos temas de ícones. É importantante ressaltar que este framework não impõe dependências aos widgets Qt do módulo [[te::qt::widgets]] nem aos componentes da TerraLib presents no Qt Designer que se encontram no módulo [[te::qt::designer]]. De toda a TerraLib, a dependência deste framework ocorre apenas nas classes que realizam o elo de ligação entre os vários módulos da TerraLib (geometry, raster, data access, qt::widgets entre outros) e o framework, os chamados plugins. Esta seção descreve em maiores detalhes este framework e como ele pode ser usado para criação de novas aplicações. ===== Design ===== A figura abaixo ilustra as principais classes do framework e o relacionamento com alguns singletons que auxiliam na configuração da aplicação. {{:wiki:designimplementation:qt:qt_af_overview.png|TerraLib Application Framework for Qt}} A classe concreta **CoreApplication** representa o núcleo básico de uma aplicação GIS construída sobre o Qt. Esta classe utiliza o padrão singleton de forma que apenas ums instância é permitida. As aplicações poderão criar derivações desta classe que definam novos elementos de interface (GUI) ou até mesmo realizem a personalização da inicialização do framework. As seções a seguir fornecem maiores detalhes de cada parte do framework. ==== Application Settings ==== O framework utiliza 4 arquivos básicos de configuração no formato XML: * **config.xml:** configurações básicas da aplicação. * **application_plugins.xml:** lista dos plugins registrados/instalados no sistema. * **user_settings.xml:** configurações da aplicação dependentes do usuário do sistema operacional. * **datasources.xml:** lista das fontes de dados registradas por um determinado usuário. As informações acima são baseadas nas implementações das classes: * **OSSettingsDir:** * **ApplicationSettings:** * **SystemApplicationSettings:** * **UserApplicationSettings:** * **ApplicationPlugins:** * **UserPlugins:** * **UserRecentProject:** Outros arquivos importantes do framework são os documentos de configuração de cada plugin. Atualmente, é esperado que cada plugin forneça seu arquivo de configuração. Maiores detalhes sobre estes arquivos podem ser encontrados na [[:wiki:designimplementation:plugin|seção sobre o módulo de plugins]]. ==== Application Startup ==== A classe base das aplicações realiza a inicialização de toda a TerraLib, plugins e elementos de interface gráfica do framework. Durante esta incialização é possível apresentar uma janela que forneça ao usuário o feedback das operações sendo realizadas. A classe **SplashScreenManager** (singleton) pode ser usado pelas implementações de aplicações para informar a janela de inicialização do aplicativo. Após a inicialização a barra de menus, barras de botões e de status estarão criadas. A incialização pode ser //bypassada// por qualquer classe derivada de **CoreApplication** bastando re-implementar os métodos de inicialização. ==== Application Items ==== O singleton da aplicação funciona também como um ponto central para registro de componentes da aplicação que precisam/podem ser localizados por plugins ou outros componentes. Assim, novos MapDisplays podem ser adicionados e descobertos por plugins que precisam de alguma interação com os Mapdisplays abertos. Vejam que existe um sinal específico para indicar que um novo item/componente foi adicionado ao sistema. Este mecanismo não deve, em geral, ser usado pela maioria dos componentes, mas existe. ==== Special Signals ==== Existem dois sinais especiais na API de CoreApplication. O primeiro declarado como: signals: void triggered(Event* e); pode ser utilizado para realizar um broadcast a todos os componentes/plugins que estiverem conectados ao framework. Todo objeto registrado no framework que deseje "escutar" eventos do tipo ''te::qt::af::Event'' deverá implementar um //slot// chamado: ''onApplicationTriggered(Event*)''. O framework realiza automaticamete algo como: connect(this, SIGNAL(triggered(Event*)), appItem, SLOT(onApplicationTriggered(Event*)), Qt::UniqueConnection); Desta forma, os itens de aplicação **não são obrigados** a declarar e implementar um //slot// com este nome. Este recurso só existe para automatizar o processo de conexão de objetos ao framework. Portanto, os itens de aplicação e plugins poderão ainda se conectar a este barramento de forma manual, por exemplo, durante o startup de um plugin. É fundamental compreender que a TerraLib 5 utiliza o próprio mecanismo de comunicação sinal/slot do Qt para notificar os widgets/plugins de efeitos colateriais na aplicação. O fator de criarmos no nível do framework uma classe abstrata para eventos nos possibilita capturar de forma genérica eventos de broadcast. Qualquer outro tipo de evento mais específico deverá usar os sinais e slots definidos no nível dos componentes. A enumeração anônima no arquivo ''Enums.h'' tem o objetivo de facilitar a criação de identificadores para os tipos de eventos bem conhecidos, aqueles que irão residir no repositório da TerraLib, e possibilitar o uso dos recursos de auto-completar das IDEs que usamos. O segundo sinal, definido como: signals: void appItemAdded(const QString& id, QObject* appItem); serve para que o framework notifique a inclusão de um novo item da aplicação. Quem precisar ser notificado deste tipo de evento deverá conectar este sinal da forma que achar adequado. Com o tempo outros sinais serão adicionados neste nível. ==== Project ==== O framework está baseado no conceito de projeto. Sendo assim ele mantém a lista dos //n// projetos mais recentemente acessados pelo usuário e tenta carregar o último usado. O singleton **UserRecentProjects** cuida do gerenciamento das entradas na barra de menus da aplicação. Atualmente o projeto é capaz de armazenar: * Título do projeto * Autor * Lista de palavras-chave * Lista de layers Não é feita suposião de como o layer é serializado no projeto, isto depende da implementação do serializador do layer. Os layers no MGis salvam as informações de retângulo, projeção, estilo, fonte de dados, query, dataset e o que mais for pertinente a implementação. Uma informação importante que está //hard-coded// hoje são as informações de estado do //MapDisplay//. Na verdade o projeto deveria possibilitar salvar junto informações dos itens de aplicação. Isto ainda é um problema em aberto pois é preciso separar este uso da definição do componente o que nos levaria a criar //proxies// para cada componente que precise manter seu estado junto ao projeto. A idéia é que possamos salvar estados de gráficos, modelos ou qualquer outro componente que esteja aberto e que dependa do projeto que está em uso. Algumas configurações de componentes, como fonte preferida, organização e etc são preferencias do usuário e poderão ser salvas através do uso da classe ''te::common::UserApplicationSettings'' diretamente pelos componentes. ==== Icons and Images ==== Os ícones que aparecem nas barras de botões e nos menus podem ser configurados dependendo do tema escolhido pelo usuário da aplicação ou até mesmo ser fixados pela aplicação. O Qt oferece, a partir da versão 4.6.3, um recurso interessante para gerenciamento dos ícones, que são os temas. O MGis usa este suporte de forma que não é preciso especificar a localização dos ícones, basta adotar nomes que são únicos. Além disso, o tamanho dos ícones dependem das dimensões dos controles usados para exibí-los. Assim, este recurso possibilita que um mesmo nome possa ser bem exibido em um botão 16x16, 24x24, 32x32, 48x48, 64x64 ou qualquer outra dimensão para o qual a imagem original do ícone possa ser escalada. No MGis usei ícones no formato SVG, de forma que eles podem ser escalávei para qualquer tamanho. Acho que devemos adotar esta mesma estratégia para a TerraLib como um todo, ou seja, sempre que possível tentar construir ícones no formato SVG e caso não seja possível, usar pelo menos o formato SVG com o ícone gerado em diversos tamanhos (pelo menos: 16x16, 24x24, 32x32, 48x48, 64x64). As imagens (tudo que não for usado com QIcon) devem usar o resource do Qt. O problema básico é que o resource tem limitações enm relação ao diretório onde o recurso (imagens, videos, documentos) se localiza. Ele obriga de certa forma que um arquivo de descrição do recurso esteja no mesmo nível dos recursos ou pelo menos que eles estejam em sub-diretórios. Se for possível explorar o CMake para gerar os arquivos de resource automaticamente, seria o ideal. Além disso, no MGis eu preferi carregar os resources dinamicamente, isto é, o framework hoje é responsável por carregar os resources bem conhecidos (dá até para parafrasearmos o OGC e criarmos o termo WKR!). ==== Application Shutdown ==== Uma atenção especial é com a finalização da aplicação. Um plugin quando solicitado a finalização deverá limpar tudo que ele carregar não deixando nada a cargo da aplicação. A ordem de desligamento é a seguinte: - Shutdown dos plugins - Descarga dos plugins - Destrutor da aplicação - Finalização da Plataforma TerraLib ==== Plugins ==== Os plugins deste framework estarão hospedados no repositório na pasta: ''terralib/qt/plugins''. Já temos alguns componentes para lidar com plugins: **PluginManager** e **PluginBuilder**. === PluginManager === Componente para gerenciameno dos plugins. Possibilita desde o desligamento de um plugin até a instalação via Web. Este componente já está integrado no framework. === PluginBuilder === Componente que permite criar o esqueleto de novos plugins. Apesar de estar integrado ao framework ainda não está operacional. === Plugin Templates === Enquanto não conseguimos uma boa implementação do **PluginBuilder** podemos usar um template como o mostrado abaixo: ''Plugin.h'' class Plugin : public QObject, public te::plugin::Plugin { Q_OBJECT public: Plugin(const te::plugin::PluginInfo& pluginInfo); ~Plugin(); void startup(); void shutdown(); private slots: void onTemplateActionTriggered(); private: QAction* m_pluginAction; }; ''Plugin.cpp'' #include "../../../qt/af/CoreApplication.h" #include "Plugin.h" // Qt #include #include Plugin::Plugin(const te::plugin::PluginInfo& pluginInfo) : te::plugin::Plugin(pluginInfo), m_pluginAction(0) { } te::qt::plugins::Plugin::~Plugin() { } void Plugin::startup() { if(m_initialized) return; m_pluginAction = new QAction(tr("&Template Plugin"), this); QMenu* pluginsMenu = te::qt::af::CoreApplication::getInstance()->getMenu("plugins"); if(pluginsMenu != 0) pluginsMenu->addAction(m_pluginAction); connect(m_pluginAction, SIGNAL(triggered()), this, SLOT(onTemplateActionTriggered())); m_initialized = true; } void Plugin::shutdown() { if(!m_initialized) return; QMenu* pluginsMenu = te::qt::af::CoreApplication::getInstance()->getMenu("plugins"); if(pluginsMenu != 0) pluginsMenu->removeAction(m_pluginAction); m_initialized = false; } void Plugin::onTemplateActionTriggered() { int isteps = 1000; int jsteps = 1000; int somaij = 0; for(int i = 0; i != isteps; ++i) for(int j = 0; j < jsteps; ++j) somaij = i + j; } ===== From Theory to Practice ===== Na prática a classe **CoreApplication** poderia ser usada diretamente como no code snippet abaixo: // TerraLib #include // Qt #include int main(int argc, char** argv) { QApplication app(argc, argv); te::qt::af::CoreApplication terralibApp; int waitVal = app.exec(); return waitval; } Outro uso possível seria através da personalização de uma aplicação. O code snippet abaixo mostra como adiconar uma caixa de díalogo about-box para uma aplicação chamada de TerraView: ''TerraView.h'' // TerraLib #include class TerraView : public te::qt::af::CoreApplication { Q_OBJECT public: TerraView(); ~TerraView(); static TerraView* getInstance(); protected slots: void showAboutDialog(); }; ''TerraView.cpp'' // TerraView #include "AboutDialog.h" #include "TerraView.h" // Qt #include #include TerraView::TerraView() { QAction* helpAbout = new QAction(QIcon::fromTheme("terraview"), tr("&About..."), this); te::qt::af::CoreApplication::m_helpMenu->addAction(helpAbout); connect(helpAbout, SIGNAL(triggered()), this, SLOT(showAboutDialog())); } TerraView::~TerraView() { } TerraView* TerraView::getInstance() { return static_cast(te::qt::af::CoreApplication::sm_instance); } void TerraView::showAboutDialog() { std::auto_ptr dialog(new AboutDialog(this)); dialog->exec(); } Neste último caso podemos ainda personalizar um pouco mais o barramento ''TerraView'' introduzindo novos métodos e componentes que sejam específicos desta nova aplicação. É preciso notar que esta opção **pode** trazer algumas implicações, a mais imediata é que um plugin que enxergue a API de TerraView possivelmente não funcionará com outros aplicativos derivados de ''CoreApplication''. O contrário será sempre válido, todo plugin dependente apenas do Framework (classe **CoreApplication**) poderá ser incorporado automaticamente a qualquer aplicativo da família baseada neste framework. É esperado que a maioria dos plugins existentes no repositório da TerraLib estejam ligados com a classe base **CoreApplication**. ===== Module Summary ===== ------------------------------------------------------------------------------- Language files blank comment code scale 3rd gen. equiv ------------------------------------------------------------------------------- C++ 7 410 267 886 x 1.51 = 1337.86 C/C++ Header 11 324 428 390 x 1.00 = 390.00 ------------------------------------------------------------------------------- SUM: 18 734 695 1276 x 1.35 = 1727.86 ------------------------------------------------------------------------------- ===== Final Remarks ===== Decisões importante: * Dos arquivos de configuração citados, podemos optar por remover a lista de fontes de dados do framework de forma que um plugin mantenha este histórico. E ai? Eu ainda não tenho opinião formada! * Criar uma janela com dica do dia/inicialização? Particularmente eu não gosto destas janelas mas quase todos os softwares insistem em disponibilizá-las. * Vamos usar uma biblioteca de icones pronta e acrescentar os icones mais específicos da TerraLib? * Vamos criar uma pasta ''icons'' no repositório? Eu gostaria que existisse uma pasta com este nome e com os icones usados pelos componentes e pelo framework. * Existe um problema prático com a biblioteca de ícones para o caso de plugins, não dá para colocar os icones onde bem entendermos, eles devem respeitar uma configuração que é dos sistemas UNIX... de forma que os instaladores de plugins ou o plugin manager fique atento para copiar os icones para pasta principal de icones. * Qual o conjunto mínimo de menus/submenus/botões que a aplicação base terá? Hoje ela tem: ...TODO... * Vamos usar o mesmo conceito de projeto usado no MGis? O que mais vamos incorporar a ele? * Vamos deixar o PluginBuilder e PluginManager integrados ao Framework ou serão lugados via plugin? Por mim ficam hard-coded! O que mais precisamos evoluir? * Conceito de ferramenta (te::af::Tool). Próximos passos: * adiconar o help central * adiconar barra de progresso à barra de status -> Dock->ProgressView * adicionar o layer explorer e mapdisplay * adicionar método ''removeAppItem'' na API de CoreApplication e acrescentar o sinal para este caso. * criar template de plugin. * criar os projetos CMake. * ???????? Melhorias futuras: * tornar a carga de plugins durante a incialização do framework mais robusta e mais explicativa. * componente de gerenciamento de plugins poderia informar o motivo de um plugin não estar carregado ou com problemas. Acrescentar um ícone com status antes de cada checkbox de plugin: running, stopped, failed. ===== More Final Remarks ===== Quero reforçar que este framework não impede que: * Novos frameworks mais específicos sejam criados: {{:wiki:designimplementation:qt:qt_other_af_overview.png|}} * Alguma aplicação opte por não usar nada disso: {{:wiki:designimplementation:qt:qt_no_af_overview.png|}} * Ou usemos outro toolkit para replicar o esforço feito com o Qt: {{:wiki:designimplementation:qt:wx_other_af_overview.png|}} ===== References =====