O ambiente de trabalho K

Capítulo 2. As Bibliotecas KDE e Qt

A empresa Norueguesa Troll Tech (\|\|) disponibiliza um chamado conjunto de ferramentas de GUI, chamado Qt . Assim, GUI significa "Graphical User Interface - Interface Gráfico de Utilizador", e assim, as aplicações baseadas na Qt representam-se com botões, janelas etc, permitindo inserção de dados do utilizador visualizando as funções que a aplicação disponibiliza. Este conjunto de ferramentas é necessário para o desenvolvimento de aplicações gráficas que corram no Interface X-Window em Sistemas Unix, porque o X não contém um interface de utilizador pré-definido em si. Apesar de outros conjuntos de ferramentas estarem também disponíveis para criar Interfaces de Utilizador, a Qt oferece algumas vantagens técnicas que tornam o desenho de aplicações muito fácil. Adicionalmente, o conjunto de ferramentas Qt também está disponível para o sistema Microsoft Windows, o que permite aos programadores disponibilizarem as suas aplicações para ambas as plataformas.

A Equipa KDE (\|\|) juntou-se com o objectivo de tornar a utilização de Sistemas Unix mais amigável, e decidiu utilizar o conjunto de ferramentas Qt para o desenvolvimento de um gestor de janelas no X-Window, mais uma variedade de ferramentas incluídas com os pacotes KDE. O Ambiente de Trabalho K contém assim o gestor de janelas kwm, o gestor de ficheiros kfm e o painel de controlo kpanel como sendo os componentes principais mais uma variedade de utilitários e aplicações de primeira classe. Depois do KDE ter saido, muitos dos programadores voltaram os seus olhos no sentido deste novo ambiente e do que tem para lhes oferecer. As bibliotecas KDE estão a disponibilizar métodos e classes fundamentais que fazem com que todas as aplicações desenhadas com elas tenham um aspecto similar e consistente, e o utilizador tem a grande vantagem de apenas ter de se acostumar com a utilização específica de uma aplicação, não com o manuseamento de diálogos ou botões. Também, os programas KDE integram-se no ambiente de trabalho e são capazes de interagir com o gestor de ficheiros através do arrastar e largar (drag and drop), oferecer gestão de sessões e muito mais, se todas as funcionalidades oferecidas pelas bibliotecas KDE forem utilizadas.

Ambos, o conjunto de ferramentas Qt e as bibliotecas KDE , são implementadas na linguagem de programação C++; assim aplicações que façam uso destas bibliotecas são também maioritariamente escritas em C++. No capítulo seguinte, faremos uma breve viagem através das bibliotecas para ver o que já é disponibilizado e como as aplicações Qt e KDE são criadas em geral.

2.1. O Conjunto de Ferramentas GUI Qt

Como foi dito, a biblioteca Qt é um conjunto de ferramentas que oferece elementos gráficos que são utilizados para criar GUI's de aplicações e são necessárias para programação X-Window. Adicionalmente, este conjunto de ferramentas oferece:

Posto isto conhecer as classes Qt é essêncial, mesmo se você apenas deseja programar aplicações KDE. Para ter uma ideia do conceito basico sobre como as aplicações GUI são construidas e compiladas, iremos primeiro observar um programa exemplo apenas Qt ; depois extende-lo-emos para um programa KDE.

2.1.1. A Primeira Aplicação Qt

Como normalmente, programas em C++ têm de conter a função main(), que é o ponto de partida para a execução da aplicação. Como queremos que seja graficamente visível em janelas e ofereça interacção do utilizador, temos primeiro que saber, como se podem mostrar a si próprias ao utilizador. Por exemplo, iremos observar o primeiro programa de tutor incluido com a Documentação Electrónica de Referência Qt e explicar os passos de execução básicos; também como e porquê as janelas da aplicação aparecem:

 #include <qapplication.h>
 #include <qpushbutton.h>
 
 int main( int argc, char **argv )
 {
 QApplication a( argc, argv );
 
 QPushButton hello( "Hello world!" );
 hello.resize( 100, 30 );
 
 a.setMainWidget( &&;hello );
 hello.show();
 return a.exec();
 }

