Pesquisa em Computação Gráfica
Tutorial API Gráfica Qt


Equipe

Professor Cesar Tadeu Pozzer; Francisco Tiago Avelar; Vitor Conrado Faria Gomes.

{pozzer,avelar,vconrado}@inf.ufsm.br


Índice

A biblioteca Qt representa um importante framework para desenvolvimento de aplicações gráficas envolvendo recursos aprimorados de interação entre computador e usuário, como emprego de botões, caixas de texto, menus e atalhos do teclado, entre outros. O objetivo é relacionar o potencial de Qt voltado à integração com recursos OpengGL nativos. Dessa forma, a construção de programas utilizando noções intuitivas de interfaces gráficas integradas com OpenGL passa a ser proposta muito mais simples de abordagem no ponto de vista da disciplina de Computação Gráfica (ELC1015).

Para obter uma melhor justificativa a respeito da adoção da biblioteca gráfica Qt em comparação com as demais implementações multi-plataforma existentes, é recomendável ler o artigo Estudo Comparativo de Bibliotecas Gráficas Integradas com OpenGL.

Os exemplos demonstram o uso de componentes de Qt em ordem de aplicabilidade. A medida que o entendimento dos exemplos é avançado, maior é o emprego de componentes Qt e a demonstração do potencial no desenvolvimento de programas.

Em razão de Qt possuir um núcleo diversificado, a quantidade de espaço em memória secundária necessária para portar todas os arquivos de bibliotecas dinâmicas é grande. Em virtude do enfoque das aplicações representar animações gráficas em OpenGL, a meta foi concentrar os pacotes de Qt para este devido fim. Vale a pena mencionar que o framework Qt apresenta uma coleção muito maior de classes e recursos que possibilitam aplicações integradas com comunicação através de diversos protocolos de redes de computadores, interação com sistemas gerenciadores de bancos de dados (SGBDs), manipulação de documentos XML, etc. Para conhecimento das características completas a respeito de Qt, consulte as referências no final do documento.

Em relação aos exemplos contidos na seqüência, as bibliotecas necessárias foram reduzidas num conjunto de três principais a partir dos pacotes principais providos por Qt em aquisição íntegra. Vale mencionar que são:

  • libQtCore
  • libQtGui
  • libQtOpenGL

Demais arquivos de cabeçalho se mantiveram inalterados por não representar espaço adicional significativo e evitar a problemas de dependência interna. Um aplicativo adicional específico de Qt denominado MOC (Meta Object Compiler) é necessário para integrar classes que possuem funções-membro de eventos para interação entre computador e usuário com as respectivas implementações. A devida atenção será dedicada no decorrer dos exemplos e a compreensão será mais simples.

Os requisitos para rodar os exemplos de forma bem sucedida encontram-se reunidos em requisitos.tar.bz2. Faça o download e descompacte para uma pasta específica. Ao descompactar, haverá três pastas que devem ser dispostas dentro de um mesmo nível de diretório. As três pastas estão listadas abaixo:

  • bin
  • include
  • lib

Para a associação entre o código-fonte a ser compilado e as bibliotecas de Qt necessárias, é possível fazer uma referência simbólica de caminho segundo a orientação na seqüência. Vale a pena mencionar que a ligação deve ser feita entre cada pasta de forma individual, pois as especificações das diretivas de compilação de um Makefile dependem diretamente da organização das pastas para o correto funcionamento. Os comandos devem ser realizados a partir do interior pasta na qual se encontra o código-fonte do programa.

ln -s {caminho da pasta bin} bin

ln -s {caminho da pasta include} include

ln -s {caminho da pasta lib} lib

Um exemplo é demonstrado para possibilitar uma maior compreensão. Considere que o conteúdo do pacote requisitos.tar.bz2 foi extraído para a pasta /home/usuario/requisitos. Então o procedimento passa a ser simples como abaixo:

usuario@localhost:~$ pwd

/home/usuario

usuario@localhost:~$ cd exemplo

usuario@localhost:~/exemplo$ ln -s /home/usuario/requisitos/bin bin

usuario@localhost:~/exemplo$ ln -s /home/usuario/requisitos/include include

usuario@localhost:~/exemplo$ ln -s /home/usuario/requisitos/lib lib

