c-gui-programming-with-qt-4-2ndedition -cap1e2

45
C++ GUI Programando com Qt4, Segunda Edição Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com 1 Autores: Jasmin Blanchette e Mark Summerfield Tradução por: Danilo Domingos, Arthur dos Santos Dias Revisão e edição por: Thiago Rossener Nogueira Visão Geral O único oficialmente publicado guia das melhores práticas para programação Qt 4.3 Usando o Qt da Trolltech é possível criar aplicações C++ de alta performance que rodem em máquinas Windows, Linux/Unix, Mac OS X, e demais extensões Linux sem que seja necessário fazer alterações de código. Agora, dois membros da Trolltech lhe oferecem este guia completo para que você alcance resultados surpreendentes com a mais recente versão do Qt: Qt 4.3. Carregado com exemplos práticos, realistas e conselhos profundos, este é o livro usado pela Trolltech para ensinar Qt para seus próprios novos funcionários. Revisado e expandido constantemente, este livro nos revela os melhores padrões atuais para se trabalhar com Qt para diversos usos, que vão desde implementação de arquiteturas de modelagem até o uso da engine gráfica do Qt 4.3. Você encontrará soluções para diversas tarefas de desenvolvimento GUI, assim como técnicas sofisticadas para sistemas com acesso a banco de dados, integração com XML, uso de subclasses, composição, e muito mais. Seja você um novo usuário de Qt, ou um usuário antigo que está aprendendo a nova versão, este livro certamente vai lhe ajudar a tirar vantagem de tudo que o Qt 4.3 é capaz de fazer. Eis algumas novidades que encontrarão neste livro: Atualizado completamente, com uma novíssima cobertura de banco de dados, XML, e programação Qtopia. Cobertura de tudo que mudou do Qt 4.2 para 4.3, incluindo integração com Windows Vista, suporte nativo a CSS para estilização de aplicativos, e geração de arquivos SVG. Capítulos separados para assuntos relacionados a 2D e 3D, cobertura das novas classes de visualização gráfica do Qt 4.3, além de uma cobertura total do QPainter‟s OpenGL. Novos capítulos a respeito de otimização look-and-feel e sobre criação de scripts para aplicações. Ilustra a arquitetura de visualização e modelagem do Qt4, suporte a plugins, manutenção de layout, processamento de eventos, classes containers, e muito mais. Apresenta técnicas avançadas vistas em nenhum outro livro - desde criação de plugins até interação com APIs nativas. Inclui um novo apêndice de Qt Jambi, a nova versão Java do Qt. qtbrasil.com

Upload: cedemir-pereira

Post on 14-Dec-2014

678 views

Category:

Education


11 download

DESCRIPTION

livro traduzido de c++ com QT(alguns capitulos)

TRANSCRIPT

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

1

Autores: Jasmin Blanchette e Mark Summerfield

Tradução por: Danilo Domingos, Arthur dos Santos Dias

Revisão e edição por: Thiago Rossener Nogueira

Visão Geral

O único oficialmente publicado guia das melhores práticas para programação Qt 4.3

Usando o Qt da Trolltech é possível criar aplicações C++ de alta performance que rodem em máquinas Windows, Linux/Unix, Mac OS X, e demais extensões Linux sem que seja necessário fazer alterações de código. Agora, dois membros da Trolltech lhe oferecem este guia completo para que você alcance resultados surpreendentes com a mais recente versão do Qt: Qt 4.3.

Carregado com exemplos práticos, realistas e conselhos profundos, este é o livro usado pela

Trolltech para ensinar Qt para seus próprios novos funcionários. Revisado e expandido constantemente, este livro nos revela os melhores padrões atuais para se trabalhar com Qt para diversos usos, que vão desde implementação de arquiteturas de modelagem até o uso da engine gráfica do Qt 4.3. Você encontrará soluções para diversas tarefas de desenvolvimento GUI, assim como técnicas sofisticadas para sistemas com acesso a banco de dados, integração com XML, uso de subclasses, composição, e muito mais. Seja você um novo usuário de Qt, ou um usuário antigo que está aprendendo a nova versão, este livro certamente vai lhe ajudar a

tirar vantagem de tudo que o Qt 4.3 é capaz de fazer.

Eis algumas novidades que encontrarão neste livro:

Atualizado completamente, com uma novíssima cobertura de banco de dados, XML, e programação Qtopia.

Cobertura de tudo que mudou do Qt 4.2 para 4.3, incluindo integração com Windows Vista, suporte nativo a CSS para estilização de aplicativos, e geração de arquivos SVG.

Capítulos separados para assuntos relacionados a 2D e 3D, cobertura das novas classes de visualização gráfica do Qt 4.3, além de uma cobertura total do QPainter‟s OpenGL.

Novos capítulos a respeito de otimização look-and-feel e sobre criação de scripts para aplicações.

Ilustra a arquitetura de visualização e modelagem do Qt4, suporte a plugins, manutenção de layout, processamento de eventos, classes containers, e muito mais.

Apresenta técnicas avançadas vistas em nenhum outro livro - desde criação de plugins até interação com APIs nativas.

Inclui um novo apêndice de Qt Jambi, a nova versão Java do Qt.

qtbrasil.com

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

2

Tabela de Conteúdos Parte 1: Qt Básico Capítulo 1: Começando Hello Qt Fazendo Conexões Alinhando Widgets Usando a Documentação de Referência Capítulo 2: Criando Dialogs Herdando de QDialog Signals e Slots a Fundo Design Rápido de Dialogs Modificando a Forma dos Dialogs

Dialogs Dinâmicos Classes Nativas de Widgets e Dialogs Capítulo 3: Criando Janelas Principais Subclasse QMainWindow Criando Menus e Barras de Ferramentas Ajustando a Barra de Status Desenvolvendo o Menu Arquivo

Usando Dialogs Armazenando Configurações Documentos Múltiplos Misturar Telas Capítulo 4: Implementação da Funcionalidade da Aplicação O Widget Central Subclasse QTableWidget Carregando e Salvando Implementação do Menu Editar Implementando os Outros Menus Subclasse QTableWidgetItem Capítulo 5: Criando Widgets Customizáveis Customizando Qt Widgets Subclasse QtWidget Integrando Widgets Customizáveis com Qt Designer

Buffering Duplo Parte II: Qt Intermediário

Capítulo 6: Gerenciamento de Layout Modelando Widgets em um Form Layouts Empilhados Splitters Áreas Roláveis Janelas Dock e Barras de Ferramentas Interface de Documento Múltiplo

Capítulo 7: Processamento de Eventos Reimplementando Manipuladores de Eventos Instalando Filtros de Eventos Manter Respondendo durante Processamento Intensivo Capítulo 8: Gráficos 2D Pintando com QPainter Transformações de Coordenadas de Sistema Renderizando em Alta-Qualidade com QImage Renderizando Itens Básicos com Visualizadores de Gráfico Imprimindo Capítulo 9: Arrastar e Soltar Habilitando Arrastar e Soltar Suportando Tipos de Arrastar Personalizados

Manipulação de Clipboard

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

3

Capítulo 10: Classes de Visualização de Itens Usando Classes de Visualização de Itens Convenientes Usando Modelos Predefinidos Implementando Modelos Customizáveis Implementando Delegates Customizáveis Capítulo 11: Classes de Container Containers Sequenciais Containers Associativos Algoritmos Genéricos String, Byte Arrays e Variants Capítulo 12: Input/Output Lendo e Escrevendo Dados Binários

Lendo e Escrevendo Texto Atravessando Diretórios Incorporando Recursos Processo Interno de Comunicação Capítulo 13: Bancos de Dados Conectando e Consultando Visualização de Tabelas

Edição de Registros Usando Formulários Apresentação de Dados em Formulários Tabulares Capítulo 14: Multithreading Criação de Threads Sincronização de Threads Comunicação com a Thread Principal Usando Classes do Qt em Threads Secundárias Capítulo 15: Rede Escrevendo Clientes FTP Escrevendo Clientes HTTP Gravando Aplicativos Cliente-Servidor TCP Enviar e Receber Datagramas UDP Capítulo 16: XML Ler XML com QXmlStreamReader Ler XML com DOM

Ler XML com SAX Escrever XML Capítulo 17: Fornecendo Ajuda Online Tooltips, Status Tips, e Ajuda “O que é isso?” Usando um Navegador Web para Fornecer Ajuda Online Usando o QTextBrowser como um Motor de Ajuda Simples Usando o Qt Assistant para uma Poderosa Ajuda Online Parte III: Qt Avançado Capítulo 18: Internacionalização Trabalhando com Unicode Fazendo Aplicações de Tradução Consciente Troca de Linguagem Dinâmica Traduzindo Aplicações Capítulo 19: Aparência e Personalização Usando Qt Style Sheets Herdando de QStyle Capítulo 20: Gráficos 3D Desenhando Usando OpenGL Combinando OpenGL e QPainter Fazer Sobreposições Usando Objetos Framebuffer Capítulo 21: Criando Plugins Estendendo Qt com Plugins

Fazendo Aplicações com Plugins Concientes

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

4

Escrevendo Aplicações com Plugins Capítulo 22: Script de Aplicações Resumo da Linguagem EMAScript Estendendo Aplicações Qt com Script Implementando Extensões GUI Usando Scripts Automatização de Tarefas Usando Scripts Capítulo 23: Recursos Específicos da Plataforma Interface com APIs Nativas Usando o ActiveX no Windows Manipulação de Gerenciamento de Sessão X11 Capítulo 24: Programação Incorporada Introdução ao Qt/ Incorporado ao Linux

