Você está na página 1de 33

C++ Programando GUI com Qt4, Segunda Edio Publicado por: Prentice Hall Data de Publicao: 04/02/2008 Traduzido

por: Arthur Dias Danilo Domingos

Viso Geral
O nico oficialmente publicado Guia das melhores prticas para programao Qt 4.3
Usando Trolltechs Qt possvel criar aplicaes C++ de alta performance que rodem em mquinas Windows, Linux/Unix, Mac OS X, e demais extenses Linux sem que seja necessrio fazer alteraes de cdigo. Agora, dois membros da TrollTech lhe oferecem este guia completo para que voc alcance resultados surpeendentes com a mais recente verso do Qt: Qt 4.3. Carregado com exemplops prticos e realistas e IN-DEPH ADVICE, este o livro usado pela Trolltech para ensinar Qt para seus prprios novos funcionrios. Revisado e expandido constantemente, este livro nos revela os melhores padres atuais para se trabalhar com Qt para diversos usos, que vo desde implementao de arquiteturas de modelagem at o uso da engine grfica do Qt 4.3. Voc encontrar solues para diversas tarefas de desenvolvimento GUI, assim como tcnicas sofisticadas para sistemas com acesso a banco de dados, integrao com XML, uso de subclasses, composio, e muito mais. Seja voc um novo usurio de Qt, ou um usurio antigo que est aprendendo a nova verso, este livro certamente vai lhe ajudar a tirar vantagem de tudo que o Qt 4.3 capaz de fazer. Eis algumas novidades que encontraro neste livro: Atualizado completamente, com um novssima cobertura de Databases, XML, e programao Qtopia Notificaes e cobertura de tudo que mudou do Qt 4.2 para 4.3, incluindo Integrao com Windows Vista, suporte nativo a CSS para estilizao de aplicativos, e gerao de arquivos SVG Captulos separados para assuntos relacionados a 2D e 3D, cobertura das novas classes de visualizao grfica do Qt 4.3, alm de uma cobertura total do QPainters OpenGL Novos captulos a respeito de otimizao look-and-feel e sobre criao de scripts para aplicaes

Ilustra a arquitetura de visualizao e modelagem do Qt4, suporte a plugins, Manuteno de Layout, processamento de eventos, classes containers, e muito mais Apresenta tcnicas avanadas vistas em nenhum outro livro - desde criao de plugins at interao com APIs nativas Inclui um novo apndice de Qt Jambi, a nova verso Java do Qt

Tabela de Contedos Parte 1: Qt Bsico Captulo 1: Comeando Ol Qt Fazendo conexes Modelando Widgets Usando Documentao de Referncia Captulo 2: Criando Dialogs Subclasse QDialog Signals e Slots Design de um Dialog Rpido Dialogs com mudana no Tamanho Dialogs Dinmicos Classes Dialog e Widget construdas Captulo 3: Criando Janelas Principais Subclasse QMainWindow Criando Menus e Barras de Ferramentas Ajustando a Barra de Status Desenvolvendo o Menu Arquivo Usando Dialogs Armazenando Configuraes Documentos Mltiplos Misturar Telas Captulo 4: Implementao da Funcionalidade da Aplicao O Widget Central Subclasse QTableWidget Carregando e Salvando Implementao do Menu Editar Implementando os Outros Menus Subclasse QTableWidgetItem Captulo 5: Criando Widgets Customizveis Customizando Qt Widgets Subclasse QtWidget Integrando Widgets Customizveis com Qt Designer

Buffering Duplo Parte II: Qt Intermedirio Captulo 6: Manuteno de Layout Modelando Widgets em um Form Layouts Empilhados Splitters reas Rolveis DOCK Janelas e Barras de ferramentas

Parte I: O Bsico do Qt ( Por Danilo Domingos) 1. Comeando Hello Qt Fazendo Conexes Alinhando Widgets Utilizando a Referncia Este captulo mostra como combinar C++ bsico com a funcionalidade disponibilizada por Qt para criar algumas aplicaes de interface grfica pequenas. Este captulo tambm introduz duas idias chave do Qt: signals e slots e layouts. No captulo 2, iremos mais a fundo, e no captulo 3, comearemos a construir aplicaes mais realsticas. Se voc j conhece Java ou C# mas tem uma experincia limitada com C++, ento recomendado que voc comece lendo o Apndice D Introduo ao C++. HELLO QT

Vamos comear com um programa bem simples. Vamos estud-lo linha a linha e depois ver como compil-lo e rod-lo. 1 #include <QApplication> 2 #include <QLabel> 3 int main(int argc, char *argv[]) 4{ 5 QApplication app(argc, argv);

6 7 8 9}

QLabel *label = new QLabel("Hello Qt!"); label->show(); return app.exec();