Supondo que a pasta exemplo exista contendo o código-fonte do programa a partir do caminho padrão $HOME de usuário, será necessário migrar para a devida pasta e realizar as associações simbólicas como demonstradas acima. As três primeiras linhas simplesmente demonstram a migração para a pasta contendo o código-fonte e o restante realiza as ligações. O comando ls -l exibe as ligações entre o nome simbólico e o caminho real.

Exemplos

O primeiro exemplo utilizando Qt representa um programa contendo uma pequena janela com um texto. Através disso, será possível perceber os componentes mínimos de Qt para compor exemplos simples de entender.

Falta escrever mais...

É possível realizar o download do código deste exemplo.

Arquivo Janela.hh
 1 //Arquivo Janela.hh
 2 #ifndef JANELA_DEMONSTRACAO_CG_01
 3 #define JANELA_DEMONSTRACAO_CG_01
 4 
 5 #include <QMainWindow>
 6 #include <QLabel>
 7 
 8 class Janela : public QMainWindow
 9 {
10    public:
11       Janela();
12       ~Janela();
13 
14    private:
15       QLabel *rotulo;
16 
17 };
18 
19 #endif

Analisando o Arquivo Janela.hh, consta na linha 5 a inclusão de QMainWindow específica de Qt. Através desse componente, é possível construir uma interface gráfica para uma aplicação. Considerando o próprio leiaute, pode ser feita a adição de demais componentes como barra de tarefas, barra de menu e barra de status, por exemplo. A partir da janela principal, existe uma área central que pode ser ocupada por qualquer outro tipo de componente. O leiaute é representado esquematicamente abaixo:

Modelo de leiaute em QT

Na linha 6, há a inclusão de QLabel, o qual é utilizado para exibir texto ou uma imagem. Não há forma de interação com o usuário através de eventos. A aparência de um rótulo pode ser ajustada de diversas maneiras e pode conter mnemônicos para demais componentes.

A seguir segue a implementação da classe Janela.

Arquivo Janela.cc
 1 //Arquivo Janela.cc
 2 #include "Janela.hh"
 3 
 4 Janela::Janela()
 5 {
 6    setWindowTitle( QString("API Qt") );
 7    resize(172, 32);
 8 
 9    rotulo = new QLabel( QString("Ola Mundo") );
10    setCentralWidget(rotulo);
11 }
12 
13 Janela::~Janela()
14 {
15    delete rotulo;
16 }

No arquivo Janela.cc consta a atribuição de um título para a janela gráfica e o seu tamanho em pixels, respectivamente nas linhas 6 e 7. Conforme visualizado no modelo de leiaute de Qt, o rótulo contendo a cadeia de caracteres "Ola Mundo" foi atribuído como componente central na linha 10 após ter sido instanciado na linha 9.

A seguir consta a função principal "main" correspondendo a chamada padrão de inicialização de programa.

Arquivo Principal.cc
 1 //Arquivo Principal.cc
 2 #include "Janela.hh"
 3 #include <QApplication>
 4 
 5 int main(int argc, char** argv)
 6 {
 7    QApplication aplicacao(argc, argv);
 8 
 9    Janela janela;
10    janela.show();
11 
12    return aplicacao.exec();
13 }

A linha 3 do arquivo Principal.cc contém a inclusão do componente QApplication responsável pelo funcionamento inicial para execução do programa. Possui a função de gerenciar o controle da interface gráfica da aplicação e as configurações principais. Além disso, contém o laço principal "main", no qual todos os eventos do sistema de janelas e outras fontes são processadas e encaminhadas para o devido tratamento, assim como gerenciamento da inicialização, finalização e gerenciamento de seção da aplicação.

Abaixo é possível conferir o aspecto gráfico do programa exemplificado.

olamundo1

É possível realizar o download do código deste exemplo.

A partir deste segundo exemplo envolvendo a incorporação de maiores recursos de Qt seguindo a mesma idéia do primeiro exemplo, a compreensão do uso de componentes mais elaborados e a vinculação de classes com eventos fica facilitada através da avaliação descritiva de código.

O arquivo Principal.cc não apresenta modificações em relação ao primeiro exemplo. A seguir é demonstrado o cabeçalho da nova classe Janela.