Customizando Qt/ Incorporado ao Linux Integrando Aplicações Qt com Qtopia Usando APIs do Qtopia Parte IV: Apêndices Apêndice A: Obtenção e Instalação do Qt Nota sobre Licenciamento Instalando Qt/Windows Instalando Qt/Mac Instalando Qt/X11 Apêncide B: Construção de Aplicações em Qt Uso do qmake Usando Ferramentas de Terceiros para Construir Apêndice C: Introdução ao Qt Jambi Iniciando com o Qt Jambi Usando o Qt Jambi na IDE Eclipse Integrando Componentes do C++ com o Qt Jambi Apêndice D: Introdução ao C++ para Programadores Java e C# Introdução ao C++ Principais Diferenças da Linguagem Biblioteca Padrão do C++ Sobre os Autores Jasmin Blanchette

Mark Summerfield Produção Índice

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

5

Parte I: O Básico do Qt

1. Começando

Hello Qt

Fazendo Conexões

Alinhando Widgets

Usando a Documentação de Referência

Este capítulo mostra como combinar C++ básico com a funcionalidade disponibilizada pelo Qt para criar algumas aplicações de interface gráfica pequenas. Este capítulo também introduz duas idéias chave do Qt: “signals e slots” e layouts. No Capítulo 2, iremos mais a fundo, e no Capítulo 3, começaremos a construir aplicações mais realísticas. Se você já conhece Java ou C# mas tem uma experiência limitada com C++, então é recomendado que você comece lendo a “Introdução ao C++” no Apêndice D.

Hello Qt

Vamos começar com um programa bem simples. Vamos estudá-lo linha a linha e depois

veremos 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 QLabel *label = new QLabel("Hello Qt!");

7 label->show();

8 return app.exec();

9 }

As linhas 1 e 2 incluem as definições das classes QApplication e QLabel. Para cada classe do

Qt existe um arquivo header com o mesmo nome (e distinção entre maiúsculas e minúsculas)

que contém a definição da classe.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

6

A linha 5 cria um objeto QApplication para gerenciar os recursos da aplicação no geral. O

construtor de QApplication requer os argumentos argc e argv porque o Qt interpreta alguns

argumentos de linha de comando próprios 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 gráfica. O termo vem da expressão “window gadget” e é equivalente a tanto “controle” como “container” na terminologia do Windows. Botões, menus, barras de rolagem e frames são exemplos de widgets. Widgets podem conter outros widgets; por exemplo, uma janela é, geralmente, um widget que contém um QMenuBar,

algumas QToolBars, uma QStatusBar, e alguns outros widgets. A maioria das aplicações usa

uma QMainWindow ou um QDialog como janela principal da aplicação, mas o Qt é tão flexível

que qualquer widget pode ser uma janela. Neste exemplo, o widget QLabel é a janela da

aplicação.

A linha 7 torna a label visível. Widgets são criados, por padrão, como invisíveis para que possamos customizá-los antes de serem exibidos, deste modo evitando “flickering”.

A linha 8 passa o controle da aplicação 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 ações do usuário como clicks do mouse ou então pressionamento de teclas. Ações do usuário geram eventos (também chamados de “mensagens”) para os quais o programa responde, geralmente executando uma ou mais funções. Por exemplo, quando o usuário clica em um widget, os eventos “pressionamento do botão do mouse” e “liberação do botão do mouse” são gerados. Neste ponto, aplicações gráficas diferem significativamente de programas BAT convencionais, nos quais praticamente só processam uma entrada, desenvolvem algum procedimento, e terminam sem interação humana.

Por simplicidade, nós não nos preocupamos em chamar o delete para o objeto QLabel ao final

da função main(). Este vazamento de memória (“memory leak”) é inofensivo num programa

tão pequeno, já que a memória alocada será desalocada quando o programa terminar.

Já é possível testar o programa na sua máquina. Ele deve se parecer com o mostrado na Figura 1.1. Primeiro você terá que instalar o Qt 4.3.2 (ou uma versão mais recente), este procedimento é explicado no Apêndice A. De agora em diante, vamos assumir que você tem uma cópia corretamente instalada do Qt e que o diretório bin está na sua variável PATH de

ambiente. (No Windows isso é feito automaticamente pelo instalador do Qt) Você também precisará que o código deste programa esteja num arquivo hello.cpp num diretório chamado

hello. Você mesmo pode escrever o arquivo hello.cpp ou copiá-lo dos exemplos que

