Tutorial Qt – Capitulo 05 – Uma aplicação Qt feita à mão

Esse capitulo é uma pequena introdução em como escrever signals e slots. O objetivo é criar uma aplicação em Qt usando apenas o terminal e um editor de texto. A aplicação resultante é mostrada na figura a seguir.

No capitulo anterior, os layouts foram usados para definir e controlar o layout dos widgets. Essa abordagem pode ser feita via código também, mas para adicionar vários QVBox e QHBox que usaremos dessa vez. Um QVBox é uma caixa onde colocamos widgets em uma linha vertical, enquanto QHBox funciona em uma linha horizontal. Para implementar os layouts necessários as caixas vertical e horizontal devem ser aninhadas. A figura a abaixo demosntra como devemos fazer esse aninhamento. Observe que as caixas horizontal e vertical são identificadas pela direção da seta no canto de cada caixa.

A aplicação inteira será implementada como um widget único. Isso está Ok para um exemplo pequeno. Se layouts tem que ser usados, uma caixa de diálogo deve ser mais apropriada. As diferentes abordagens que podem ser usadas são discutidas no próximo capitulo.

Como a caixa externa (na figura acima) é uma QVBox o widget criado é derivado de QVBox. A classe é chamada HandMade e o arquivo de cabeçalho é chamado handmade.h. O código é mostrado logo abaixo.

Observe as diretivas #ifndef, #define e #endif. Essas diretivas são chamadas para proteger o arquivo de cabeçalho de múltiplas declarações. Garante que a definição da classe apenas seja vista pelo compilador.

A primeira coisa que fazemos dentro dessa área protegida é incluir a definição da classe que pretendemos derivar nossa classe (QVBox). Todas as outras classes usadas (QListBox e QLCDNumber) são referênciadas como ponteiros, de forma que a única coisa que o compilador precisa saber é que elas são classes (já que ponteiros tem o mesmo tamanho na memória independente do que eles apontam). Assim, ao invés de incluir declarações de classes uma declaração adiantada é usada. Isso reduz o número de arquivos de cabeçalho que precisam ser usados e reduz o tempo de compilação. Quando mais cedo pretendemos usar os membros das classes (ou declara-los estaticamente) a definição da classe se faz necessária, assim os cabeçalhos terão que ser incluidos no arquivo da implementação de nossa classe feita à mão.

Depois dessas declarações, segue a declaração da classe propriamente dita. A primeira coisa que uma classe Qt (isto é, derivada de um QObject) precisa incluir é a macro Q_OBJECT. Isso garante que os signals, slots e outros itens específicos do Qt funcionem adequadamente.

A classe em si contém um construtor e dois slots. Os parâmetros do construtor são os parâmetros padrão do Qt – um parent e um nome. O parent é usado pelo gerenciador de memória do Qt e o nome é usado pelo depurador. Esses parâmetros serão passados para a classe base na implementação. Os dois ponteiros que declares (lcd e lb) serão usados na implementaçã odos slots. Aqueles de vocês que lembram do último capitulo e que o slot init() foi usado no lugar do construtor deve se perguntar qual abordagem será usada aqui. O construtor gerado pelo Designer (ou, na verdade, uma ferramenta chamada uic) cria os widgets da caixa de diálogo e depois o init() se existir. Isso salva você de ter que criar uma sub-classe para a caixa de diálogo que criamos do Designer para adicionar nosso próprio construtor. Já que teremos que criar a sub-classe para nosso widget feito à mão, simplesmente pulamos o init() e usamos o construtor.


#ifndef HANDMADE_H
#define HANDMADE_H
#include <qvbox.h>
class QLCDNumber;
class QListBox;
class HandMade : public QVBox
{
Q_OBJECT
public:
HandMade( QWidget *parent=0, char *name=0 );
protected slots:
void addNumber();
void removeNumber();
protected:
QLCDNumber *lcd;
QListBox *lb;
};
#endif

O próximo passo para a criação de nossa classe é implementa-la. O código é mostrado no próximo exemplo e salvo como handmade.cpp.

O começa incluindo todos os arquivos de cabeçalho que usaremos – inclusive handmade.h. Como o código é todo comentado, você deve ler os comentários para entede-lo. Vale a pena notar que as caixas são preenchidas de cima para baixo e da esquerda para a direita. É um bom exercício relacionar a figura acima com o código. Lembre que a caixa externa é manipulada pela derivação de QVBox. Observe também quão fácil os parâmetros parent e name são passados para a classe base de nossa classe.