Arquivo Janela.hh
 1 //Arquivo Janela.hh
 2 #ifndef JANELA_DEMONSTRACAO_CG_02
 3 #define JANELA_DEMONSTRACAO_CG_02
 4 
 5 #include <QMainWindow>
 6 #include <QLabel>
 7 #include <QMenuBar>
 8 #include <QMenu>
 9 #include <QStatusBar>
10 #include <QAction>
11 
12 class Janela : public QMainWindow
13 {
14    Q_OBJECT  // deve ser incluso por causa de Qt signals/slots
15 
16    public:
17       Janela();
18       ~Janela();
19 
20    private:
21       QLabel *rotulo;
22       QAction *acaoSair;
23       QMenu *menuArquivo;
24       void criaAcao();
25       void criaMenu();
26 
27    private slots:
28       void implementaAcaoSair();
29 };
30 
31 #endif  

Em relação ao primeiro exemplo de Olá Mundo, existe a inclusão de mais três novos componentes compreendendo a linha 7 até a linha 10.

QMenuBar representa uma barra de menus posicionada horizontalmente consistindo de uma lista de itens de menu com expansão para baixo.

QMenu é um componente para utilizar em barra de menus, menus de contexto (normalmente acionados pelo botão direito do mouse) e outros menus-pups.

QStatusBar pode ser instanciado para adicionar uma barra de status horizontal na parte inferior da janela para apresentar informações de status, além de facilitar a orientação de uso do programa para o usuário. O indicador de status pode ser de três tipos:

  • Temporáro: ocupa a maior parte da barra de tarefas. Usada para exibir dicas em formato texto ou entradas de menu, por exemplo.
  • Normal: ocupa parcialmente a barra de status e pode ser ocultado por mensagens temporárias. Utilizado para exibir um número de página ou número de linha num processador de texto, por exemplo.
  • Permanente: nunca é ocultado. Utilizado para indicações importantes, como notificação da tecla CAPS LOCK acionada ou data/horário.

QAction é um importante componente de Qt para associar classe com eventos. Oferece uma forma abstrata de interface envolvendo ações inseridas em componentes. Em muitas aplicações os comandos podem ser invocados através de menus, barra de botões e atalhos de teclado. Uma vez que o usuário espera que o comando pode ser tratado de uma mesma maneira, independente de como a interface é utilizada, é útil representar cada comando como uma ação. Em outras palavras, um mesmo evento, como destacar um texto em negrito, pode ser efetuado por múltiplas maneiras, mas a ação correspondente é apontada de uma única forma através de QAction.

Conforme foi mencionado no início do tutorial, Qt utiliza uma solução própria para vincular classes com a implementação de eventos para interação do programa com o usuário. Para tanto, o utilitário MOC na verdade é um gerador de código C++ intermediário que utiliza componentes de Qt mais especializados para realizar a associação de classes com eventos. Para que o vínculo seja possível, obrigatoriamente deve existir a inclusão da macro Q_OBJECT (linha 14 - não há ponto-e-vírgula após a especificação da macro) para ser interpretada por MOC de modo a gerar o código-fonte correspondente para posteriormente ser compilado normalmente por g++. O protótipo do evento especificado no cabeçalho é apresentado numa região particular do código incluindo a palavra-chave do mecanismo de visibilidade do paradigma de orientação a objeto. Isso pode ser visto explicitamente nas linhas 27 e 28. Slots são regiões mapeadas que serão utilizadas por MOC para fazer a associação do correta de eventos com os Signals (disparos) causados pela interação com a interface gráfica pelo usuário.

O conceito de Signals/Slots fica mais simples de compreender analisando a implementação da classe Janela a seguir.

Arquivo Janela.cc
 1 //Arquivo Janela.cc
 2 #include "Janela.hh"
 3 
 4 Janela::Janela()
 5 {
 6    setWindowTitle( tr("API Qt") );
 7    resize(230, 100);
 8 
 9    this->criaAcao();
10    this->criaMenu();
11 
12    rotulo = new QLabel("Ola Mundo");
13    setCentralWidget(rotulo);
14 
15    statusBar()->showMessage( tr("Bem-vindo a API Qt.") );
16    statusBar()->setSizeGripEnabled(false);
17 }
18 
19 Janela::~Janela()
20 {
21    delete rotulo;
22    delete acaoSair;
23    delete menuArquivo;
24 }
25 
26 void Janela::criaAcao()
27 {
28    acaoSair = new QAction( QString("&Sair"), this );
29    acaoSair->setShortcut( QString("CTRL+S") );
30    acaoSair->setStatusTip( QString("Encerra o aplicativo.") );
31    connect( acaoSair, SIGNAL(triggered()), this, SLOT(implementaAcaoSair()) );
32 }
33 
34 void Janela::criaMenu()
35 {
36    menuArquivo = menuBar()->addMenu( QString("&Arquivo") );
37    menuArquivo->addAction(acaoSair);
38 }
39 
40 void Janela::implementaAcaoSair()
41 {
42    close();
43 }