acompanham este livro, que está disponível em examples/chap01/hello/hello.cpp. (Todos

os exemplos estão disponíveis no site do livro, http://www.informit.com/title/0132354160.)

Figura 1.1 - Hello no Linux

Do prompt de comando, vá até o diretório hello, e digite

qmake –project

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

7

para criar um arquivo projeto que é independente da plataforma, e depois digite

qmake hello.pro

para criar um arquivo “makefile” específico para a plataforma que está usando. (A ferramenta qmake é discutida em mais detalhes no Apêndice 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 botão fechar na barra de título da janela.

Se você estiver usando o Windows e tiver instalado a versão Open Source do Qt e o

compilador MinGW, você terá um atalho chamado “prompt de comando do Qt” que tem todas as variáveis de ambiente corretamente ajustadas. Se você conseguiu visualizar a janela então você pode compilar aplicações do Qt utilizando qmake e make como descritos anteriormente. Os

executáveis são colocados na pasta “debug” ou “release” da aplicação (por exemplo,

C:\examples\chap01\hello\release\hello.exe).

Se você estiver utilizando o Microsoft Visual C++ com uma versão comercial do Qt, você utilizará o nmake ao invés de make. Alternativamente, você pode criar um arquivo projeto do

Visual Studio a partir do arquivo hello.cpp digitando

qmake -tp vc hello.pro

e compilando o programa no Visual Studio. Se você estiver usando o Xcode no Mac, você pode gerar um projeto Xcode usando o comando

qmake -spec macx-xcode hello.pro

Antes de irmos para o próximo exemplo vamos nos divertir um pouco: Substitua a linha

QLabel *label = new QLabel("Hello Qt!");

por

QLabel *label = new QLabel("<h2><i>Hello</i> "

"<font color=red>Qt!</font></h2>");

E recompile a aplicação. Quando rodar, ela deve parecer com a Figura 1.2. Como este exemplo ilustra, é fácil diferenciar uma aplicação de interface de usuário Qt utilizando apenas uma formatação HTML.

Figura 1.2: Uma label com formatação HTML básica

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

8

Fazendo Conexões O segundo exemplo mostra como responder às ações do usuário. A aplicação consiste em um

botão que o usuário pode clicar para sair. O código é muito similar ao exemplo anterior, com exceção do uso de um QPushButton no lugar de uma QLabel como nosso widget principal, e

nós estamos conectando a ação do usuário (clique) a um bloco de código.

O código desta aplicação está em examples/chap01/quit/quit.cpp; a aplicação em

andamento é mostrada na Figura 1.3. Aqui está o conteúdo 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 }

Figura 1.3 – A aplicação que fecha

Os widgets do Qt emitem sinais para indicar que uma ação de usuário ou uma mudança de estado ocorreu. [*] Por exemplo, QPushButton emite um sinal clicked() quando o usuário

clica no botão. Um sinal pode se conectar com uma função (chamada de slot nesse contexto) para que quando o sinal for emitido, o slot seja executado automaticamente. No nosso exemplo, nós conectamos o sinal clicked() do botão com o slot quit() do objeto

QApplication. As macros SIGNAL() e SLOT() fazem parte da sintaxe.

[*] Sinais do Qt não tem nenhuma relação com sinais do Unix. Neste livro, estamos tratando somente dos sinais do Qt.

Vamos construir a aplicação. Assumimos que você criou um diretório chamado quit contendo

o arquivo quit.cpp. Rode o qmake no diretório quit para gerar o arquivo projeto, e depois

rode-o novamente para gerar o “makefile”, como segue: qmake -project

qmake quit.pro

Agora construa a aplicação e rode-a. Se você clicar em Quit, ou pressionar a barra de espaço

(que pressiona o botão), a aplicação terminará.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

9

Alinhando Widgets Nesta seção, 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 aplicação (mostrada na Figura 1.4) pergunta pela idade do usuário, a qual o usuário pode informar manipulando um spin box ou um slider.

Figura 1.4 – A aplicação da idade

A aplicação consiste em três widgets: um QSpinBox, um QSlider e um QWidget. O QWidget é

a janela principal da aplicação. O QSpinBox e o QSlider são apresentados dentro da QWidget;

eles são filhos de QWidget. Alternativamente, podemos dizer que QWidget é pai de QSpinBox e

QSlider. O QWidget não tem pais porque está sendo usado como uma janela top-level. Os

construtores de QWidget e de todas as suas subclasses tem QWidget * como parâmetro que

especifica quem é o pai da widget em questão.

Aqui está o código fonte: 1 #include <QApplication>

2 #include <QHBoxLayout>

3 #include <QSlider>

4 #include <QSpinBox>

5 int main(int argc, char *argv[])

6 {

7 QApplication app(argc, argv);

8 QWidget *window = new QWidget;

9 window->setWindowTitle("Enter Your Age");

10 QSpinBox *spinBox = new QSpinBox;

11 QSlider *slider = new QSlider(Qt::Horizontal);

12 spinBox->setRange(0, 130);

13 slider->setRange(0, 130);

14 QObject::connect(spinBox, SIGNAL(valueChanged(int)),

15 slider, SLOT(setValue(int)));

16 QObject::connect(slider, SIGNAL(valueChanged(int)),

17 spinBox, SLOT(setValue(int)));

18 spinBox->setValue(35);

19 QHBoxLayout *layout = new QHBoxLayout;

20 layout->addWidget(spinBox);

21 layout->addWidget(slider);

22 window->setLayout(layout);

23 window->show();

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

10

24 return app.exec();

25 }

As linhas 8 e 9 preparam a QWidget que servirá como a janela principal da aplicação. Podemos

chamar setWindowTitle() para escolher o texto que será exibido na barra de título da janela.

As linhas 10 e 11 criam um QSpinBox e um QSlider, e as linhas 12 e 13 atribuem seus

intervalos válidos. Podemos assumir que o usuário tem até 130 anos de idade. Poderíamos passar window para os construtores de QSpinBox e QSlider especificando que estes widgets

devem ter window como pai deles, mas não é necessário 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

então emite um sinal valueChanged(int) porque o próprio valor mudou, ativando o slot

setValue(int) do spin box. Mas nesse ponto, setValue(int) não emite nenhum sinal, já que

o valor do spin box já é 35. Isso evita a recursão infinita. A Figura 1.5 ilustra a situação.

Figura 1.5 – Mudando o valor de um widget, muda os dois

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

11

Nas linhas 19 até 22, nós alinhamos o spin box e o slider usando um gerenciador de layouts. Este gerenciador é um objeto que define o tamanho e a posição dos widgets que estão sob sua responsabilidade. O Qt tem três 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.

Estilos de Widgets

Os screenshots que vimos até agora têm sido tirados no Linux, mas aplicações em Qt podem parecer nativas de cada plataforma suportada. O Qt consegue fazer isso emulando o “look and feel” de cada plataforma, ao invés de conter um kit de widgets de uma plataforma particular.

Figura 1.6 – Estilos pré-definidos

O estilo Plastique é o estilo padrão para aplicações Qt/X11 rodando sob o KDE, e o Cleanlooks é o padrão sob o GNOME. Estes estilos utilizam gradientes e “anti-aliasing”

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

12

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 têm seu pai redefinido para o widget no qual o layout foi

instalado, e por essa razão não temos que especificar um pai explícito quando construímos o widget que será colocado em um layout.

Figura 1.7 – Os widgets e o layout da aplicação da idade

Apesar de não termos escolhido a posição e o tamanho dos widgets explicitamente, o QSpinBox e o QSlider são dispostos visualmente de modo agradável de um lado até o outro.

Isso ocorre porque QHBoxLayout automaticamente designa posições e tamanhos razoáveis

para os widgets dos quais é responsável, baseado nas necessidades deles. Os gerenciadores de layouts nos privam da ocupação de posicionar, por puro código, os objetos na tela e garantem que a janela se redimensione suavemente.

O modo apresentado por Qt para construir interfaces de usuário é simples de entender e é muito flexível. O procedimento mais comum que programadores Qt utilizam é instanciar os widgets necessários e depois definir suas propriedades conforme necessário. Programadores adicionam widgets aos layouts, que automaticamente cuidam do posicionamento e redimensionamento. O comportamento da interface de usuário é gerenciada conectando widgets uns aos outros usando o mecanismo de sinais e slots.

para gerar um “look and feel” moderno. Usuários de aplicações Qt podem substituir os estilos padrão utilizando o comando –style na linha de comando. Por exemplo, para

iniciar a aplicação 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 são visualizados nas plataformas nativas, já que dependem de dispositivos de tema de cada plataforma.

Um estilo adicional chamado QtDotNet está disponível no Qt Solutions. Também é

possível criar estilos customizados, como será explicado no Capítulo 19.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

13

Usando a Documentação de Referência

A documentação de referência do Qt é uma ferramenta essencial para qualquer desenvolvedor. Ela cobre todas as classes e funções no Qt. Esse livro faz o uso de diversas classes e funções

do Qt, mas não cobre todas elas e nem fornece todos os detalhes das que são mencionadas. Para tirar proveito máximo do Qt, você deve se familiarizar com a documentação do Qt o mais rápido possível.

A documentação está disponível em HTML no diretório doc/html do Qt e pode ser lida através

de qualquer browser. Você também pode usar o Qt Assistant, o browser de ajuda do Qt, que tem recursos poderosos de busca e indexação que o tornam mais rápido e fácil 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 seção “Referência API” na página inicial fornecem diferentes modos de navegar pelas classes de Qt. A página “Todas as classes” lista todas as classes na API do Qt. A página “Classes Principais” lista apenas as classes mais utilizadas de Qt. Como um exercício, procure as classes e as funções que utilizamos neste capítulo.

Figura 1.8 – Documentação do Qt no Qt Assistant sob Windows Vista

Note que funções herdadas são documentadas na classe base; por exemplo, QPushbutton não

tem uma função própria show(), mas herda uma de QWidget. A Figura 1.9 mostra como as

classes que vemos até agora se relacionam umas com as outras.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

14

Figura 1.9 – Árvore de herança para as classes de Qt vistas até agora

A documentação de referência para a atual versão do Qt e para algumas versões mais recentes está disponível online em http://doc.trolltech.com/. Este site também tem artigos selecionados do Qt Quarterly, o newsletter dos programadores Qt enviado para todas as licenças comerciais.

Este capítulo introduziu os conceitos chave de conexões signal-slot e layouts. Ele também começou a revelar a perspectiva total e consistente de orientação a objetos até a construção e uso de widgets. Se você procurar pela documentação do Qt, você encontrará uma uniformidade de exibição que a torna bem direta no que se diz respeito ao uso de novos

widgets, e você também descobrirá que Qt escolheu cuidadosamente os nomes para funções, parâmetros, enums e assim por diante, que fazem com que programar em Qt se torne incrivelmente agradável e fácil.

Os capítulos seguintes da Parte I se apoiam 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 básica de leitura, processamento e escrita de arquivos.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

15

2. Criando Dialogs

Herdando de QDialog

Signals e Slots a Fundo

Design Rápido de Dialogs

Modificando a Forma dos Dialogs

Dialogs Dinâmicos

Classes Nativas de Widgets e Dialogs

Este capítulo vai te ensinar como criar caixas de diálogo utilizando Qt. Caixas de diálogo apresentam aos usuários opções e escolhas, e permitem que eles ajustem opções dos seus

parâmetros preferidos e que façam suas escolhas. Eles são chamados de caixas de diálogo, ou apenas “dialogs”, porque eles fornecem os meios pelos quais os usuários “conversam” com as aplicações.

A maioria das aplicações GUI (“graphics user interface”, ou interface gráfica de usuário) consiste em uma mainwindow com um menubar e uma toolbar, em conjunto com dezenas de dialogs que complementam a mainwindow. Também é possível criar dialogs que respondam diretamente às escolhas do usuário aplicando as ações necessárias (por exemplo, uma calculadora).

Criaremos nosso primeiro dialog puramente por código e mostraremos 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 rápido (do que código puro) e faz com que seja fácil 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 própria. Fazendo isso, a tornamos independente, um componente encapsulado, com signals e slots próprios.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

16

Figura 2.1. A caixa de diálogo de busca.

O código fonte está dividido em dois arquivos: finddialog.h e finddialog.cpp.

Começaremos pelo header (finddialog.h):

1 #ifndef FINDDIALOG_H

2 #define FINDDIALOG_H

3 #include <QDialog>

4 class QCheckBox;

5 class QLabel;

6 class QLineEdit;

7 class QPushButton;

As linhas 1 e 2 (e 27) protegem o header contra múltiplos includes.

A linha 3 inclui a definição 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 serão utilizadas na implementação do dialog (em finddialog.cpp). [*] Uma “foward declaration” diz ao compilador C++ que esta classe existe sem dar mais detalhes sobre a definição 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);

A macro Q_OBJECT no começo da definição da classe é necessária para todas as classes que

definem seus próprios signals e slots.

[*] Foward declarations só podem ser utilizadas quando as variáveis forem ponteiros. O

compilador não precisa, de inicio, de mais informações sobre a classe. Já que todos os ponteiros tem tamanho fixo: 4 bytes.

O construtor de FindDialog é um exemplo típico de classes Qt. O parâmetro parent especifica

o widget pai. O padrão para este parâmetro é um ponteiro nulo, significando que o widget não tem pai.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

17

13 signals:

14 void findNext(const QString &str, Qt::CaseSensitivity cs);

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

A seção signals declara dois sinais que o dialog emitirá quando o usuário clicar no botão

“Find”. Se a opção “Search Backward” estiver selecionada, o dialog emite findPrevious()

caso contrário, emite findNext().

A palavra-chave signals é, na verdade, uma macro. O pré-processador C++ o converte para

padrões C++ antes que o compilador veja. Qt::CaseSensitivity é um enum que pode

assumir os valores Qt::CaseSensitive (um) e Qt::CaseInsensitive (zero).

16 private slots:

17 void findClicked();

18 void enableFindButton(const QString &text);

19 private:

20 QLabel *label;

21 QLineEdit *lineEdit;

22 QCheckBox *caseCheckBox;

23 QCheckBox *backwardCheckBox;

24 QPushButton *findButton;

25 QPushButton *closeButton;

26 };

27 #endif

Na seção privada da classe, declaramos dois slots. Para implementar os slots, precisaremos acessar a maioria dos widgets filhos do dialog, então mantemos ponteiros para eles também. A palavra-chave slots é, como signals, uma macro que se transforma em uma construção que

C++ pode digerir.

Para as variáveis private, usamos “foward declarations” das respectivas classes. Isso foi possível porque são todos ponteiros e não precisamos acessá-los no header, então o compilador não precisa de todas as definições da classe. Poderíamos ter incluído os includes (<QCheckBox>, <QLabel>, etc.), mas utilizando “foward declarations” quando possível, isso

torna o tempo de compilação menor.

Vamos para o arquivo da implementação da classe FindDialog, o finddialog.cpp:

1 #include <QtGui>

2 #include "finddialog.h"

Primeiramente incluímos <QtGui>, um arquivo header que contém a definição das classes GUI

do Qt. Qt consiste de vários módulos, cada um com sua própria biblioteca. Os módulos mais importantes são QtCore, QtGui, QtNetwork, QtOpenGL, QtScript, QtSql, QtSvg e QtXml. O header <QtGui> contém a definição de todas as classes que são parte dos módulos QtCore e

