Tutorial Qt – Capitulo 06 – Um Livro de Endereços

Esse será nossa primeira aplicação real: um livro de endereços. Nós desenharemos e implementaremos as classes básicas e a interface com o usuário nesse capítulo. Vamos começar criando um projeto C++ vazio no Qt Designer. Vamos chama-lo adbook.pro para esse tutorial.

A primeira coisa que queremos adicionar é uma Janela principal. A janela que usaremos deve estar vazia, assim certifique-se de desmarcar todas as alternativas na Assistente mostrado na figura 7-1.

The Main Window Wizard with no options checked.
Figure 7-1 The Main Window Wizard with no options checked.

Agora altere o nome do formulário para frmMain e o titulo da janela para ‘Addred Book’.

Ao invés de conectar os botões a cada slot diretamente como fizemos até agora, iremos agrupar essas ações. Isso nos dá um signal para manipular múltiplos eventos como itens de menu, um acelerador de teclado (isto é, um atalho) e um botão de barra de ferramenta. Adicione quatro ações usando o novo botão de ações (mostrado na figura 7-2) de acordo com a tabela a seguir. Para fazer isso, simplesmente clique no novo botão de ação, e depois altere a ação usando o editor de propriedades. O resultado é mostrado na figura 7-2.

Os caracteres &  dizem ao Qt para sublinhar o caractere seguinte e usa-lo como atalho do teclado.

The new action button.
Figure 7-2 The new action button.
Name Text menuText
aExit Exit E&xit
aAddContact Add Contact &Add Contact
aEditContact Edit Contact &Edit Contact
aRemoveContact Remove Contact &Remove Contact
The actions.
Figure 7-3 The actions.

Clique com o botão direito do mouse no formulário para ativar  o menu de contexto. A partir dele, selecione a entrada ‘add menu item’. Para isso duas vezes para criar um formulário com duas entradas de menu.

Agora vamos renomear os itens do menu. Em versões antigas do Qt, isso é feito clicando com p botão direito em cada entrada criada recentemente e escolhando a opção renomear no menu. Nas últimas versões, dÊ um clique sobre o item para usar o editor de propriedades para alterar o nome. Dessa forma, renomeie-os para ‘&File’ e ‘&Contacts’ da esquerda para a direita.

Agora, simplesmente asrraste e solte as ações para os menus. Coloque a ação aExit no menu File e os restante das ações no menu Constacts. A Figura 7-4 mostra o resultado.

The Contacts menu.
Figure 7-4 The Contacts menu.

Agora, adicione uma listview ao formulário e nomeio lvContacts. Mude a propriedade allColumnsShowFocus para True. Quando isso tiver sido feito, dê um clique com o botão direito na listview e seelcione-a para edita-la. Limpe a lista de itens (figura 7-5) e crie as colunas ‘Name’, ‘E-mail’ e ‘Phone Number’ (figura 7-6).

The list view items.
Figure 7-5 The list view items.
The list view columns.
Figure 7-6 The list view columns.

Para tornar a listview o widget principal do formulário, selecione o formulário e aplique o layout de grid. Nas propriedades, altere o layoutMargin para zero para faxer com que a listview alcance os cantos do formulário. O formulário resultante é mostrado na figure 7-7.

The main window in preview mode.
Figure 7-7 The main window in preview mode.

Agora é hora de adicionar algumas funcionalidades ao projeto. Começamos pela adição dos seguintes slots privados ao formulário: init(), addContact(), editContact() e removeContact(). A caixa de diálogo de edição de slots resultante é mostrada na figura 7-8.

The slots.
Figure 7-8 The slots.

Clique no botão de conectar a ação atual no editor de ações e conecte os signals como mostrado na tabela abaixo. Para verificar se isso foi feito corretamente, dÊ um clique com o botão direito no formulário (por exemplo na barra de menu fora de qualquer item) e selecione Connections no menu de contexto. A caixa de diálogo resultante deveria mostrar algo parecido com a figura 7-9 (Por favor observe que a aparência dessa caixa de diálogo pode diferir um pouco entre versões diferentes do Qt. O conteúdo é o mesmo em todas as versões).

Source Signal Slot (always of frmMain)
aExit activated() close()
aAddContact activated() addContact()
aEditContact activated() editContact()
aRemoveContact activated() removeContact()
The connections.
Figure 7-9 The connections.

O próximo passo é criar uma caixa de diálogo para editar contatos novos e antigos. A figura 7-10 mostra como essa caixa de diálogo deve se parecer quando estiver pronta.

The contact editing dialog.
Figure 7-10 The contact editing dialog.

Comece adicionando uma nova caixa de diálogo ao projeto. Nomei-a como dlgContact e coloque o titulo como ‘Contact’. Na caixa de diálogo, adicione dois botões e nomeios como pbOk e pbCancel. Depois adicione uma groupbox. Na groupbox, adicione três labels e três lineedits. Nomeie-as como leName, leEMail e lePhone. Não esqueça de adicionar dois espaçadores como mostrado na figura 7-11. Além disso, altere o texto dos botões e labels de acordo com o mostrado na figura.