O construtor da classe Janela realiza a atribuição do título da janela gráfica (linha 6) e o tamanho da janela, em pixels (linha 7). O diferencial estabelecido ocorre nas linhas 9 e 10. A chamada da função-membro criaAcao aponta para a execução do código especificado a partir da linha 28 até a linha 31. Após instanciar uma QAction incluindo a cadeia na caracteres "Sair" como parâmetro (o símbolo '&' indica o corresponde acesso através do teclado) na linha 28, uma tecla de atalho foi atribuída na linha 29 e a dica a ser exibida na barra de status é definida na linha 30.

A atenção especial deve ser feita na linha 31. A chamada de connect, especificada em QObject na API Qt, possui a função de realizar a associação entre a QAction instanciada em acaoSair com o método implementaAcaoSair(). Observe que o sinal que gera o disparo do método é definido por triggered(). Isso significa que o critério para invocar o evento é quando o gatilho (trigger) for acionado pelo usuário, seja através do mouse ou teclas de atalho do teclado. Em outras palavras, o ponteiro acaoSair gera um SIGNAL, quando tiver o gatilho acionado, para a própria classe this envolvendo o SLOT contendo o evento implementaAcaoSair(). É por essa razão que em Janela.hh, na linha 27, existe a palavra signals, pois assim é possível para MOC fazer associação correta entre eventos e as respectivas implementações. Um outro exemplo pode ser dado como

connect( radio, SIGNAL(toggled(bool)), this, SLOT(eventoRadio(bool)) ).

A interpretação é semelhante. Um botão radio utilizado em formulários para definir uma escolha única entre um grupo de possibilidades, possui o sinal disparado quando for alterado (toggled(bool)), incluindo como parâmetro um valor lógico representando selecionado (true) ou não-selecionado (false), para tratamento na própria classe this no SLOT contendo a chamada da função-membro representando o evento eventoRadio(bool). Existem diversas possibilidades de geração de sinais associados a componentes para chamada de eventos, mas o método utilizando connect possui comportamento como foi apresentado.

Não esquecendo a linha 10 do arquivo Janela.cc, existe a chamada da função membro criaMenu(). A execução do código nas linhas 36 e 37 simplesmente cria um item de menu "Arquivo" e vincula acaoSair ao objeto menuArquivo para interação com o usuário, respectivamente.

As linhas 40 até a linha 43 contêm a implementação de implementaSair que corresponde ao fechamento da janela do aplicativo.

A captura de tela a seguir demonstra o aspecto gráfico do segundo exemplo de Olá Mundo.

Ola Mundo 2 tela normal  Ola Mundo 2 menu Arquivo pressionado 

É possível realizar o download do código deste exemplo.

Uma vez compreendido os componentes básicos de Qt incluindo a associação de objetos com eventos de usuário através da função-membro connect da classe QObject específica de Qt, uma visão mais especializada que vem a compor o tema principal deste tutorial passa a ser abordada de um modo mais descritivo. A essência será mantida na classe Cena que detém a manipulação nativa de componentes Opengl. Ao fazer o download do código de forma íntegra, será possível verificar demais classes auxiliares que regem o exemplo de forma completa.

A integração de Qt com OpenGL é ilustrada através de um exemplo simples que envolve a criação de uma interface gráfica elaborada para interação entre a primitiva GL_TRIANGLES de OpenGL e o usuário. Considerando o leiaute de Qt demonstrado inicialmente no tutorial, o componente central será uma animação gráfica regido por eventos disparados pela barra de botões, além de uma barra de status para exibir uma descrição dos eventos.

A seguir há uma captura de tela para que se tenha uma idéia global do exemplo.

exemplo opengl