QtGui. Incluir este header nos economiza a inclusão individual de cada classe do nosso dialog.

No arquivo finddialog.h, ao invés de incluir <QDialog> e utilizar “foward declarations” para

QCheckBox, QLabel, QLineEdit e QPushButton, poderíamos simplesmente ter incluído

<QtGui>. Entretanto não é aconselhável incluir um arquivo header tão grande, especialmente

em aplicações grandes.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

18

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 4, passamos o parâmetro parent para o construtor da classe base. Depois criamos os

objetos filhos. A função tr() marca a string literal para futuras traduções para outras línguas.

Esta função é declarada em QObject e em todas as classes que contém a macro Q_OBJECT. É

um bom hábito cercar strings visíveis aos usuários com tr(), mesmo que você não tenha

planos imediatos para traduzir sua aplicação para outras línguas. Traduções serão estudadas no Capítulo 18.

Em strings literais, utilizamos o símbolo „&‟ para indicar teclas de atalho. Por exemplo, a linha 11 cria o botão Find, o qual o usuário pode ativar utilizando Alt+F nas plataformas que

suportam teclas de atalho. O símbolo „&‟ também pode ser utilizado para controlar o foco: na linha 6 criamos uma label com a tecla de atalho (Alt+W), e na linha 8 nós definimos o lineedit

como „companheiro‟ (buddy) da label. Um buddy é um widget que aceita o foco quando a tecla de atalho do outro é pressionada. Então quando o usuário pressiona Alt+W (atalho da label), o

foco vai para o lineedit (buddy da label).

Na linha 12, fazemos com que o botão Find seja o padrão chamando setDefault(true). O

botão padrão é o botão que, quando o usuário pressiona Enter, é pressionado. Na linha 13,

desabilitamos o botão Find. Quando um widget está desabilitado ele geralmente é mostrado

em tons acinzentados e não responderá às interações do usuário.

15 connect(lineEdit, SIGNAL(textChanged(const QString &)),

16 this, SLOT(enableFindButton(const QString &)));

17 connect(findButton, SIGNAL(clicked()),

18 this, SLOT(findClicked()));

19 connect(closeButton, SIGNAL(clicked()),

20 this, SLOT(close()));

O slot privado enableFindButton(const QString &) é chamado sempre que o texto do

lineedit mudar. O slot privado findClicked() é chamado quando o usuário clicar no botão

Find. O dialog se fecha quando o usuário clicar em Close. O slot close() é herdado de

QWidget, e o seu comportamento padrão é esconder o widget de visualização (sem deletá-lo).

Estudaremos o código para os slots enableFindButton() e findClicked() mais adiante.

Já que QObject é um dos ancestrais do nosso dialog, então podemos omitir o prefixo

„QObject::‟ das chamadas de connect().

21 QHBoxLayout *topLeftLayout = new QHBoxLayout;

22 topLeftLayout->addWidget(label);

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

19

23 topLeftLayout->addWidget(lineEdit);

24 QVBoxLayout *leftLayout = new QVBoxLayout;

25 leftLayout->addLayout(topLeftLayout);

26 leftLayout->addWidget(caseCheckBox);

27 leftLayout->addWidget(backwardCheckBox);

28 QVBoxLayout *rightLayout = new QVBoxLayout;

29 rightLayout->addWidget(findButton);

30 rightLayout->addWidget(closeButton);

31 rightLayout->addStretch();

32 QHBoxLayout *mainLayout = new QHBoxLayout;

33 mainLayout->addLayout(leftLayout);

34 mainLayout->addLayout(rightLayout);

35 setLayout(mainLayout);

Depois disso, nós alinhamos os widgets filhos utilizando gerenciadores de layout. Layouts podem conter ambos widgets e outros layouts. Misturando QHBoxLayouts, QVBoxLayout e

QGridLayout, é possível 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 é responsável

por toda a área do layout. Os outros três layouts são sub-layouts. A pequena „mola‟ na parte inferior direita da Figura 2.2. é um spacer (ou “stretch”). Ele usa o espaço vazio abaixo dos botões Find e Close, garantindo que esses botões ocupem o topo do layout em que estão.

Figura 2.2. Os layouts do dialog de busca

Um aspecto sutil de classes de gerenciamento de layouts é que não são widgets. Ao invés disso, são derivadas de QLayout. Que por sua vez é derivado de QObject. Na figura, widgets

são representados por linhas sólidas e layouts são representados por linhas tracejadas para destacar a diferença entre eles. Durante a execução do programa, layouts são invisíveis.

Quando sub-layouts são adicionados ao layout pai (linhas 23, 33 e 34), os sub-layouts têm 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 têm seus pais redefinidos como o dialog. A hierarquia resultante é mostrada na Figura 2.3.

36 setWindowTitle(tr("Find"));

37 setFixedHeight(sizeHint().height());

38 }

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

20

Figura 2.3. As relações de parentesco do dialog

Finalmente, definimos o título a ser mostrado na barra de título e uma altura fixa para a janela, já que não existem widgets dentro do dialog que possam ocupar um espaço vertical

significativo. A função QWidget::sizeHint() retorna o tamanho ideal do widget.

Isso completa a revisão do construtor da classe FindDialog. Já que criamos os objetos e

layouts através de „new‟, é razoável pensar que devemos deletar cada widget e layout com um

„delete‟. Mas não é necessário, o Qt automaticamente deleta objetos filhos quando o pai é

destruído, e todos os widgets e layouts do nosso dialog são descendentes do próprio dialog.

Agora, vamos definir os slots do dialog:

39 void FindDialog::findClicked()

40 {

41 QString text = lineEdit->text();

42 Qt::CaseSensitivity cs =

43 caseCheckBox->isChecked() ? Qt::CaseSensitive

44 : Qt::CaseInsensitive;

45 if (backwardCheckBox->isChecked()) {

46 emit findPrevious(text, cs);

47 } else {

48 emit findNext(text, cs);

49 }

50 }

51 void FindDialog::enableFindButton(const QString &text)

52 {

53 findButton->setEnabled(!text.isEmpty());

54 }

O slot findClicked() é chamado quando o usuário clica no botão Find. Ele emite o sinal

findPrevious() ou findNext(), dependendo da opção „Search Backward‟. A palavra-chave

„emit‟ é específica do Qt; como outras extensões Qt ela é convertida em C++ padrão pelo pré-

processador C++.

O slot enableFindButton() é chamado sempre que o usuário muda o texto contido no

lineedit. Isso ativa o botão se houver algum texto no lineedit, e desabilita caso contrário.

Estes dois slots finalizam o dialog. Podemos, então, criar um main.cpp para testar nosso

widget FindDialog:

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

21

1 #include <QApplication>

2 #include "finddialog.h"

3 int main(int argc, char *argv[])

4 {

5 QApplication app(argc, argv);

6 FindDialog *dialog = new FindDialog;

7 dialog->show();

8 return app.exec();

9 }

Para compilar o programa, utilize qmake como de usual. Uma vez que a definição da classe

FindDialog contém a macro Q_OBJECT, então 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 próxima seção).

Para que o „moc‟ funcione corretamente é necessário colocar a definição da classe em um

arquivo header, fora da implementação. O código gerado pelo moc inclui esse arquivo header e

adiciona alguns códigos padrões do C++ por si próprio.

Classes que utilizam a macro Q_OBJECT devem ser executadas pelo „moc‟. Isso não é um

problema porque o qmake automaticamente adiciona as regras necessárias para o „makefile‟.

Mas se você esquecer de gerar seu „makefile‟ usando qmake e o moc não tiver rodado, o linker

reclamará dizendo que algumas funções foram declaradas mas não 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 saída do Visual C++ começa 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 aplicação.

Agora, execute o programa. Se as teclas de atalho são exibidas em sua plataforma, verifique se as teclas de atalho Alt+W, Alt+C, Alt+B, e Alt+F acionam o comportamento correto.

Pressione a tecla tab para navegar pelos widgets com o teclado. A ordem de tabulação padrão,

é a ordem na qual os widgets foram criados. Isso pode ser mudado usando QWidget::setTabOrder().

Forneça uma ordem razoável para a tecla tab e bons atalhos de teclado de forma que usuários que não querem (ou que não podem) usar um mouse sejam capazes de usar ao máximo a aplicação. Controle total sobre o teclado também é apreciado por digitadores rápidos.

No Capítulo 3, nós usaremos o dialogo de busca numa aplicação real, e nós conectaremos os sinais findPrevious() e findNext() a alguns slots.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

22

Signals e Slots a Fundo

O mecanismo de sinais e slots é fundamental para programação em Qt. Ele permite que o programador estabeleça um elo entre os objetos sem que os objetos saibam uns dos outros.

Nós já conectamos alguns sinais e slots, declaramos nossos próprios sinais e slots, implementamos nossos slots e emitimos nossos sinais. Vamos dar uma olhada no mecanismo mais de perto.

Slots são quase idênticos a funções membro ordinárias do C++. Elas podem ser virtuais, podem ser sobrecarregadas, podem ser publics, protecteds ou privates, podem ser diretamente chamadas como qualquer outra função membro do C++, e os seus parâmetros

podem ser de qualquer tipo. A diferença é que um slot também pode ser conectado a um sinal, que no caso será chamado toda vez que o sinal for emitido.

A estrutura da função connect() se dá da seguinte forma:

connect(sender, SIGNAL(signal), receiver, SLOT(slot));

Onde „sender‟ e „receiver‟ são ponteiros para QObjects e „signal‟ e „slot‟ são assinaturas de

funções sem nomes de parâmetros. As macros SIGNAL() e SLOT() essencialmente convertem

seus argumentos para uma string.

Nos exemplos que vimos até agora, nós sempre conectamos sinais diferentes com slots diferentes. Existem outras possibilidades a serem levadas em consideração.

