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:

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

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

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

  • 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

  • Application Settings: a widget to customize application settings.
  • 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 Qt.

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

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

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

  • Alguma aplicação opte por não usar nada disso:

  • Ou usemos outro toolkit para replicar o esforço feito com o Qt:

References


QR Code
QR Code wiki:designimplementation:qt:af (generated for current page)