A definição do cabeçalho da classe Cena está definida abaixo. Perceba a inclusão das bibliotecas específicas de OpenGL na linha 7 até a linha 9 e a declaração da macro Q_OBJECT na linha 21.

Arquivo Cena.hh
 1 //Arquivo Cena.hh
 2 #ifndef CENA_DEMONSTRACAO_API
 3 #define CENA_DEMONSTRACAO_API
 4 
 5 #include <QGLWidget>
 6 #include <QTimer>
 7 #include <GL/glut.h>
 8 #include <GL/glu.h>
 9 #include <GL/gl.h>
10 #include <iostream>
11 #include <stdlib.h>
12 
13 #define LIM_SUP_ACELERACAO 300
14 #define LIM_INF_ACELERACAO 1
15 #define INC_ACELERACAO 5
16 
17 using namespace std;
18 
19 class Cena : public QGLWidget
20 {
21    Q_OBJECT // deve ser incluso por causa de Qt signals/slots
22 
23    public:
24       Cena(QWidget * ancestral);
25       void setSentidoRotacao(bool sentido);
26       void desacelerar();
27       void acelerar();
28       void parar();
29       void iniciar();
30 
31    protected:
32       void initializeGL();
33       void resizeGL(int largura, int altura);
34       void paintGL();
35       void updateGL();
36 
37    private:
38       int valor_rotacao;
39       int fator_aceleracao;
40       int valor_translacao;
41       bool sentido_rotacao;
42       QTimer *temporizador;
46 };
47 
48 #endif

Tendo visto o arquivo Cena.hh, não apresenta modificações de acordo com o que foi apresentado nos exemplos anteriores, exceto pela obrigatoriedade em incluir as bibliotecas de OpenGL. A partir disso, agora uma análise mais apurada pode ser realizada referente a implementação da classe Cena. Considere o código abaixo:

Arquivo Cena.cc
  1 //Arquivo Cena.cc
  2 #include "Cena.hh"
  3 
  4 Cena::Cena(QWidget *ancestral) : QGLWidget(ancestral)
  5 {
  6    temporizador = new QTimer(this);
  7    this->valor_rotacao = 0;
  8    this->valor_translacao = 0;
  9    this->sentido_rotacao = true; // verdadeiro para sentido horario
 10    this->fator_aceleracao = 30;
 11 }
 12 
 13 void Cena::initializeGL()
 14 {
 15    glClearColor(0.0, 0.0, 0.0, 0.0);
 16    glShadeModel(GL_SMOOTH);
 17    glMatrixMode(GL_PROJECTION);
 18    glLoadIdentity();
 19    gluPerspective(45, 1, 0.5, 1000);
 20 
 21    connect(temporizador, SIGNAL(timeout()), this, SLOT(updateGL()));
 22 }
 23 
 24 void Cena::resizeGL(int largura, int altura)
 25 {
 26    //
 27 }
 28 
 29 void Cena::paintGL()
 30 {
 31    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 32 
 33    glLoadIdentity();
 34 
 35    glRotatef(this->valor_rotacao, 0.0, 0.0, 1.0);
 36 
 37    glTranslated(-0.2, -0.2, -1.0);
 38 
 39    glBegin(GL_TRIANGLES);
 40       glColor3f(0.2, 0.3, 0.8);
 41       glVertex3f(0.0, 0.0, 0.0);
 42       glColor3f(0.1, 0.9, 0.7);
 43       glVertex3f(0.4, 0.0, 0.0);
 44       glColor3f(0.5, 0.4, 0.3);
 45       glVertex3f(0.2, 0.4, 0.0);
 46    glEnd();
 47 }
 48 
 49 void Cena::updateGL()
 50 {
 51    if(this->sentido_rotacao == true)
 52    {
 53       this->valor_rotacao -= 1;
 54    } else 
 55    {
 56       this->valor_rotacao += 1;
 57    }  
 58 
 59    if( abs(this->valor_rotacao) > 360 )
 60    {
 61       this->valor_rotacao = 0;
 62    }
 63    glDraw();
 64 }
 65 
 66 void Cena::setSentidoRotacao(bool sentido)
 67 {
 68    if(sentido == true) // sentido horario
 69    {
 70       this->sentido_rotacao = true;
 71    } else // sentido anti-horario 
 72    {
 73       this->sentido_rotacao = false;
 74    }
 75 }
 76 
 77 void Cena::acelerar()
 78 {
 79    this->fator_aceleracao -= INC_ACELERACAO;
 80 
 81    // testa se o fator de aceleracao nao passou do limite inferior
 82    if(this->fator_aceleracao < LIM_INF_ACELERACAO)
 83    {
 84       this->fator_aceleracao = LIM_INF_ACELERACAO;
 85    }
 86 
 87    temporizador->stop();
 88    temporizador->start(fator_aceleracao);
 89 }
 90 
 91 void Cena::desacelerar()
 92 {
 93    this->fator_aceleracao += INC_ACELERACAO;
 94 
 95    // testa se o fator de aceleracao nao passou do limite superior
 96    if(this->fator_aceleracao > LIM_SUP_ACELERACAO)
 97    {
 98       this->fator_aceleracao = LIM_SUP_ACELERACAO;
 99    }
100 
101    temporizador->stop();
102    temporizador->start(fator_aceleracao);
103 }
104 
105 void Cena::parar()
106 {
107    temporizador->stop();
108 }
109 
110 void Cena::iniciar()
111 {
112    temporizador->start(fator_aceleracao);
113 }