Um sinal pode ser conectado a vários 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 são chamados um a um, numa ordem não especificada.

Vários sinais podem ser conectados ao mesmo slot: connect(lcd, SIGNAL(overflow()),

this, SLOT(handleMathError()));

connect(calculator, SIGNAL(divisionByZero()),

this, SLOT(handleMathError()));

Quando qualquer um 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 também é emitido. Além disso, conexões signal-signal são indistinguíveis de conexões signal-slot.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

23

Conexões podem ser removidas: disconnect(lcd, SIGNAL(overflow()),

this, SLOT(handleMathError()));

Isso é raramente necessário, porque o Qt automaticamente remove todas as conexões 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 parâmetros na mesma ordem:

connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),

this, SLOT(processReply(int, const QString &)));

Excepcionalmente, se um sinal tem mais parâmetros que o slot ao qual está conectado, os parâmetros adicionais são simplesmente ignorados.

connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),

this, SLOT(checkErrorCode(int)));

Se os tipos de parâmetros são incompatíveis, ou se o sinal ou slot não existirem, o Qt irá informar através de um warning em tempo de execução (se a aplicação foi construída no modo debug). Similarmente, o Qt irá lançar um warning se os nomes dos parâmetros estiverem inseridos nas assinaturas do sinal ou do slot.

Até agora, apenas utilizamos sinais e slots com widgets. Mas o mecanismo é implementado para QObject e não é limitado a programação 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;

};

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

24

void Employee::setSalary(int newSalary)

{

if (newSalary != mySalary) {

mySalary = newSalary;

emit salaryChanged(mySalary);

}

}

Perceba como o slot setSalary() é implementado. Nós emitimos o sinal salaryChanged()

apenas se newSalary != mySalary. Isso garante que as conexões cíclicas não levem a loops

infinitos.

O Sistema Meta-Object do Qt

Uma das maiores conquistas do Qt foi a extensão do C++ com um mecanismo de criação de componentes de software independentes que podem ser ligados sem nem saberem informações sobre o componente aos quais estão conectados.

O mecanismo é chamado sistema meta-object e fornece dois serviços chave: signals-slots, e introspecção. A funcionalidade da introspecção é necessária para implementar signals e slots, e permite programadores obterem “informações meta” sobre sub-classes QObject em tempo de execução, incluindo a lista de sinais e slots suportados pelo objeto

e nome da sua classe. O mecanismo também suporta propriedades (amplamente usadas pelo Qt Designer) e tradução de texto (para internacionalização), e estabelece as bases do módulo QtScript. A partir do Qt 4.2, propriedades podem ser adicionadas dinamicamente, um recurso que veremos em ação nos Capítulos 19 e 22.

O C++ padrão não fornece suporte para as meta-informações dinâmicas que o sistema de meta-objeto de Qt precisa. O Qt resolve isso com uma ferramenta separada, o moc.

Ele analisa as definições de Q_OBJECT e faz com que a informação fique disponível

através de funções C++. Já que o moc implementa toda a sua funcionalidade usando

C++ puro, o sistema meta-object funciona com qualquer compilador C++.

O mecanismo funciona da seguinte forma:

A macro Q_OBJECT declara algumas funções de introspecção que devem ser

implementadas em cada subclasse de QObject: metaObject(), tr(),

qt_metacall(), entre outras.

A ferramenta moc gera implementações para as funções declaradas por Q_OBJECT

e para todos os signals.

As funções membro de QObject (como connect() e disconnect() ) usam as

funções de introspecção para fazer seus trabalhos.

Tudo isso é feito automaticamente pelo qmake, moc e QObject, então você dificilmente

terá que pensar nisso. Mas se estiver curioso você pode ler a documentação da classe QMetaObject e os códigos C++ gerados pelo moc para ver como a implementação

funciona.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

25

Design Rápido de Dialogs

O Qt foi planejado para ser agradável e intuitivo para o desenvolvimento de código a mão, e não é difícil encontrar programadores que desenvolvem suas aplicações inteiras puramente escrevendo código C++. Porém, muitos programadores preferem utilizar uma aproximação visual para desenvolver formulários, porque eles acham que este método é mais natural e rápido do que apenas código, e eles querem estar aptos a testar e mudar designs com mais rapidez e facilidade do que quando desenvolvidos com códigos puros.

O Qt Designer expande as opções disponíveis aos programadores fornecendo uma capacidade

visual de design. Qt Designer pode ser usado para desenvolver todos ou apenas alguns dos formulários da aplicação. Formulários que são criados utilizando o Qt Designer são convertidos em código C++, então o Qt Designer pode ser usado com uma variedade de ferramentas convencionais e não impõe especificações especiais ao compilador.

Nessa seção, usaremos o Qt Designer para criar o diálogo “Ir para a célula” mostrado na Figura 2.4. Criar o dialog no Qt Designer ou por código 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 conexões signal/slots.

5. Implementar os próprios slots do dialog.

Figura 2.4. O dialog de “Ir para célula”.

Para iniciar o Qt Designer, clique em “Qt by Trolltech v4.x.y|Designer” no menu Iniciar

no Windows, digite “designer” na linha de comando do Unix, ou dê um duplo-clique em

Designer no Mac OS X 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 nós criaremos os botões „OK‟ e

„Cancel‟ manualmente e mostraremos como é feito.) Você deve, agora, ter uma janela

chamada „Untitled‟.

Por padrão, a interface de usuário do Qt consiste em várias janelas „top-level‟. Se você preferir uma interface MDI (multiple document interface) com uma janela „top-window‟ e várias subjanelas, como mostrado na Figura 2.5, clique em Edit|Preferences e ajuste o modo de

interface do usuário para „Docked Window‟.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

26

Figura 2.5. Qt Designer em modo „Docked Window‟ no Windows Vista

O primeiro passo é criar widgets filhos e colocá-los no formulário. Criar uma label, um line editor, um spacer horizontal, e dois botões. 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 formulário. O spacer, que é invisível no formulário final, é mostrado no Qt Designer como uma pequena mola azul.

Agora arraste a borda inferior do formulário pra encurtá-lo. Isso deveria produzir um formulário que é similar ao da Figura 2.6. Não gaste muito tempo posicionando os itens precisamente no formulário. Os gerenciadores de layouts vão posicioná-los de forma precisa posteriormente.

Figura 2.6. O formulário com alguns widgets

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 botão. Defina a propriedade objectName como “okButton”, a

propriedade enabled para “false”, a propriedade text para “OK”, e a propriedade

default para “true”.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

27

4. Clique no segundo botão. Defina a propriedade objectName para “cancelButton” e a

propriedade text para “Cancel”.

5. Clique no fundo do formulário para selecioná-lo. Ajuste a propriedade objectName para

“GoToCellDialog” e a propriedade windowTitle 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 edição que te permite escolher os

Buddies. Depois, clique na tabela, mantenha o botão apertado e arraste a seta vermelha para o line Edit, e depois libere o botão. A label deveria exibir, agora, o texto “Cell Location”,

como mostrado na Figura 2.7 e tem o line Edit como seu buddy. Para deixar o modo de edição de buddies clique em Edit|Edit Widgets.

Figura 2.7 Formulário com as propriedades ajustadas

O próximo passo é alinhar os widgets no formulário:

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 botões. Clique em Form|Lay

Out Horizontally.

3. Clique no fundo do formulário para remover a seleção anterior de qualquer widget, depois clique em Form|Lay Out Vertically.

4. Clique em Form|Adjust Size para redimensionar o formulário para o seu tamanho

preferencial.

As linhas vermelhas mostradas no formulário mostram os layouts que foram criados, como é mostrado na Figura 2.8. Eles não são visíveis quando o formulário é executado.

Figura 2.8. Formulário com Layouts.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

28

Agora clique em Edit|Edit Tab Order. Um número em um retângulo azul aparecerá próximo

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 edição de tabs.

Figura 2.9. Definindo a ordem do „tab‟.

Para visualizar o dialog, clique em Form|Preview. Note a ordem dos tabs que você predefiniu

pressionando Tab repetidamente. Feche o dialog utilizando o botão fechar na barra de título.

Salve as alterações como gotocelldialog.ui em um diretório chamado gotocell, e crie um

arquivo main.cpp no mesmo diretório utilizando um editor de texto:

#include <QApplication>

#include <QDialog>

#include "ui_gotocelldialog.h"

int main(int argc, char *argv[])

{

QApplication app(argc, argv);

Ui::GoToCellDialog ui;

QDialog *dialog = new QDialog;

ui.setupUi(dialog);

dialog->show();

return app.exec();

}

Agora execute o qmake para criar um arquivo „.pro‟ e um makefile (qmake –project; qmake

gotocell.pro). A ferramenta qmake é inteligente o bastante para detectar o arquivo de

interface do usuário gotocelldialog.ui e para gerar as regras apropriadas do makefile para

chamar uic, o compilador de interfaces de usuário do Qt. A ferramenta uic converte o arquivo

gotocelldialog.ui em código C++ e põe o resultado no arquivo ui_gotocelldialog.h.

O arquivo ui_gotocelldialog.h gerado contém as definições para a classe

Ui::GoToCellDialog, que é um equivalente C++ do gotocelldialog.ui. A classe declara

variáveis membro que armazenam os layouts e widgets filhos do formulário, e uma função setupUi() que inicializa o formulário. A classe gerada se parece com isso:

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

29

class Ui::GoToCellDialog

{

public:

QLabel *label;

QLineEdit *lineEdit;

QSpacerItem *spacerItem;

QPushButton *okButton;

QPushButton *cancelButton;

...

void setupUi(QWidget *widget) {

...

}

};

A classe gerada não tem nenhuma classe base. Quando utilizamos essa classe no main.cpp,

nós criamos um QDialog e o passamos para setupUI().