As linhas 1 e 2 incluem as definies das classes QApplication e QLabel. Para cada classe de Qt existe um arquivo header com o mesmo nome (e distino entre maisculas e minsculas) que contem a definio da classe. A linha 5 cria um objeto QApplication para gerenciar os recursos da aplicao no geral. A construtora de QApplication requer os argumentos argc e argv porque Qt interpreta alguns argumentos de linha de comando prprios do Qt. A linha 6 cria um widget QLabel que mostra o texto Hello Qt!. No Qt e na terminologia Unix, um widget um elemento visual numa interface grfica. O termo vem da expresso window gadget e equivalente a tanto controle como container na terminologia do Windows. Botes, menus, barras de rolagem e frames so exemplos de widgets. Widgets podem conter outros widgets; por exemplo, uma janela , geralmente, um widget que contem um QMenuBar, algumas QToolBars, uma QStatusBar, e alguns outros widgets. A maioria das aplicaes usa uma QMainWindow ou um QDialog como janelas principais da aplicao, mas Qt to flexvel que qualquer widget pode ser uma janela. Neste exemplo, o widget QLabel a janela da aplicao. A linha 7 torna a label visvel. Widgets so criados, por padro, como invisveis para que possamos customiz-los antes de serem exibidos, deste modo evitando flickering. A linha 8 passa o controle da aplicao para o Qt. Neste ponto, o programa entra no chamado loop de eventos. Imagine o loop de eventos como um modo de espera onde o programa espera por aes do usurio como clicks do mouse ou ento pressionamento de teclas. Aes do usurio geram eventos (tambm chamados de mensagens) para os quais o programa responde, geralmente executando uma ou mais funes. Por exemplo, quando o usurio clica em um widget, os eventos pressionamento do boto do mouse e liberao do boto do mouse so gerados. Neste ponto, aplicaes grficas diferem significativamente de programas BAT convencionais, nos quais praticamente s processam uma entrada, desenvolvem algum procedimento, e terminam sem interao humana.

Por simplicidade, ns no nos preocupamos em chamar o delete para o objeto QLabel ao final da funo main(). Este vazamento de memria (memory leak) inofensivo num programa to pequeno, j que a memria alocada ser desalocada quando o programa terminar. J possvel testar o programa na sua mquina. Ele deve se parecer com o mostrado na Figura 1.1. Primeiro voc ter que instalar o Qt 4.3.2 (ou uma verso mais recente), este procedimento explicado no Apndice A. De agora em diante, vamos assumir que voc tem uma cpia corretamente instalada do Qt e que o diretrio Bin est na sua varivel PATH de ambiente. (No Windows isso feito automaticamente pelo instalador do Qt) Voc tambm precisar que o cdigo deste programa esteja num arquivo hello.cpp nun diretrio chamado hello. Voc mesmo pode escrever o arquivo hello.cpp ou copi-lo dos exemplos que acompanham este livro, que est disponvel em examples/chap01/hello/hello.cpp. (Todo os exemplos esto disponveis no site do livro, http://www.informit.com/title/0132354160.)

Figura 1.1 - Hello no Linux

Do prompt de comando, v at o diretrio hello e digite:


qmake project

Para criar um arquivo projeto que independente da plataforma, e depois digite: qmake hello.pro Para criar um arquivo makefile especfico para a plataforma que est usando. (A ferramenta qmake discutida em mais detalhes no apndice B.) Digite make para construir o programa. Rode-o digitando hello no Windows, ./hello no Unix, e open hello.app no Mac. Para terminar o programa, clique no boto fechar na barra de ttulo da janela.

Se voc estiver usando o Windows e tiver instalado a verso Open Source do Qt e o compilador MinGW, voc ter um atalho chamado prompt de comando do Qt que tem todas as variveis de ambiente corretamente ajustadas. Se voc conseguiu visualizar a janela ento voc pode compilar aplicaes do Qt utilizando qmake e make como descritos anteriormente. Os executveis so colocados na pasta debug ou release da aplicao (por exemplo, C:\examples\chap01\hello\release\hello.exe). Se voc estiver utilizando o Microsoft Visual C++ com uma vero comercial de Qt, voc utilizar o nmake ao invs de make. Alternativamente, voc pode criar um arquivo projeto do Visual Studio a partir do arquivo hello.cpp digitando:
qmake -tp vc hello.pro

Antes de irmos para o prximo exemplo vamos nos divertir um pouco: Substitua a linha
QLabel *label = new QLabel("Hello Qt!"); QLabel *label = new QLabel("<h2><i>Hello</i> " "<font color=red>Qt!</font></h2>");

E recompile a aplicao. Quando rodar, ela deve parecer com a Figura 1.2. Como este exemplo ilustra, fcil diferenciar uma aplicao de interface de usurio Qt utilizando apenas uma formatao HTML. Figura 1.2: Uma label com formatao HTML bsica

FAZENDO CONEXES O Segundo exemplo mostra como responder s aes do usurio. A aplicao consiste em um boto que o usurio pode clicar para sair. O cdigo muito similar ao exemplo anterior, com exceo do uso de um QPushButton no lugar de uma QLabel como nosso widget principal, e ns estamos conectando a ao do usurio (clique) a um bloco de cdigo. O cdigo desta aplicao est em examples/chap01/quit/quit.cpp; a aplicao em andamento mostrada na Figura 1.3. Aqui est o contedo do arquivo: 1 #include <QApplication>

2 #include <QPushButton> 3 int main(int argc, char *argv[]) 4 { 5 QApplication app(argc, argv); 6 QPushButton *button = new QPushButton("Quit"); 7 QObject::connect(button, SIGNAL(clicked()), 8 &app, SLOT(quit())); 9 button->show(); 10 return app.exec(); 11 }

Os widgets de Qt emitem sinais para indicar que uma ao de usurio ou uma mudana de estado ocorreu. [*] Por exemplo, QPushButton emite um sinal clicked() quando o usurio clica no boto. Um sinal pode se conectar com uma funo (chamada de slot nesse contexto) para que quando o sinal for emitido, o slot seja executado automticamente. No nosso exemplo, nos conectamos o sinal clicked() do boto para o slot quit() do objeto QApplication. Os macros SIGNAL() e SLOTS() fazem parte da sintaxe. Vamos construir a aplicao. Assumimos que voc criou um diretrio chamado quit contendo o arquivo quit.cpp. Rode o qmake no diretrio quit para gerar o arquivo projeto, e depois rode-o denovo para gerar o makefile, como segue: qmake -project qmake quit.pro Agora construa a aplicao e rode-a. Se voc clicar em Quit, ou pressionar a barra de espao (que pressiona o boto), a aplicao terminar. ALINHANDO WIDGETS Nesta seo, iremos criar um pequeno exemplo que demonstra como usar layouts para gerenciar a geometria dos widgets em uma janela e como usar sinais e slots para

sincronizar dois widgets. A aplicao (mostrada na Figura 1.4) pergunta pela idade do usurio, a qual o usurio pode informar manipulando um spin box ou um slider.

A aplicao consiste em trs widgets: um QSpinBox, um QSlider e um QWidget. O QWidget a janela principal da aplicao. O QSpinBox e o QSlider so apresentados dentro da QWidget; eles so filhos de QWidget. Alternativamente, podemos dizer que QWidget pai de QSpinBox e QSlider. QWidget no tem pais porque est sendo usada como uma janela top-level. As construtoras de QWidget e de todas as suas subclasses tem QWidget * como parmetro que especifica quem o pai da widget em questo. Cdigo Fonte: 1 2 3 4 #include #include #include #include <QApplication> <QHBoxLayout> <QSlider> <QSpinBox>

5 int main(int argc, char *argv[]) 6 { 7 QApplication app(argc, argv); 8 9 10 11 12 13 14 15 16 17 18 19 QWidget *window = new QWidget; window->setWindowTitle("Enter Your Age"); QSpinBox *spinBox = new QSpinBox; QSlider *slider = new QSlider(Qt::Horizontal); spinBox->setRange(0, 130); slider->setRange(0, 130); QObject::connect(spinBox, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int))); QObject::connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int))); spinBox->setValue(35); QHBoxLayout *layout = new QHBoxLayout;