#include <qlcdnumber.h>
#include <qlistbox.h>
#include <qpushbutton.h>
#include <qhbox.h>
#include <qslider.h>
#include "handmade.h"
HandMade::HandMade( QWidget *parent, char *name ) : QVBox( parent, name )
{
// A horizontal box for the list, slider and LCD
QHBox *hb = new QHBox( this );
// The list is placed first (i.e. to the left)
lb = new QListBox( hb );
// A vertical box to put the LCD above the slider
// This is placed to the right of the list
QVBox *vb = new QVBox( hb );
// The LCD is created first and is thus shown at the top
lcd = new QLCDNumber( vb );
// The slider is created second and is this placed below the LCD
QSlider *s = new QSlider( Horizontal, vb );
// We connect the slider valueChanged(int) signal to the LCD
connect( s, SIGNAL(valueChanged(int)), lcd, SLOT(setValue(int)) );
// A horizontal box for the buttons, below the list, slider and LCD
hb = new QHBox( this );
// The add button will be to the left
QPushButton *pb = new QPushButton( "Add", hb );
// Connect it to the addNumber() slot
connect( pb, SIGNAL(clicked()), this, SLOT(addNumber()) );
// The remove button with be to the right of the add button
pb = new QPushButton( "Remove", hb );
// Connect it to the removeNumber slot
connect( pb, SIGNAL(clicked()), this, SLOT(removeNumber()) );
}

Depois do código acima, segue a implementação dos slots. O código é mostrado no próximo exemplo, e é também colocado no arquivo handmade.cpp. Esse código também está todo comentado, e  não deve ser nenhum problema interpreta-lo (Dê uma olhada na Documentação do Qt se não conseguir entender algum método). Observe que suamos os ponteiros declaros na definição da classe (lcd e lb) ao invés do ponteiro pb que apenas está disponível dentro do construtor.


void HandMade::addNumber()
{
// Simply insert the number shown by the LCD
lb->insertItem( QString::number( lcd->intValue() ) );
}
void HandMade::removeNumber()
{
// Return if no item is selected
if( lb->currentItem() < 0 )
return;
// Remove the selected item
lb->removeItem( lb->currentItem() );
}

Finalmente chegamos a última parte do código que deve ser acrescentado. No arquivo main.cpp, um função main comum é implementada, criando uma instância da nossa classe feita à mão e começa um loop de eventos Qt. O código é mostrado a seguir.


#include <qapplication.h>
#include "handmade.h"
int main( int argc, char **argv )
{
QApplication a( argc, argv );
HandMade *hm = new HandMade();
a.setMainWidget( hm );
hm->show();
return a.exec();
}

Para criar o Makefile uma descrição de projeto é necessária. Colorque o código do exemplo abaixo em um arquivo chamado handmade.pro. Depois execute qmake && make && ./handmade a partir de um terminal, use-o e depois execute o resultado (O caracter & duplo garante que o passo anterior deve ser executado com sucesso antes de passar ao próximo). Essa tarefa pode ser feita automaticamente pelo qmake usando o comando “qmake -project”.


SOURCES = handmade.cpp main.cpp
HEADERS = handmade.h

Quando estiver rodando a aplicação, ela parece que não funciona. O LCD não mudará quando o slider é movido! A explicação pode ser encontrada nas mensagens de erro mostradas no terminal. A mensagem de erro é mostrada abaixo:


QObject::connect: No such slot QLCDNumber::setValue(int)
QObject::connect: (sender name: 'unnamed')
QObject::connect: (receiver name: 'unnamed')

Foram mencionados anteriormente os parâmetros padrão do Qt, parent e nome, e dito que o nome é usado pelo depurador. Se tivermos dados a cada widget um nome, então esses nomes apareceriam nas mensagens de erro ao invés de “unnamed”, tornando mais fácil para nós depurar o código. Nesse exemplo pequeno, nós podemos localizar o problema sem os nomes. O slot faltante setValue(int) do LCD é chama do por display(int) de acordo com a documentação do Qt. Corrija a conexão no construtor de acordo com o código abaixo:


// We connect the slider valueChanged(int) signal to the LCD
connect( s, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) );

Depois de recompilar a aplicação tudo deve funcionar corretamente (só execute o make, não há necessidade de executar p qmake já que não foram acrescentados novos arquivos). Tente mover o slider, adicionando alguns números a lista e removendo outros.

Sumário

O exemplo desse capitulo mostra como é fácil produzir aplicações usando o Qt, mas sem usar a ferramenta de desenho. Em aplicações reais, o layout normalmente é manipulado no Designer, mas o código ainda terá um grande papel no fornecimento de funcionalidade.

Você viu como os problemas com signal e slots são mostrados. Isso introduz o problema de testar todas as conexões, mas isso pode ser manipulado facilmente se todas as conexões forem feitas usando o Designer.

Também, você viu quão fácil é criar e usar arquivos de projeto com o qmake. Comparado com a escrita de makefiles a mão, essa  abordagem é bem mais fácil.

Finalmente, vimos quão fácil é combinar widgets existentes em um único widget.

O código do exemplo desse capitulo pode ser baixa aqui ex06.tar
Traduzido de http://www.digitalfanatics.org/projects/qt_tutorial/chapter06.html