Analisando a implementação da classe Cena, o construtor apresenta como parâmetro um ponteiro para QWidget (linha 4) que representa uma classe mais genérica de componente em Qt. A variável ancestral serve para associar Cena com a classe a qual pertence, ou seja, a janela principal do programa (vide Principal.cc exemplo completo ao efetuar o download), sendo onde é instanciada e posicionada como componente central.

Existem quatro funções-membro virtuais implementadas na classe Cena que especializa QGLWidget, as quais são:

  • initializeGL(): responsável pela inicialização de uma animação em OpenGL, tal como a cor de fundo, câmera, matriz de projeção, etc.
  • resizeGL(): tratamento da janela de exibição na animação quando exister redimensionamento de tela. Usado para definir os novos limites para plotagem da animação.
  • paintGL(): função chamada para atualizar uma animação em OpenGL. É o local de disposição das coordenadas de objeto, podendo ser seguido de transformações, como rotação e translação.

O método updateGL() (linha49) altera a variável de classe valor_rotacao que define o sentido da rotação do triângulo. Na linha 63 ocorre a chamada de glDraw() que por sua vez invoca o método paintGL() especificado nas linhas 29 até a linha 47, cuja função é redesenhar a animação com o novo valor de rotação (linha 35), assim como demais especificações de OpenGL que permanecem inalteradas. No caso de resizeGL(int, int) (linha 24), não há foi especificado um modo para redefinir a animação no caso da janela ter o tamanho modificado por questões de simplicação de código.

Por questões de desempenho, a atualização de uma área gráfica contendo uma animação em OpenGL somente é feita quando for sobreposta por algum componente gráfico adicional de janela, como um menu suspenso, item de menu expandido ou minimização/maximização de janela. Desse modo, caso não ocorrer qualquer modificação sobre QGLWidget definido, no caso do exemplo, como item central, a renderização do componente não será feita. Em conseqüência disso, o efeito de rotação do triângulo sobre o mesmo eixo não será produzido, independente do fator de aceleração sofrer modificação (métodos nas linhas 77 e 91) e inversão de sentido na rotação (linha 66).

Para que o efeito da animação seja produzido, a atualização automática de tela do componente OpenGL precisa ser feita de forma automatizada. Para isso, foi utilizado o componente QTimer que representa um temporizador existente na API Qt. Dado um certo tempo sendo atingido, ocorre um disparo de sinal timeout(), isso pode ser verificado na linha 21, sendo também escrito abaixo:

connect( temporizador, SIGNAL(timeout()), this, SLOT(updateGL()) );

Retomando o modo de Qt para vincular classes com eventos, a instância de QTimer declarada como temporizador (linha 42 em Cena.hh) gera um sinal de tempo excedido, acionando, na própria classe Cena, para o SLOT contendo o método updateGL() responsável para verificar o sentido da rotação e invocar glDraw() (linha 63) para atualizar a tela de forma automática. Repare que updateGL(), prototipado na linha 35 do arquivo Cena.hh, não foi definido dentro de um SLOT específico de código. Isso de justifica em razão de updateGL() já representar um SLOT público na API Qt.


Documento criado em 13 de novembro de 2007, às 20:47