20 21 22 23 24 25 }

layout->addWidget(spinBox); layout->addWidget(slider); window->setLayout(layout); window->show(); return app.exec();

As linhas 8 e 9 preparam a QWidget que servir como a janela principal da aplicao. Podemos chamar setWindowTitle() para escolher o texto que ser exibido na barra de ttulo da janela. As linhas 10 e 11 criam um QSpinBox e um QSlider, e as linhas 12 e 13 atribuem seus intervalos validos. Podemos assumir que o usurio tem at 130 anos de idade. Poderamos passar window para as construtoras de QSpinBox e QSlider especificando que estes widgets tivessem window como pai deles, mas no necessrio porque o sistema de layout ir configurar isso sozinho e automaticamente atribuir o pai do spin box e do slider, como veremos a seguir. As duas chamadas QObject::connect() mostradas nas linhas 14 at 17 asseguram que o spin Box e o slider estejam sincronizados para que eles sempre mostrem o mesmo valor. Sempre que o valor de um dos widgets mudar, o sinal valueChanged() de um deles ser emitido, e o slot setValue(int) do outro ser chamado com o novo valor. A linha 18 predefine o valor do spin Box para 35. Quando isso acontece, o QSpinBox emite o sinal valueChanged(int) com um argumento do tipo int valendo 35. Esse argumento passado para o slot setValue(int) do QSlider, que ajusta o valor do slider para 35. O slider ento emite um sinal valueChanged(int) porque o prprio valor mudou, ativando o slot setValue(int) do spin Box. Mas nesse ponto, setValue(int) no emite nenhum sinal, j que o valor do spin Box j 35. Isso evita a recurso infinita. A Figura 1.5 ilustra a situao.

Nas linhas 19 at 22, ns alinhamos o spin box e o slider usando um gerenciador de layouts. Este gerenciador um objeto que define o tamanho e a posio dos widgets que esto sob sua responsabilidade. Qt tem trs tipos principais de gerenciadores de layouts: QHBoxLayout alinha os widgets horizontalmente da esquerda para a direita (da direita para a esquerda para algumas culturas). QVBoxLayout alinha os widgets verticalmente de cima para baixo. QGridLayout alinha os widgets em um grid. A chamada de QWidget::setLayout() na linha 22 instala o gerenciador na janela. Mas na verdade, o QSpinBox e o QSlider tm seu pai redefinido para o widget no qual o layout foi instalado, e por essa razo no temos que especificar um pai explicito quando costrurmos a widget que ser colocada em um layout.

Apesar de no termos escolhido a posio e o tamanho dos widgets explicitamente, o QSpinBox e o QSlider so dispostos visualmente de modo agradvel de um lado at o outro. Isso ocorre porque QHBoxLayout automaticamente designa posies e tamanhos razoveis para os widgets dos quais responsvel, baseado nas necessidades deles. Os gerenciadores de layouts nos privam da ocupao de posicionar, por puro cdigo, os objetos na tela e garantem que a janela se redimensione suavemente. O modo apresentado por Qt para construir interfaces de usurio simples de entender e muito flexvel. O procedimento mais comum que programadores Qt utilizam instanciar os widgets necessrios e depois definir suas propriedades conforme necessrio. Programadores adicionam widgets aos layouts, que automaticamente cuidam do posicionamento e redimensionamento. O comportamento da interface de usurio gerenciada conectando widgets uns aos outros usando o mecanismo de sinais e slots. As screenshots tiradas at agora foram tiradas no Linux, mas aplicaes Qt so mostradas com a interface nativa de cada plataforma. Qt consegue fazer isso emulando o look and feel de cada plataforma, ao invs de conter um kit de widgets de uma plataforma particular.