Esta aplicação meramente pinta uma janela contendo um botão com "Hello world" ("Olá Mundo") como texto. Como todas as aplicações baseadas na Qt , primeiro tem de declarar uma instância da classe QApplication, representada por a.

Depois, o programa cria uma instância da classe QPushButton chamada hello, isto será o botão. O constructor do hello obtém uma string (conjunto de caracteres) como parâmetro, que é o conteudo do widget visível como sendo o texto do botão.

Depois o método resize() é chamado sobre o botão hello. Isto muda o tamanho de defeito que um widget (que é neste caso o QPushButton) tem quando criado para o comprimento de 100 pixels e a altura de 30 pixels. Finalmente, o método setMainWidget() é chamado para a e o método show() para hello. A QApplication é finalmente executada por a.exec(), entra no ciclo de acontecimento principal e aguarda até que tenha de devolver um valor inteiro para a camada do Sistema Operativo assinalando que a aplicação terminou.

2.1.2. A Documentação de Referência para a Qt

Agora, vamos observar rapidamente a documentação de referência da biblioteca Qt . Para fazer isto, inicie o KDevelop e seleccione "biblioteca Qt " a partir do menu "Ajuda" na barra de menu. O navegador de documentação abre e mostra-lhe a página inicial da referência Qt . Isto irá ser o seu primeiro local para obter informação sobre a Qt , suas classes e as funções disponíveis que oferece. Também, o programa acima mostrado é o primeiro que é incluído na secção dos tutores. Para obter as classes que desejamos observar, QApplication e QPushButton, seleccione "Lista Alfabética de Classes" e procure os nomes correspondentes. Siga qualquer uma delas para observar a documentação de classes .

Para a QApplication, verá o construtor e todos os outros métodos que esta classe disponibiliza. Se seguir uma hiper-ligação, irá obter mais informação sobre a utilização e significado dos métodos, o que é muito útil quando você às vezes não consegue detectar a utilização correcta ou deseja ver um exemplo. Isto também vale para a documentação da biblioteca KDE, que utiliza um tipo de documentação similar; assim isto é praticamente tudo o que tem de saber sobre a utilização da referência de classes com o navegador de documentação.

2.1.3. Interpretação do Exemplo

Começando com a QApplication, você irá ver todos os métodos utilizados no nosso primeiro exemplo:

  • o construtor QApplication(),

  • o método setMainWidget() e

  • o método exec().

A interpretação sobre porque utilizamos estes métodos é muito simples:

  1. primeiro criamos uma instância da classe QApplication com o construtor, para que possamos utilizar os elementos GUI disponibilizados pela Qt ,

  2. criamos um widget que será o contentor da janela do nosso programa,

  3. definimos o widget como sendo o widget principal para a,

  4. executamos a instância a da QApplication.

O segundo objecto do nosso programa é o botão, como uma instância da classe QPushButton. Dos dois construtores dados para criar uma instância, nós utilizamos o segundo: este aceita um texto, que é a etiqueta do conteudo do botão; aqui, a etiqueta é o texto "Hello world!". Depois chamamos o método resize() para modificar o tamanho do botão de acordo com o seu conteudo- o botão tem de ser maior para tornar o texto completamente visível.

Mas e então o método show()? Agora, você vê que tal como a maior parte dos outros widgets, QPushButton é baseado numa herança única- aqui, a documentação diz, Herda QButton. Siga a hiper-ligação para a classe QButton. Isto irá mostrar-lhe imensos outros métodos que são herdados pelo QPushButton, que iremos utilizar mais tarde para explicar o mecanismo sinal/espaço. De qualquer modo, o método show() não está listado, pelo que, tem de ser um método que é disponibilizado também por herança. A classe que o QButton herda, é QWidget. Basta seguir de novo a hiper-ligação, e irá ver imensos métodos que a classe QWidget disponibiliza; incluindo o método show(). Agora nós compreendemos o que foi feito no exemplo com o botão:

  1. criar uma instância do QPushButton, utilizar uma segunda construção para definir o texto do botão,

  2. redimensionar o widget para o seu conteudo,

  3. definir o widget como sendo o widget principal da instância a da QApplication,

  4. dizer ao widget para se mostrar no ecrã chamando show(), um método herdado de QWidget.