The widgets before applying the layouts.
Figure 7-11 The widgets before applying the layouts.

Agora, continuaremos com os layouts. Selecione as lineedits e aplique um layout vertical. Faça o mesmo com os labels. Depois selecione os botões e os espaçadores horizontais e aplique um layout horizontal. Agora, selecione o frame e aplique um layout horizontal. Finalmente, selecione o formulário e aplique um layout vertical.

Passemos para os signals e slots. Tudo que temos que fazer é conectar os signals clicked() dos botões para aceitar e rejeitar os slots da caixa de diálogo. Depois que tiver feito isso, a caixa de diálogo está pronta. Isso mostra quão rápido e fácil é criar uma caixa de diálogo.

Antes que possamos adicionar o código para que a janela principal, precisamos de uma estrutura para armazenar nossos contatos. Nesse momento, não é necessário com a funcionalidade que nós introduzimos nesse capítulo, bas será necessário depois. No momento, adicione um arquivo de cabeçalho C++ ao projeto e coloque o código abaixo nele, salvando como contact.h.

#ifndef CONTACT_H
#define CONTACT_H
#include <qstring.h>
class Contact
{
public:
QString name, eMail,phone;
};#endif

Para ser capaz de usar essa estrutura a partir de nossa janela principal, temos que inclui-la. Colocaremos os contatos em uma QValueList, assim precisaremos declarar a lista QValueList<Contact> m_contacts na classe da lista de variáveis. Existem três arquivos que precisam ser incluidos, a caixa de diálogo (dlgcontact.h), uma messagebox (qmessagebox.h) e a classe da lineedit (qlineedit.h). Se os arquivo da lineedit for omitido, não será possivel acessar nenhum membro da lineedit a partir do código na janela principal já que a janela principal não contém lineedits. Todos os widgets da janela, caixa de diálogo ou widgets compostos serão automaticamente incluidos. Todas as inclusões e declarações são mostradas na figura 7-12.

The file inclusions and declarations.
Figure 7-12 The file inclusions and declarations.

Finalmente o momento para escrever alguns códigos ativos chegou. Vamos começar pelo slot de inicialização  init(). O código mostrado no exemplo abaixo simplismente limpa  a listview. Isso não é necessário, mas mostra onde colocar o código de inicialização.

void frmMain::init()
{
// Clear the list
lvContacts->clear();
}

Em seguida, adicionamos a habilidade de adicionar contatos. O exemplo abaixo mostra o código e é bem direto, mostrando como as caixas de diálogos podem ser usadas eficientemente. Primeiro, incializa, exiba, se o resultado for aceito, confira os dados e execute a ação. Nesse caso adicionamos um novo contato a nossa lista.

void frmMain::addContact()
{
// Create a new dialog
dlgContact dlg( this );
// Show it and wait for Ok or Cancel
if( dlg.exec() == QDialog::Accepted )
{
// We got Ok
Contact c;
// Extract the information from the dialog
c.name = dlg.leName->text();
c.eMail = dlg.leEMail->text();
c.phone = dlg.lePhone->text();
// Check for duplicate names
for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
{
if( (*it).name == c.name )
{
// Warn for duplicates and fail
QMessageBox::warning( this, tr("Duplicate"), tr("The person ") + (*it).name + tr(" allready exists.") );
return;
}
}
// No duplicates found, add the contact to the list and show it in the listview
m_contacts.append( c );
lvContacts->insertItem( new QListViewItem( lvContacts, c.name , c.eMail, c.phone ) );
}
}

Depois dessa quantidade de código, vamos para a explicação dele. Os links levam a documentação oficial Qt  para que você possa obter mais informações.

A primeira coisa que fazemos é instânciar nossa sub-classe QDialog, dlg. A chamada ao método exec() esperará pelo fechamento da caixa de diálogo e retorna o status. Estamos interessados em saber se o resultado da caixa de diálogo foi aceita pelo usuário, através da sentença if.
O Contact, c, é preenchido com as informações da caixa de diálogo. Nós obtemos o texto das lineedits usando o método text().

Depois um conteinere Qt, o QValueList, é usado. O Qt é compatível com STL, mas os seus conteineres são mais versáteis. Se uma duplicata é encontrada, uma mensagem de aviso é mostrada usando o método warning() de QMessageBox.

Finalmente, o contato é adicionado a lista de contatos e um novo QListViewItem é adicionado a listview.
Continuamos com o código no exemplo abaixo. Agora adiconamos a funcionalidade de remover contatos da lista. Observe quão fácil é remover itens da lustview, apenas apagamos o item. Não há necessidade de se preocupar em liberar memória e isso graças ao Qt.

void frmMain::removeContact()
{
// Get a pointer to the selected item
QListViewItem *item = lvContacts->currentItem();
// Check if there is a valid select item
if( item )
{
// Find the corresponding item in the internal list
for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
{
if( (*it).name == item->text(0) )
{
// We have found it, remove it from the list and listview
m_contacts.remove( it );
delete item;
return;
}
}
}
}