O estilo Plastique o estilo padro para aplicaes Qt/X11 rodando sob o KDE, e o Cleanlooks o padro sob o GNOME. Estes estilos utilizam gradientes e anti-aliasing para gerar um look nfeel moderno. Usurios de aplicaes Qt podem substituir os estilos padro utilizando o comando style na linha de comando. Por exemplo, para iniciar a aplicao de idade acima utilizando o estilo Motif sob X11, simplesmente digite o comando: ./age -style motif Diferente dos outros estilos, Windows XP, Windows Vista, e Mac apenas so visualizados nas plataformas nativas, j que dependem e dispositivos de tema

de cada plataforma. Um estilo adicional chamado QtDotNet esta disponvel no Qt Solutions. Tambm possvel criar estilos customizados, como ser explicado no Captulo 19. UTILIZANDO A REFERNCIA A documentao de referncia do Qt uma ferramenta essencial para qualquer desenvolvedor. Ela cobre todas as classes e funes no Qt. Esse livro faz o uso de diversas classes e funes do Qt, mas no cobre todas elas e nem fornece todos os detalhes das que so mencionadas. Para tirar proveito mximo do Qt, voc deve se familiarizar com a documentao do Qt o mais rpido possvel. A documentao est disponvel em HTML no diretrio doc/HTML do Qt e pode ser lida atravs de qualquer browser. Voc tambm pode usar o Qt Assistant, o browser de ajuda do Qt, que tem recursos poderosos de busca e indexao que o tornam mais rpido e fcil comparado com um web browser. Para iniciar o Qt Assistant, clique em Qt by Trolltech v4.x.x|Assistant no Windows, digite assistant na linha de comando no Unix, ou d um duplo-clique em Assistant na busca do Mac. Os links na seo Referncia API na pgina inicial fornecem diferentes modos de navegar pelas classes de Qt. A pgina Todas as classes list todas as classes na API do Qt. A pgina Classes principais lista apenas as classes mais utilizadas de Qt. Como um exerccio, procure as classes e funo que utilizamos neste captulo.

Note que funes herdadas so documentadas na classe base; por exemplo, QPushbutton no tem uma funo prpria show(), mas herda uma de QWidget. A Figura 1.9 mostra como as classes que vemos at agora se relacionam umas com as outras.

A documentao de referncia para a atual verso do Qt e para algumas verses mais recentes est disponvel online em http://doc.trolltech.com/. Este site tambm tem artigos selecionados do Qt Quarterly, o newsletter dos programadores Qt enviado para todas as licenas comerciais. Este captulo introduziu os conceitos chave de conexes signal-slot e layouts. Ele tambm comeou a revelar a perspectiva de total e consistente de orientao a objetos at construo e uso de widgets. Se voc procurar pela documentao do Qt, voc encontrar uma uniformidade de exibio que a torna bem direta no que se diz respeito ao uso de novos widgets, e voc tambm descobrir que Qt escolheu cuidadosamente os nomes para funes, parmetros, enums e assim por diante, que fazem com que programar em Qt se torne incrivelmente agradvel e fcil. Os captulos seguintes da parte 1 se apiam nos fundamentos aqui abordados, mostrando como criar uma GUI completa com menus, toolbars, janelas de documentos, status bars, e dialogs, em conjunto com a funcionalidade bsica de leitura, processamento e escritura de arquivos.

2. Criando Dialogs Herdando de QDialog Signals e Slots a Fundo

Design Rpido de Dialogs Modificando a Forma dos Dialogs Dialogs Dinmicos Classes Nativas de Widgets e Dialogs Este captulo vai te ensinar como criar caixas de dilogo utilizando Qt. Caixas de dilogo apresentam aos usurios opes e escolhas, e permitem que eles ajustem opes dos seus parmetros preferidos e que faam suas escolhas. Eles so chamados de caixas de dilogo, ou apenas dialogs, porque eles fornecem os meios pelos quais os usurios conversam com as aplicaes. A maioria das aplicaes GUI (graphics user interface, ou interface grfica de usurio) consiste em uma mainwindow com um menubar e uma toolbar, em conjunto com dezenas de dialogs que complementam a mainwindow. Tambm possvel criar dialogs que respondam diretamente s escolhas do usurio aplicando as aes necessrias (por exemplo, uma calculadora). Criaremos nosso primeiro dialog puramente por cdigo e mostrar como funciona. Depois veremos como criar dialogs pelo Qt Designer, a ferramenta visual de design do Qt. Utilizar o Qt designer um jeito muito mais rpido (do que cdigo puro) e faz com que seja fcil testar designs diferentes e at mesmo modificar designs j existentes no futuro.

Herdando de QDialog
Nosso primeiro exemplo um dialog de busca escrito totalmente em C++. Ele mostrado na Figura 2.1. Vamos implementar o dialog como uma classe prpria. Fazendo isso, a tornamos independente, um componente encapsulado, com signals e slots prprios.

O cdigo fonte est dividido em dois arquivos: finddialog.h e finddialog.cpp. Comearemos pelo header (finddialog.h):

1 2 3 4 5 6 7

#ifndef FINDDIALOG_H #define FINDDIALOG_H #include <QDialog> class QCheckBox; class QLabel; class QLineEdit; class QPushButton;