Após chamar o método exec(), a aplicação é visível para o utilizador, mostrando uma janela com um botão apresentando "Hello world!". Agora, programas GUI comportam-se de um modo ligeiramenta diferente das aplicações processuais. A coisa principal aqui é que a aplicação entra num chamado "ciclo de evento principal". Isto significa que o programa tem de aguardar por acções do utilizador e depois reagir a estas, também que para uma aplicação Qt , o programa tem de estar no ciclo de evento principal para iniciar o manuseamento de eventos. A secção seguinte diz-lhe de forma breve o que isto significa para o utilizador e o que a Qt oferece para processar eventos do utilizador.

(Para utilizadores já avançados: O botão não tem pai declarado no construtor, por isso é por si só um widget de nível superior e corre num ciclo de evento local que não tem de aguardar pelo ciclo de evento principal, veja a documentação de classe QWidget e O Guia de Referência da Biblioteca KDE)

Sumário:

Uma aplicação Qt tem sempre de ter uma instância da classe QApplication. Isto permite que possamos criar janelas que são a representação gráfica de programas para o utilizador final e permitem interacção. O conteudo da janela em si é chamado o "Widget Principal", significando que todos os elementos gráficos são baseados na classe QWidget e podem ser qualquer tipo de widget que sirva as necessidades da aplicação para comunicar com o utilizador. Assim, todos os elementos de utilizador têm de herdar de uma forma ou de outra QWidget para serem visíveis.

2.1.4. Interacção do Utilizador

Após ler as secções anteriores, você já deverá saber:

  • o que a biblioteca Qt disponibiliza em termos de aplicações GUI,

  • como é que um programa que utilize a Qt é criado e

  • onde e como obter informação sobre classes que deseja utilizar com o navegador de documentação

Agora iremos começar a dar "vida" à aplicação através do processamento de eventos do utilizador. Geralmente, o utilizador tem duas formas de interagir com o programa: o rato e o teclado. Para ambas as formas, um interface gráfico de utilizador (GUI) tem de disponibilizar métodos que detectem acções e métodos que façam alguma coisa como reacção a estas acções.

O sistema de Janelas assim envia todos os eventos de interacção para a respectiva aplicação. A QApplication envia-os então para a janela activa como um QEvent e os prórpios widgets têm de decidir o que fazer com os eventos. Um widget recebe o evento e processa QWidget::event(QEvent*)/, que então decide que evento foi executado e como reagir; event() é assim quem faz o manuseamento principal de eventos. Depois, a função event() passa o evento para os chamados filtros de eventos, que determinam que aconteceu e o que fazer com o evento. Se nenhum filtro encontra um responsável pelo evento, os manuseadores especializados de eventos são chamados. Assim podemos decidir entre:

a) Eventos de Teclado -- teclas TAB e Shift-TAB:

muda o focus de entrada do teclado do widget actual para o widget seguinte na ordem de focus. O focus pode ser atribuido aos widgets chamando setFocusPolicy() e processar os seguintes indicadores de eventos:

  • virtual void focusInEvent ( QFocusEvent * )

  • virtual void focusOutEvent ( QFocusEvent * )

b) todos os restantes eventos de teclado:

  • virtual void keyPressEvent ( QKeyEvent * )

  • virtual void keyReleaseEvent ( QKeyEvent * )

c) movimentos do rato:

  • virtual void mouseMoveEvent ( QMouseEvent * )

  • virtual void enterEvent ( QEvent * )

  • virtual void leaveEvent ( QEvent * )

d) acções de botões do rato:

  • virtual void mousePressEvent ( QMouseEvent * )

  • virtual void mouseReleaseEvent ( QMouseEvent * )

  • virtual void mouseDoubleClickEvent ( QMouseEvent * )

e) eventos da janela que contém o widget:

  • virtual void moveEvent ( QMoveEvent * )

  • virtual void resizeEvent ( QResizeEvent * )

  • virtual void closeEvent ( QCloseEvent * )

Note que todas as funções de eventos são virtuais e protegidas; assim você pode re-implementar os eventos que necessitar nos seus próprios widgets e especificar como o seu widget tem de reagir. O QWidget também contém alguns outros métodos virtuais que podem ser úteis nos seus programas; de qualquer modo, é suficiente saber sobre o QWidget de uma forma geral.