Se você executar o programa agora, o diálogo vai funcionar, mas ele não funciona exatamente como queremos:

O botão OK está sempre desabilitado.

O botão Cancel não faz nada.

O line Edit aceita qualquer texto, ao invés de só aceitar posições validas de célula.

Nós podemos fazer com que o diálogo funcione corretamente escrevendo algum código. O jeito mais organizado de fazer isso é criando uma classe que é derivada de ambos QDialog e

Ui::GoToCellDialog e que implemente a funcionalidade faltante (deste modo provando a

máxima de que qualquer problema do software pode ser resolvido simplesmente adicionando outra camada de meios indiretos). A convenção de nomeação é dar o mesmo nome da classe gerada pelo o uic mas sem o prefixo “Ui::”.

Usando um editor de texto, crie um arquivo chamado gotocelldialog.h que contenha o

seguinte código:

#ifndef GOTOCELLDIALOG_H

#define GOTOCELLDIALOG_H

#include <QDialog>

#include "ui_gotocelldialog.h"

class GoToCellDialog : public QDialog, public Ui::GoToCellDialog

{

Q_OBJECT

public:

GoToCellDialog(QWidget *parent = 0);

private slots:

void on_lineEdit_textChanged();

};

#endif

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

30

Aqui, utilizamos herança pública porque queremos acessar todos os widgets do dialog de fora do dialog. A implementação pertence ao arquivo gotocelldialog.cpp:

#include <QtGui>

#include "gotocelldialog.h"

GoToCellDialog::GoToCellDialog(QWidget *parent)

: QDialog(parent)

{

setupUi(this);

QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");

lineEdit->setValidator(new QRegExpValidator(regExp, this));

connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));

connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));

}

void GoToCellDialog::on_lineEdit_textChanged()

{

okButton->setEnabled(lineEdit->hasAcceptableInput());

}

No construtor, chamamos setupUi() para inicializar o formulário. Graças à herança múltipla,

podemos acessar os membros de Ui::GoToCellDialog diretamente. Depois de criar a

interface de usuário, setupUi() irá automaticamente conectar qualquer slot que siga a

convenção de nome on_objectName_signalName() para o sinal de objectName

correspondente. No nosso exemplo, isso significa que setupUi() irá estabelecer a seguinte

conexão signal-slot:

connect(lineEdit, SIGNAL(textChanged(const QString &)),

this, SLOT(on_lineEdit_textChanged()));

Também no construtor, nós criamos um validador (validator) para restringir o intervalo da entrada. O Qt fornece três classes internas do tipo validator: QIntValidator,

QDoublrValidator, e QRegExpValidator. Aqui podemos usar um QRegExpValidator com a

expressão regular “[A-Za-z][1-9][0-9]{0,2}”, que significa: Permita uma letra maiúscula ou minúscula, seguida de um digito entre 1 e 9, seguido de zero, 1, ou dois dígitos entre 0 e 9. (Para uma introdução à expressões regulares, veja a documentação da classe QRegExp.)

Passando this para o construtor do QRegExpValidator, fazemos com que ele seja filho do

objeto GoToCellDialog. Fazendo isso, não precisamos nos preocupar em deletar o

QRegExpValidator posteriormente; ele será deletado automaticamente quando o pai dele for

deletado.

O mecanismo pai-filho é implementado em QObject. Quando nós criamos um objeto (um

widget, um validator, ou qualquer outro) com um pai, o pai adiciona o objeto à lista dos seus

filhos. Quando o pai é deletado, ele percorre a sua lista de filhos e deleta cada um. Os filhos, por sua vez, deletam todos os seus filhos, e assim por diante recursivamente até que nenhum sobre. Este mecanismo simplifica muito o gerenciamento de memórias, reduzindo o risco de vazamento de memória (memory leak). Os únicos objetos que devemos chamar delete são

objetos que criamos com new sem ter pai. E se deletarmos um objeto filho antes do pai, o Qt

irá remover aquele objeto da lista de filhos do pai automaticamente.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

31

Para widgets, o pai tem um significado adicional: Widgets filhos são mostrados dentro da área do pai. Quando deletamos o Widget pai, não somente os filhos são liberados da memória como também desaparecem da tela.

Ao fim do construtor, nós conectamos o botão OK ao slot accept() do QDialog e o botão

Cancel ao slot reject(). Ambos os slots fecham o dialog, mas accept() define o valor de

resultado do dialog para QDialog::Accepted (que é 1), e o reject() define o resultado para

QDialog::Rejected (que é 0). Quando nós usamos esse dialog, nós podemos usar o resultado

para ver se o usuário clicou OK e atua de acordo.

O slot on_lineEdit_textChanged() ativa ou desativa o botão OK, dependendo se o line edit

contém uma localização válida de célula. QLineEdit::hasAcceptableInput() usa o validador

que escolhemos no construtor.

Isso completa o dialog. Podemos agora reescrever o arquivo main.cpp para usá-lo:

#include <QApplication>

#include "gotocelldialog.h"

int main(int argc, char *argv[])

{

QApplication app(argc, argv);

GoToCellDialog *dialog = new GoToCellDialog;

dialog->show();

return app.exec();

}

Construa o arquivo gotocell.pro usando qmake –project (já que adicionamos arquivos fonte

ao projeto), execute qmake gotocell.pro para atualizar o makefile, depois construa e execute

a aplicação de novo. Digite “A12” no editor, e perceba que o botão OK se torna ativo. Tente

digitar alguns textos aleatórios para ver como o validator funciona. Clique Cancel para fechar

o dialog.

O dialog funciona corretamente, mas para usuários do Mac, os botões estão um pouco diferentes. Escolhemos adicionar cada botão individualmente, para mostrar como era feito, mas nós deveríamos ter usado um QDialogButtonBox, um widget que contém os botões que

especificamos e que os apresenta na forma correta para o sistema de janelas no qual a aplicação está sendo executada, como mostrado na Figura 2.10.

Figura 2.10. O dialog „Ir para célula‟ no Windows Vista e no Mac.

Para fazer com que o dialog use um QDialogButtonBox, nós devemos modificar ambos o

design e o código. No Qt Designer, existem apenas quatro passos a tomar:

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

32

1. Clique no formulário (não em nenhum dos widgets ou layouts) e depois clique em Form|Break Layout.

2. Selecione e delete os botões OK e Cancel, o spacer horizontal e o layout horizontal.

3. Arraste um “Button Box” para o formulário, abaixo da label de localização e do line Edit.

4. Clique no formulário e depois Form|Lay Out Vertically.

Se nós estivéssemos fazendo apenas mudanças no design, como mudar o layout do dialog ou propriedades de widgets, nós estaríamos aptos a simplesmente reconstruir a aplicação. Mas nesse caso removemos alguns widgets e adicionamos novos widgets, e nestes casos devemos normalmente modificar o código também.

As mudanças necessárias devem ser feitas no arquivo gotocelldialog.cpp. Aqui está a nova

versão do construtor:

GoToCellDialog::GoToCellDialog(QWidget *parent)

: QDialog(parent)

{

setupUi(this);

buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);

QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");

lineEdit->setValidator(new QRegExpValidator(regExp, this));

connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));

connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

}

Na versão anterior nós inicialmente desabilitamos o botão OK no Qt Designer. Nós não

podemos fazer isso com um QDialogButtonBox, então nós fazemos isso no código,

imediatamente depois da chamada setupUi(). A classe QDialogButtonBox tem um enum de

botões padrão, e podemos usar isso para acessar botões particulares, neste caso o botão OK.

Convenientemente, o nome padrão para um QDialogButtonBox do Qt Designer é buttonBox.

Ambas as conexões são feitas a partir do Button Box ao invés de cada botão propriamente dito. O sinal accepted() é emitido quando o botão com a propriedade AcceptRole é clicado, e

similarmente o sinal rejected() é emitido por um botão com a propriedade RejectRole. Por

padrão, o botão QDialogButtonBox::Ok carrega a propriedade AcceptRole, e o botão

QDialogButtonBox::Cancel carrega a propriedade RejectRole.

Apenas mais uma mudança é necessária, no slot on_lineEdit_textChanged():

void GoToCellDialog::on_lineEdit_textChanged()

{

buttonBox->button(QDialogButtonBox::Ok)->setEnabled(

lineEdit->hasAcceptableInput());

}

A única coisa diferente de antes é que ao invés de se referir a um botão particular armazenado como variável membro, nós acessamos o botão OK do Button Box.

Uma das facilidades do Qt Designer é que ele permite que programadores tenham grande liberdade para modificar seus próprios formulários sem serem forçados a mudar seus códigos. Quando você desenvolve um formulário puramente por C++, mudanças no design podem levar um tempo considerável. Com o Qt Designer, nenhum tempo é perdido já que o uic

simplesmente refaz o código fonte de qualquer formulário que tenha mudado. A interface de

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

33

usuário do dialog é salva em um arquivo .ui (um arquivo no formato XML), enquanto que

funcionalidades próprias são implementadas derivando da classe gerada pelo uic.

Modificando a Forma dos Dialogs

Nós vimos como criar caixas de diálogo que sempre mostram os mesmos widgets sempre que são usados. Em alguns casos, é necessário que caixas de diálogo mudem suas formas. Os tipos mais comuns de caixas de diálogo que mudam suas formas são diálogos que se expandem e diálogos de múltiplas páginas. Ambos os tipos podem ser implementados no Qt, tanto por código como pelo Qt Designer.

Diálogos que se expandem geralmente apresentam uma aparência simples mas têm um botão „ativável‟ que permite que o usuário alterne entre o diálogo simples e o diálogo expandido. Estes diálogos são comumente usados por aplicações que atingem tanto usuários casuais quanto avançados, escondendo as opções avançadas a menos que o usuário peça explicitamente para vê-las. Nesta seção, usaremos o Qt Designer para criar o diálogo de expansão mostrado na Figura 2.11.