As linhas 1 e 2 (e 27) protegem o header contra mltiplos includes. A linha 3 inclui a definio de QDialog, a classe base para dialogs em Qt. QDialog derivado de QWidget. A linha 4 at 7 apresenta foward declarations das classes de Qt que sero utilizadas na implementao do dialog (em finddialog.cpp). [*] Uma foward declaration diz ao compilador C++ que esta classe existe sem dar mais detalhes sobre a definio da classe (normalmente localizada no header da classe). Voltaremos a falar disso em breve. Depois, definimos FindDialog como uma subclasse de QDialog: 8 class FindDialog : public QDialog 9 { 10 Q_OBJECT 11 public: 12 FindDialog(QWidget *parent = 0); O macro Q_OBJECT no comeo da definio da classe necessrio para todas as classes que definem seus prprios signals e slots. [*] Foward declarations s podem ser utilizadas quando as variveis forem ponteiros. O compilador no precisa, de inicio, de mais informaes sobre a classe. J que todos os ponteiros tem tamanho fixo: 4 bytes. A construtora de FindDialog um exemplo tpico de classes Qt. O parmetro parent especifica o widget pai. O padro para este parmetro um ponteiro nulo, significando que o widget no tem pai.
13 signals: 14 void findNext(const QString &str, Qt::CaseSensitivity cs);

15 void findPrevious(const QString &str, Qt::CaseSensitivity cs);

A seo signal declara dois sinais que o dialog emitir quando o usurio clicar no boto Find. Se a opo Search Backward estiver selecionada, o dialog emite findPrevious() caso contrrio, emite findNext(). A palavra-chave signals , na verdade, um macro. O pr-processador C++ converte-o para padres C++ antes que o compilador veja. Qt::CaseSensitivity um enum que pode assumir os valores Qt::CaseSensitive (um) e Qt::CaseInsensitive (zero). 16 17 18 19 20 21 22 23 24 25 26 27 private slots: void findClicked(); void enableFindButton(const QString &text); private: QLabel *label; QLineEdit *lineEdit; QCheckBox *caseCheckBox; QCheckBox *backwardCheckBox; QPushButton *findButton; QPushButton *closeButton; }; #endif

Na seo privada da classe, declaramos dois slots. Para implementar os slots, precisaremos acessar a maioria dos widgets filhos do dialog, ento mantemos ponteiros para eles tambm. A palavra-chave slot , como signals, um macro que se transforma em uma construo que C++ pode digerir. Para as variveis private, usamos foward declarations das respectivas classes. Isso foi possvel porque so todos ponteiros e no precisamos acess-los no header, ento o compilador no precisa de todas as definies da classe. Poderamos ter includo os includes (<QCheckBox>, <QLabel>, etc.), mas utilizando foward declarations quando possvel torna o tempo de compilao menor. Vamos para o arquivo da implementao da classe FindDialog, finddialog.cpp: 1 #include <QtGui> 2 #include "finddialog.h"

Primeiramente inclumos <QtGui>, um arquivo header que contm a definio das classes GUI de Qt. Qt consiste de vrio mdulos, cada um com sua prpria livraria. O mdulos mais importantes so QtCore, QtGui, QtNetwork, QtOpenGL, QtScript, QtSql, QtSvg e QtXml. O header <QtGui> contem a definio de todas as classes que so parte dos mdulos QtCore e QtGui. Incluir este header nos economiza a incluso individual de cada classe do nosso dialog. No arquivo finddialog.h, ao invs de incluir <QDialog> e utilizar foward declarations para QCheckBox, QLabel, QLineEdit e QPushButton, poderamos simplesmente ter includo <QtGui>. Entretanto no aconselhvel incluir um arquivo header to grande, especialmente em aplicaes grandes. 3 FindDialog::FindDialog(QWidget *parent) 4 : QDialog(parent) 5 { 6 label = new QLabel(tr("Find &what:")); 7 lineEdit = new QLineEdit; 8 label->setBuddy(lineEdit); 9 caseCheckBox = new QCheckBox(tr("Match &case")); 10 backwardCheckBox = new QCheckBox(tr("Search &backward")); 11 findButton = new QPushButton(tr("&Find")); 12 findButton->setDefault(true); 13 findButton->setEnabled(false); 14 closeButton = new QPushButton(tr("Close")); Na linha 3, passamos o parmetro parent para a construtora da classe base. Depois criamos os objetos filhos. A funo tr() marca a string literal para futuras tradues para outras lnguas. Esta funo declara em QObject e em todas as classes que contem o macro Q_OBJECT. um bom habito cercar strings visveis aos usurios com tr(), mesmo que voc no tenha planos imediatos para traduzir sua aplicao para outras lnguas. Tradues sero estudadas no Captulo 18. Em strings literais, utilizamos o smbolo & para indicar teclas de atalho. Por exemplo, a linha 11 cria o boto Find, o qual o usurio pode ativar utilizando Alt+F nas plataformas que suportam teclas de atalho. O smbolo & tambm pode ser utilizado para controlar o foco: na linha 6 criamos uma label com a tecla de atalho (Alt+W), e na linha 8 nos definimos o lineedit como companheiro (buddy) da label. Um buddy um widget que aceita o foco quando a tecla de atalho do outro pressionada. Ento

quando o usurio pressiona Alt+W (atalho da label), o foco vai para o lineedit (buddy da label). Na linha 12, fazemos com que o boto Find seja o padro chamando setDefault(true). O boto padro o boto que, quando o usurio pressiona Enter, pressionado. Na linha 13, desabilitamos o boto Find. Quando um widget est desabilitado ele geralmente mostrado em tons acinzentados e no responder s interaes do usurio. 15 16 17 18 19 20 connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(enableFindButton(const QString &))); connect(findButton, SIGNAL(clicked()), this, SLOT(findClicked())); connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));

