Revista QT - Segunda Edição
Revista QT - Segunda Edição
Revista QT - Segunda Edição
Agora com 62
Páginas!
revistaqt.com
Índice
5 – Qt + PHP – parte 2
Continuação do tutorial sobre desenvolvimento de aplicações
híbridas (Desktop + Web) usando Qt e PHP.
35 – Estilo de codificação Qt
O estilo de codificação adotado pela equipe de
desenvolvimento do Qt
41 – Tutorial Qml
Tradução do Tutorial de Qml disponível na
documentação do Qt
57 – Caixa de Entrada
Respostas a e-mails de leitores.
Caríssimos leitores,
Há uns três meses, quando resolvi criar uma revista sobre o Qt
eu já sabia que seria não seria nada fácil.
Pra começar esta seria a minha primeira experiência como
“editor” e pra piorar eu estava sozinho nessa empreitada.
Com apenas 24 páginas, a primeira edição da Revista Qt foi
publicada no dia 8 de setembro deste ano. Eram poucos e
um tanto inseguros, mas eram os meus primeiros passos no
sentido da minha ideia.
Dois meses e muito trabalho depois, aqui estou eu de novo,
desta vez escrevendo o editorial da segunda edição da Revista
Qt, que passa a ter editorias definidas, uma diagramação
ligeiramente melhorada continua sendo feita por mim :) e
com o mesmo objetivo: compartilhar conhecimento sobre Qt.
Seguindo orientações do meu amigo Pierre Freire, criei editorias, de acordo com a
natureza de cada artigo a ser publicado.
Estas serão as primeiras editorias da Revista Qt:
Iniciar
Esta editoria será dedicada a artigos para iniciantes em Qt. Se já
existisse na primeira edição, os artigos: “Apresentando o Qt”,
“Instalação do Qt SDK” e “Alô Qt Creator” estariam nesta
editoria, por requererem um conhecimento muito básico do Qt.
Artigos com um nível mais alto de complexidade ou que exijam
maiores conhecimentos dos leitores serão publicados nesta
editoria. O artigo “Qt + PHP – parte 1” da primeira edição é um
exemplo de artigo desta editoria.
Versão Brasileira
Caixa de Entrada
Notícias
Geek & Poke
Além destas, existem planos para outras editorias, como uma que apresente cases de
aplicação do Qt.
Encerrando este tutorial, agradeço pelas críticas e mensagens de apoio que tenho
recebido desde o lançamento da primeira edição da revista.
Um grande abraço.
André Luiz de Oliveira Vasconcelos
editor
Na primeira parte deste tutorial, vimos um pequeno exemplo de aplicação híbrida Desktop + PHP. A
partir de agora passamos a ver um exemplo mais complexo, utilizando o Zend Framework no
servidor. Como os tutoriais publicados nesta seção da revista – Laboratório – serão voltados aos
programadores mais experientes não vamos nos deter em aspectos básicos de programação Qt
ou PHP.
Para esta segunda parte, precisaremos do Zend Framework, que pode ser obtido no endereço:
http://framework.zend.com/download/latest
O motivo de adotar o Zend Framework neste projeto é apenas um: simplificar o desenvolvimento
do lado servidor da nossa aplicação. Isto porque o ZF (vamos chamá-lo assim daqui pra frente)
possui uma série de classes prontas para usar . Como usaremos apenas alguns componentes
básicos do ZF, podemos utilizar a versão minimal.
Além do ZF, vamos precisar do MySQL instalado na máquina que vamos usar como servidor. O
MySQL pode ser obtido no endereço:
http://dev.mysql.com/downloads/
Obviamente, como a parte servidora de nossa aplicação será desenvolvida em PHP, precisamos
também do Apache e do PHP 5 instalados no servidor.
✔ Ubuntu 10.10
✔ Apache2
✔ PHP5
✔ MySQL 5.1.49-1
✔ Zend Framework 1.10.8-minimal
HTTP
Interface em Qt
A aplicação desenvolvida nesta parte do tutorial carrega dados a partir de um servidor e mostra em
um Grid. Os dados ficam em um banco MySQL e são lidos por uma aplicação em PHP disponível
no servidor.
A interface da nossa aplicação – feita em Qt – faz uma requisição à aplicação em PHP no servidor,
e apresenta o resultado. Teremos um botão para fazer nova requisição ao servidor e atualizar as
informações no Grid.
Do lado servidor temos um programa em PHP com ZF que recebe o nome de uma classe e o nome
de um método, executa o método e retorna o resultado como um XML.
CREATE DATABASE `teste` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
CREATE DATABASE `teste` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
Agora que temos o banco de dados vamos à criação do lado servidor da nossa aplicação em PHP.
O documentRoot do Apache em minha máquina aponta para o diretório
/home/vasconcelos/Projetos/www. Se você não faz ideia do que seja documentRoot, recomendo
que procure na Internet por um tutorial de instalação e configuração do Apache.
Estando no diretório correspondente ao documentRoot do Apache, crie um diretório chamado
phpapp:
$ mkdir phpapp
$ mkdir phpapp
$ cd phpapp
$ cd phpapp
$ mkdir Classes
$ mkdir Classes
Como esta aplicação fará uso do ZF, vamos criar em seu diretório um link simbólico para o diretório
contendo as bibliotecas do cara (o ZF).
$ ln -s ../ZendFramework-1.10.8-minimal/library/Zend Zend
$ ln -s ../ZendFramework-1.10.8-minimal/library/Zend Zend
Agora que temos o diretório de nossa aplicação e um link para o diretório com as bilbiotecas do ZF
criados, vamos escrever o código PHP para ela, começando pelo arquivo index.php, que deverá
ser criado no diretório da aplicação – phpapp.
www.revistaqt.com
www.twitter.com/revistaqt
// Processa a requisição
// Processa a requisição
$server->handle();
$server->handle();
No quadro acima temos todo o código do arquivo index.php. Como mencionei no início desta parte
do tutorial, a adoção do ZF simplificou muito o trabalho. A classe Zend_Rest_Server faz toda a
“mágica” acontecer. O Rest Server recebe uma requisição para execução de um método em uma
determinada classe e retorna o resultado.
Nosso próximo passo será criar uma classe para conexão ao banco de dados. Antes de
continuarmos, quero fazer algumas observações. A finalidade deste exemplo é demonstrar a
abordagem de aplicações híbridas (Qt + PHP), não servir de base para uma aplicação “real”. Aqui
não estou preocupado com o tratamento de erros, por exemplo. Observe que no index.php não
está sendo tratada a situação de inexistência do arquivo correspondente à classe que esteja sendo
requisitada ao servidor.
A classe de conexão que vamos criar a seguir também não possuirá tratamento de erros e servirá
apenas para executar queries.
require_once('Zend/Db.php');
require_once('Zend/Db.php');
Esta classe de conexão usa o Design Pattern conhecido como Singleton. Mais informações sobre
o assunto podem ser encontradas em http://pt.wikipedia.org/wiki/Singleton.
[10]
10] Revista Qt – Novembro/Dezembro-2010
Para concluir a parte servidora desta aplicação falta apenas criar a classe que será requisitada
pelo programa em Qt.
<?php
<?php
// Classes/ListaEstado.php
// Classes/ListaEstado.php
class ListaEstado {
class ListaEstado
public function {retorna()
{ public function retorna()
{ include_once1('Classes/Conexao.php');
include_once1('Classes/Conexao.php');
$db = Conexao::getConexao();
$db = $db->executaQuery("SELECT
return Conexao::getConexao();
return $db->executaQuery("SELECT
id_estado,
id_estado,
sigla,
sigla,
nome
FROMnome
FROM
estado");
} estado");
} }
}
É isso. Para testar acesse a aplicação pelo browser, passando na url os argumentos class e
method, como mostra a figura abaixo:
O resultado apresentado dependerá do browser que você estiver utilizando. O Firefox apresenta o
XML retornado pela aplicação como mostra a próxima figura.
Agora que o servidor está pronto, vamos criar a parte cliente da nossa aplicação em Qt.
Como já foi esclarecido tanto no Editorial, como no início desta parte do tutorial, a editoria
Laboratório (da qual o presente artigo faz parte) é dedicada àqueles com mais experiência em Qt.
Serão abordados aqui, tópicos um pouco mais avançados e que vão portanto requerer do leitor o
conhecimento básico de Qt.
Usando o Qt Creator, crie um projeto chamado QtPHP, tendo sua classe principal chamada QtPHP.
Quem tiver dúvidas sobre como criar uma aplicação em Qt, pode consultar o artigo Alô, Qt Creator
publicado na primeira edição da Revista Qt.
Com um projeto com o nome de QtPHP criado, vamos à crição da interface (bem simples) da
aplicação. Arraste para a janela da aplicação um componente QpushButton. Troque sua
propriedade objectName para btnAtualizar e sua propriedade text para Atualizar.
[12]
12] Revista Qt – Novembro/Dezembro-2010
Arraste um componente QtableWidget para a janela da aplicação.
Coloque um componente Spacer entre o botão Atualizar e a borda da janela, como mostrado
abaixo.
Usando o menu de contexto da janela de nossa aplicação (botão direito do mouse), selecione a
opção Layout → Layout in a Grid.
Com a interface da nossa aplicação “desenhada”, podemos passar ao código-fonte, mas antes
vamos a uma breve descrição de seu funcionamento.
enviarRequisicao
QU
rl
QNetworkRequest
tratarResultado
Re
sp
ost
a
http
http://localhost/phpapp/?class=ListaEstado&method=retorna
http://localhost/phpapp/?class=ListaEstado&method=retorna
O slot enviarRequisicao monta uma URL que é submetida através de um objeto do tipo
QNetworkRequest pelo método get de um objeto QNetworkAccessManager. O signal finished do
objeto QNetworkAccessManager será conectado ao slot tratarResultado que receberá um objeto
do tipo QNetworkReply com o resultado da requisição feita ao servidor.
O slot tratarResultado processará o XML recebido como resposta do servidor, preenchendo o grid
QTableWidget que colocamos na interface do programa.
[14]
14] Revista Qt – Novembro/Dezembro-2010
Nosso programa utilizará os módulos QtNetwork e QtXml, portanto, edite o arquivo qtPHP.pro e
altere a linha:
O código da função main, no arquivo main.cpp não apresenta qualquer alteração em relação ao
criado pelo Qt Creator, como vemos a seguir:
#include <QtGui/QApplication>
#include"qtphp.h"
#include <QtGui/QApplication>
#include "qtphp.h"
int main(int argc, char *argv[])
{ int main(int argc, char *argv[])
{ QApplication a(argc, argv);
QApplication
QtPHP w; a(argc, argv);
QtPHP w;
w.show();
w.show();
return a.exec();
} return a.exec();
}
Agora edite o arquivo qtphp.h para que seu conteúdo seja igual ao mostrado na listagem abaixo:
#ifndef QTPHP_H
#ifndefQTPHP_H
#define QTPHP_H
#define QTPHP_H
#include <QMainWindow>
#include<QNetworkAccessManager>
#include <QMainWindow>
#include<QNetworkRequest>
#include <QNetworkAccessManager>
#include<QNetworkReply>
#include <QNetworkRequest>
#include<QMessageBox>
#include <QNetworkReply>
#include<QdomDocument>
#include <QMessageBox>
#include<QTimer>
#include <QdomDocument>
#include <QTimer>
namespace Ui {
namespace
class Ui {
QtPHP;
} class QtPHP;
}
class QtPHP : public QMainWindow
{ class QtPHP : public QMainWindow
{ Q_OBJECT
Q_OBJECT
public:
public:
explicit QtPHP(QWidget *parent = 0);
explicit QtPHP(QWidget *parent = 0);
~QtPHP();
~QtPHP();
private slots:
private
voidslots:
enviarRequisicao();
voidtratarResultado(QNetworkReply
void enviarRequisicao(); * resposta);
void tratarResultado(QNetworkReply * resposta);
private:
private:
Ui::QtPHP *ui;
Ui::QtPHP *ui;
QNetworkAccessManager * requisicaoRede;
}; QNetworkAccessManager * requisicaoRede;
};
#endif // QTPHP_H
#endif // QTPHP_H
As linhas em destaque (cor vermelha) no código acima referem-se àquelas que devem ser
incluídas em relação ao arquivo originalmente criado pelo Qt Creator.
Temos a inclusão das definições das classes que usaremos no programa, dos protótipos dos slots
enviarRequisicao e tratarResultado e do atributo requisicaoRede.
Começando pelos includes feitos para o header da classe e para o header da classe
correspondente à interface gráfica.
#include "qtphp.h"
#include"ui_qtphp.h"
#include "qtphp.h"
#include "ui_qtphp.h"
As linhas em destaque (cor vermelha) foram incluídas no método construtor originalmente criado
pelo Qt Creator.
Primeiro temos a instanciação de um objeto do QNetworkAccessManager que é o responsável
neste caso pela comunicação entre nosso programa em Qt e a aplicação Web. Este objeto é um
atributo da classe QtPHP, de modo que esteja acessível em todos os métodos da mesma. Se não
fosse um atributo da classe, precisaríamos passar por referência a todos os métodos que precisam
utilizá-lo.
Como temos um botão, chamado btnAtualizar que deverá executar uma requisição ao servidor,
conectamos o signal clicked do mesmo ao slot enviarRequisicao da nossa classe.
Quando ocorrer uma requisição, o objeto QNetworkAccessManager aguarda pela resposta do
servidor e emite um signal finished quando isto ocorrer. Para que nosso programa “saiba” que a
requisição foi atendida e reaja à resposta, conectamos o signal finished do objeto
QNetworkAccessManager ao slot tratarResultado, passando ao mesmo um ponteiro para um
objeto do tipo QNetworkReply.
[16]
16] Revista Qt – Novembro/Dezembro-2010
A seguir temos a configuração do objeto QTableWidget que exibirá a lista de estados obtida do
servidor. Estamos setando o número de colunas para três (3) e ocultando o header vertical, ou
seja, a coluna mostrada à esquerda da tabela com a numeração das linhas.
Continuando, setamos o cabeçalho (header) do objeto QTableWidget, com “Id” para a primeira
coluna, “Sigla” para a segunda coluna e “Nome” para a terceira.
Agora vem um macete publicado por Mark Summerfield em seu livro Advanced Qt Programming da
Prentice Hall. Observe a utilização de uma chamada ao método estático singleShot da classe
QTimer para chamar o método enviarRequisicao. A ideia de colocar a chamada ao método
enviarRequisicao no método construtor era de que, ao executar o programa, a lista de estados
seja automaticamente carregada e exibida, sem que o usuário tenha que clicar no botão Atualizar.
Até aí tudo bem, mas porque não coloquei no final do método construtor simplesmente uma
chamada ao método enviarRequisicao? De acordo com Mark, deve-se limitar ao construtor,
chamadas a métodos que estejam relacionados à criação do objeto. A chamada ao método de
carga da lista de estados pressupõe que a janela de nossa aplicação esteja “pronta”. Chamadas
diretas a métodos do objeto que está sendo construído são consideradas inseguras, porque não
existe a garantia de que o objeto esteja pronto durante a execução do construtor.
Assim, Mark recomenda o uso de uma chamada ao método estático singleShot de QTimer com
zero como argumento correspondente ao intervalo, como foi feito aqui:
Nenhuma alteração foi implementada no método destrutor da classe criado pelo Qt Creator, como
visto a seguir:
/**
*/**@brief Destrutor
*/* @brief Destrutor
*/
QtPHP::~QtPHP()
{ QtPHP::~QtPHP()
{ delete ui;
} delete ui;
}
O próximo método da nossa classe é o enviarRequisicao, que é um slot privado da classe QtPHP,
para que possa ser conectado ao signal clicked do botão Atualizar.
void QtPHP::enviarRequisicao()
{ void QtPHP::enviarRequisicao()
{ ui->tableWidget->setRowCount(0);
ui->tableWidget->setRowCount(0);
ui->tableWidget->clearContents();
ui->tableWidget->clearContents();
// Monta a URL para requisição dos dados ao servidor
// Monta
QString urla =URL para requisição dos dados ao servidor
"http://localhost/phpapp/?class=ListaEstado&method=retorna";
QString url = "http://localhost/phpapp/?class=ListaEstado&method=retorna";
// Executa a requisição ao servidor
// Executa a requisição ao servidor
requisicaoRede->get(QNetworkRequest(QUrl(url)));
} requisicaoRede->get(QNetworkRequest(QUrl(url)));
}
O slot tratarResultado recebe como argumento um ponteiro para um objeto do tipo QNetworkReply
passado para ele pelo objeto QNetworkAccessManager no momento em que o signal finished for
emitido (lembre-se de que no construtor da classe QtPHP, conectamos o signal finished do objeto
requisicaoRede que é um QNetworkAccessManager ao slot tratarResultado).
O primeiro passo em tratarResultado é verificar se ocorreu erro na requisição feita ao servidor.
Para isso verificamos se o resultado da chamada ao método error do objeto resposta é diferente de
QNetworkReply:NoError. Caso tenha ocorrido um erro, o programa emite uma mensagem
informando ao usuário e retorna.
A resposta recebida do servidor será um XML com a seguinte estrutura:
<ListaEstado generator="zend" version="1.0">
<ListaEstado
<retorna>generator="zend" version="1.0">
<retorna>
<key_0>
<key_0>
<id_estado>1</id_estado>
<id_estado>1</id_estado>
<sigla>AM</sigla>
<sigla>AM</sigla>
<nome>Amazonas</nome>
</key_0><nome>Amazonas</nome>
</key_0>
...
...
<key_25>
<key_25>
<id_estado>26</id_estado>
<id_estado>26</id_estado>
<sigla>AP</sigla>
<sigla>AP</sigla>
<nome>Amapá</nome>
<nome>Amapá</nome>
</key_25>
</key_25>
<status>success</status>
<status>success</status>
</retorna>
</retorna>
</ListaEstado>
</ListaEstado>
[18]
18] Revista Qt – Novembro/Dezembro-2010
Na primeira linha temos a tag raiz do nosso XML, identificando nesse caso o nome da classe que
foi executada para emissão da resposta – ListaEstado. Os atributos generator e version indicam
respectivamente quem gerou o XML e qual a versão. A próxima tag do XML tem o nome do método
executado – retorna. Em seguida, temos para cada um dos registros retornados a tag key_n, onde
n representa o número sequencial do registro, começando por zero (0). Para cada registro, temos
três tags indicando os nomes dos campos retornados, a saber:
<id_estado>
<sigla>
<nome>
O valor contido em nas tags acima é o conteúdo do registro propriamente dito.
No próximo passo, caso não tenha ocorrido um erro no retorno da requisição do servidor, temos a
criação de um objeto do tipo QDomDocument que será utilizado para tratamento do XML
retornado. Na atribuição do conteúdo ao objeto QDomDocument verificamos se ocorreu erro. Se a
resposta do servidor não puder ser atribuída ao objeto QDomDocument, o programa emite uma
mensagem ao usuário e retorna.
Com o XML da resposta carregado no objeto QDomDocument podemos usar suas facilidades para
navegar pelas tags do documento.
Neste trecho do código, utilizamos um objeto QDomElement para armazenar o primeiro elemento
do documento DOM. Se o nome da tag do primeiro elemento do XML recebido do servidor não for
ListaEstado, o programa avisa ao usuário e retorna.
O primeiro filho da tag retorna, lida no passo anterior, corresponde ao primeiro registro retornado
pela consulta.
// Nó correspondente ao status - o último
// Nó correspondente
QDomNode ao status - o último
status = metodo.lastChild();
QDomNode status = metodo.lastChild();
if(status.toElement().text() != "success"){
if(status.toElement().text() != "success"){
QMessageBox::critical(this,"Erro","O servidor retornou erro");
QMessageBox::critical(this,"Erro","O servidor retornou erro");
return;
} return;
}
Antes de começar a navegar pelos registros, temos a leitura do último filho da tag retorna, que
corresponde ao status da execução do comando. Caso o texto deste último elemento seja diferente
de “success”, significa que ocorreu um erro. Neste caso, o programa avisa ao usuário e retorna.
// Percorre os registros
// linha
int Percorre= os0; registros
int linha = 0;
while(!registro.isNull() && registro != status){
while(!registro.isNull() && registro != status){
ui->tableWidget->insertRow(linha);
ui->tableWidget->insertRow(linha);
QDomNode campo = registro.firstChild();
QDomNode
int coluna campo
= 0; = registro.firstChild();
//int coluna os= campos
Percorre 0;
// Percorre os campos
while(!campo.isNull()){
while(!campo.isNull()){
QTableWidgetItem * item = new QTableWidgetItem(campo.toElement().text());
QTableWidgetItem * item = new QTableWidgetItem(campo.toElement().text());
ui->tableWidget->setItem(linha, coluna, item);
ui->tableWidget->setItem(linha, coluna, item);
coluna++;
coluna++;
campo = campo.nextSibling();
} campo = campo.nextSibling();
}
linha++;
linha++;= registro.nextSibling();
registro
} registro = registro.nextSibling();
} }
}
Se a execução do programa chegou a este ponto, só falta percorrer a lista de registros e popular o
QTableWidget da nossa aplicação com os dados. Neste trecho do código, o primeiro loop while
percorre registro, até que seja o final desde que não seja o registro de status.
Como cada campo de um registro é um filho seu, temos um segundo loop para percorrer os filhos
de cada registro e setar a linha/coluna correspondente no QTableWidget com o conteúdo do
campo.
Para navegar entre os itens de um elemento do DOM, usamos o método nextSibling.
[20]
20] Revista Qt – Novembro/Dezembro-2010
void QtPHP::tratarResultado(QNetworkReply * resposta)
{ void QtPHP::tratarResultado(QNetworkReply * resposta)
{ // Verifica se houve erro na resposta
// Verifica se houve!=erro
if(resposta->error() na resposta
QNetworkReply::NoError){
if(resposta->error() != QNetworkReply::NoError){
QMessageBox::critical(this, "Erro",
QMessageBox::critical(this,
"Não foi"Erro",
possível recuperar dados do servidor");
return; "Não foi possível recuperar dados do servidor");
} return;
}
// Cria objeto DOM para tratamento do XML de resposta e
////verifica
Cria objeto
se a DOM para tratamento
resposta do XML deaoresposta
pode ser atribuída e
este objeto
// verifica doc;
QDomDocument se a resposta pode ser atribuída ao este objeto
QDomDocument doc;
if(!doc.setContent(resposta)){
if(!doc.setContent(resposta)){
QMessageBox::critical(this,"Erro","Erro tratando resultado");
QMessageBox::critical(this,"Erro","Erro tratando resultado");
return;
} return;
}
// Verifica se o retorno foi gerado pela classe ListaEstado
// Verificaclasse
QDomElement se o retorno foi gerado pela classe ListaEstado
= doc.documentElement();
QDomElement classe !=
if(classe.tagName() = doc.documentElement();
"ListaEstado"){
if(classe.tagName() != "ListaEstado"){
QMessageBox::critical(this,"Erro",
QMessageBox::critical(this,"Erro",
"O XML recebido não é da classe ListaEstado");
return; "O XML recebido não é da classe ListaEstado");
} return;
}
// Nó correspondente ao nome do método - retorna
// Nó correspondente
QDomNode ao nome do método - retorna
metodo = classe.firstChild();
QDomNode metodo = classe.firstChild();
// Nó correspondente ao registro
// Nó correspondente
QDomNode ao registro
registro = metodo.firstChild();
QDomNode registro = metodo.firstChild();
// Nó correspondente ao status - o último
// Nó correspondente
QDomNode ao status - o último
status = metodo.lastChild();
QDomNode status = metodo.lastChild();
if(status.toElement().text() != "success"){
if(status.toElement().text() != "success"){
QMessageBox::critical(this,"Erro","O servidor retornou erro");
QMessageBox::critical(this,"Erro","O servidor retornou erro");
return;
} return;
}
// Percorre os registros
// linha
int Percorre= os0; registros
int linha = 0;
while(!registro.isNull() && registro != status){
while(!registro.isNull() && registro != status){
ui->tableWidget->insertRow(linha);
ui->tableWidget->insertRow(linha);
QDomNode campo = registro.firstChild();
QDomNode
int coluna campo
= 0; = registro.firstChild();
//int coluna os= campos
Percorre 0;
// Percorre os campos
while(!campo.isNull()){
while(!campo.isNull()){
QTableWidgetItem * item = new QTableWidgetItem(campo.toElement().text());
QTableWidgetItem * item = new QTableWidgetItem(campo.toElement().text());
ui->tableWidget->setItem(linha, coluna, item);
ui->tableWidget->setItem(linha, coluna, item);
coluna++;
coluna++;
campo = campo.nextSibling();
} campo = campo.nextSibling();
}
linha++;
linha++;= registro.nextSibling();
registro
} registro = registro.nextSibling();
} }
}
A explicação sobre o funcionamento deste programa não foi feita linha a linha, mas para aqueles
com alguma experiência em Qt, os comentários colocados no código-fonte já devem ajudar
bastante. A mesma observação vale para a parte PHP desta aplicação.
Na próxima edição continuamos com mais exemplos de aplicações híbridas, utilizando Qt e PHP.
Um grande abraço.
[22]
22] Revista Qt – Novembro/Dezembro-2010
Por: Oliver Widder
[email protected]
http://geekandpoke.typepad.com
Com o crescimento da Apple no mercado, tem se tornado muito comum o uso de notebooks e desktops da
empresa de Cupertino por usuários e empresas, com isto a demanda por novos aplicativos tem crescido
bastante e este novo filão para os programadores esta apenas começando.
O objetivo deste tutorial é abordar a instalação do SDK ( Kit de Desenvolvimento da QT para Mac OS ), outras
informações os links abaixo poderão ajudar bastante.
http://www.revistaqt.com
http://qt.nokia.com
Ambiente testado:
MacBook White
Sistema operacional Snow Leopard 10.6.4
1 – Acessando o site da Nokia
1.1 O Kit de desenvolvimento da QT pode ser obtido gratuitamente no site http://qt.nokia.com/ acessando
o link de DOWNLOADS:
[24]
24] Revista Qt – Novembro/Dezembro-2010
1.2 – Você tem agora a opção de usar a versão comercial ou lgpl ( livre ), no nosso tutorial estamos baixando a
versão lgpl, conforme a imagem abaixo:
1.3 – Caso o download não comece, clique no link, conforme a figura:
1.4 – A velocidade do download vai depender da sua banda larga, agora a ordem é esperar.
O download foi efetuado com sucesso e o arquivo QtSDK.mpkg irá aparecer no desktop do seu computador,
ele irá abrir automaticamente, caso isto não aconteça clique no icone.
[26]
26] Revista Qt – Novembro/Dezembro-2010
2.1 – A tela do instalador aparece e o processo se inicia.
2.2 – Tela de boas vindas com algumas informações sobre o produto.
2.4 – Informações sobre o licenciamento do software, caso tenha interesse leia e siga em frente.
[28]
28] Revista Qt – Novembro/Dezembro-2010
2.5 – Esta janela, quer uma confirmação se você aceita os termos da licença clique em Agree caso você
concorde em usar.
2.6 – O instalador irá mostrar as unidades de disco e o espaço disponível , e o espaço que a instalação do QT
vai ocupar no disco.
2.7 – Se quiser mudar o local de instalação. No tutorial deixamos no padrão.
[30]
30] Revista Qt – Novembro/Dezembro-2010
2.9 – O processo de instalação foi iniciado, agora é esperar.
2.10 – Se você chegou a este ponto, significa que o QT foi instalado com sucesso no seu computador.
3.1 – Usando o Finder ou o atalho no seu desktop acesse o seu Macintosh HD.
Dentro da pasta Developer selecione Applications, agora entre na pasta QT.
[32]
32] Revista Qt – Novembro/Dezembro-2010
.Dentro da pasta Qt e vários aplicativos estarão disponíveis:
✔Assistant,
✔Qt Creator
✔qtdemo ( Exemplos feitos em Qt )
Clique no Qt Creator para conhecer a IDE de desenvolvimento.
3.2 – O Qt Creator é a IDE de desenvolvimento oficial.
O próximo passo agora é o seu, estudar, pesquisar e conhecer este fascinante mundo do QT.
[34]
34] Revista Qt – Novembro/Dezembro-2010
Estilo de codificação QT Por: André Vasconcelos
[email protected]
Estilo de Codificação Qt
Este é um resumo da convenção de codificação que usamos para escrever código do Qt. Estes
dados foram recolhidos pela "mineração" dos fontes do Qt, fóruns de discussão, tópicos de email e
através da colaboração dos desenvolvedores.
Indentação
● Quatro (4) espaços são usados para identação
● Espaços, não Tabs!
Declaração de variáveis
● Declare cada variável em uma linha separada
● Evite usar nomes curtos (por exemplo: “a”, “rbarr”, “nughdeget”) sempre que possível
● Nomes de variáveis com apenas um caractere só devem ser usadas para contadores e variáveis
temporárias, quando o propósito da variável seja óbvio
● Declare uma variável no momento em que ela seja necessária
// Errado
int a, b;
char *c, *d;
// Correto
int height;
int width;
char *nameOfThis;
char *nameOfThat;
// Errado
short Cntr;
char ITEM_DELIM = '\t';
// Correto
short counter;
char itemDelimiter = '\t';
● Classes sempre começam com uma letra maiúscula. Classes públicas do Qt começam com uma
letra Q (maiúscula). Funções públicas na maioria das vezes, começam com uma letra q
(minúscula)
Espaços em branco
● Use linhas em branco para agrupar declarações onde for adequado
● Sempre use apenas uma linha em branco
● Sempre use um espaço único depois de uma palavrachave e antes de chaves.
// Errado
if(foo){
}
// Correto
if (foo) {
}
● Para ponteiros ou referências, sempre use um único espaço entre o tipo e o caracter '*' ou '&'*’,
mas nenhum espaço entre o ‘*’ ou ‘&’ e o nome da variável.
char *x;
const QString &myString;
const char * const y = "hello";
● Não use espaço depois de um cast.
● Evite conversões (casts) no estilo de C quando possível.
// Errado
char* blockOfMemory = (char* ) malloc(data.size());
// Correto
char *blockOfMemory = (char *)malloc(data.size());
char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size()));
[36]
36] Revista Qt – Novembro/Dezembro-2010
Chaves
● Como regra básica, a chave da esquerda vem na mesma linha do começo da declaração:
// Errado
if (codec)
{
}
// Correto
if (codec) {
}
● Exceção: Implementações de funções e declarações de classes sempre têm a chave esquerda no
começo de uma nova linha:
static void foo(int g)
{
qDebug("foo: %i", g);
}
class Moo
{
};
● Use chaves quando o corpo de uma declaração condicional contiver mais de uma linha, ou
quando contiver apenas uma linha que seja um pouco mais complexa
// Errado
if (address.isEmpty()) {
return false;
}
for (int i = 0; i < 10; ++i) {
qDebug("%i", i);
}
// Correto
if (address.isEmpty())
return false;
for (int i = 0; i < 10; ++i)
qDebug("%i", i);
// Correto
if (address.isEmpty() || !isValid()
|| !codec) {
return false;
}
● Exceção 2: Use chaves também em blocos ifthenelse caso o bloco de código do if ou do else
tenha mais de uma linha
// Errado
if (address.isEmpty())
return false;
else {
qDebug("%s", qPrintable(address));
++it;
}
// Correto
if (address.isEmpty()) {
return false;
} else {
qDebug("%s", qPrintable(address));
++it;
}
// Errado
if (a)
if (b)
...
else
...
// Correto
if (a) {
if (b)
...
else
...
}
● Use chaves quando o corpo de uma declaração for vazia
// Errado
while (a);
// Correto
while (a) {}
[38]
38] Revista Qt – Novembro/Dezembro-2010
Parênteses
● Use parênteses para agrupar expressões:
// Errado
if (a && b || c)
// Correto
if ((a && b) || c)
// Errado
a + b & c
// Correto
(a + b) & c
Declarações Switch
● Os rótulos "case" das declarações switch estão sempre na mesma coluna da declaração switch
● Todo caso deve ter uma declaração break (ou return) no final ou um comentário para indicar que
intencionalmente não foi colocado o break
switch (myEnum) {
case Value1:
doSomething();
break;
case Value2:
doSomethingElse();
// fall through
default:
defaultHandling();
break;
}
Quebras de linhas
● Mantenha as linhas com menos de cem (100) caracteres; utilize quebras se necessário.
● Vírgulas vão no final das linhas quebradas; operadores vão no início de uma nova linha. O
operador estará no final de uma linha para evitar ter que rolar (scroll) a tela se o editor for muito
estreito.
// Errado
if (longExpression +
otherLongExpression +
otherOtherLongExpression) {
}
Exceção geral
● Sintase livre para quebrar uma regra se esta fizer o seu código parecer ruim.
[40]
40] Revista Qt – Novembro/Dezembro-2010
Tutorial QML / Por: André Vasconcelos
Layout Anchorbased [email protected]
em QML /
Introdução à
Linguagem QML
Mais uma da série “Versão Brasileira”. Desta vez trago uma tradução do QML Tutorial disponível
no endereço http://doc.qt.nokia.com/4.7-snapshot/qml-tutorial.html. O Tutorial QML está
dividido em três partes. De lambuja, a tradução de mais dois tutoriais relacionados ao Tutorial
QML. Um sobre layouts baseados em âncoras (Anchor-based) e outro sobre a linguagem QML.
Vamos lá...
Tutorial QML
Este tutorial traz uma introdução ao QML, a linguagem de marcação para o Qt Quick. Ele não
cobre todos os aspectos; a ênfase é no ensino dos princípios fundamentais, e os recursos são
apresentados à medida em que sejam necessários.
Através dos diferentes passos deste tutorial nós aprenderemos sobre os tipos básicos, criaremos
nosso próprio componente QML com propriedades e sinais (signals), e criaremos uma animação
simples com a ajuda dos estados (states) e transições (transitions).
O primeiro capítulo começa com um programa "Hello world!" mínimo e o capítulo seguinte
apresenta novos conceitos.
$QTDIR/examples/declarative/tutorials/helloworld
Capítulos do tutorial:
1. Tipos básicos
2. Componentes QML
O primeiro programa é um exemplo de "Hello world!" muito simples que apresenta alguns
conceitos básicos do QML. A figura abaixo é um screenshot deste programa.
Passo a passo
Import
Primeiro nós precisamos importar os tipos necessários a este exemplo. Na maioria dos arquivos
QML importaremos os tipos QML embutidos (como Rectangle, Image, etc) que vêm com o Qt
usando:
[42]
42] Revista Qt – Novembro/Dezembro-2010
Elemento Rectangle
Rectangle {
Rectangle
id: page{
id: page
width: 500; height: 200
width:"lightgray"
color: 500; height: 200
color: "lightgray"
Declaramos um elemento raiz do tipo Rectangle. Este é um dos blocos de construção básicos que
você pode usar para criar uma aplicação em QML. Nós atribuimos um id para poder fazer
referência ao mesmo mais tarde. Neste caso, nós o chamamos de "page". Nós também setamos
as propriedades width (tamanho), height (altura) e color (cor). O elemento Rectangle contém
muitas outras propriedades (como x e y), mas estes são deixados com seus valores default.
Elemento Text
Text {
Text
id:{ helloText
id: helloText
text: "Hello world!"
y:text:
30 "Hello world!"
y: 30
anchors.horizontalCenter: page.horizontalCenter
anchors.horizontalCenter:
font.pointSize: page.horizontalCenter
24; font.bold: true
} font.pointSize: 24; font.bold: true
}
Adicionamos um elemento Text como filho do elemento raiz Rectangle que exibe o texto "Hello
world!".
A propriedade y é usada para posicionar o texto verticalmente a 30 pixels do topo de seu pai.
Visualizando o exemplo
Para ver o que você criou, execute a ferramenta de visualização QML (localizada no diretório bin
do SDK do Qt) com o nome do arquivo como primeiro argumento. Por exemplo, para executar o
exemplo completo do Tutorial 1 a partir do local de instalação, você deveria digitar:
bin/qmlviewer $QTDIR/examples/declarative/tutorials/helloworld/tutorial1.qml
Este capítulo acrescenta um seletor de cores (color picker) para alterar a cor do texto.
Nosso seletor de cores é feito de seis células com diferentes cores. Para evitar escrever o mesmo
código várias vezes para cada célula, criamos um novo componente Cell. Um componente
proporciona uma forma de definir um novo tipo que podemos reutilizar em outros arquivos QML.
Um componente QML é como uma caixa preta e interage com o mundo exterior através de
propriedades, sinais (signals) e funções e é geralmente definido em seu próprio arquivo QML.
(Para mais detalhes veja a seção "Definindo novos componentes"). O nome do arquivo do
componente deve sempre começar com uma letra maiúscula.
[44]
44] Revista Qt – Novembro/Dezembro-2010
Passo a passo
O componente Cell
Item {
Item
id:{ container
id: container
property alias cellColor: rectangle.color
property
signal alias cellColor:
clicked(color rectangle.color
cellColor)
signal clicked(color cellColor)
width: 40; height: 25
width: 40; height: 25
Nós precisamos que nosso componente tenha também um signal que chamaremos de clicked
com um parâmetro do tipo color. Usaremos este signal para mudar a cor do texto no arquivo
QML principal mais tarde.
Rectangle {
Rectangle
id:{ rectangle
id: rectangle"white"
border.color:
border.color:parent
anchors.fill: "white"
} anchors.fill: parent
}
MouseArea {
MouseAreaanchors.fill:
{ parent
anchors.fill: parent
onClicked: container.clicked(container.cellColor)
} onClicked: container.clicked(container.cellColor)
}
Um MouseArea define um signal chamado clicked. Quando o signal é disparado precisamos emitir
nosso próprio signal clicked com a cor como parâmetro;
Criamos o seletor de cores colocando 6 células com diferentes cores em uma tabela.
Quando o signal clicked de nossa célula é disparada, precisamos atribuir a cor do texto para a
cellColor passada como seu parâmetro. Podemos reagir a qualquer signal do componente através
de uma propriedade de nome "onSignalName" (veja Manipuladores de sinais).
[46]
46] Revista Qt – Novembro/Dezembro-2010
Tutorial QML 3 - States e Transitions
Neste capítulo, nós criamos este exemplo um pouco mais dinâmico pela introdução de states e
transitions.
Queremos que nosso texto mova-se para a base da tela, rode e fique vermelho quando clicado.
states: State {
states: State { when: mouseArea.pressed == true
name: "down";
name: "down"; when:
PropertyChanges mouseArea.pressed
{ target: helloText; y: == true
160; rotation: 180; color: "red" }
} PropertyChanges { target: helloText; y: 160; rotation: 180; color: "red" }
}
Primeiro nós criamos um novo state para nosso elemento text. Este state será ativado quando o
MouseArea for pressionado e desativado quando for solto.
O state down inclui um conjunto de mudanças de propriedades em relação ao state default (os
itens como são inicialmente definidos no QML). Especificamente, nós setamos a propriedade y do
texto para 160, a rotação para 180 e a cor para vermelho.
transitions: Transition {
transitions:
from: ""; Transition
to: "down";{ reversible: true
from: ""; to: "down";
ParallelAnimation { reversible: true
ParallelAnimation {
NumberAnimation { properties: "y,rotation"
NumberAnimation {duration:
properties:
500 "y,rotation"
duration: 500
easing.type: Easing.InOutQuad
} easing.type: Easing.InOutQuad
}
ColorAnimation { duration: 500 }
} ColorAnimation { duration: 500 }
} }
}
Como não queremos que o texto apareça na base da tela instantaneamente, mas ao invés disse
mova-se suavemente, adicionamos uma transition entre os dois states.
As propriedades from e to definem os states entre os quais a transition vai ser executada. Nesse
caso, queremos uma transition do state default oara nosso state down.
Como queremos que a mesma transition seja executada ao contrário quando voltando do state
down para o state default, setamos a propriedade reversible para true. Isto equivale a escrever
as duas transitions separadamente.
O elemento ParallelAnimation garante que os dois tipos de animação (number e color) comecem
ao mesmo tempo. Podeos também executá-las (as animações) uma após a outra usando
SequentialAnimation ao invés de ParallelAnimation.
Para mais detalhes sobre states e transitions, veja States QML e o exemplo de states e
transitions.
[48]
48] Revista Qt – Novembro/Dezembro-2010
Layout Anchor-based em QML
Além dos mais tradicionais - Grid, Row e Column, QML também proporciona um modo de
organizar itens usando o conceito de âncoras (anchors). Cada item pode ser imaginado como se
tivesse um conjunto de sete linhas-âncoras invisíveis: left, horizontalCenter, right, top,
verticalCenter, baseline e bottom.
A linha-base (não mostrada acima) corresponde à linha imaginária na qual o texto estará
assentado. Para itens sem texto, ela é a mesma que "top".
Neste caso, a borda esquerda de rect2 é vinculada à borda direita de rect1, produzindo o
seguinte:
Neste caso, a margem de 5 pixels é reservada para a esquerda de rect2, produzindo o seguinte:
Limitações
Por razões de performance, você só pode ancorar um item ao seus irmãos e pais diretos. Por
exemplo, a segunte âncora pode ser considerada inválida e produziria uma advertência:
Item {
Item {
id: group1
id: group1
Rectangle { id: rect1; ... }
} Rectangle { id: rect1; ... }
}
Item {
Item {
id: group2
id: group2
Rectangle { id: rect2; anchors.left: rect1.right; ... } // âncora inválida
} Rectangle { id: rect2; anchors.left: rect1.right; ... } // âncora inválida
}
[50]
50] Revista Qt – Novembro/Dezembro-2010
Introdução à linguagem QML
Objetos são especificados pelo seu tipo, seguido por um par de chaves. Tipos de objetos sempre
começam com uma letra maiúscula. No exemplo acim, existem dois objetos, um Rectangle e um
Image. Entre as chaves, podemos especificar informações sobre o objeto, como suas
propriedades.
Propriedades são especificadas como propriedade: valor. No exemplo acima, podemos ver que
Image possui uma propriedade chamada source, à qual foi atribuido o valor "pics/logo.png". A
propriedade e seus valores são separadas por uma dois pontos.
Rectangle {
Rectangle
width:{ 100
width: 100
height: 100
} height: 100
}
A declaração import importa o módulo Qt, que contém todos os elementos padrão do QML. Sem
esta declaração import, os elementos Rectangle e Image não estariam disponíveis
Expressões
Além de atribuir valores às propriedades, você pode também atribuir expressões escritas em
Javascript.
Rotation {
Rotation
angle: {360 * 3
} angle: 360 * 3
}
Estas expressões podem incluir referências a outros objetos e propriedades, caso em que um
vínculo é estabelecido: quando o valor da expressão muda, a propriedade à qual a expressão foi
atribuída é automaticamente atualizada para aquele valor.
Item {
Item
Text{ {
Text
id:{ text1
id: text1
text: "Hello World"
} text: "Hello World"
}
Text {
Text
id:{ text2
id: text2
text: text1.text
} text: text1.text
} }
}
No exemplo acima, o objeto text2 mostrará o mesmo texto do objeto text1. Se text1 mudar,
text2 é automaticamente alterado para o mesmo valor.
Observe que para referir-se a outros objetos, usamos os valores de seus ids (veja abaixo mais
informações sobre a propriedade id).
Comentários QML
Comentar em QML é semelhante ao JavaScript.
Comentários são ignorados pelo engine. Eles são úteis para explicar o que você está fazendo;
para referência futura ou para outros que leiam seus arquivos QML.
Comentários também podem ser usados para evitar a execução de código, o que às vezes é útil
para rastrear problemas.
[52]
52] Revista Qt – Novembro/Dezembro-2010
Text {
Text text:
{ "Hello world!"
text: "Hello
//opacity: 0.5 world!"
} //opacity: 0.5
}
No exemplo acima, o objeto Text será sua opacidade normal, posto que a linha opacity: 0.5 foi
transformada em um comentário.
Propriedades
Nomeando propriedades
Propriedades começa com uma letra minúscula (com exceção das propriedades anexadas).
Tipos de propriedades
QML suporta propriedades de muitos tipos (veja Tipos básicos QML). Os tipos básicos incluem
int, real, bool, string, color e lists.
Item {
Item
x: {10.5 // uma propriedade 'real'
x: 10.5
... // uma propriedade 'real'
...
state: "details" // uma propriedade 'string'
state:true
focus: "details" ////uma
umapropriedade
propriedade'bool'
'string'
} focus: true // uma propriedade 'bool'
}
Propriedades QML são o que é conhecido como type-safe. Isto é, elas apenas permitem atribuir
um valor que combine com o tipo da propriedade. Por exemplo, a propriedade x do item é um
real, e se você tentar atribuir uma string a ela, você obterá um erro.
Item {
Item
x: {"hello" // ilegal!
} x: "hello" // ilegal!
}
A propriedade id
Cada objeto pode receber uma propriedade especial e única chamada id. Nenhum outro objeto
dentro do mesmo documento QML pode ter o mesmo valor de id. Atribuir um id permite que o
objeto seja referido por outros objetos e scripts.
O primeiro elemento Rectangle abaixo tem um id, "myRect". O segundo elemento Rectangle
define seu próprio tamanho (width) por referência a myRect.width, o que significa que ele terá o
mesmo valor de width que o primeiro elemento Rectangle.
Observe que um id deve começar com uma letra minúscula ou um underscore e não pode conter
outros caracteres que não sejam letras, números e underscores.
Propriedades List
Propriedades List parecem com isso:
Item {
Item {
children: [
children: [
Image {},
Image
Text {} {},
] Text {}
} ]
}
A lista é encerrada entre colchetes, com uma vírgula separando os elementos da lista. Em casos
onde você esteja atribuindo um único item à lista, você pode omitir os colchetes:
Image {
Image {
children: Rectangle {}
} children: Rectangle {}
}
Propriedades Default
Cada tipo de objeto pode especificar uma de suas listas ou propriedades de objeto como sua
propriedade default. Se a propriedade foi declarada como propriedade default, a tag da
propriedade pode ser omitida.
[54]
54] Revista Qt – Novembro/Dezembro-2010
State {
State {
PropertyChanges {}
PropertyChanges{}{}
PropertyChanges
} PropertyChanges {}
}
Propriedades agrupadas
Em alguns casos, propriedades formam um grupo lógico e usam um ponto ou notação agrupada
para demonstrar isso.
Text {
Text {
font.pixelSize: 12
font.pixelSize:
font.bold: true 12
} font.bold: true
}
ou assim:
Text {
Text { { pixelSize: 12; bold: true }
font
} font { pixelSize: 12; bold: true }
}
Propriedades anexadas
Alguns objetos anexam propriedades a outro objeto. Propriedades anexadas são da forma
Tipo.propriedade, onde Tipo é o tipo de elemento que anexa a propriedade.
Por exemplo:
Component {
Component {
id: myDelegate
id:
Text { myDelegate
Text {
text: "Hello"
text: ListView.isCurrentItem
color: "Hello" ? "red" : "blue"
} color: ListView.isCurrentItem ? "red" : "blue"
} }
}
ListView {
ListView {
delegate: myDelegate
} delegate: myDelegate
}
Outro exemplo de propriedade anexada é o elemento Keys que anexa propriedades para
manipulação de teclas pressionadas para um item visual, por exemplo:
Item {
Item focus:
{ true
focus: true
Keys.onSelectPressed: console.log("Selected")
} Keys.onSelectPressed: console.log("Selected")
}
MouseArea {
MouseArea {
onPressed: console.log("mouse button pressed")
} onPressed: console.log("mouse button pressed")
}
MouseArea {
MouseArea {
acceptedButtons: Qt.LeftButton | Qt.RightButton
acceptedButtons:
onPressed: Qt.LeftButton
if (mouse.button | Qt.RightButton
== Qt.RightButton)
onPressed: ifconsole.log("Botão
(mouse.button == Qt.RightButton)
direito do mouse pressionado")
} console.log("Botão direito do mouse pressionado")
}
[56]
56] Revista Qt – Novembro/Dezembro-2010
O leitor Cárlisson Galdino escreveu:
Primeiro, acho que houve um equívoco. Uma biblioteca GPL não permite de certa forma linkagem, sem que interfira na
licença do software que está sendo desenvolvido.
Para garantir espaço às bibliotecas livres no mercado, concorrendo com as privativas, foi criada a Library General Public
License, que depois foi rebatizada para Lesser General Public License.
Posso estar enganado, mas acho difícil que uma biblioteca sob LGPL interfira na licença do software que a utiliza, caso se
trate de meros includes. Seria bom você contactar alguém da FSF (o Alexandre Oliva, por exemplo) para informações mais
precisas a respeito.
Continue a linha "how-to simples" e vá evoluindo o tipo de aplicação. Como criar uma agenda de contatos, um cliente
twitter, um editor de textos... Isso é muito bom pra quem está estudando!
Seria bom também um comparativo-não-tendencioso de recursos do Qt frente aos toolkits que existem: Gtk+, Fltk,
wxWindow...
Outra seria uma série apresentando, em cada edição, um how-to similar ao que você fez, mas focando o uso do Qt em outra
linguagem. Um para Python, na edição seguinte para Perl, etc... Seria uma forma de agradar também programadores fora
do mundo C++ (ou que preferem outras linguagens).
[]s,
Resposta do editor:
É verdade, Cárlisson, eu até publiquei um pedido público de desculpas com a explicação na segunda-feira (dia 20):
http://revistaqt.blogspot.com/2010/09/mea-culpa.html
A primeira edição foi bem limitada mesmo, porque tive que fazer tudo sozinho. Até as charges liberadas pelo Oliver da Geek
and Poke eu tive que editar no Gimp pra traduzir o texto... :) Não tenho muita habilidade com a diagramação também. A idéia
é que outros se empolguem e participem do projeto.
Com relação ao Android, existe um "port" do Qt para ele (http://gitorious.org/~taipan/qt/android-lighthouse), mas não é um
projeto da Nokia.
Achei muito interessante seu trabalho com a revista Você tem razão sobre a qualidade das imagens. Minha idéia foi
porque são poucas informações em português sobre reduzir a quantidade de páginas para aqueles que (como eu)
Qt que acho na internet. Gostei bastante da parte QT + preferissem imprimir a revista, mas no final das contas, olhando a
PHP. Seguinte, algumas imagem estão com a versão impressa, algumas imagens não ficaram boas mesmo.
resolução muito baixa e ficam com aparência ruim.
Vou melhorar isso na próxima edição, "espalhando" um pouco mais o
Até mais!!!! conteúdo. Nesta primeira edição, acabei fazendo tudo sozinho e
escaparam alguns detalhes.
Att,
Preciso muito desse tipo de retorno, para ir melhorando a revista.
Neste artigo veremos como montar um pacote com tudo que uma aplicação nossa em Qt precisa
para ser executada no Linux. Em edições futuras veremos como proceder para outras
plataformas.
[58]
58] Revista Qt – Novembro/Dezembro-2010
Para este artigo, vamos utilizar o programa Alô, Mundo! publicado na primeira edição da Revista
Qt. Nosso primeiro passo será descobrir quais são as bilbliotecas utilizadas pelo executável de
nossa aplicação, com o utilitário ldd:
33 bibliotecas pra um “Alô, Mundo!”? É o preço que pagamos pelas facilidades que o Qt nos
proporciona. Mas não se desespere (ainda...) porque muitas destas bibliotecas estão presentes
na instalação de qualquer distribuição atual de Linux.
O resultado do utilitário ldd em seu computador pode ser diferente do apresentado aqui,
dependendo da sua distribuição Linux e da versão de Qt que esteja utilizando. Eu uso o Ubuntu
10.10 com o Qt 4.7 instalado via Synaptic.
Para testar a instalação de minhas aplicações em Qt, instalei uma máquina virtual Ubuntu
rodando no Virtual Box. Esta máquina virtual é uma instalação “limpa” do Ubuntu sem as
bibliotecas de desenvolvimento adicionais. Dessa forma, ela funciona como a máquina de um
usuário que não seja programador.
Agora que sabemos de quais bibliotecas o programa AloMundo precisa para rodar, vamos copiar
estes arquivos, juntamente com o arquivo executável AloMundo para um diretório chamado
AloMundoLinux que usaremos para distribuir a aplicação.
Para indicar a localização das bilbiotecas, usamos devemos setar a variável de ambiente
LD_LIBRARY_PATH antes de executar o programa. Agora vem o macete retirado do diretório do
Google Earth: um script utilizado para setar a variável de ambiente e chamar o executável. Este
script deverá estar no diretório AloMundoLinux.
#!/bin/sh
# #!/bin/sh
# #Agente SAA
# # Agente SAA
#
FindPath()
{ FindPath()
{ fullpath="`echo $1 | grep /`"
iffullpath="`echo
[ "$fullpath" =$1""| ]; grep /`"
then
if oIFS="$IFS"
[ "$fullpath" = "" ]; then
oIFS="$IFS"
IFS=:
IFS=:
for path in $PATH
dofor
if path
[ -x in $PATH
"$path/$1" ]; then
do if if
[ -x "$path/$1"
[ "$path" = ""];];then
then
if path="."
[ "$path" = "" ]; then
fi path="."
fi
fullpath="$path/$1"
fullpath="$path/$1"
break
fi break
done fi
done
IFS="$oIFS"
fi IFS="$oIFS"
iffi[ "$fullpath" = "" ]; then
if fullpath="$1"
[ "$fullpath" = "" ]; then
fi fullpath="$1"
fi
# Is the sed/ls magic portable?
if# [Is-Lthe sed/ls magic
"$fullpath" portable?
]; then
if #fullpath="`ls
[ -L "$fullpath" -l ]; then
"$fullpath" | awk '{print $11}'`"
#fullpath="`ls
fullpath=`ls -l "$fullpath"
-l "$fullpath" |sed| -e
awk's/.*
'{print $11}'`"
-> //' |sed -e 's/\*//'`
fi fullpath=`ls -l "$fullpath" |sed -e 's/.* -> //' |sed -e 's/\*//'`
fi
dirname $fullpath
} dirname $fullpath
}
CPATH="`FindPath $0`"
CPATH="`FindPath $0`"
LD_LIBRARY_PATH=.:${CPATH}
LD_LIBRARY_PATH=.:${CPATH}
export LD_LIBRARY_PATH
export LD_LIBRARY_PATH
cd "${CPATH}/"
cd "${CPATH}/"
exec "./AloMundo" "$@"
exec "./AloMundo" "$@"
[60]
60] Revista Qt – Novembro/Dezembro-2010
Ainda não acabou... no caso do nosso pequeno AloMundo, não utilizamos imagens, apenas um
label com o texto “Alô, Mundo!”, mas para aplicações onde sejam utilizadas imagens, devemos
incluir os plugins para tratamento dos formatos utilizados. Os plugins dos formatos de imagens
ficam no diretório qt/plugins/imageformats a partir do diretório onde esteja instalado o Qt.
Digamos que sua aplicação utilize imagens do tipo jpeg. Nesse caso você deve criar no diretório
de sua aplicação um diretório chamado imageformats e copie para ele o arquivo
qt/plugins/imageformats/libqjpeg.so para ele.
Com os arquivos da aplicação presentes no diretório AloMundoLinux, basta gerar um pacote,
compactando-o. Pode-se utilizar, por exemplo, o utilitário tar para isso:
Ok, você pode a essa altura estar imaginando: se eu tiver 10 aplicações em Qt terei em cada uma
delas uma cópia das bibliotecas utilizadas? Não necessariamente. Você pode adotar como regra,
a criação de um diretório na máquina do cliente para armazenar as bibliotecas e utilizar este
diretório no seu script start. Alguns fabricantes de software usam a estratégia de criar um
diretório com o seu nome e dentro deste diretório instalar seus produtos.
Neste caso, o script start setará a variável LD_LIBRARY_PATH para apontar para o diretório
compartilhado onde estarão as bibliotecas.
Até a próxima.
Editor
André Luiz de O. Vasconcelos
Cartoons
Oliver Widder (Geek & Poke)
Colaboradores
Pierre Andrade Freire
Rogério Etsuo Yamamaru