Figura 2.11. Caixa de diálogo de classificação nos modos simples e expandido.

Este diálogo é um diálogo de classificação de uma aplicação de planilhas, onde o usuário pode selecionar uma ou várias colunas para que sejam classificadas. A versão simples do diálogo

permite que ele escolha apenas uma chave de classificação, enquanto que a versão estendida fornece os meios para duas chaves extras. O botão „more‟ permite que o usuário alterne entre

as versões.

Criaremos o widget com sua versão expandida no Qt Designer, e esconderemos as chaves extras em tempo de execução conforme necessário. O diálogo parece complicado, mas é fácil fazê-lo no Qt Designer. O truque é fazer a primeira chave primeiro, depois duplicá-la duas vezes para obter as outras:

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

34

1. Clique em File|New Form e escolha o modelo “Dialog com botões”.

2. Crie um botão OK e arraste para o canto superior direito do formulário. Mude a sua

propriedade do objectName para “okButton” e defina sua propriedade default para

“true”.

3. Crie um botão Cancel, e arraste-o para baixo do botão OK. Mude sua propriedade

objectName para “cancelButton”.

4. Crie um spacer vertical e arraste-o para baixo do botão Cancel, depois crie um botão

More, e arraste-o para baixo do spacer vertical. Mude sua propriedade objectName para

“moreButton”, defina sua propriedade text para “&More”, e sua propriedade checkable

para “true”.

5. Clique no botão OK, segure o Shift e clique no botão Cancel, no spacer vertical, no

botão More e depois clique em Form|Lay Out Vertically.

6. Crie um group box, duas labels, duas comboboxes, e um spacer horizontal e coloque-os em qualquer lugar do formulário.

7. Arraste o canto inferior direito para aumentá-lo. Insira os outros widgets no groupbox e posicione-os aproximadamente como é mostrado na Figura 2.12 (a).

8. Arraste a borda direita do segundo combo box para fazê-lo, aproximadamente, duas vezes maior que o primeiro combobox.

9. Defina a propriedade title do group box para “&Primary Key”, a propriedade text da

primeira label para “Column:”, e a propriedade text da segunda label para “Order:”.

10. Clique com o botão direito na primeira combobox e escolha Edit Items a partir do

menu para editar os itens da combo box. Crie um item com o texto “None”.

11. Clique com o botão direito na segunda combo box e escolha Edit Items. Crie um item

“Ascending” e outro “Descending”.

12. Clique na group box, Form|Lay Out in a Grid. Clique novamente no group box e

depois Form|Adjust Size. Isso vai gerar o layout mostrado na Figura 2.12(b).

Figura 2.12. Alinhando os widgets filhos em grid.

Se um layout não ficar do jeitinho que você queria ou se você cometer um erro, você pode sempre clicar em Edit|Undo ou Form|Break Layout, depois reposicionar os widgets e tentar

de novo.

Vamos agora adicionar as chaves extras:

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

35

1. Aumente a altura do diálogo para que ele seja capaz de armazenar as outras duas chaves.

2. Segure o Ctrl (Alt no Mac), clique e arraste o primeiro group box para criar uma cópia

dele (e de seu conteúdo) sobre o original. Arraste a cópia para baixo do original, ainda

segurando o Ctrl (ou Alt). Repita esse processo para criar um terceiro group box,

arrastando-o para baixo do segundo.

3. Mude as propriedades title deles para “&Seconday Key” e “&Tertiary Key”.

4. Crie um spacer vertical e coloque-o entre o primeiro e o segundo group box.

5. Organize os widgets no padrão grid mostrado na Figura 2.13(a).

6. Clique no formulário para limpar possíveis seleções, clique em Form|Lay Out in a

Grid. Agora arraste o canto inferior direito do formulário para cima e para esquerda

para deixá-lo menor possível. O formulário deveria se parecer com a Figura 2.13(b).

7. Ajuste a propriedade sizeHint dos dois spacers verticais para [20,0].

Figura 2.13. Alinhando os widgets do formulário em grid.

O layout em grid resultante tem duas colunas e quarto linhas, totalizando oito células. O primeiro group box, o spacer vertical da esquerda, o segundo group box, e o terceiro group box, cada um ocupa uma única célula. O layout vertical contendo os botões OK, Cancel, e More

ocupa duas células.

Isso deixa duas células vazias na área inferior direita do diálogo. Se você não tem o mesmo resultado, desfaça o layout, reposicione os widgets, e tente de novo.

Renomeie o formulário para “SortDialog” e mude o título da janela para “Sort”. Defina os nomes dos widgets filhos para aqueles da Figura 2.14.

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

36

Figura 2.14. Nomeando os widgets

Clique em Edit|Edit Tab Order. Clique em cada combo box de cima para baixo, nos botões

OK, Cancel, e More. Clique em Edit|Edit Widgets para deixar o modo de edição de tabs.

Agora que o formulário foi desenhado, estamos prontos para torná-lo funcional designando algumas conexões signals-slots. O Qt Designer nos permite que estabeleçamos conexões entre widgets que compartilham do mesmo formulário. Precisamos estabelecer duas delas.

Clique em Edit|Edit Signals/Slots para entrar no modo de conexões do Qt Designer.

Conexões são representadas por setas azuis entre os widgets do formulário, como é mostrado na Figura 2.15, e elas também são listadas na janela de edição de signals/slots do Qt Designer. Para estabelecer uma conexão entre dois widgets, clique no widget remetente e arraste a seta vermelha para o widget destinatário, e solte o botão do mouse. Isso vai fazer com que um diálogo seja exibido, permitindo que você escolha o sinal e o slot que pertencerão a conexão.

Figura 2.15. Conectando os widgets

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

37

A primeira conexão a ser feita é entre o botão okButton e o slot accept() do formulário.

Arraste a seta vermelha do botão okButton para uma parte vazia do formulário, solte o botão

para configurar a conexão pelo diálogo de configuração que será aberto, como mostrado na Figura 2.16. Escolha clicked() como sinal e accept() como slot, e clique OK.

Figura 2.16. Diálogo de editor de conexões do Qt Designer

Para a segunda conexão, arraste a seta vermelha do botão cancelButton para uma parte

vazia do formulário, e no diálogo de configuração conecte o sinal clicked() ao slot reject()

do formulário.

A terceira conexão a ser estabelecida é entre o botão moreButton e o group box

secondaryGroupBox. Arraste a seta vermelha entre estes widgets, então selecione o sinal

toggled(bool) e o slot setVisible(bool). Por padrão, o Qt Designer não lista o slot

setVisible(bool) na lista de slots, mas ele aparece se você habilitar a opção “Show all

signals and slots”.

A quarta e última conexão é entre o sinal toggled(bool) do botão moreButton e o slot

setVisible(bool) do terceiro group box. Uma vez que as conexões foram estabelecidas,

clique em Edit|Edit Widgets para deixar o modo de conexões.

Salve o diálogo como sortdialog.ui em um diretório chamado sort. Para adicionar código ao

formulário, nós usaremos o mesmo principio de herança múltipla que usamos para o diálogo “Go to Cell” na seção anterior.

Primeiro, crie um arquivo sortdialog.h com o seguinte conteúdo:

#ifndef SORTDIALOG_H

#define SORTDIALOG_H

#include <QDialog>

#include "ui_sortdialog.h"

class SortDialog : public QDialog, public Ui::SortDialog

{

Q_OBJECT

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

38

public:

SortDialog(QWidget *parent = 0);

void setColumnRange(QChar first, QChar last);

};

#endif

Agora crie o sortdialog.cpp:

1 #include <QtGui>

2 #include "sortdialog.h"

3 SortDialog::SortDialog(QWidget *parent)

4 : QDialog(parent)

5 {

6 setupUi(this);

7 secondaryGroupBox->hide();

8 tertiaryGroupBox->hide();

9 layout()->setSizeConstraint(QLayout::SetFixedSize);

10 setColumnRange('A', 'Z');

11 }

12 void SortDialog::setColumnRange(QChar first, QChar last)

13 {

14 primaryColumnCombo->clear();

15 secondaryColumnCombo->clear();

16 tertiaryColumnCombo->clear();

17 secondaryColumnCombo->addItem(tr("None"));

18 tertiaryColumnCombo->addItem(tr("None"));

19 primaryColumnCombo->setMinimumSize(

20 secondaryColumnCombo->sizeHint());

21 QChar ch = first;

22 while (ch <= last) {

23 primaryColumnCombo->addItem(QString(ch));

24 secondaryColumnCombo->addItem(QString(ch));

25 tertiaryColumnCombo->addItem(QString(ch));

26 ch = ch.unicode() + 1;

27 }

28 }

O construtor esconde as partes secundárias e terciárias do diálogo. Ele também define a propriedade sizeContraint do layout do formulário para QLayout::SetFixedSize, fazendo

com que o diálogo se torne não redimensionável pelo usuário. O layout, então, assume a responsabilidade de redimensionar e dimensiona o diálogo automaticamente quando widgets são mostrados ou escondidos, garantindo que o diálogo será sempre mostrado no melhor

tamanho.

O slot setColumnRange() inicializa o conteúdo dos combo boxes baseado nas colunas

selecionadas da planilha. Inserimos um item chamado “None” para as chaves (opcionais) secundária e terciária.

As linhas 19 e 20 apresentam uma linguagem sutil de layout. A função QWidget::sizeHint()

retorna o „tamanho ideal‟ do widget, que o sistema de layout tenta seguir. Isso explica porque

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

39

diferentes tipos de widgets, ou widgets similares com conteúdos diferentes, podem ser designados com diferentes tamanhos pelo sistema de layout. Para comboboxes, isso significa que os comboboxes secundário e terciário, que contém “None”, acabam maiores que o primeiro combobox, que contém só entradas de letras individuais. Para evitar essa inconsistência, nós definimos o tamanho máximo da primeira combobox para o tamanho ideal do segundo combobox.