O slot privado enableFindButton(const QString &) chamado sempre que o texto do lineedit mudar. O slot privado findClicked() chamado quando o usurio clicar no boto Find. O dialog se fecha quando o usurio clicar em Close. O slot close() herdado de QWidget, e o seu comportamento padro esconder o widget de visualizao (sem deletlo). Estudaremos o cdigo para os slots enableFindButton() e findClicked() mais adiante. J que QObject um dos ancestrais do nosso dialog, ento podemos omitir o prefixo QObject:: das chamadas de connect(). 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 QHBoxLayout *topLeftLayout = new QHBoxLayout; topLeftLayout->addWidget(label); topLeftLayout->addWidget(lineEdit); QVBoxLayout *leftLayout = new QVBoxLayout; leftLayout->addLayout(topLeftLayout); leftLayout->addWidget(caseCheckBox); leftLayout->addWidget(backwardCheckBox); QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->addWidget(findButton); rightLayout->addWidget(closeButton); rightLayout->addStretch(); QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addLayout(leftLayout); mainLayout->addLayout(rightLayout); setLayout(mainLayout);

Depois disso, ns alinhamos os widgets filhos utilizando gerenciadores de layout. Layouts podem conter ambos widgets ou outros layouts. Misturando QHBoxLayouts, QVBoxLayout e QGridLayout, possvel gerar dialogs bem sofisticados. Para nosso dialog, usaremos dois QHBoxLayout e dois QVBoxLayout, como mostrados na Figura 2.2. O layout externo o principal; ele instalado no FindDialog na linha 35 e responsvel por toda a rea do layout. Os outros trs layouts so sublayouts. A pequena mola na parte inferior direita da Figura 2.2. um spacer (ou stretch). Ele usa o espao vazio abaixo dos botes Find e Close, garantindo que esses botes ocupem o topo do layout em que esto.

Um aspecto sutil de classes de gerenciamento de layouts que no so widgets. Ao invs disso, so derivadas de QLayout. Que por sua vez derivado de QObject. Na figura, widgets so representados por linhas solidas e layouts so representados por linhas tracejadas para destacar a diferena entre eles. Durante a execuo do programa, layouts so invisveis. Quando sub-layouts so adicionados ao layout pai (linhas 23, 33 e 34), os sublayouts tm seus pais redefinidos. Da, quando o layout principal instalado no dialog (linha 35), ele se torna filho do dialog e todos os widgets dentro dos layouts tm seus pais redefinidos como o dialog. A hierarquia resultante mostrada na Figura 2.3. 36 setWindowTitle(tr("Find")); 37 setFixedHeight(sizeHint().height()); 38 }

Finalmente, definimos o titulo a ser mostrado na barra de ttulo e uma altura fixa para a janela, j que no existem widgets dentro do dialog que possam ocupar um espao vertical significativo. A funo QWidget::sizeHint() retorna o tamanho ideal do widget. Isso completa a reviso da construtora da classe FindDialog. J que criamos os objetos e layouts atravs de new, razovel pensar que devemos deletar cada widget e layout com um delete. Mas no necessrio, Qt automaticamente deleta objetos filhos quando o pai destrudo, e todos os widgets e layouts do nosso dialog so descendentes do prprio dialog. Agora, vamos definir os slots do dialog: 39 40 41 42 43 44 45 46 47 48 void FindDialog::findClicked() { QString text = lineEdit->text(); Qt::CaseSensitivity cs = caseCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive; if (backwardCheckBox->isChecked()) { emit findPrevious(text, cs); } else { emit findNext(text, cs);

49 50 51 52 53 54

} } void FindDialog::enableFindButton(const QString &text) { findButton->setEnabled(!text.isEmpty()); }

O slot FindClicked chamado quando o usurio clica no boto Find. Ele emite o sinal findPrevious() ou findNext(), dependendo da opo Search Backward. A palavrachave emit especfica do Qt; como outras extenses Qt ela convertida em C++ padro pelo pr-processador C++. O slot enableFindButton() chamado sempre que o usurio muda o texto contido no lineedit. Isso ativa o boto se houver algum texto no lineedit, e desabilita caso contrrio. Estes dois slots finalizam o dialog. Podemos, ento, criar um main.cpp para testar nosso widget FindDialog: 1 2 3 4 5 6 7 8 9 #include <QApplication> #include "finddialog.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); FindDialog *dialog = new FindDialog; dialog->show(); return app.exec(); }

Para compilar o programa, utilize qmake como de usual. Uma vez que a definio da classe FindDialog contem o macro Q_OBJECT, ento o makefile gerado pelo qmake incluir regras especiais para executar o moc, o meta-object compiler do Qt. (O sistema meta-object ser estudado na prxima seo). Para que o moc funcione corretamente necessrio colocar a definio da classe em um arquivo header, fora da implementao. O cdigo gerado pelo moc inclui esse arquivo header e adiciona alguns cdigos padres do C++ por si prprio. Classes q utilizam o macro Q_OBJECT devem ser executadas pelo moc. Isso no um problema porque qmake automaticamente adiciona as regras necessrias para o makefile. Mas se voc esquecer de gerar seu makefile usando qmake e o moc no tiver rodado, o linker reclamar dizendo que algumas funes foram declaradas mas no implementadas. As mensagens podem ser relativamente obscuras. O GCC produz mensagens de erro desse tipo:

finddialog.o: In function `FindDialog::tr(char const*, char const*)': /usr/lib/qt/src/corelib/global/qglobal.h:1430: undefined reference to `FindDialog::staticMetaObject' A sada do Visual C++ comea assim: finddialog.obj : error LNK2001: unresolved external symbol "public:~virtual int __thiscall MyClass::qt_metacall(enum QMetaObject ::Call,int,void * *)" Se isso acontecer com voc algum dia, execute o qmake de novo para atualizar o arquivo makefile, e reconstrua a aplicao. Fornecer uma ordem razovel para a tecla tab e outros atalhos garante que usurios que no querem (ou que no podem) usar um mouse sejam capazes de usar ao mximo a aplicao. Controle total sobre o teclado tambm apreciado por digitadores rpidos. No Captulo 3, ns usaremos o dialogo de busca numa aplicao real, e ns conectaremos os sinais findPrevious() e findNext() a alguns slots.

Signals e Slots a Fundo


O mecanismo de sinais e slots fundamental para programao em Qt. Ele permite que o programador estabelea um elo entre os objetos sem que os objetos saibam uns dos outros. Ns j conectamos alguns sinais e slots, declaramos nossos prprios sinais e slots, implementamos nossos slots e emitimos nossos sinais. Vamos dar uma olhada no mecanismo mais de perto. Slots so quase idnticos a funes membro ordinrias do C++. Elas podem ser virtuais, podem ser sobrecarregadas, podem ser publicas, protecteds ou privates, podem ser diretamente chamadas como qualquer outra funo membro do C++, e os seus parmetros podem ser de qualquer tipo. A diferena que um slot tambm pode ser conectado a um sinal, que no caso ser chamado toda vez que o sinal for emitido. A declarao da funo connect() se d da seguinte forma:

connect(sender, SIGNAL(signal), receiver, SLOT(slot)); Onde sender e receiver so ponteiros para QObjects e signal e slot so assinaturas de funes sem nomes de parmetros. Os macros SIGNAL() e SLOT() essencialmente convertem seus argumentos para uma string. Nos exemplos que vimos at agora, ns sempre conectamos sinais diferentes com slots diferentes. Existem outras possibilidades: Um sinal pode ser conectado a vrios slots: connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int))); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updateStatusBarIndicator(int))); Quando o sinal emitido, os slots so chamados um a um, numa ordem no especificada. Vrios sinais podem ser conectados ao mesmo slot: connect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError())); connect(calculator, SIGNAL(divisionByZero()), this, SLOT(handleMathError())); Quando qualquer dos sinais for emitido, o slot chamado. Um sinal pode ser conectado a outro sinal: connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SIGNAL(updateRecord(const QString &))); Quando o primeiro sinal emitido, o Segundo sinal tambm emitido. Alm disso, conexes signal-signal so indistinguveis de conexes signal-slot. Conexes podem ser removidas: disconnect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));

Isso raramente necessrio, porque Qt automaticamente remove todas as conexes envolvendo um objeto quando ele deletado. Para conectar um sinal a um slot com sucesso (ou outro signal), eles precisam ter os mesmos tipos de parmetros na mesma ordem:
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)), this, SLOT(processReply(int, const QString &)));

Excepcionalmente, se um signal tem mais parmetros que o slot ao qual est conectado, os parmetros adicionais so simplesmente ignorados.
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)), this, SLOT(checkErrorCode(int)));

Se os tipos de parmetros so incompatveis, ou se o sinal ou slot no existir, Qt ira informar atravs de um warning em tempo de execuo (se a aplicao for construda no modo debug). Similarmente, Qt ir lanar um warning se os nomes dos parmetros estiverem inseridos nas assinaturas dos signals ou slots. At agora, apenas utilizamos sinais e slots com widgets. Mas o mecanismo implementado para QObject e no limitado a programao GUI. O mecanismo pode ser usado por qualquer subclasse de QObject: class Employee : public QObject { Q_OBJECT public: Employee() { mySalary = 0; } int salary() const { return mySalary; } public slots: void setSalary(int newSalary); signals: void salaryChanged(int newSalary); private: int mySalary; }; O Sistema Meta-Object de Qt

Uma das maiores conquistas do Qt foi a extenso do C++ com um mecanismo de criao de componentes de software independentes que podem ser ligados sem nem saberem informaes sobre o componente aos quais esto conectados. O mecanismo chamado sistema meta-object e fornece dois servios chave: signals e slots, e introspeco. A funcionalidade da introspeco necessria para implementar signals e slots, e permite programadores obterem informaes meta sobre sub-classes QObject em tempo de execuo, incluindo a lista de sinais e slots suportados pelo objeto e nome da classe. O mecanismo tambm suporta propriedades (amplamente usadas pelo Qt Designer) e traduo de texto (para internacionalizao), e estabelece as bases do mdulo QtScript. A partir do Qt 4.2, propriedades podem ser adicionadas dinamicamente, um recurso que veremos em ao nos captulos 19 e 22. O C++ padro no fornece suporte para as meta-informaes dinmicas que o sistema de meta-objeto de Qt precisa. Qt resolve isso com uma ferramenta separada, o moc. Ele analisa as definies de Q_OBJECT e faz com que a informao fique disponvel atravs de funes C++. J que o moc implementa toda a sua funcionalidade usando C++ ento o sistema meta-objeto funciona com qualquer compilador C++. O mecanismo funciona da seguinte forma: O macro Q_OBJECT declara algumas funes de introspeco que devem ser implementadas em cada subclasse de QObject: metaObject(), tr(), qt_metacall(), entre outras. A ferramenta moc gera implementaes para as funes declaradas por Q_OBJECT e para todos os signals. As funes membro de QObject (como connect() e disconnect() ) usam as funes de introspeco para fazer seus trabalhos Tudo isso feito automaticamente pelo qmake, moc e QObject, ento voc dificilmente ter que pensar nisso. Mas se estiver curioso voc pode ler a documentao da classe QMetaObject e os cdigos C++ gerados pelo moc para ver como a implementao funciona. void Employee::setSalary(int newSalary) { if (newSalary != mySalary) { mySalary = newSalary;

emit salaryChanged(mySalary); } } Perceba como o slot setSalary() implementado. Ns emitimos o sinal salaryChanged() apenas se newSalary != mySalary. Isso garante que as conexes cclicas no levem a loops infinitos.