2.1.5. Interacção de Objectos através de Sinais e Espaços

Agora estamos a chegar às vantagens mais óbvias do conjunto de ferramentas Qt : o mecanismo sinal/espaço. Isto oferece uma solução bastante "à-mão" e útil para interacção de objectos, que é normalmente resolvido através de funções de callback para conjuntos de ferramentas X-Window. Como esta comunicação requer uma programação estrita e às vezes torna a criação de interfaces gráficos muito difícil (como referido pela documentação Qt e explicado em Programação com Qt por K.Dalheimer), a Troll Tech inventou um novo sistema onde objectos podem emitir sinais que podem ser conectados a métodos declarados como espaços. Para a parte C++ do programador, ele apenas tem de saber algumas coisas sobre este mecanismo:

  1. a declaração de classe da classe que utiliza sinais/espaços tem de conter a macro Q&_;OBJECT no início (sem o ponto e vírgula); e tem de derivar da classe QObject,

  2. um sinal pode ser emitido por uma palavra-chave emit, por ex. emit signal(parâmetros); a partir de qualquer função membra de uma classe que permita sinais/espaços,

  3. todos os sinais utilizados pelas classes que não são herdadas têm de ser adicionados à declaração da classe através de uma secção signals:,

  4. todos os métodos que podem ser conectados com um sinal são declarados em secções com a palavra-chave adicional slot, por ex. public slots: dentro da declaração da classe,

  5. o compilador de meta-objectos moc tem de correr sobre o ficheiro header para expandir as macros e produzir a implementação (que não é necessário saber.). Os ficheiros resultantes do moc são compilados também pelo compilador C++.

Outra forma de utilizar sinais sem derivar de QObject é utilizar a classe QSignal- veja a documentação de referência para mais informação e exemplos de utilização. Nos exemplos seguintes, nós assumimos que você está a derivar da QObject.

Desta forma, a sua classe é capaz de enviar sinais para qualquer lado e de disponibilizar espaços a onde se possam conectar sinais. Utilizando os sinais, você não tem de se preocupar com quem os está a receber- você apenas tem de emitir o sinal e qualquer que seja o espaço que lhe queira conectar poderá reagir à emissão. Também os espaços podem ser utilizados como métodos normais durante a implementação.

Agora, para conectar um sinal a um espaço, tem de utilizar os métodos connect() que são disponibilizados através do QObject ou, onde disponível, métodos especiais que os objectos disponibilizam para definir a conecção para um certo sinal.

2.1.5.1. Exemplo de Utilização

Para explicar a forma como definir interacção de objectos, iremos agarrar de novo no nosso primeiro exemplo e extende-lo através de uma conecção simples:

 #include <qapplication.h>
 #include <qpushbutton.h>
 
 int main( int argc, char **argv )
 {
 QApplication a( argc, argv );
 
 QPushButton hello( "Hello world!" );
 hello.resize( 100, 30 );
 
 a.setMainWidget( &&;hello );
 
 connect(&&;hello, SIGNAL( clicked() ), &&;a, SLOT( quit() ));
 
 hello.show();
 return a.exec();
 }

Como vê, a única adição para dar ao botão mais interacção é utilizar um método connect(): connect(&&;hello, SIGNAL( clicked() ), &&;a, SLOT( quit() )); é tudo o que temos de acrescentar. Qual é o significado agora? A declaração da classe de QObject diz sobre o método connect():

bool connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * member )

Isto significa, você tem de especificar um ponteiro de uma instância de QObject que é o emissor do sinal, significando que pode emitir este sinal como primeiro parâmetro; depois você tem de especificar o sinal a que se deseja conectar. Os dois últimos parâmetros são o objecto receptor que disponibiliza um espaço, seguido pela função membro que é na realidade o espaço que será executado aquando da emissão do sinal.

Utilizando sinais e espaços, os objectos do seu programa podem interagir entre eles facilmente sem dependerem explicitamente do tipo de objecto receptor. Você irá aprender mais sobre a utilização deste mecanismo para utilização produtiva mais tarde neste manual. Mais informação sobre o mecanismo Sinal/Espaço pode ser encontrado no Guia de Referência da Biblioteca KDE e na referência electrónica Qt .