Aqui está uma função teste main() que define o intervalo para incluir as colunas de „C‟ a „F‟ e

depois mostra o diálogo:

#include <QApplication>

#include "sortdialog.h"

int main(int argc, char *argv[])

{

QApplication app(argc, argv);

SortDialog *dialog = new SortDialog;

dialog->setColumnRange('C', 'F');

dialog->show();

return app.exec();

}

Isso completa o diálogo de extensão. Como o exemplo mostra, este tipo de diálogo não é tão mais difícil de desenhar do que o diálogo plano: Tudo que precisamos era um botão de alternância, algumas conexões extras, e um layout não redimensionável. Em aplicações de produção, é bem comum que o botão de alternância tenha o texto “Advanced >>>” quando o

diálog básico é exibido e “Advanced <<<” quando a extensão é mostrada. Isso é fácil de

conseguir com Qt chamando setText() no QPushButton sempre que for clicado.

O outro tipo comum de diálogos que mudam as formas, diálogos de múltiplas páginas, são até mais fáceis de criar em Qt, tanto por código ou com o Qt Designer. Tais diálogos podem ser construídos de diferentes maneiras:

Um QTabWidget pode ser usado. Ele fornece uma tab bar que controla um

QStackedWidget interno.

Um QListWidget e um QStackWidget podem ser usados em conjunto, com o item atual

do QListWidget determinando qual página o QStackedWidget mostra, conectando o

sinal QListWidget::currentRowChanged() ao slot

QStackWidget::setCurrentIndex().

Um QTreeWidget pode ser usado com um QStackedWidget num modo similar ao

QListWidget.

Nós falaremos mais da classe QStackedWidget no Capítulo 6.

Dialogs Dinâmicos

Dialogs dinâmicos são aqueles que são criados em tempo de execução a partir de arquivos .ui

produzidos no Qt Designer. Ao invés de converter o arquivo .ui para código C++ utilizando

uic, nós podemos carregar o arquivo em tempo de execução usando a classe QUiLoader:

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

40

QUiLoader uiLoader;

QFile file("sortdialog.ui");

QWidget *sortDialog = uiLoader.load(&file);

if (sortDialog) {

...

}

Podemos acessar os widgets filhos utilizando QObject::findChild<T>():

QComboBox *primaryColumnCombo =

sortDialog->findChild<QComboBox *>("primaryColumnCombo");

if (primaryColumnCombo) {

...

}

A função findChild<T>() é uma função membro template que retorna o objeto filho que

corresponde com o nome e tipo de dados. Por causa de uma limitação do compilador, isso não está disponível para MSVC 6. Se você precisa usar o compilador MSVC 6, utilize a função global qFindChild<T>(), que funciona essencialmente da mesma forma.

A classe QUiLoader está localizada em uma biblioteca separada. Para usar QUiLoader a partir

de uma aplicação Qt, nós devemos adicionar esta linha ao arquivo .pro da aplicação:

CONFIG += uitools

Diálogos dinâmicos tornam possível mudar o layout de um formulário sem recompilar a aplicação. Eles podem também ser usados para criar aplicações “thin-client”, onde o executável meramente tem um formulário front-end interno e todos os outros formulários são criados quando necessário.

Classes Nativas de Widgets e Dialogs

O Qt fornece um conjunto completo de widgets nativos e diálogos comuns que atendem a maioria das situações. Nesta seção, apresentamos as imagens de quase todos eles. Alguns widgets especializados são adiados: Cobrimos widgets de mainwindows, como QMenuBar,

QToolBar e QStatusBar no Capítulo 3, e nós cobrimos widgets relacionados ao layout, como

QSplitter e QScrollArea no Capítulo 6. A maioria dos widgets nativos e diálogos são usados

nos exemplos apresentados neste livro. Nas imagens mostradas nas Figuras 2.17 até 2.26, todos os widgets são mostrados usando o estilo Plastique.

Figura 2.17. Botões do Qt

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

41

Figura 2.18. Containers de página única do Qt

Figura 2.19. Containers de múltiplas páginas do Qt

Figura 2.20. Item views do Qt

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

42

Figura 2.21. Widgets de exibição do Qt

Figura 2.22. Widgets de entrada do Qt

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

43

Figura 2.23. Diálogos de feedback do Qt

Figura 2.24. Diálogos de fonte e cor do Qt

Figura 2.25. Diálogos de impressão e arquivo do Qt

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

44

Figura 2.26. Diálogo Qwizard do Qt

O Qt oferece quatro tipos de "botões": QPushButton, QToolButton, QCheckBox e

QRadioButton, eles são mostrados na Figura 2.17. QPushButton e QToolButton são mais

comumente usados para iniciar uma ação quando clicados, mas eles também podem se comportar como botões de alternância (clique para afundar, clique para restaurar). QCheckBox

pode ser usado para independentes opções on/off, enquanto QRadioButtons normalmente são

mutuamente exclusivos.

Widgets containers do Qt são widgets que contêm outros widgets. Eles são mostrados na Figura 2.18 e Figura 2.19. QFrame também pode ser usado para simplesmente desenhar linhas

e serve como a classe base para muitas outras classes widget, incluindo QToolBox e QLabel.

QTabWidget e QToolBox são widgets multi-página. Cada página é um widget filho, e as páginas

são numeradas a partir de 0. Para QTabWidgets, tanto a forma como a posição das guias

podem ser definidos.

Os item views, mostrados na Figura 2.20, são otimizados para lidar com grandes quantidades

de dados e muitas vezes usam barras de rolagem. O mecanismo de rolagem é implementado em QAbstractScrollArea, uma classe base para item views e outros tipos de widgets com

elementos de rolagem.

A biblioteca do Qt inclui um mecanismo Rich Text que pode ser usado para exibir e editar texto formatado. O motor suporta especificações de fonte, alinhamento de texto, listas, tabelas, imagens e hiperlinks. Documentos Rich Text podem ser criados pró-gramaticalmente elemento por elemento ou fornecidos como texto em formato HTML. As tags HTML precisas e

propriedades CSS que o motor suporta são documentadas em http://doc.trolltech.com/4.3/richtext-html-subset.html. O Qt oferece alguns widgets que são utilizados exclusivamente para exibição de informações, eles são mostrados na Figura 2.21. QLabel é o mais importante deles, e ela pode ser usada

para mostrar textos simples, HTML e imagens.

A QTextBrowser é uma subclasse de QTextEdit de apenas leitura que pode exibir texto

formatado. Essa classe é usada em detrimento de QLabel para grandes documentos de texto

formatados, porque ao contrário de QLabel, ela fornece automaticamente as barras de

rolagem quando necessário, e também fornece amplo suporte para teclado e mouse. O Qt Assistant 4.3 usa QTextBrowser para apresentar a documentação para o usuário.

O Qt fornece diversos widgets para entrada de dados, como mostrado na Figura 2.22. QLineEdit pode restringir a sua entrada usando uma máscara de entrada, um validador, ou

C++ GUI Programando com Qt4, Segunda Edição

Tradução livre realizada pelos membros do Fórum QtBrasil www.qtbrasil.com

45

ambos. QTextEdit é uma subclasse QAbstractScrollArea capaz de editar grandes

quantidades de texto. A QTextEdit pode ser definida para editar texto simples ou Rich Text.

Em último caso, é capaz de exibir todos os elementos Rich Text que o motor do Qt suporta. Ambos QLineEdit e QTextEdit são totalmente integrados com a área de transferência.

O Qt fornece uma caixa de mensagem versátil e um diálogo de erro que lembra quais mensagens mostrou, estes são mostrados na Figura 2.23. O progresso das operações demoradas pode ser indicado através da QProgressDialog ou usando a QProgressBar

mostrado na Figura 2.21. QInputDialog é muito conveniente quando uma única linha de texto

ou um número único é exigido do usuário.

O Qt fornece o conjunto padrão de diálogos comuns que tornam mais fácil pedir ao usuário

para selecionar uma cor, fonte, ou arquivo, ou para imprimir um documento. Estes são mostrados na Figura 2.24 e Figura 2.25.

No Windows e no Mac OS X, o Qt usa os diálogos nativos, em vez de seus próprios diálogos comuns quando possível. As cores também podem ser escolhidas através de um dos widgets de seleção de cores do Qt Solutions, e fontes podem ser escolhidas usando QFontComboBox.

Finalmente, QWizard fornece uma estrutura para a criação de wizards (também chamados de

assistentes no Mac OS X). Wizards são úteis para tarefas complexas ou pouco freqüentes que os usuários podem ter dificuldade em aprender. Um exemplo de um assistente é mostrado na Figura 2.26.

Muitas funcionalidades prontas para uso são fornecidas pelos widgets e diálogos comuns. Mais necessidades especiais podem ser satisfeitas definindo propriedades do widget, ou conectando sinais aos slots e implementando comportamento personalizado nos slots.

Se nenhum dos elementos comuns ou diálogos fornecidos com o Qt é adequado, um pode estar disponível a partir do Qt Solutions, ou de versões comerciais ou não comerciais de terceiros. O Qt Solutions oferece um conjunto de widgets adicionais, incluindo selecionadores de cores diferentes, um controle de botão giratório, menus de pizza, e um navegador de propriedade, bem como uma caixa de diálogo de cópia.

Em algumas situações, pode ser desejável para criar um widget personalizado a partir do zero.

O Qt torna isso direto, e widgets personalizados podem acessar todas as mesmas funcionalidades de desenho de plataformas que os widgets nativos do Qt. Widgets personalizados podem ainda ser integrados com o Qt Designer, para que possam ser utilizados da mesma forma como os widgets nativos de Qt. O Capítulo 5 explica como criar widgets personalizados.