Table of Contents

Qt → Application Framework

A TerraLib 5 fornece um framework para construção de aplicações GIS baseadas no Qt. A figura abaixo ilustra a concepção geral deste framework.

Architecture of TerraLib Application Framework for Qt

A idéia central deste framework é fornecer:

É 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.

Decisões de Projeto

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.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Project xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:te_map="http://www.terralib.org/schemas/maptools"
         xmlns:te_qt_af="http://www.terralib.org/schemas/qt/af"
         xmlns="http://www.terralib.org/schemas/qt/af"
         xsd:schemaLocation="http://www.terralib.org/schemas/qt/af project.xsd"
         version="5.0.0"
         release="2011-01-01">
 
  <Title>Minas Gerais</Title>
  <Author>Gilberto Ribeiro de Queiroz</Author>
  <te_map:LayerList>
     ...
  </te_map:LayerList>
 
</Project>

Configuração da Aplicação

O framework é reponsável por realizar uma série de inicializações, entre elas:

O arquivo XML abaixo mostra as informações básicas que o framework manipula.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Application xmlns:te_qt_af="http://www.terralib.org/schemas/qt/af"
             xmlns:xlink="http://www.w3.org/1999/xlink"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://www.terralib.org/schemas/qt/af"
             xsd:schemaLocation="http://www.terralib.org/schemas/qt/af config.xsd"
             version="5.0.0">
  <Organization>INPE</Organization>
  <Name>TerraView</Name>
  <Title>TerraView</Title>
  <IconName>terraview</IconName>
  <UserSettingsFileName>user_settings.xml</UserSettingsFileName>
  <PluginsFileName>application_plugins.xml</PluginsFileName>
  <HelpFile xlink:href="terraview.qhc"/>
  <DataSourcesFileName>datasources.xml</DataSourcesFileName>
  <IconThemeInfo>
    <BaseDirectory xlink:href="../../icons"/>
    <DefaultTheme>tango</DefaultTheme>
  </IconThemeInfo>
  <ToolBarDefaultIconSize>24</ToolBarDefaultIconSize>
 
</Application>

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.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Plugins xmlns:te_qt_af="http://www.terralib.org/schemas/qt/af"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://www.terralib.org/schemas/qt/af"
         xsd:schemaLocation="http://www.terralib.org/schemas/qt/af app_plugins.xsd"
         version="5.0.0">
  <Plugin>
    <Name>te.da.ogr</Name>
    <Path xlink:href="plugins\ogr\plugin_info.xml" />
  </Plugin>  
  <Plugin>
    <Name>te.da.pgis</Name>
    <Path xlink:href="plugins\postgis\plugin_info.xml" />
  </Plugin>
  <Plugin>
    <Name>te.plugin.builder</Name>
    <Path xlink:href="plugins\plugin_builder\plugin_info.xml" />
  </Plugin>
</Plugins>

Algumas das preferência do usuário conhecidas pelo framework são mostradas no documento XML abaixo.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<UserSettings xmlns:te_qt_af="http://www.terralib.org/schemas/qt/af"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://www.terralib.org/schemas/qt/af"
         xsd:schemaLocation="http://www.terralib.org/schemas/qt/af usettings.xsd"
         version="5.0.0">
  <SelectedIconTheme>tango</SelectedIconTheme>
 
  <LastSearchedFolder xlink:href="C:/Users/gribeiro/Documents"/>
 
  <ToolBarIconSize>24</ToolBarIconSize>
 
  <SpecificPlugins>
    <Plugin>
      <Name>te.da.gdal</Name>
      <Path xlink:href="plugins\gdal\plugin_info.xml" />
    </Plugin>
    <Plugin>
      <Name>te.plugin.exchanger</Name>
      <Path xlink:href="plugins\plugin_exchanger\plugin_info.xml" />
    </Plugin>
    <Plugin>
      <Name>te.cellspace.builder</Name>
      <Path xlink:href="plugins\plugin_cellspace_builder\plugin_info.xml" />
    </Plugin>
  </SpecificPlugins>
 
  <EnabledPlugins>
    <Plugin>te.da.ogr</Plugin>
    <Plugin>te.da.gdal</Plugin>
    <Plugin>te.da.pgis</Plugin>
    <Plugin>te.plugin.builder</Plugin>
  </EnabledPlugins>
 
  <DataSourcesFileName>datasources.xml</DataSourcesFileName>
 
  <MostRecentProject xlink:href="../../conf/project.xml"/>
 
  <RecentProjects>
    <Project xlink:href="../../conf/brasil.xml" title="Atlas Brasil"/>
    <Project xlink:href="../../conf/project.xml" title="Estudo Agricola do Estado de Minas Gerais"/>
    <Project xlink:href="../../conf/super-project.xml" title="Super projeto"/>
  </RecentProjects>
</UserSettings>

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 ser acessada aqui.

BaseApplication

Lista de menus:

OLD!!!! Qt → Application Framework

A TerraLib 5 fornece um framework para construção de aplicações GIS baseadas no Qt.

Architecture of TerraLib Application Framework for Qt

A idéia central deste framework é fornecer:

É importantante ressaltar que este framework não impõe dependências aos widgets Qt do módulo widgets nem aos componentes da TerraLib presents no Qt Designer que se encontram no módulo 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.

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:

As informações acima são baseadas nas implementações das classes:

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 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:

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 16×16, 24×24, 32×32, 48×48, 64×64 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: 16×16, 24×24, 32×32, 48×48, 64×64).

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:

  1. Shutdown dos plugins
  2. Descarga dos plugins
  3. Destrutor da aplicação
  4. 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 <QtGui/QAction>
#include <QtGui/QMenu>
 
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 <terralib/qt/af/CoreApplication.h>
 
// Qt
#include <QtGui/QApplication>
 
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 <terralib/qt/af/CoreApplication.h>
 
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 <QtGui/QAction>
#include <QtGui/QMenu>
 
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<TerraView*>(te::qt::af::CoreApplication::sm_instance);
}
 
void TerraView::showAboutDialog()
{
  std::auto_ptr<AboutDialog> 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:

O que mais precisamos evoluir?

Próximos passos:

Melhorias futuras:

More Final Remarks

Quero reforçar que este framework não impede que:

References