Agora falta apenas mais um slot. O exemplo abaixo mostra o código usado para editar os contatos. Pode ser visto como uma mistura entre os casos de adicionar e remover contatos. Primeiro localize a entrada selecionada, monta a caixa de diálogo, exibe e altera a entrada se tudo estiver ok. O algoritmo de teste de duplicatas pode precisar de um comentário curto. Se o nome foi alterado, então a lista não vai conseguirarmazenar nenhuma duplicata, mas se o nome for o mesmo, nós permitimos uma ocorrência que é a entrada original.

void frmMain::editContact()
{
// Get a pointer to the selected item
QListViewItem *item = lvContacts->currentItem();
// Check if there is a valid select item
if( item )
{
// Find the corresponding item in the internal list
for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
{
if( (*it).name == item->text(0) )
{
// We have found it, show the dialog with it
dlgContact dlg( this );
dlg.leName->setText( (*it).name );
dlg.leEMail->setText( (*it).eMail );
dlg.lePhone->setText( (*it).phone );
// Show it and wait for Ok or Cancel
if( dlg.exec() == QDialog::Accepted )
{
// Check for duplicate names by counting the number of occurencies
int occurencies = 0;
for( QValueList<Contact>::iterator i2 = m_contacts.begin(); i2 != m_contacts.end(); ++i2 )
if( (*i2).name == dlg.leName->text() )
occurencies++;
// If the name is changed we allow no occurencies, otherwise one
if( (dlg.leName->text() != (*it).name && occurencies > 0) || (occurencies > 1) )
{
// Warn for duplicates and fail
QMessageBox::warning( this, tr("Duplicate"), tr("The person ") + dlg.leName->text() + tr(" allready exists.") );
return;
}// Update the internal list
(*it).name = dlg.leName->text();
(*it).eMail = dlg.leEMail->text();
(*it).phone = dlg.lePhone->text();
// Update the list view
item->setText( 0, (*it).name );
item->setText( 1, (*it).eMail );
item->setText( 2, (*it).phone );
}return;
}
}
}
}

Antes que possamos produzir uma aplicação funcional, precisamos adicionar uma função main() trivial. Adicione um arquivo de código fonte C++ chamado main.cpp ao projeto e coloque o código do exemplo abaixo. O código exibe a janela principal e fica esperando pelo comando de fechar a janela.

#include <qapplication.h>
#include "frmmain.h"
int main( int argc, char **argv )
{
QApplication a( argc, argv );
frmMain *m = new frmMain();
m->show();
a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) );
return a.exec();
}

A figura 7-13 mostra a aplicação em ação. Para compila ela, execute ‘qmake adbook.pro && make’ a partir do terminal, e inicie a aplicação.

The application in action.
Figure 7-13 The application in action.

Janela principal, caixa de diálogo e widget

Muitos usuários  inciantes no Qt ficam confusos com a variedade de métodos para exibir um janela com alguns widgets nela. Os primeiros exemplo s do Qt usam um widget diretamente da função main() enquanto outros usam caixas de diálogos. Adicione a essa lista a opção de usar uma Janela principal e a confusão é total.

Para encurtar a história, use janelas principais em aplicações grandes e caixas de diálogo em aplicações pequenas. Abaixo segue algumas razões para isso.

A habilidade de usar widgets diretamente da função main() é muito útil quando estamos testando widgets e escrevendo aplicações realmente pequenas sem nenhuma código. Assim que for necessário adicionar slots para as ações, como adicionar uma nova entrada, limpar um lista ou executar qualquer outra ação específica da aplicação, essa abordagem deve ser evitada. Uma aplicação mais útil fornece algumas funções, isso pode ser uma desculpa para não usar widgets diretamente a menos que esteja testando o widget.
Aplicações baseadas em caixas de diálogos são ótimas desde que seja fáceis de ser colocadas juntas e possam ser usadas para executar tarefas simples que não requerem uma janela principal. Não é necessário limitar uma aplicação base em caixa de diálogo a uma única caixa, mas assim que você começar a ter uma caixa de diálogo que exiba um ‘documento’  para que outra caixa de diálogo funcione, você deveria considerar o uso de uma janela principal.

Como mencionado do parágrafo anterior, a abordagem de uso de uma janela principal é recomendada quando você chega no nível de ter que trabalhar em um documento. Isso não significa um documento de texto exibido WYSIWYG (What You See Is What You Get) como nos processadores de texto modernos, mas talvez uma coleção de dados. Um exemplo de um documento é a lista de contatos usada na aplicação desse tutorial. Existem várias vantagens de usar essa última abordagem. Uma que acho importante é que é fácil criar aplicações que permitam que o usuário trabalhe com vários documentos, simplesmente crie várias instâncias de sua janela principal. Isso significa que cada documento consegue uma janela própria e essa é abordagem humano-computador recomendada pelos pesquisadores. Até recentemente muitas aplicações como o Office usavam uma interface MDI (muitos documentos em uma grande janela principal), mas em versões mais recentes, uma interface SDI (uma janela por documento) é usada.

Sumário

O código fonte do exemplo desse capitulo pode ser encontrado aqui ex07.tar
Traduzido de http://www.digitalfanatics.org/projects/qt_tutorial/chapter07.html