Design Rpido de Dialogs


Qt foi planejado para ser agradvel e intuitivo para desenvolver cdigo, e no difcil encontrar programadores que desenvolvem suas aplicaes inteiras escrevendo cdigo C++. Porm, muitos programadores preferem utilizar uma aproximao visual para desenvolver formulrios. Porque eles acham que este mtodo mais natural e rpido do que puro cdigo, e eles querem estar aptos a testar e mudar designs com mais rapidez e facilidade do que quando desenvolvidos com cdigos puros. O Qt Designer expande as opes disponveis aos programadores fornecendo uma capacidade visual de design. Qt Designer pode ser usado para desenvolver todos ou apenas alguns dos formulrios da aplicao. Formulrios que so criados utilizando o Qt Designer so convertidos em cdigo C++, ento o Qt Designer pode ser usado com uma variedade de ferramentas convencionais e no impe especificaes especiais ao compilador. Nessa seo, usaremos o Qt Designer para criar o dilogo Ir para a clula mostrado na Figura 2.4. Criar o dialog no Qt Designer ou por cdigo sempre envolve alguns passos fundamentais: 1. Criar e inicializar widgets filhos. 2. Coloc-los em um layout. 3. Atribuir a ordem dos tabs. 4. Estabelecer conexes signal/slots. 5. Implementar os prprios slots do dialog.

Para iniciar o Qt Designer, clique em Qt by Trolltech v4.x.y|Designer no menu Iniciar, digite designer na linha decomando do Unix, ou d um duplo-clique em Designer no MAC finder. Quando o Qt Designer iniciar, ele ir mostrar uma lista de templates. Clique no template Widget e depois clique em Create. (O template Dialog with Buttons Bottom pode parecer tentador, mas para esse exemplo ns criaremos os botes OK e Cancel manualmente e mostrar como feito.) Voc deve, agora, ter uma janela chamada Untitled. Por padro, a interface de usurio do Qt consiste em vrias janelas top-level. Se voc preferir uma interface MDI (multiple document interface) com uma janela topwindow e vrias subjanelas, como mostrado na Figura 2.5, clique em Edit|Preferences e ajuste o modo de interface do usurio para Docked Window.

O primeiro passo criar widgets filhos e coloc-los no formulrio. Criar uma label, um line editor, um spacer horizontal, e dois botes. Para cada item, arraste o nome ou o cone da caixa de widgets do Qt Designer e solte-o aproximadamente onde ele deveria estar no formulrio. O spacer, que invisvel no formulrio final, mostrado no Qt Designer como uma pequena mola azul. Agora arraste a borda inferior do formulrio pra encurt-lo. Isso deveria produzir um formulrio que similar ao da Figura 2.6. No gaste muito tempo posicionando os itens precisamente no formulrio. Os gerenciadores de layouts vo posicion-los de forma precisa posteriormente.

Utilizando o editor de propriedades do Qt Designer ajuste as propriedades de cada widget: 1. Clique na label TextLabel. Certifique-se de que a propriedade objectName dela seja label e mude a propriedade text para &Cell Location:. 2. Clique no line editor. Certifique-se de que a propriedade objectName seja lineEdit. 3. Clique no primeiro boto. Defina a propriedade objectName como okButton, a propriedade enabled para false, a propriedade text para OK, e a propriedade default para true. 4. Clique no segundo boto. Defina a propriedade objectName para cancelButton e a propriedade texto para Cancel. 5. Clique no fundo do formulrio para selecion-lo. Ajuste a propriedade objectName para GoToCellDialog e a propriedade windoTitle para Go to Cell. Todos os widgets parecem bem, exceto pela label que mostra &Cell Location. Escolha Edit|Edit Buddies para entrar em um modo especial de edio que te permite escolher os Buddies. Depois, clique na tabela, mantenha o boto apertado e arraste a seta vermelha para o line Edit, e depois libere o boto. A label deveria exibir, agora, o texto Cell Location, como mostrado na Figura 2.7 e tem o line Edit com seu buddy. Para deixar o modo de edio de buddies clique em Edit|Edit Widgets.

O prximo passo alinhar os widgets no formulrio: 1. Clique na label Cell Location , pressione a tecla Shift e clique no line Edit para selecionar os dois widgets. Clique em Form|Lay Out Horizontally. 2. Clique no spacer, segure o Shift e clique em ambos os botes. Clique em Form|Lay Out Horizontally 3. Clique no fundo do formulrio para remover a seleo anterior de qualquer widget, depois clique em Form|Lay Out Vertically. 4. Clique em Form|Adjust Size para redimensionar o formulrio para o seu tamanho preferencial. As linhas vermelhas mostradas no formulrio mostram os layouts que foram criados, como mostrado na Figura 2.8. Eles no so visveis quando o formulrio executado.

Agora clique em Edit|Edit Tab Order. Um nmero em um retngulo azul aparecer prximo de cada widget que possa admitir foco, como mostrado na Figura 2.9. Clique em cada widget para escolher a ordem de cada um, depois clique em Edit|Edit Widgets para deixar o modo de edio de tabs.