Sei sulla pagina 1di 203

Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 1: O QUE É O GAMBAS ....................................................... 06

1.1 A linguagem BASIC e sua história ............................................................... 07


1.2 Um ambiente Livre ..........................................................................................09
1.3 Elementos do Gambas .....................................................................................10
1.4 Como obtê-lo ................................................................................................10
1.5 Compilação e dependências ............................................................................12
1.6 Familiarizar-se com sua IDE .......................................................................... 12
O primeiro exemplo .......................................................................................14
Melhor com um exemplo gráfico ................................................................. 16
Um pouco de magia ......................................................................................00
1.7 Componentes do sistema ................................................................................18
 É óbvio que para os que falam o idioma espanhol, a palavra Gambas nos sugere
algumas coisas, mas todas elas não faz sentido no mundo dos computadores em geral e do
desenvolvimento da programação em particular. O autor original do Gambas, Benoit Minisini,
não fala uma palavra em nosso idioma e inocentemente nomeou sua obra com o título de
GAMBAS: Gambas almost means BASIC, ou seja, 'Gambas quase significa BASIC'. Não é a
primeira vez que o nome de uma marca criada em outros idiomas produz estas estranhas
coincidências, recordamos que cocacola(TM) teve que mudar sua pronuncia na China porque a
primeira versão do seu nome significava morder o cabeça de cera, alguns de nós nos
recordamos da nossa cara de assombro ao ver os anúncios de um carro em que Suzuki chama de
Pajero. Tampouco lembramos que havia outras linguagem de programação com nome de
animal, como Camel o Python, como o nome está em inglês em espanhol não resulta em uma
palavra tão chocante.
 Em fim, como Benoit, que tem os direitos autorais, não deseja trocar o nome, nós teremos
que ir nos acostumando que Gambas é algo mais que uma espécie de gostoso marisco. E fazer
do Gambas o sistema visual de desenvolvimento de programação em Linux para todos, como fez
um dia o Visual Basic(TM) no Windows. Porem como o tempo não passa em vão, Gambas tenta
não cometer os erros de antes. Na aplicação da linguagem BASIC alcançando com o Gambas
aplicações poderosas, profissionalidade e modernidade, sem abandonar a simplicidade e clareza
desta linguagem de programação de alto nível. Nunca mais poderá dizer que construir aplicações
visual para o Linux é um processo longo e complexo que leva anos de trabalho só para gurus e
maníacos da informática.
Gambas não é só um sistema de programação, é também um ambiente de programação
visual para desenvolver aplicações gráficas ou de console. Possibilitando desenvolver aplicações
muito rapidamente. O programador desenha as janelas de forma gráfica, arrasta objeto da caixa
de ferramentas e escreve código em BASIC para cada objeto. Gambas é orientado a evento, o que
significa que chama automaticamente os procedimentos quando o usuário da aplicação escolhe
um menu, da um click com o mouse, move um objeto na tela, etc.

A linguagem BASIC: e sua história

O nome BASIC corresponde as siglas Beginner's All Purpose Symbolic Instruction Cod
(Código de instruções de uso universal para principiantes). A linguagem foi desenvolvida em
1964 no Dartmouth College pelos matemáticos John George Kemeny e Tom Kurtzas. Tentavam
construir uma linguagem de programação fácil de aprender para seus estudantes de licenciatura.
Devia ser um passo intermediário antes de aprender outras mais poderosas naquela época , como
FORTRAN ou ALGOL. este ultimo era a linguagem mais utilizada nas aplicações de
processamento de dados, enquanto o FORTRAN era utilizado para as aplicações científicas
Ambos eram difíceis de aprender, tinham grande quantidades de regras nas estruturas de seus
programas e suas sintaxe. O primeiro programa feito em BASIC foi executado as quatro da
madrugada de 1 de maio de 1964. Devido a sua simplicidade, BASIC se tornou imediatamente
muito popular e acabou sendo usado tanto em aplicações científicas quanto comerciais. Teve o
mesmo impacto nas linguagens de programação que a aparição do PC sobre os grandes
computadores.
Quando se desenvolveu o BASIC era no tempo que a informática estava
preza a universidades e grandes empresas, com computadores do
tamanho de uma casa. Porem de repente as coisas começaram a mudar.
Em 1971 a Intel fabricava o primeiro microprocessador . Em 1975, a
empresa MITS lançou no mercado um kit de computadores chamado
Altair 8800 a um preço de 397 dólares. Era um computador barato,
porem não era para pessoas inexperiente, tinha que saber eletrônica para
poder monta-lo. Além disso tinha só 256 bytes (não é um erro, só bytes,
nada de kbytes, megas ou gigas) e se programava em código de
maquinas a base de 0 e 1, movendo uns interruptores frontal. Dois jovem
viram um modelo em uma revista de eletrônica e decidiram monta-lo. E
ofereceram ao dono da MITS, além disso fizeram um interpretador
BASIC para os novos modelos de Altair. Eram William Gates e Paul
Figura 1. Lançamento do Altair 8800
Allen, e aquele BASIC, com um tamanho de 4 Kbytes foi o primeiro
produto que uma nova empresa chamada Microsoft. entregou.

Foi só o inicio. No final dos anos 70, Allen e Gates haviam portado BASIC para um bom
numero de plataformas: Atari, Apple,
Commodore... e em 1981, quando desenvolveram o dos para a IBM e seu novo PC, adicionaram
também seu próprio interpretador BASIC ao sistema, nos anos posteriores seguiram outras
versões feitas por outras companhias como Borland, porem o declínio do BASIC devido a
utilização das interface gráficas e janelas que a Apple popularizou e a Microsoft adaptou com
sucessivas versões do Windows(TM), os convenceram que o padrão BASIC não era uma
linguagem apropriada para este ambiente.
No entanto, em março de 1988 um desenvolvedor de software chamado Alan Cooper¹
tentava vender uma aplicação que permitia personalizar facilmente o ambiente de janelas usando
o mouse. Este programa se chamava Tripod e conseguiu que William Gates o contratasse para
desenvolver uma nova versão que se chamara Ruby, foi acrescentado uma pequena linguagem de
programação. Microsoft utilizou essa linguagem para sua própria versão do BASIC, Quickbasic e
em 20 de março de 1991 lançou no mercado com o nome de Visual Basic à principio foi um
verdadeiro fracasso de vendas, porem a versão 3 publicada no outono de 1996 foi um êxito total,
tanto que atualmente é a linguagem de programação mais usada. Visual Basic seguiu e evoluiu,
já está na versão 6.0, em 2002 foi integrado na plataforma .NET de desenvolvimento, o que para
muitos dos seguidores a um suposto abandono da Microsoft, e que foi trocado boa parte das
sintaxes adicionando complexidade e contradição com o espírito e com o nome da linguagem.
Em todo caso nos dias de hoje calcula-se que entre 70% e 80% de todas as aplicações
desenvolvida no Windows foram feitas com algumas das versões do Visual Basic.
As causas do êxito do Visual Basic são numerosas, porem entre outras citar-se como
óbvia o uso da linguagem BASIC que foi pensado para uma aprendizagem fácil. Outro dos
motivos é dispor de ambiente de desenvolvimento cômodo, que faz coisa pra criança desenhar a
interface gráfica de qualquer aplicação, afastando o programador da perda de tempo em escrever
o código necessário para criar as janelas, botões, etc., deixando concentrar-se unicamente na
solução do problema que qualquer programador tenta resolver . Com a popularização de sistemas
livres como o GNU/Linux, estas e outras faz-se prever que a aparição de um ambiente
equivalente e livre seria um êxito e contribuiria para muitos novos software que poderemos
utilizar. Sabemos de varias tentativas que não foram bem sucedidas por lentidão em sua
evolução, também por sua dificuldade de uso ou por não ser totalmente livre e não haver uma
comunidade por traz dando apoio ao projeto. Finalmente, Benoit Minisini, um programador com
experiência na escrita de compiladores que estava farto de lutar contra as falhas de desenho do
Visual Basic, e desejava poder usar um ambiente GNU/Linux fácil em sua distribuição, e
começou a desenvolver seu próprio ambiente para Linux baseado em BASIC. Em 28 de fevereiro
de 2002 pois na internet a primeira versão pública do Gambas: gambas 0.20. Benoit eliminou do
desenho da linguagem muitos dos problemas que o Visual Basic tinha, como a gestão de erro e
adicionando características comuns a linguagens atuais mas modernas, como a orientação a
objeto e a própria estrutura dos programas². Como prova de fogo o próprio ambiente de
desenvolvimento foi programado em Gambas desde a primeira versão, servindo ao mesmo
tempo de demonstração do poder da linguagem e da detecção das necessidades e correção de
erros que foram incorporados nas diversas versões.
Em janeiro de 2005, Benoit publicou a versão 1.0 na qual ia ser incorporado vários
componentes desenvolvido por outros programadores que colaboraram com ele: Daniel Campos,
Nigel Gerrard, Laurent Carlier, Rob Kudla e Ahmad Kahmal. Esta versão se considerou
suficientemente estável e encerrou um ciclo. A partir dessa data começou a versão 2.0. Esta
inclui algumas melhorias na linguagem, muito mais componentes e um novo modelo de objeto
que permitira usar o Gambas no futuro para desenvolvimento de aplicações web com a mesma
filosofia e facilidade que atualmente se usa para aplicações de escritório.
1.2 Um ambiente Livre
Gambas é um ambiente se desenvolvimento distribuído com a licença GPL GNU
(General Public LIcence³). Isto significa que é distribuído sempre com o código fonte e respeita
as quatro liberdades que define a Free Software Foundation:

1. A liberdade de usar o programa com qualquer propósito.

2. A liberdade de estudar como funciona o programa e adapta-lo as suas próprias


necessidades. Acesso ao código fonte é uma condição prévia para isto.

3. A liberdade de distribuir cópias, Dessa forma podemos ajudar aos outros

4 A liberdade de melhorar o programa e fazer públicas as melhorias para que todo mundo
e a comunidade se beneficie. Acesso ao código fonte é uma condição prévia para isto.

Um dos enganos mais comuns no uso do software livre é acreditar que este modelo de
desenvolvimento obriga que o trabalho seja publicado grátis, o que não é de tudo verdade. Estas
quatro liberdades permitem que, Qualquer um possa vender cópias do Gambas (entregando
sempre o código fonte e respeitando essas quatro liberdades) ou qualquer aplicação
desenvolvida com este programa. As aplicações desenvolvida com Gambas podem ou não ser
publicado sobre a licença GPL.
Também qualquer programador é livre para alterar a própria linguagem e modifica-lo a
seu gosto, e sempre entregar o código correspondente a essas modificações, respeitando os
direitos autorais dos desenvolvedores originais.
Aparte as liberdades próprias da natureza de um projeto de software livre sobre
GNU/Linux, Gambas adiciona ainda mais facilidade para o programador:

.Uma ajuda muito completa da linguagem a cada um dos componentes, algo que terão muito a
agradecer os que se empenharem a programar em Gambas, o que nos é habitual nos projetos de
software livre. A ajuda publicada está em inglês, porem existe um grupo de pessoas trabalhando na
tradução para o espanhol. se todos nós colaborarmos na tradução, em breve estará completa e
disponível para o resto dos usuários.

.Uma API (interface para programar as aplicações) simples e bem documentada, o que facilita
os programadores criarem novos componentes para Gambas. A API não é de utilidade imediata para
os que desenvolvem com esta linguagem, porem permite que os programadores avançado possam
adicionar novas funcionalidade ao ambiente de desenvolvimento e criar novas ferramentas para o
Gambas.

A linguagem está preparada para ser independente do gerenciador de janelas que use. Isto
significa que, sem trocar uma só linha do código, uma aplicação pode ser compilada para ser
executada em um desktop Gnome ou KDE, usando as bibliotecas próprias desses desktop como
sendo uma aplicação nativa deste ambiente. No futuro vamos poder desenvolver componentes
para o Windows, Fluxbox e outros gerenciadores de janelas não poderão modificar seus códigos
para que suas aplicações usem seus ambientes nativos. Marcando, apenas uma opção para
escolher qual componente usar antes de compilar, (Atualmente só podemos escolher entre gtk e
qt para Gnome e KDE).
Para gerar distintas aplicações para distintos ambientes a partir do mesmo código fonte. Esta
característica não se encontra disponível em nenhuma outra linguagem existente o que torna o
Gambas um ambiente único.

1.3 Elementos do Gambas


Para podermos desenvolver e executar programas feitos com Gambas, são necessários
diversos elementos:

.Um compilador, que se encarregara de transformar o código fonte em arquivos que


fazem parte de um projeto feito em Gambas, em um programa executável.

. Um interpretador capaz de fazer com que os programas feitos em Gambas sejam


executados pelo sistema operacional.

. Um ambiente de desenvolvimento que facilite a programação e desenhos das interfaces


gráficas dos programas.

Componentes que adicionam funcionalidades a linguagem. A palavra componente tem


um significado específico, que não se refere a partes genéricas, e sim a bibliotecas específicas
que lhe dotam de mais possibilidades. Na atualidade existe componentes para usar xml, conexões
de rede, opengl, sdl, ODBC, Varias bases de dados, expressões regulares, desktop baseados em
qt e gtk, etc. Estes componentes são desenvolvido por vários programadores, seguindo as
diretrizes da API do Gambas e a documentação publicada por Benoit Minisini.

1.4 Como obtê-lo


As novas versões do Gambas são publicadas através da pagina oficial do projeto:
http://gambas.sourceforge.net. Na atualidade existem dois Gambas: o estável 1.0 e o de
desenvolvimento 1.9 que acabara na versão 2.0. Da versão estável só se publicou novas versões
quando foi para corrigir alguma falha que foi descoberta.
No momento que escrevi estas linhas, a versão estável era a 1.0.11 e não se prever que
haja mudanças no futuro. A versão de desenvolvimento está em continua mudanças, melhorias e
adição de novos componentes. isto a faz muito atrativa, devido a existências de importantes
funcionalidades que não estão na versão estável (como os componentes gtk e ODBC). Como
essa é uma versão de desenvolvimento, é muito provável que tenha falhas não descoberta e com
certeza sofrera mudanças em um futuro próximo. Neste texto trataremos de todas as novidades
que a versão de desenvolvimento contem para que o o usuário possa escolher qual das versões
trabalhar. Boa parte das diferença se encontra nos novos componentes. Algumas destas serão
tratados neste texto, porque se o leitor quiser trabalhar com eles deverá usar a versão
desenvolvimento.

As novas versões são publicado sempre em forma de código fonte, para que usuário que
desejarem compilem e obtenham todas as partes que o Gambas tem. Os autores de alguns dos
componentes que são feitos para o Gambas, são publicados em vários sites da web porem todas
são enviadas a Benoit Minisini e passam a fazer parte da publicação completa desta linguagem de
programação e da seguinte versão. Deste modo pode se dizer que que quando Benoit faz uma
nova publicação, o pacote do código fonte contem as ultimas versões de todo o conjunto no
momento.

Como a compilação do Gambas e todos os seus pacotes associados pode ser uma tarefa
difícil para usuários inexperiente, é comum que se criem pacotes binários já compilados
preparado para ser instalados em várias distribuições gnu/Linux. Existe atualmente pacotes
disponíveis para Debian, Fedora, Mandriva, Gentoo, Slackware, QiLinux e Suse. Em alguns
casos, como para Fedora e Debian, está disponível tanto os pacotes da versão estável como a de
desenvolvimento.

No caso do Debian, os pacotes são criados em gnuLiEx e posteriormente, convertidos a


Debian para que esteja disponível e usável nesta distribuição e em todas suas derivadas, como
Kinoppix, Guadalinex, Progeny, Xandros, Linspire, Skolelinux, etc. Por este motivo, as ultimas
versões estão sempre disponíveis antes nos repositórios do gnuLinEx já que foram convertidos e
aprovados em Debian. As linhas do arquivo /etc/apt/sources.list de um sistema Debian para
instalar a versão mais atualizada dos pacotes Gambas são:

Para a versão estável: deb http://apt.linex.org/linex gambas/

Para a versão de desenvolvimento: deb http://www.linex.org/sources/linex/debian/cl gambas

A seguir, em qualquer um dos casos, para instalar todos os pacotes do Gambas, tem que
executar como root:

apt-get update
apt-get install gambas

Mesmo que o código fonte do Gambas seja distribuído em um único arquivo compactado,
a instalação dos pacotes compilados e feita com uma boa quantidade de arquivos.
A razão é que nem todos são necessário para executar aplicações feitas em Gambas. As
distribuições de Linux vem seguindo um critério de separar em vários pacotes o ambiente de
desenvolvimento (pacote gambas-ide), o interpretador (pacote gambas-runtime), foi feito um
pacote separado para cada um dos componentes. Se Você quiser programar em Gambas são
necessário a maioria deles, ao menos os que o ambiente de desenvolvimento necessita. se quiser
apenas executar um programa feito com esta linguagem , só é necessário gambas-runtime e um
pacote para cada um dos componentes que o programa usa. Por exemplo se for um programa que
foi feito para o desktop Gnome e não usa nenhum outro componente, só será necessário no
sistema os pacotes gambas-runtime e gambas-gb-gtk.
1.5 Compilação e dependências

Se em lugar de instalar pacotes já compilados para as distribuições gnu/Linux


desejarmos compilar a partir do código fonte, deveremos seguir os passos habituais do sistema
GNU. É baixar, descomprimir o arquivo com os fontes, dentro do diretório que se criou ao
descomprimir e usando um terminal, executar as seguintes instruções:

./configure
make
make install

O ultimo deles devemos faze-lo como root, se quisermos que o programa esteja
disponível para todos os usuários do computador. se estivermos acostumados a compilarmos
aplicações no sistema GNU, dispomos de um compilador instalado e de bastantes bibliotecas de
desenvolvimento. as instruções anteriores trataram de compilar e instalar todos os componentes
do Gambas, que são muitos. Se não tivermos as bibliotecas correspondentes a algum deles
simplesmente não se compilarão e a instrução ./configure nos informará delas. É importante
saber que o ambiente de desenvolvimento foi feito sobre a biblioteca gráficas qt, portanto, para
poder usar o ambiente necessitamos ter instalado ao menos essas bibliotecas de
desenvolvimento com uma versão igual ou superior a 3.0. A versão do compilador gcc deve ser
esta também, no mínimo. Cada um dos componentes tem dependências de suas próprias
bibliotecas e dependerá da distribuição Linux que usamos, para saber o nome do pacote que
deveremos instalar antes de podermos realizar a compilação.

1.6 Familiarizando com a IDE

Mesmo que um programa em Gambas possa ser feito com um editor de texto plano
qualquer, seria um desperdício não aproveitar um dos maiores atrativos que a linguagem tem: sua
IDE, o ambiente de desenvolvimento. A IDE do Gambas poupa o programador boa parte do
trabalho mas tedioso, lhe proporciona ferramentas que tornam muito mais fácil sua tarefa, com
utilitários de ajuda, desenhos da interface, auto completando as instruções, tradução do
programa, etc. Na imagem seguinte podemos ver algumas das janelas mais importantes do
ambiente, que se usa durante o desenvolvimento de uma aplicação:
Figura 2. Ambiente de desenvolvimento do Gambas

Quando o Gambas inicia, o primeiro que aparece é a janela de bem vindo

Figura 3. Janela de bem vindo


Aqui temos as opções de começar um novo projeto ou aplicação, abrir um projeto desde
que tenhamos seus arquivos disponíveis, abrir um usado recentemente ou um dos numerosos
exemplos que está incluído na ajuda do Gambas.

Antes de escolher qualquer uma dessas opções é necessário saber que todos os códigos
fontes de uma aplicação feito em Gambas é o que se denomina projeto. O projeto é formado por
uma série de arquivos que no Gambas está sempre dentro de um único diretório. porem o
desenvolvedor pode criar vários sub diretórios e organizar a seu gosto, porem qualquer gráfico,
texto e código que faça parte da aplicação estará dentro do diretório do projeto. Por isso se
escolhermos nesta janela Novo projeto..., o assistente sempre criará um novo diretório com o
nome do projeto e ai ira introduzir todos os arquivos necessário para o desenvolvimento da
aplicação. Assim, para enviar a alguem o código fonte de uma aplicação feito em Gambas ou
mudar de computador ou de disco, é só copiar o diretório com o nome do projeto, sem ter que
preocupar-se com outros arquivos. Do mesmo modo, se a partir do ambiente de desenvolvimento
escolhermos um arquivo ou um gráfico para integra-lo ao nosso trabalho, o arquivo será
automaticamente copiado para o diretório do projeto.

O primeiro exemplo
Uma das formas mais habituais de começar a trabalhar com uma linguagem de
programação é fazendo um pequeno programa que mostra uma mensagem Olá mundo. isto é
puro BASIC, quer dizer, que é igual o que fizeram os autores do BASIC por volta de 1964.

Naquele tempo as interfaces gráficas não existiam porque este primeiro programa era um
programa feito, no terminal. No BASIC original, fazer que aparecesse uma mensagem no
terminal era tão simples como escrever na linha:

PRINT "Olá Mundo"

Não é necessário nenhum cabeçalho prévio, a instrução PRINT serve para mostrar
qualquer cadeia de texto no terminal, que no BASIC se apresenta entre aspas duplas. não existe
programa tão simples como esse. Vamos ver como faze-lo com o ambiente de desenvolvimento
Gambas:
 

 

 

 

 
  
     ! 
   ∀ #
 



  ∃ !  
  



 
   



   

%   
    
& ∋
  
 
 

∀ 

(
     ) 

∗


+

,( 
− . 

Figura 4. Assistente do Gambas





  ,

 

1 1
  2
3 



 
+


 !
 
 

 

 


 / 
 


+


+ !
+    
 


4+ 

  
 

 ∗
    
 
 ∀1

   −!− ∀



, 

− (
  1 ∀ 1  

 

! 

 ) 
%    

 
      5∀

 ∀     
  
  
 +     /
  

 
       


 
  
∀ 1

      !  


∀  
 6∋∀ (



∀ 
 7
 +  
       
   ∀ ∗


  
 

 8
∀ 1 
  !



+ 


∀ 

 

 
 − (
 ,  

   ,
 1 
 


  1   7  
 5



 

%   ∀  1 
 9 
%
 ∀1

 
 
 1 

, 
−
 3
 0 


Dados, cujo nome indica sua finalidade, Armazenar ai os arquivos de dados que a aplicação
requeira.
Desde o início, Gambas nos da duas formas de criar os programas, inclusive são tão
simples como o que iremos fazer. Podemos escolher entre uma programação orientada a objetos,
paradigma típico das linguagens de programação mais poderosas ou uma programação
estruturada simples. Sendo assim, o arquivo que contem o nosso programa pode ser uma Classe
ou um Módulo. Para simplificar, no momento vamos usar um Módulo. Damos um click com o
botão direito do Mouse sobre a arvore de pastas aparecerá um menu contextual. Escolhemos a
opção Novo | Módulo. Surgirá uma janela na qual escrevemos o nome do módulo, por exemplo
meuprograma.module, onde podemos escrever o código.
Por fim podemos escrever nosso código em BASIC. O faremos justo antes da linha onde
está o END, Tal como está mostrando a (Figura 7).
Depois que escrevermos o código o
qual já conhecemos, pressionamos a tecla
ENTER, vemos que o ambiente colore o texto
de uma maneira particular. Podemos parar um
instante e observar as varias cores que mostra a
(Figura 7):
.Em cinza aparece uma linha que
começa com uma aspa simples ('). isto indica
que a linha é um comentário, Quer dizer que
 :

    
não se trata de nenhum código de programação

e o texto após a aspa não será executado nunca, são comentários que o programador pode e deve
colocar para facilitar que outros (ou ele mesmo, com o passar do tempo) entenda o que o
programa faz nesse ponto.

.Em azul podemos ver palavras chave da linguagem BASIC.

.Em rosa aparece a cadeia de texto.

.A esquerda vemos um ressalte amarelo no começo das linhas que tenham sido
modificadas. Isto aparecerá sempre nas linhas que contenham modificações e que ainda não
tenham sido compiladas.

Bem, já está pronto o programa. Para comprovar basta da um click no botão verde. com o
símbolo de Play, que está na tela do projeto. Ao faze-lo aparecerá uma nova janela chamada
Console na qual se verá a saída do nosso programa. Neste caso será um simples texto Olá
Mundo. Este é todo o código necessário, já podemos compilar o programa para gerar um arquivo
executável que funciona sem a necessidade do ambiente de desenvolvimento. Para isso no menu
da janela do projeto deve escolher: Projeto | Criar executável. Aparecerá uma janela de diálogo
para escolher o diretório onde queremos criar o executável. Click OK e o gerará. Vamos encerrar
agora o ambiente de desenvolvimento do Gambas e abrir um terminal ao passar para console do
Linux, podemos provar o funcionamento. Para isso, no diretório onde foi criado o executável,
fazemos:

jose@a00-o04:~$ cd gambas/olamundo/

jose@a00-o04:~/gambas/olamundo/$ ./olamundo.gambas

Olá Mundo

Melhor com um exemplo gráfico


O Exemplo anterior Mostrava uma aplicação de console, que nos recorda os velhos
tempos de outros sistemas operacionais e a forma de trabalhar dos hackers da informática.
Na realidade, fazer esse tipo de programa nos demonstra o potencial do Gambas, Mostra
que é realmente simples e igualmente fácil de realizar como em outras linguagens como Python
ou qualquer que seja a versão do BASIC.

É melhor fazer o programa Olá Mundo para o ambiente gráfico que inunda os escritórios
e os computadores atuais. Para isso faremos igual a antes, iniciaremos o Gambas e criando um
Novo projeto seguindo exatamente os mesmos passos, exceto que no lugar de escolhermos Criar
um projeto de texto, quando o assistente nos apresentar as várias opções, escolheremos Criar um
projeto gráfico.

Para não repetir nome, podemos denominar o projeto olamundo2. Ao acabar o processo
aparecerá de novo a janela do projeto, porem nesta ocasião terá um Caminho a mais na arvore:
Formulários. Um formulário é a área onde se desenha a interface gráfica da aplicação, quer
dizer, onde se insere objetos como botões, caixa de texto, listas, caixas de verificação, etc. Os
formulários corresponderão as janelas que a aplicação mostrará.

Dando um click com o botão direito do mouse sobre a árvore do projeto, escolhemos
agora no menu contextual que aparece: Novo | formulário. por simplicidade neste caso nem
sequer precisa trocar o nome, só da um click no botão OK aparecerá na tela a janela do
formulário e uma janela para escrever código BASIC quase idêntica a do exemplo anterior, sem
nada escrito. O resultado será algo parecido ao seguinte:

 
  
Dando um click sobre a palavra Form na janela da direita, correspondente a Caixa de
Ferramenta, Aparecerá vários objetos que podemos colocar em nosso formulários. entre estes
está o ícone do botão (se distingue rapidamente por ter a palavra OK escrito dentro). Dando um
click do mouse nele, podemos desenhar no formulário um botão, tem só que arrasta-lo com o
mouse e o botão esquerdo pressionado, para darmos a forma que queremos. Gambas escreve o
texto Button1, porem como este texto não é muito intuitivo o melhor é troca-lo. Para isso é só
selecionar o botão e depois, na janela propriedades, dá um click na linha onde está Text. Agora
Você pode escrever um novo texto, por exemplo Pressiona-me.

Falta a página 34 e 35
................................................................................................
uma aplicação nativa para o desktop KDE. Porem, com o componente Gtk disponível na versão
de desenvolvimento do Gambas, permite fazer programas enlaçados com as bibliotecas do Gtk
para realizar aplicações do desktop Gnome.
Se tivermos instalados os pacotes do Gambas correspondentes a versão de
desenvolvimento, incluindo pacote gambas-gb-gtk, podemos recuperar a aplicação olamundo2 do
exemplo anterior, ir à janela do Projeto e da um click no menu Projeto | Propriedades | , aba
Componentes, e selecionar gb.gtk. O resultado ao executar a aplicação é que se trata de uma
nova, porem antes do KDE e agora do Gnome. e sem trocar uma só linha de código!

  



 !  

∀

Até os dias de hoje não existe nenhuma outra linguagem de programação que possa fazer
isto.

1.7 Componentes do sistema


Ao longo dos parágrafos anteriores apareceram várias referências aos componentes do
Gambas, incluindo sua descrição no terceiro parágrafo deste capítulo. Este permitem entender
esta linguagem de programação. A interface desenvolvida por Bonoit para sua programação fez
com que vários programadores quisesse colaborar com ele, desenvolvendo novos componentes
que vão sendo adicionada as diversas versões do Gambas em cada nova publicação. Na versão
1.0 estável do Gambas só podia desenvolver em C e C++, porem a partir da versão 1.9.4 da
versão de desenvolvimento podemos escrever componentes também em Gambas, o que abre
numerosas possibilidades futuras já que é muito mais simples que em C.
A lista de componentes disponíveis é ampla e aumenta continuamente na versão de
desenvolvimento. Porem a versão estável está fixada os seguintes:
* gb.compress: para compressão de arquivos no formato zip e bzip2.

* gb.qt: para objetos visuais das bibliotecas gráficas qt.

* gb.qt.ext: para objetos visuais das bibliotecas gráficas qt que não são padrão.

* gb.qt.editor: um editor de texto que faz uso das bibliotecas gráficas qt.

* gb.qt.kde: objetos próprios do desktop KDE.

* gb.qt.kde.html: um navegador web do desktop KDE.

* gb.net: objetos para conectar um servidor de rede e outras comunicação.

* gb.net.curl: objeto para construir servidores de rede.

* gb.db: objeto de conexão a base de dados.

* gb.db.mysql: driver para conectar ao servidor de base de dados MySQL

* gb.db.postgresql: driver par a conectar ao servidor de base de dados postgresql.

* gb.db.sqlite: driver para usar base dedados sqlite 2.x.

* gb.xml: objetos para manipular arquivos XML.

* gb.vb: coleções de funções para facilitar a migração do Visual Basic.

* gb.sdl: objetos para reproduzir, mesclar e gravar arquivos de som.

* gb.pcre: objetos para usar expressões regulares na linguagem.

Na versão de desenvolvimento existe estes componentes (alguns dos quais tem sido muito
melhorados e aumentados, como o gb.sdl ) e outros mais, com uma lista em continuo
crescimento. No momento que escrevo essas linhas podemos contar com:

* gb.crypt: objetos para encriptação DES e MD5.

* gb.form: objetos para formulários independentes da bibliotecas gráficas usadas.

* gb.gtk: objetos gráficos para formulários das bibliotecas gtk. Tem os mesmos
objetos que os componentes gb.qt, porem enlaçado com esta outra biblioteca de
desenvolvimento.

* gb.db.odbc: driver para conectar a base de dados através do ODBC.

* gb.info: objetos que dão varias informações sobre os componentes e o sistema


onde a aplicação é executada.
gb.opengl: objetos com desenhos tridimensionais para aceleração OpengL.

gb.xml.rpc: objetos para o uso do protocolo rpc-xml.

gb.sdl.image: objetos para desenhos em duas dimensões com aceleração gráfica.

gb.v4l: objetos para captura de vídeos em Linux.

gb.gtk.pdf: utilizado para renderizar documentos pdf nas aplicações feitas em


Gambas.

A lista dos componentes disponíveis para o programador pode se ver na aba Componentes,

acessível através do menu Projeto |


Propriedades. Cada um dos componentes
corresponde a um pacote compilado na
distribuição, de forma que para termos
adicionado ao projeto, por exemplo, o
componente gb.sdl, teremos que instalar o
pacote gambas-gb-sdl, e nos computadores
onde se queira executar a aplicação
compilada.

Ao darmos um click a cada um dos


componentes aparecerá uma pequena
descrição de sua função, o nome do autor
ou autores do componente, e uma lista dos
controles que estará disponível para o
desenvolvedor se selecionar o componente
 &#  
(figura 12).
Os controles são classes para criar objetos úteis na programação. Os objetos
criados podem ser visuais (como abas, Caixas de textos , etc.) ou objeto de código
(como servidores de rede ou conexões de base de dados). se o componente tem
objeto visuais, estes se incorporam em algumas das abas da Caixa de
Ferramentas do ambiente de desenvolvimento.

Figuras 13, 14, 15.

Caixa de Ferramentas
com componentes
e objetos gráficos
Nas imagens anteriores podemos ver alguns objetos gráficos que estão disponíveis ao
selecionarmos um determinado componente.

Cada componente tem sua própria documentação que encontra-se incluída na ajuda do
Gambas. Na versão estável esta ajuda está sempre disponível; na versão de desenvolvimento só
estará disponível se tiver sido selecionada para seu uso no projeto.

Todas as coisas que podemos fazer com o Gambas e não fazem parte da própria
linguagem BASIC, Programamos mediante o uso de componentes. Isto significa que, por
exemplo, para fazermos uma aplicação de base de dados é necessário selecionar o componente
gb.db ou não estarão disponível os objetos de conexão de base de dados. O mesmo ocorre com
as conexão de redes, captura de vídeo, etc. Estes objetos não fazem parte da linguagem BASIC.

NOTAS
1 - http://www.cooper.com/alan/father_of_vb.html

2 - diferença entre Gambas e Visual Basic:

http://wiki.gnulinex.org/gambas/210

3 - http://fsf.org/licensing/licenses/gpl.html

4 - Tradução não oficial para o Espanhol em:

http://gugs.sindominio.net/licencias/

5 - Documentação do Gambas em espanhol:

http://wiki.gnulinex.org/gambas/

6 - Instruções para colaborar na documentação em espanhol:

http://wiki.gnulinex.org/gambas/6

7 – Se a instalação for feita a partir de m sistema gnuLinEx, não é necessário adicionar


esta linha já que a versão de desenvolvimento do Gambas é parte do repositório oficial do
gnuLinEx.

8 – Devido a maior extensão do texto Criar um projeto de texto em português ser maior
que em outros idiomas, é possível que esta mensagem apareça cortada e não se veja por
completo. Não fique preocupado com isso, não afeta em nada a sua seleção e suponhamos que
seja corrigida em posteriores versões da IDE.
Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 2: PROGRAMAÇÃO BÁSICA ..............................................................


04
2.1 Organização de um projeto em Gambas ......................................................... 04
Declaração de variáveis ................................................................................ 05
Subrotinas e funções ..................................................................................... 07
2.2 Tipo de Dados ............................................................................................... . 09
Tipos de C onversões...................................................................................... 10
Matrizes ........................................................................................................ 12
2.3 Operadores Matemáticos ................................................................................ 13
Operadores lógicos ....................................................................................... 14
2.4 Manipulação de Cadeias ................................................................................ 14
2.5 Controle de fluxo ............................................................................................ 16
IF.. THEN... ELSE ........................................................................................ 16
Select ............................................................................................................ 17
FOR .............................................................................................................. 18
WHILE e REPEAT ...................................................................................... 19
Depuração na IDE do Gambas ..................................................................... 20
2.6 Entrada e Saída de Arquivos .......................................................................... 21
2.7 Controle de Erros ........................................................................................... 24
2.8 Programação orientada a objeto com Gambas ............................................... 25
2.9 Propriedades, Métodos e Eventos .................................................................. 29

 o Capitulo 1 Vimos alguns detalhes sobre a
programação com Gambas. Agora estudaremos, principalmente, as
sintaxes e instruções mais importantes da linguagem BASIC que o
Gambas usa. Boa parte destas instruções existem no BASIC padrão e algumas outras
são extensões próprias do Gambas.
No entanto o mais importante, neste capitulo é usarmos quase sempre exemplos de
código de console, como vimos no parágrafo o primeiro exemplo do capitulo 1. Fazendo deste
modo evitaremos as interferências que pode complicar a explicação de conceitos relacionados
com a programação gráfica e os componentes. Estes conceitos será explicado nos capítulos
posteriores.

2.1 Organização de um projeto em Gambas


Antes de começar com a programação básica teremos que resumir alguns conceitos
prévios:
. Os códigos fontes dos programas feitos em Gambas está composto em um ou mais arquivos

que formam um projeto. Este projeto se arquiva em um diretório do mesmo nome.
. Os Arquivos de código podem ser: Módulos (contem código em BASIC que se
executam diretamente), Classes (contem o código em BASIC que executa um objeto de
classe) e Formulários (áreas onde se desenha a interface gráfica da aplicação e que
correspondem as janelas do programa).

. Os projetos de textos só contem Módulos ou Classes . as aplicações gráficas contem


Formulários e Classes, mas também podem conter Módulos.

.O projeto pode conter outros arquivos de dados, documentos, textos, etc., sem código
BASIC para ser executado pela aplicação.

Os arquivos que contenham código em BASIC (Módulos e Classes) sempre estão


estruturados da seguinte maneira:

. Declaração de variáveis.

. Subrotinas e funções.

Declaração de variáveis
Os programas manipulam dados continuamente. Estes dados podem ser de muitos tipos:
números, letras, textos, etc. e trocam seus valores ao longo da execução do programa (neste caso
recebem o nome de variáveis) ao permanecer com um valor fixo durante todo o tempo (então se
denominam constantes). A estes dados que um programa usa é necessário atribuir um nome
identificador.

O BASIC permite usar variáveis e constantes sem havermos declarado antes, quer dizer,
sem haver exposto no inicio do programa uma lista de todas as variáveis que iremos usar.

Isso produz programas ilegíveis e quando chega a um tamanho médio é uma fonte
constante de erros. Para evitar isto, Gambas obriga a declarar as constantes e as variáveis antes de
utiliza-las

Os lugares onde se pode declarar variáveis, depende do âmbito que se vai usar. Se
declararmos dentro de uma subrotina ou função (nos parágrafos seguintes veremos com detalhes
o que são subrotinas e funções), estará disponível para ser usada somente dentro dessa subrotina
ou função. Se declararmos no inicio de um arquivo de código (seja um Módulo ou um Classe)
estará disponível para todo o código desse arquivo, em todas as subrotinas.

Para declarar uma variável em uma subrotina ou função é só empregar a sintaxe:

DIM nome_variável AS tipo_variável

O tipo_variável faz referência ao tipo de dados da variável: número inteiro, cadeia de


texto, número decimal, etc. O nome da variável o programador escolhe livremente. É
recomendável que seja algo que indique o uso da variável. Devemos fugir de nomes como a, b, c,
etc., e usar por exemplo, idade, data_nascimento, altura, etc.

As variáveis que declaramos dentro de uma subrotina ou função só serão usadas dentro
delas. Quando termina se destruirão. Isso permite usar o mesmo nome da variável dentro de
várias subrotinas e seu valor nunca se confundirá ou mesclará.

Para declararmos uma variável no inicio de um Módulo ou Classe usamos a sintaxe:

[STATIC] (PUBLIC  PRIVATE) nome_variável AS tipo_variável

Se definirmos a variável como PRIVAT, estará disponível dentro do arquivo, mas não
será acessível nos outros arquivos do mesmo projeto. Se à declararmos PUBLIC, podemos
acessa-la no arquivo onde foi declarada ou de qualquer arquivo do projeto.

A palavra STATIC se usa nos arquivos de Classe, não nos Módulos. Serve para definir
um comportamento especial em todos os objetos de uma mesma classe. Em poucas palavras
poderíamos explicar com um exemplo: se temos uma classe cachorro, teremos algumas variáveis
como cor, raça, peso, etc., e cada objeto cachorro terá seu próprio valor em cada uma dessas
variáveis.

Ao mesmo tempo, podemos declarar uma variável que seja número_de_patas, de forma
que se trocarmos seu valor de 4 para 3, todos os objetos cachorro terão 3 patas e cada um
continuará com seu próprio peso, cor, etc. Neste caso a variável número_de_patas se declarará
STATIC no código BASIC no arquivo da classe cachorro. Veremos todos estes comportamentos
mais adiante neste mesmo capítulo.

As constantes se define só no inicio de arquivos, Módulo ou Classe, não podemos


defini-las dentro das subrotinas e funções. A sintaxe é:

(PUBLIC | PRIVATE ) CONST nome_constante AS tipo_constante = valor

Portanto, é igual as variáveis, podem ser privada ou públicas.

Vejamos um exemplo de tudo isso:





 
   
 


  
  PRIVATE CONST Pi = 3.141592
'No meu teste só funcionou como está no exemplo

PRIVATE idade AS Date


PRIVATE altura AS Single
PRIVATE CONST Ppi AS Float = 3.141592
PUBLIC qualidade AS Integer

PUBLIC SUB Subrotina1()

DIM num AS Integer


DIM nome AS String
 

 



 


 
  
 

 



Neste exemplo vimos que as variáveis idade e altura podem ser usadas em todo o
arquivo. Portanto, quando executar a subrotina1, idade valerá 30 e, depois que executar a
subrotina2 valerá 32. Vimos também que a variável num está definida nas duas subrotinas. O
valor de num desaparece ao acabar cada uma das subrotinas e, portanto, durante a subrotina1
valerá 54 e durante a subrotina2 valerá 4 mas depois que se executa o END de cada uma dessas
subrotinas, simplesmente não existirá e se fizermos referência a num em qualquer outro ponto do
programa se produzirá um erro.

Finalmente vemos que a variável qualidade está definida como pública. Isso
significa que de qualquer outro arquivo do programa pode ser feita referência a ela mediante o
nome exemploVariavel.qualidade, onde exemplo Variável é o nome que foi dado ao arquivo
onde foi declarado qualidade.

Subrotinas e Funções
E impensável escrever todo o código de um programa sem a mínima organização. Em
BASIC o código se organiza dividindo-o em procedimentos. Existem dois tipos de
procedimentos: subrotinas e funções. Uma subrotina é um procedimento que executa algo, mas
não devolve nenhum valor. Exemplos de subrotinas seriam procedimentos para desenhar algo na
tela, tocar um som, etc. No entanto, uma função é um procedimento que sempre devolve algo ao
terminar sua execução. Exemplos de funções seriam os cálculos de uma operação matemática
que devolve um resultado, o processo para pedir dados ao usuário da aplicação, etc.

Já temos visto no capitulo anterior a sintaxes para declarar as subrotinas, já que que o
ambiente de desenvolvimento escreve automaticamente as subrotinas que o programa executa ao
darmos um click sobre um botão. A sintaxe completa é:

(PUBLIC | PRIVATE) SUB nome_da_subrotina (p1 AS tipo_da_variável, p2


AS tipo_da_variável, ......)

...código que a subrotina executa

END
As palavras PUBLIC e PRIVATE significa exatamente o mesmo que quando se define
variáveis: determina se a subrotina pode ser chamada só do arquivo onde foi codificado
(PRIVATE) ou de qualquer arquivo da mesma aplicação (PUBLIC).
As variáveis p1, p2, etc., permite passar parâmetros à subrotina, que se comportam dentro
dela como variáveis declaradas dentro da própria subrotina. Quer dizer, desaparecem ao executar
o END final. Pode se passar tantos parâmetros quando se desejar numa subrotina, declarando
todos, obviamente
Existem algumas subrotinas com nomes especiais no Gambas, portanto o programador
não deve usar esses nomes. São as seguintes:
Main: existe em todas as aplicações do Gambas, que são no modo texto, não no modo
gráficos. Este é o ponto onde começa a executar o programa. Se não houvesse a subrotina Main,
Gambas daria uma mensagem de erro ao iniciar já que não saberia por onde começar.
New e Free: se executam, respectivamente, ao criar-se e destruir-se um objeto. Só se
encontram nos arquivos de classe.
Objeto_evento: executa-se automaticamente quando no Objeto ocorre um evento. Já foi
visto alguns exemplos no capitulo anterior, como btnsair_Click(), que executa quando o usuário
da aplicação da um click com o botão do mouse sobre o botão btnsair. Nas aplicações gráficas, o
evento Open do formulário, que inicia a aplicação, é a primeira subrotina que o programa
executará. No ultimo paragrafo deste capitulo trataremos especificamente destes eventos, seus
significados e utilidades.
Vejamos um programa de exemplo. Para testarmos, vamos criar um novo programa de
texto seguindo os passos explicados no Capitulo 1, paragrafo O primeiro exemplo:
PUBLIC SUB Main ( )

Print_Media ( 4, 8 )

END

PUBLIC SUB Print_Media ( Valor1 AS Integer, Valor2 AS Integer )

PRINT ( Valor1 + Valor2 ) / 2

END
Mesmo que este seja um programa pouco útil, serve para expressar com simplicidade a
forma de funcionar das subrotinas. Começa executando a subrotina Main, nela só existe uma
chamada para executar a subrotina Print_media passando os números inteiros 4 e 8 como
parâmetro. A subrotina Print_media mostra no console o resultado da média entre os dois
valores que foi passado como parâmetros.
As sintaxes para declarar uma função é a seguinte:
(PUBLIC | PRIVATE) FUNCTION nome_da_função (p1 AS Tipo_da_Variável, p2 AS

Tipo_da_Variável ....) AS Tipo_de_Dados

......código que a função executa

RETURN resultado_que_a_função_executou

END
A declaração é quase idêntica a da subrotina, acrescentando mais duas coisas: o tipo de
dado que a função devolve na primeira linha e a necessidade de usar a sentença RETURN do
BASIC para indicar o valor a devolver.

Vamos a outro exemplo que produz o mesmo resultado que o anterior, porem usando uma
função:
PUBLIC SUB Main ( )

DIM Final AS Single

Final = Calcula_Media ( 4, 8 )

PRINT Final

END

PUBLIC FUNCTION Calcula_Media (Valor1 AS Integer, Valor2 AS Integer) AS Single

RETURN ( Valor1 + Valor2 ) / 2

END

Neste caso é a subrotina Main que encarrega-se de mostrar no console o resultado da


operação. É muito importante destacar a diferença entre a forma de chamar uma subrotina e
chamar uma função. No exemplo anterior vimos que para chamar a subrotina era só escrever seu
nome com seus parâmetros entre parenteses. Agora vemos que para chamar a função usamos
uma designação, Final = Calcula_Media(4,8). Isto deve ser feito sempre ao chamar uma função:
a designação serve para recolher o valor devolvido. Por motivos óbvios, a variável que recolhe o
valor da função deve ser declarada do mesmo tipo de dado que o devolvido pela função. No
exemplo anterior a função devolve um dado tipo Single (um numero real com decimais) e a
variável Final e declarada portanto, do tipo Single.

2.2 Tipos de dados


Temos visto ao longo dos textos anteriores o uso de variáveis e como são declaradas.

Ate o momento, só conhecemos alguns tipos de dados. Veremos agora todos os que o
Gambas suporta:

Boolean: é um tipo de dados que pode ser o resultado de uma comparação, só aceita os
valores: False e True (Verdadeiro ou Falso em português).

Byte: representa um numero inteiro positivo entre 0 e 255.

Short: representa um numero inteiro com valores possível entre -32.768 e +32.767

Integer: representa um numero inteiro com valores possível entre -2.147.483.648 e


+2.147.483.647.

Long: representa um numero inteiro com valores possível entre


-9.223.372.036.854.775.808 e +9.223.372.036.854.775.807.
.Single: representa um número real, com decimais, com valores possível entre
-1,7014118 +38 e +1,7014118E+38.

.Float: representa um número real, com decimais, com valores possível entre
-8,98846567431105E e + -8,98846567431105E.

.Date: serve para armazenar um valor de data e hora. Internamente, a data e hora são
armazenada no formato UTC, ao devolver o dado representa-se no formato local,
segundo a configuração do computador.

.String: é usada para armazenar uma cadeia de texto. Em BASIC as cadeias de texto
designam-se mediante aspas duplas.

.Variante: significa qualquer tipo de dados, quer dizer, pode armazenar uma Integer,
Single, String, etc. Devemos evitar seu uso porque ocupa mais memoria que as anteriores
e os cálculos com variantes são muito mais lentos.

.Oobject: representa qualquer objeto criado em Gambas.

É o programador quem escolhe o tipo de dado que deve ser declarado numa variável.
Devemos usar sempre os tipos de dados menores, já que ocupa menos memória e o processador
os manipula com mais velocidade. Isso pode limitar as opções da aplicação, por isso que as vezes
temos que optar por tipo maiores para não fechar possibilidades.

Por exemplo, se você vai usar uma variável para definir a idade de uma pessoa, o lógico é
utilizar um dado tipo byte (o valor máximo e 255). se a idade a ser armazenada é de uma arvore é
conveniente usar uma do tipo Short, já que pode haver arvores com mais de 255 anos, mas não se
conhece uma com mais de 32.767.

Tipos de conversões
Gambas permite várias conversões entre tipos de dados. A forma de fazer a conversão
pode ser implícita ou explicita. São conversões implícita quando o próprio interpretador do
Gambas encarrega-se de gestiona-las. Por exemplo:

DIM Resultado AS Single

DIM Operando1 AS Single

DIM Operando2 AS Integer

Operando1 = 3.5

Operando2 = 2

Resultado = Operando1 * Operando2

Neste caso, para poder realizar a multiplicação, o interpretador converte, de forma


transparente para o programador, o operando2 para um valor Single. São conversões explicitas
as que o programador deve fazer ao escrever o código para poder realizar operações, mesclar
dados de vários tipos, etc.
Estas conversões fazem-se mediante funções que estão incluídas no BASIC do Gambas.
Evidentemente, a conversão será feita sempre que seja possível, caso contrário é produzido um
erro. Esta é a lista de funções de conversão existente:

.Cbool(expressão): converte a expressão à um valor booleano, verdadeiro ou falso. O


resultado será falso se a expressão é falsa, o numero 0, uma cadeia de texto vazia ou um valor
nulo. Será verdadeira nos demais casos. Por exemplo:

-Devolve false as seguintes operações: Cbool(“”), Cbool(0), Cbool(NULL).

-Devolve true as operações: Cbool(1), Cbool(“Gambas”).

.CByte(expressão): converte a expressão em um valor tipo Byte. Primeiro converte a


expressão para um numero binário de 4 bytes. Se este número for maior que 255, corta
recolhendo os 8 bits de menor peso. Por exemplo, Cbyte(“17”) devolve 17, mas Cbyte(100000)
devolve 160. é porque o número 100.000 em binário é 11000011010100000, seus 8 últimos bits
são 10100000, que passado de binário a decimal é igual a 160. Se não soubermos operar com
números binários o melhor que podemos fazer é evitar este tipo de conversão que resulta em
valores tão “surpreendentes”.

.CShort(expressão), CInt(expressão) ou CInteger(expressão), e CLong(expressão):


convertem respectivamente, a expressão em um número do tipo Short, Integer, e Long. No caso
de CShort a conversão realizada igual a CByte, podendo produzir resultados estranhos
igualmente se a expressão resultante de um número for maior que 32.767.

.CDate(expressão): converte a expressão em uma data, mas devemos ter cuidado porque
não admite o formato de data local, só o formato inglês mês/dias/anos horas:minutos:segundos.
Quer dizer, CDate(“09/06/1972 01:45:12”) isto é, dia 6 de setembro de 1972

.CSingle(expressão) ou CSng(expressão) e CFloat(expressão) ou CFlt(expressão):


convertem, respectivamente, a expressão de um número do tipo Single e Float. A expressão deve
usar o ponto (.) e não a vírgula (,) como separador decimal.

.Cstr(expressão): converte a expressão em uma cadeia de texto sem levar em conta a


configuração local. Portanto, CStr(1.58) devolve a cadeia de texto 1.58, independente se a
configuração local indica que o separador decimal é um ponto ou uma vírgula o
Cstr(CDate(“09/06/1972 01:45:12”)) devolve “09/06/1972 01:45:12”.

.Str$(expressão): Converte a expressão em uma cadeia de texto, levando em conta a


configuração local. Portanto Str$(1.58) devolve a cadeia de texto 1,58, se a configuração local
estiver em português. Do mesmo modo Str$(CDate(“09/06/1972 01:45:12”)) devolve
“06/09/1972 01:45:12” se a configuração estiver em português já que neste idioma costuma
escrever as datas na forma dia/mês/ano.

.Val(expressão): converte uma cadeia de texto em um tipo Boolean, Date ou outros tipos
numéricos, dependendo do conteúdo da expressão. Val é a expressão oposta de Str$ e também
leva em conta a configuração local do computador que à executa.
O Gambas tenta converter a expressão em um tipo Date, se não pode tenta converter em um
número com decimais, se tão pouco pode, tenta converter em um número inteiro e finalmente,
tenta converter em um tipo Boolean.

Matrizes
Em numerosas ocasiões, quando se tenta resolver um problema mediante a programação,
surge a necessidade de contar com a possibilidade de armazenar vários dados na mesma variável.
A solução mais simples para este problema são as Matrizes ou Arrays. Podemos definir matrizes
que contenham qualquer tipo de dados, mas com a condição de que todos os elementos sejam do
mesmo tipo. Não há mais limites na dimensão da matriz a não ser a memória do computador ou a
capacidade do programador de operar com matrizes de dimensões grandes.

A sintaxe para trabalharmos com matrizes é a mesma que para as variáveis, no entanto
entre colchetes as dimensões da matriz. Alguns exemplos:
DIM Notas [ 2, 3 ] AS Single

DIM Idades [ 40 ] AS Integer

DIM Isto_nao_ha_quem_o_manipule [ 4, 3, 2, 5, 6, 2 ] AS String

Para acessar o valor de cada uma das celulas da matriz, teremos que referirmos sempre ao
seu índice. Em uma matriz de duas dimensões só podemos identificar facilmente por linhas e
colunas.

Temos que levar em conta que o índice começa em 0, não em 1. Quer dizer, na matriz
Listado[3] existirão só valores correspondentes a Listado[0], Listado[1], Listado[2]. Se
tentarmos acessar a Listado[3] dará um erro Out of Bounds, fora do limite. Por exemplo:

DIM Alunos [ 4, 10 ] AS String

DIM Colunas AS Integer

DIM Linhas AS Integer

Colunas = 2

Linhas = 6

Alunos [ Colunas, Linhas ] = " Antônio Sousa "

Temos que declarar estas matrizes com a dimensão máxima que vão ter. Isso nos leva a
supor que o interpretador do Gambas reserva a memória necessária para elas ao começar o uso do
programa. No entanto, tem vezes que, pelas características da aplicação, desconhecemos a
dimensão que terá a matriz, para resolver este problema, Gambas tem predefinidas matrizes
unidimensionais dinâmicas de todos os tipos de dados, exceto Boolean. Estas matrizes trabalham
de forma idêntica as anteriores, Já que fazem falta funções para adicionar novos elementos a
matriz, apaga-los ou saber o numero de elementos que elas tem.

Com este exemplo veremos o uso e funções mais usuais ao trabalharmos com matrizes
dinâmicas:
DIM Nomes AS String [ ]

'A seguinte instrução inicializa nomes para poder usa-lo.

'É um passo prévio obrigatório:

Nomes = NEW String [ ]

'Assim podemos adicionar valores a matriz:

Nomes.Add ( "Manoel" )

Nomes.Add ( "João" )

Nomes.Add ( "Antonio" )

'Count devolve o numero de elementos da matriz.

'A seguinte instruções mostrará 3 no console:

PRINT Nomes.Count

'A seguinte instrução apagará a linha de “João”

Nomes.Remove (1)

PRINT Nomes.Count 'mostrará 2

PRINT Nomes [ 1 ] 'mostrará “Antonio”

'A seguinte instrução limpará nomes

Nomes.Clear

PRINT Nomes.Count 'mostrará 0

2.3 Operadores matemáticos


Quando se trata de trabalhar com números, Gambas tem as operações habituais como
quase todas as linguagem de programação:

. +, -, *, / e se usa respectivamente, para a soma, subtração, multiplicação e divisão.

. ^ é o operador de potencia, por exemplo, 4 ^ 3 = 64

. Para a divisão existe dois operadores adicionais, \ o DIV e MOD, que devolvem,
respectivamente, a parte inteira do resultado da divisão e o resto. Quer dizer, (9 DIV 2 =
4),
(9 \ 2 = 4) e (9 MOD 4 = 1P).

Alem destes operadores existem as seguintes funções matemáticas para realizar calculos
mais complexos:

.Abs(número): devolve o valor absoluto de um número.

.Dec(número): decrementa um número.

.Frac(número): devolve a parte decimal de um número.

.Inc(número): incrementa um número.


.Int(número): devolve a parte inteira de um número.

.Max(número1, numero2 .....): devolve o número maior.

.Min(número1, número2 .....): devolve o número menor.

.Round(número,decimais): Arredonda um numero com as decimais desejadas.

.Sgn(número): Devolve o sinal de um número.

Rnd([minimo],[máximo]): Devolve um número aleatório compreendido entre minimo e


máximo, se não expressar nenhum valor para minimo ou máximo, o numero estará
compreendido entre 0 e 1. se só expressarmos um valor, o numero estará compreendido entre 0 e
esse valor. Muito importante: antes de usar Rnd é necessário executar a instrução Randomize que
inicializa o gerador de números aleatórios. Se não fizermos isso obteremos o mesmo número em
sucessivas execuções.

Operações lógicas
Para realizar operações entre variáveis do tipo Boolean ou expressões cujo resultado seja
Boolean, existem algumas instruções similares as que podemos ver quase em todas as linguagens
de programação. Trata-se de AND, OR, NOT e XOR. Se tivermos conhecimento de lógica e
números binários, não será difícil identificar e saber o seu comportamento ao tratar-se das
operações binarias mais básicas. Caso contrário, será fácil usa-las e entender seu funcionamento
com uma simples tradução para o português das três primeiras: E, OU, NÃO. Quer dizer, serve
para unir condições do tipo: cor é verde e não é marrom, que se escreveria:

Cor = “Verde” AND NOT Cor = “Marrom”

No caso de XOR é mais difícil de entender já que é uma operação especial chamada
OR exclusiva. O resultado de uma operação XOR é verdadeira quando os dois operando são
diferentes e, falso, quando os dois operando são iguais. Na prática, esta operação é utilizada em
cálculos com números binários, ou em caso de estarmos seguro que conhecemos perfeitamente
seu funcionamento

2.4 Manipulação de cadeias


Umas das tarefas mais habituais nos programas de informática é o uso de cadeias de
texto, tanto para as aplicações de base de dados, como para a simples saída de mensagens na tela.
Em Gambas estão implementado todas as funções de cadeia de texto do BASIC padrão mais as
que estão presentes no Visual Basic. Antes de proceder a sua lista, destacamos que existe um
“operador” de cadeias de texto que permite concatena-las diretamente, Trata-se do símbolo &.
Vejamos um exemplo de seu uso
DIM Nome AS String

DIM Apelido AS String

Nome = "Manoel"
Apelidos = "Alvares Gomes"

PRINT Apelidos & “ , ” & Nome

A saída no console será:

Alvares Gomes, Manoel

Vejamos agora a lista das funções disponíveis para manipular cadeias de texto:

. Asc(Cadeia,[Posição]): Devolve o código ASCII do caracter que está na posição


indicada na cadeia dada. Se não é dada a posição, devolve o código do primeiro caracter.

. Chr$: devolve um caracter a partir de seu código ASCII. Esta função é útil para
adicionar caracteres especiais a uma cadeia de texto, por exemplo:

PRINT “Manoel” & Chr$(10) & “Antonio”

insere uma tabulação entre os nomes, já que na tabela ASCII o código 10 corresponde a
um avanço de linha (Line Feed).

.InStr (Cadeia, Sub cadeia [ , Inicio ] ): Procura a sub cadeia da cadeia e devolve um
número da posição onde a encontrou. Se não dermos o valor Inicio, a busca começará
nessa posição. Por exemplo:

PRINT Instr(“Gambas é basic”, “bas”, 5)

devolve um 10, enquanto que:


PRINT Instr(“Gambas é basic”, “bas”)

devolve 4

.RinStr (Cadeia, Sub Cadeia [, Inicio ] ): função igual ao InStr, só que começa a busca
da direita para esquerda na cadeia.

devolve:

Faltam páginas 58-59

No segundo caso podemos ver como, mesmo que o separador seja o espaço em branco, o
texto não fica separado um circo ao estar rodeado do caracter de escape.

Havia, uma, vez, um

Havia, uma, vez, um circo


Controle de fluxo
São muitas as ocasiões em que o fluxo em que as instruções são executadas em um
programa não é adequado para resolver o problema.

Todo o código BASIC que temos visto até agora executa suas instruções de cima a baixo,
conforme as vai encontrando. As vezes, com frequência, teremos que voltar atrás repetir coisas,
tomar decisões, etc. Estes tipo de ações denominam-se, controle de fluxo e o BASIC do Gambas
proporciona uma boa quantidade de sentenças para isso.

IF ... THEN .... ELSE


É a sentença mais comum para tomar uma decisão: si cumpre-se uma decisão, então
executa-se algo, caso contrário, executa-se outra coisa.

Sua forma mais básica é:

IF Expressão THEN
.........
ENDIF

O si e o que executa-se é uma só instrução:

IF Expressão THEN sentença_a_executar

A sintaxe completa da instrução é:

IF Expressão [ {AND IF | OR IF } Expressão ..... ] THEN

..............

[ ELSE IF Expressão [ { AND IF | OR IF } Expressão ...... ] THEN

.............. ]

[ ELSE

.............. ]

ENDIF
Alguns exemplos de uso:

DIM Idade AS Integer

.................

IF Idade > 20 THEN PRINT "Adulto"

IF Idade < 2 AND Idade > 0 THEN

PRINT "Bebê"

ELSE IF Idade < 12 THEN


PRINT "Menino"

ELSE IF Idade < 18 THEN

PRINT "Jovem"

ELSE

PRINT "Adulto"

ENDIF

Dependendo do valor que que dermos a variável Idade ao escolher o primeiro IF,
mostrará um resultado diferente.

Select
No exemplo anterior vimos que o fluxo do programa necessita revisar varias condições
sobre uma mesma variável, produzindo um IF dentro de outro (IF aninhados). Esta estrutura não
e comoda de ler nem produz um código limpo. Para estes casos existe a sentença SELECT, que é
muito mais apropriada. Sua sintaxe é:

SELECT [CASE ] Expressão


[ CASE Expressão [ TO Expressão #2 ] [, ...... ]
............. ]
[ CASE Expressão [ TO Expressão #2 ] [, ....... ]
............. ]
[ { CASE ELSE | DEFAULT }
............. ]
END SELECT

Vejamos como se aplica o mesmo exemplo anterior das idades:


DIM Idade AS Integer

...........

SELECT CASE Idade

CASE 0 TO 2

PRINT "Bebê"

CASE 2 TO 12

PRINT "Menino"

CASE 18

PRINT "Bingo, já podes votar"

CASE 13 TO 17

PRINT "Jovem"

CASE ELSE

PRINT "Adulto"

END SELECT
Se trata de um código muito mais fácil de ler que o anterior.
For
Quando faz-se necessário contar ou realizar uma ação com número determinadas vezes, a
sentença FOR é a solução:

FOR Variável = Expressão TO Expressão [ STEP Expressão ]

..........

NEXT

O loop incrementa a variável de um em um, a não ser que especifique-se um valor


o STEEP. Podemos especificar valores negativos, de forma que converta-se em uma conta a
menos. Por exemplo:

DIM n AS Integer

FOR n = 10 TO 1 STEP -1

PRINT n

NEXT

Se quisermos interromper o loop em algum ponto, podemos usar a sentença BREAK:

DIM n AS Integer

FOR n = 10 TO 1 STEP -1

IF n = 3 THEN BREAK

PRINT n

NEXT

O loop Acabará quando n valer 3 e não será escrito os últimos três números. Dispomos
também da sentença CONTINUE, que permite saltar passos no loop.
DIM n AS Integer

FOR n = 1 TO 4

IF n = 2 THEN CONTINUE

PRINT n

NEXT

Saltará em dois ao escrever os valores de n. Existe uma variante do loop FOR que
usamos ao percorrer elementos de uma coleção, como uma matriz. A sintaxe neste caso é:

FOR EACH Variável IN Expressão

..........

NEXT
Vejamos um exemplo usando as matrizes dinâmicas que vimos neste capitulo:
DIM Matriz AS String [ ]
DIM Elemento AS String

Matriz = NEW String [ ]


Matriz.Add ("Azul")
Matriz.Add ("Roxo")
Matriz.Add ("Verde")

FOR EACH Elemento IN Matriz


PRINT Elemento;
NEXT

Escreverá na saída: AzulRoxoVerde.

WHILE e REPEAT
Quando queremos repetir a execução de uma parte do código em varias ocasiões
dependendo de uma condição, temos duas instruções distintas: WHILE e REPEAT.

Seu comportamento e quase idêntico. A diferença reside em que a condição necessária


para que execute o código é falsa desde o principio, com REPEAT executará uma vez e com
WHILE não executará nunca. A sintaxe de ambas é:

WHILE Condições REPEAT Condições

.... instruções .... instruções


WEND UNTIL Condições

No caso do loop WHILE existe uma variante da sintaxe consiste em substituir WHILE
por DO WHILE e WEND por LOOP. É exatamente o mesmo; depende do programador escolher
um formato ou outro. Vejamos um exemplo:

DIM a AS Integer DIM a AS Integer


a=1
REPEAT
WHILE a <= 10
PRINT "Ola Mundo" ; a PRINT "Ola Mundo" ; a
INC a
INC a
WEND
a=1 UNTIL a > 10

Este exemplo produzirá o mesmo resultado na execução do loop WHILE quanto no


REPEAT, em ambos os casos escreverá dez vezes “Ola Mundo” junto ao valor da variável que é
incrementada de 1 a 10. O uso destas estruturas pode ser perigoso. Se durante a execução do loop
não houver uma forma de que a condição possa ser verdadeira ou falsa, estaríamos diante de um
loop infinito e o programa entraria em situação de bloqueio.
Depuração na IDE do Gambas
Uma vez escrito parte do código de um programa, o usual é comprovar se funciona, mas o
habitual é que a maior parte das vezes não funciona na primeira tentativa. Tanto faz ser um
programador experiente ou não, as falhas fazem parte da rotina. Saber encontra-las e corrigi-las é
o que denomina-se depuração e é uma das tarefas mais importantes a realizar. Quando são falhas
de sintaxes, o ambiente de desenvolvimento só nos dá mensagens indicativas do problema,
parando a execução na linha em que ocorreu a falha.

Quando se adquire uma certa intimidade com a linguagem, as falhas de sintaxes são cada
vez menos, mas as falhas na lógica da execução do programa ocorreram sempre. Ainda mais
quando essa logica passa, por instruções de controle de fluxo como as que vimos neste capitulo, a
dificuldade de encontrar os erros é maior, já que a aplicação em várias ocasiões pela mesma parte
do código e é possível que a falha não ocorra na primeira vez que se execute o código.
Para facilitar esta tarefa, a IDE do
Gambas dispõe de varias ferramentas de
depuração. A primeira delas é a possibilidade
de fixar pontos de depuração. Quer dizer,
sinalar lugar no qual o programa parará para
permitir ver o estado das variáveis e em que
ponto da execução encontra-se.
Para fixar um ponto de interrupção em uma
linha de código, temos que colocarmos o
cursor do mouse nessa linha e pressionar a
tecla F9 ou o simbolo da mão levantada, que
está na parte superior da janela de código. As
linhas que se fixa um ponto de interrupção tem
 

o fundo roxo. 
A mesma operação que cria um ponto o elimina, ou
seja, pressionando F9 novamente o fundo roxo
desaparecerá. A execução do programa, como se explicou
no capitulo anterior, inicia-se pressionando o simbolo
pag 26 verde de Play na janela do projeto ( ou pressionando a tecla
F5). Junto ao botão verde encontra-se um botão de Pause,
que permite parar a execução, e outro de Stop que permite
detê-la a qualquer momento.
Se quisermos correr a aplicação executando uma a
uma das instruções para irmos observando por onde
transcorre o fluxo do programa, é só pressionar a tecla F8
ou qualquer um dos botões que se encontra a direita do


    simbolo Stop. Fazendo isto, o ambiente de
 
desenvolvimento saltara a primeira linha que deve
correr e irá executando linha a linha cada vez que pressionarmos F8, e o ícone que mostra a
flecha entrando entre as chaves, mencionado anteriormente. O botão que está justamente a direita
do Stop, que mostra uma flecha saltando por cima das chaves, parece produzir o mesmo efeito
(sua tecla de atalho é Shift + F8), mas não é assim: o comportamento muda quando o programa
chega a uma chamada, a uma subrotina ou função. Neste caso, o Ícone executará a chamada e
tudo o que a função ou subrotina tem que fazer, em um só passo, sem que vemos o fluxo do
programa pelo procedimento. Se tivéssemos pressionado F8 teríamos entrado na subrotina e visto
passo a passo como as instruções são executadas . Por tanto, com estes dois botões podemos
escolher como chegarmos a uma chamada ou procedimento, se quisermos também depurar esse
procedimento ou simplesmente executa-lo e passar a próxima linha de código.

Finalmente, quando pausamos a execução do programa, aparece três novas abas na parte
inferior da janela do projeto. E na aba local se ver todas as variáveis do procedimento que
estamos executando e o valor que tem nesse momento. Na aba pilha (Stack) vemos a lista de
chamadas entre procedimentos que serão produzido até chegar a esse ponto do programa. Assim
podemos saber através de que passos chegamos a
essa instrução. Finalmente, na aba Observar
(Watch) podemos introduzir qualquer expressão em
BASIC, incluindo operações com as variáveis que o
programa tem declaradas para ver qual é o resultado
e o valor que tenham no momento da pausa.
  
  

2.6 Entrada e saída de arquivos


Neste paragrafo veremos formas mais comuns de trabalhar com arquivos no Gambas.
Gambas trata os arquivos como um fluxo de dados (a palavra exata para isso é Stream), o que
tem uma aplicação muito comoda: todos os fluxos de dados são tratados da mesma maneira, pois
o código para manipular um arquivo é igual ao código para manipular uma conexão de rede, já
que todos são objetos do tipo Stream. A operação tradicional com um arquivo é abri-lo, cria-lo,
escrever e ler dados. Vejamos como se usa:
Arquivo = OPEN Nome_do_Arquivo FOR [ READ   INPUT ]

[ WRITE  OUTPUT ] [ CREAT   APPEND ] [ WATCH ]

Abrimos um arquivo com várias finalidades:

.READ ou INPUT: para ler dados, no primeiro caso não se usa buffer de dados, com
IMPUT se houver um buffer intermediário.

.WRITE ou OUTPUT: para escrever dados, com WRITE não há buffer de dados, com
OUTPUT se usa.

.CREATE: se o arquivo não existe se cria. Se não usarmos esta palavra, o arquivo deve
existir antes de abri-lo ou dará um erro.
.APPEND: os dados são adicionados ao final do texto.

.WATCH: se especificarmos essa palavra, Gambas lançará os eventos (que veremos mais
adiante) File_Read e File_Write neste caso podemos ler e escrever no arquivo.

Agora, fecharemos um arquivo que foi aberto com a sentença OPEN.

CLOSE [ # ] Arquivo

Escrevemos, convertendo em cadeia de texto, a Expressão no Arquivo aberto


anteriormente.

PRINT [ #Arquivo , ] Expressão ]

Se não especificarmos nenhum arquivo, digitamos a expressão no console, como temos


visto em vários exemplos ao longo deste capitulo. A instrução PRINT admite um certo controle
de como se colocam as expressões, dependendo de alguns símbolos de pontuação que podemos
colocar no final da sentença:

.Se não há nada depois da Expressão, adiciona-se uma nova linha ao final. Portanto, a
saída da seguinte instrução PRINT será em uma nova linha.

.Se há um ponto e virgula atrás da Expressão, a seguinte instrução PRINT será escrita
justo atrás da saída anterior, sem espaço, linhas ou tabulações intermediarias.

.Podemos utilizar uma virgula em lugar de um ponto e virgula, adiciona-se uma


tabulação, como também podemos concatenar expressões em uma mesma linha.

Seguidamente, escreveremos sem converter em cadeia de texto, a Expressão no Arquivo


aberto anteriormente. É uma instrução que costuma usar em lugar do PRINT quando os dados a
escrever não são cadeias de textos, como no caso de arquivos binários.

WRITE [ #Arquivo , ] Expressão [ , Local ]

Em qualquer caso, também pode ser usada com cadeias de texto e permitir indicar, com o
parâmetro Local, o números de caracteres que desejamos tirar em cada operação de escrita. Ao
contrario que com o PRINT, não podemos usar os sinais de pontuação para controlar a posição da
escrita.

INPUT [ #Arquivo , ] Variável1 [ , Variável2 ........ ]

Lemos, de um arquivo, um dado e atribuímos seu valor a Variável1, Variável2, etc. Os


dados devem está separados no arquivo por virgula e em linhas diferentes. Se não especificarmos
o Arquivo, lemos os dados do console, esperando que o usuário da aplicação o introduza....

READ [ #Arquivo , ] Variável [ , Local ]

Podemos dizer que é a instrução oposta a WRITE. Lemos do Arquivo dados binários e
atribuímos seu valor a Variável. Se essa é uma cadeia de texto, podemos fixar o local da
cadeia a ler.

LINE INPPUT [ Arquivo , ] Variável

Em uma linha de texto inteira do arquivo, que atribuímos a variável. Não devemos usar
para ler o fluxo binários.

Eof ( Arquivo )

Devolve True (verdadeiro) quando chega ao final do Arquivo e Falso (falso) em caso
contrário.

Lof ( Fluxo )

Se é Fluxo de dados em um arquivo, devolve seu tamanho. Se em lugar de um arquivo é


um Socket de uma conexão de rede ou um objeto Process, devolve o número de bytes que
podemos ler de uma só vez. Uma vez vista as sentenças mais comuns para manipular o fluxo de
dados, Vamos a alguns exemplos de uso:

' Ler dados de uma porta série:

' Requer selecionar o componente gb.net no projeto

DIM Arquivo AS File

Arquivo = OPEN "/dev/ttySO" FOR READ WRITE WATCH

.........................

'O evento File_Read é produzido quando há dados pra ler:

PUBLIC SUB File_Read()

DIM iByte AS Byte

READ #Arquivo, iByte


lkjkj
PRINT " Tenho um byte: " ; iByte

'Ler o conteudo do arquivo /etc/passwd e o mostra no console:

DIM Arquivo AS File

DIM Linha AS String

Arquivo = OPEN "/etc/passwd" FOR INPUT

WHILE NOT Eof (Arquivo)

LINE INPUT #Arquivo, Linha

PRINT Linha

WEND

CLOSE Arquivo
2.7 Controle de erros
É certo que, em algum momento, na maioria dos programas, seja culpa do usuário, seja
pelo próprio fluxo da execução, ocorrem erros, como tentar apagar um arquivo que não existe,
fazer uma divisão por zero, conectar a um servidor web que não responde, etc. Em todos estes
casos o Gambas mostra uma mensagem na tela e o programa é interrompido e deixa de
funcionar. É evidente que esse é um comportamento que o desenvolvedor não deseja e deve
tomar medidas aportunas para evita-lo. A forma de fazer é implementando um controle de erros
para que a aplicação saiba o que deve fazer nesses casos. Gambas implementa as instruções
necessárias para capturar os erros e processa-lo segundo o desejo do programador. As instruções
para isso são:

TRY Sentença: executa a sentença sem lançar o erro quando ocorre, o programa continua
pela sentença que está depois do TRY, tanto faz existir erro ou não. Só podemos saber se existe
erro consultando a sentença ERRO que será verdadeiro ou falso. Por exemplo:

' Apagar arquivo que não existe

TRY KILL "/tmp/teste/"

'Comprovar se tivemos exito

IF ERROR THEN PRINT "Não foi possível apagar o arquivo"

.FINALLY: É colocado no final de um procedimento. As instruções após essa sentença


serão sempre executadas, tanto faz se houve um erro no procedimento ou não.

CATCH: se colocada no final de um procedimento. As instruções após essa sentença São


executadas só se ocorrer um erro na execução do procedimento (incluindo os erros produzidos
em subrotinas ou funções chamadas a partir deste procedimento). Se existe uma instrução
FINALY, terá de ser colocada na frente de CATCH.

Vamos ver um exemplo de FINALY e CATCH:

' Mostra um arquivo no console

SUB PrintFile (Nome_Arquivo AS String)

DIM Arquivo AS File

DIM Linha AS String

OPEN Nome_Arquivo FOR READ AS #Arquivo

WHILE NOT EOF (Arquivo)

LINE INPUT #Arquivo, Linha

PRINT Linha

WEND
FINALLY 'Sempre vai ser executado, inclusive se houver erro

TRY CLOSE #Arquivo

CATCH ' Só executa se houver erro

PRINT "Impossível mostrar o arquivo" ; Nome_Arquivo

END

2.8 Programação orientada a objeto com Gambas


BASIC é uma linguagem de programação estruturada. Isto significa que os programas tem o
fluxo que temos visto até agora: Inicia em um ponto de uma subrotina e vai executando as
instruções de sima a baixo, com os saltos correspondentes as chamadas e procedimentos e varias
funções de controle de fluxo como loops, condições, etc.

Este tipo de programação permite resolver a maior parte dos problemas e está sendo usado há
muitos anos. Até os dias de hoje, seguem desenvolvendo aplicações com linguagens como BASIC
ou C, de programação estruturada. Desde os anos 70 se vem trabalhando também em outras
paradigma de programação: a programação orientada a objetos. As linguagem que adotam este
paradigma, como Smalltalk, Java o C++, tentam modelar a realidade. A execução destes programas
se baseia na interação dos vários objetos que definem o problema, tal e como ocorre na vida real,
como os objetos, pessoas e animais nos movemos, enviamos mensagens uns aos outros e
executamos ações. A programação orientada a objeto é usada a cada dia com mais frequência porque
permite uma melhor divisão das tarefas que um programa deve fazer, facilita a depuração e a
colaboração entre vários programadores e nos projetos que são de grande tamanho e, em muitos
aspectos, tem um potencial muito maior que a programação estruturada.

Por todas estas razões, é comum se adicionar características de programação orientada a


objetos a linguagens que, como o BASIC, em principio não a tenham. Gambas permite fazer
programas estruturados a moda antiga, escrevendo o código em Módulo, como já vimos antes e
também permite a programação orientada a objeto mediante o uso de Classes. E mais, a maior parte
dos componentes que são adicionados a linguagem, tem mais alternativas que o uso de objetos.

Podemos ver como funciona tudo isso no Gambas pensando no caso de que tenhamos que
escrever um programa que simula o comportamento de um carro. Usando a programação orientada a
objetos, definiremos cada uma das partes do carro mediante um arquivo de classes onde
escreveremos o código BASIC necessário para definir as características e a interface com que se
comunica com o mundo. Por exemplo, definiremos como é um volante, como é uma roda, como é
um assento, um acelerador, um motor, etc. Depois, baseado nessas informações, criaríamos cada um
dos objetos para criar o carro: um volante, quatro rodas, vários assentos, um motor, um acelerador,
etc. Cada um desses objetos responderia a certas mensagens, por exemplo, o volante responderia a
um giro atuando sobre o eixo das rodas, o motor responderia incrementando suas rotações se
recebesse uma pressão do acelerador, etc.

Cada vez que criamos um objeto baseando-nos em arquivo de classe que foi definido,
dizemos que o objeto foi instanciado. Podemos estanciar tantos objetos quanto desejar a partir de
uma classe e uma vez criados tem vida própria, são independentes uns dos outros com suas próprias
variáveis respondendo as várias ações segundo tenham sido definidos no classe.
Outra das características da programação orientada a objetos é a Herança. Quando um
objeto herda de outro objeto significa que é do mesmo tipo, mas que pode ter características
acrescentadas. Por exemplo, suponhamos que definimos a classe quadro_de texto com certas
características como tamanho de texto, alinhamento, etc. A seguir, podemos criar objeto dessa
classe e são quadro_de_texto. Com o Gambas podemos, alem do mas, criar uma nova classe, por
exemplo: quadro_de_texto_multilinha que herda do quadro_de_texto. Isso significa que um
quadro_de_texto_multilinha é um quadro_de_texto só que são adicionados mais coisas. Todo o
comportamento e propriedades do quadro_de_texto já estão codificado e não há necessidade de
faze-lo novamente.

Vamos a um exemplo simples que esclarece alguns conceitos, para isso vamos criar no
ambiente de desenvolvimento um novo projeto de programa de texto. Para começar, vamos
adicionar um Módulo com um nome qualquer e dois arquivo de classe, a um nós chamamos
SerVivo e ao outro Homem.

Este é o código que devemos escrever no arquivo de classe SerVivo.cls

' Gambas class file

PRIVATE Patas AS Integer

PRIVATE Nascimento AS Integer

PUBLIC SUB nascido(Data AS Date)

Nascimento = Year(Data)

END

PUBLIC SUB PPatas(Numero AS Integer)

Patas = Numero

END

PUBLIC FUNCTION Idade() AS Integer

RETURN Year(Now) - Nascimento

END

PUBLIC FUNCTION DPatas() AS Integer

RETURN Patas

END
Este é o código para escrever no arquivo de classes Homem.cls:
' Gambas class file

INHERITS SerVivo

PRIVATE Nome AS String

PRIVATE Apelido AS String

PUBLIC SUB PNome(Cadeia AS String)

Nome = Cadeia

END

PUBLIC SUB PApelido(Cadeia AS String)

Apelido = Cadeia

END

PUBLIC FUNCTION NomeCompleto() AS String

RETURN Nome & " " & Apelido

END

E este é o código para escrever no Módulo que havíamos criado:

' Gambas module file

PUBLIC SUB Main()

DIM Macaco AS SerVivo

DIM Sujeito AS Homem

Macaco = NEW SerVivo

Macaco.nascido(CDate("2/2/1992"))

Macaco.PPatas(3)

PRINT Macaco.Idade()

PRINT Macaco.DPatas()

Sujeito = NEW Homem

Sujeito.nascido(CDate("2/18/1969"))

Sujeito.PNome("Vicente")

Sujeito.PApelido("Pires")

PRINT Sujeito.Idade()

PRINT Sujeito.NomeCompleto()

END
Vejamos os três arquivos, inclusive o de classe SerVivo.cls tem um código muito simples:
declara um par de varáveis, um par de subrotinas com as quais podemos atribuir um valor a
essas variáveis e duas funções as quais devolvem valores de alguns cálculos realizado entre as
variáveis e a data atual.

O arquivo Homem.cls é muito similar, mas com uma sentença nova: no inicio do arquivo
foi declarado a instrução INHERITS SerVivo. Ao fazermos isso estamos dizendo que todos os
objetos que se estanciam na classe Homem será objeto SerVivo e, por tanto, terão também as
mesmas funções que um SerVivo e poderão realizar as mesmas operações.

Se for necessário alguns valores de inicialização dos objetos a ser criado, basta
adicionarmos ao arquivo de classe uma subrotina com a sintaxes: PUBLIC SUB New(). Todo o
código que se crie aqui será executado cada vez que se criar um objeto.

E finalmente, o módulo e seu procedimento Main. Ai declaramos duas variáveis:


Macaco e Sujeito, porem em lugar de declararmos como um dos tipos de dados que vimos nos
primeiros parágrafos deste capitulo, a declaramos como objeto da classe SerVivo e Homem.
Portanto falando como propriedade, Macaco e Sujeito não são variáveis, e sim objetos. As
classes correspondem aos nomes dos dois arquivos de classe que definimos anteriormente. Alem
disso, vimos que para criar um objeto de uma classe é necessário usar sempre a palavra NEW, o
que aconteceria mesmo que executando o código da subrotina New do arquivo de classe, se esta
subrotina existisse. Uma vez que o objeto tenha sido criado, podemos ter acesso as suas
subrotinas e funções escrevendo o nome do objeto e o procedimento separados por um ponto. Só
podemos acessar os procedimentos que tenham sido declarados no arquivo de classe como
PUBLIC.

Poupamos códigos obviamente para a classe Homem Afinal não tivemos que escrever as
funções que faz um SerVivo, há algumas consequências mais importantes que deduzimos deste
exemplo. Tal e como está escrito, poderíamos trocar o código da classe SerVivo, modificando e
trocando as variáveis, e o programa seguiria funcionando igual, sem ter que tocar em absoluto a
classe Homem.

Quer dizer, podíamos, por exemplo, melhorar o método para calcular a idade, levando em
conta o dia do nascimento dentro do ano. Também podíamos trocar o nome das variáveis e não
afetar a classe Homem ou o resto do programa. Outras das possibilidade que existe é o uso dos
arquivos de classes tal e como estão em outro projeto onde o modo seres vivos é necessário,
reutilizando de formas simples o código já escrito.

Já foram elaborado multidão de livros e artigos sobre a programação orientada a objetos.


A pretensão deste parágrafo é unicamente servir de mera introdução geral e explicar as sintaxes
necessária para seu uso com o Gambas. A melhor forma de aprender, e desfrutar dela e obter
todo o seu potencial é provando e vendo exemplos que á usem extensivamente.

O código fonte do ambiente de desenvolvimento Gambas faz um uso muito amplo


de classes e objetos, o que o torna uma fonte completa de exemplos. Está disponível dentro do
diretório apps/src/gambas2 do arquivo compactadoque contem os fontes do Gambas.
2.9 Propriedades, Métodos e Eventos

Os componentes que estendem as possibilidades do Gambas são de diversos tipos, mas


todos eles contem classes que definem diversos objetos. No caso dos componentes que servem
para criar interfaces gráficas, estes objetos incorporam um ícone à caixa de ferramentas do
ambiente de desenvolvimento. Não precisamos criar estes tipos de objetos escrevendo o código
correspondente e a palavra NEW, sendo só desenha-los sobre o formulário depois de ter
pressionado o botão com o ícone respectivo. E o ambiente de desenvolvimento é que se
encarrega de escrever o código necessário para cria-los.

Qualquer um dos objetos que é criado à partir dos componentes já tem um


comportamento definido em sua classe. Este comportamento incluem a interface com a qual os
objetos se comunicarão uns com os outros no programa, o necessário para que o programador
possa defini-los e faze-los atuar a sua conveniência. Para facilitar essas tarefas, estes objetos
dispõe de Propriedades, Métodos e Eventos. As Propriedades permitem trocar parâmetros do
objeto. Na realidade, ao darmos um valor a uma propriedade estamos designando valores a
algumas variáveis do objeto que este interpreta internamente para produzir um efeito. Por
exemplo, podemos designar a um formulário um valor numérico a propriedade Background, e
com isso trocaríamos sua cor de fundo. Para modificar as propriedades dispomos da janelas de
Propriedades na IDE (visível ao pressionar F4 ou a partir do menu da janela de projeto: Exibir
(View) | Propriedades), que nos permite faze-lo com uma interface gráfica muito simples.
Também podemos trocar uma propriedade mediante código fazendo referência ao nome do
objeto e a propriedade, separados por um ponto. Por exemplo: Form1.Backgroun = 0, deixaria o
fundo do formulário Form1 em cor preta.

Os métodos são as funções que o objeto pode realizar. Só podem ser atribuídas mediante
código e cada objeto tem sua própria coleção de métodos. Por exemplo: btnSair.Hide faz que o
botão btnSair oculte-se. Finalmente, os eventos são subrotinas que executam-se para indicar algo
que ocorreu ao objeto. Um exemplo é o que vimos no capitulo anterior no evento Click do botão
que executa-se quando damos um click com o mouse. Os eventos são, na aplicação gráficas, os
que marcam o fluxo do programa. Por isso, com frequência se diz que Gambas é uma linguagem
orientada a eventos. É o usuário da aplicação ao interagir, que obriga ao programa a executar
código respondendo as suas ações. Os objetos em Gambas avisam com os eventos cada vez que
produzimos uma ação sobre eles. É o programador o encarregado de escrever nas subrotinas que
tratam os eventos o código necessário para responder a eles.

O ambiente de desenvolvimento proporciona várias formas de conhecer os métodos,


eventos e propriedades que um objeto pode entender. A primeira e mais obvia é usando a ajuda,
onde podemos encontrar a lista de todas as classes de objetos disponíveis, com todas as
explicações e a lista completa de todas as propriedades. O editor de código também fornece
informações, já que ao escrever o nome de um objeto e ao digitar um ponto, aparece um menu
contextual com a lista das propriedades (em cor violeta) e métodos (em cor verde) que o objeto
tem disponíveis.
  


      
      
     
      

         

       

 
     
 

   
 !    
  

 ∀
  ! 
!
      
       
 

  !

NOTAS
1 Os computadores só entendem de números, não de letras, para podermos usar caracteres se
aplicam tabelas de conversão que atribui um numero o código a cada caractere. A tabela de
conversão usada normalmente se chama ASCII.

Em http://http//es.wikipedia.org/wiki/Ascii podemos ver a tabela completa.


Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 3: A INTERFACE GRÁFICA ............................................................... 04


3.1 Concepção .................................................................................. 04
Iniciando no console ........................................................................ 06
Ambiente de desenvolvimento ......................................................... 07
3.2 Manipulação básica dos controles ................................................................ 09
Posição e tamanho ............................................................................ 09
isibilidade ....................................................................................... 10
extos relacionados .......................................................................... 10
Cores ................................................................................................ 11
Mouse .............................................................................................. 12
Teclado ............................................................................................ 14
3.3 Galeria de controles ................................................................................ 15
Controles básicos ............................................................................ 15
Outros controles básicos miscelaneos ............................................ 17
Lista de dados ................................................................................. 18
Outros controles avançados ............................................................ 18
3.4 Diálogos ................................................................................................ 18
Classes Message ............................................................................ 18
Diálogos personalizados ................................................................ 20
3.5 Menus .................................................................................................... 23
3.6 Alinhamento dos controles .................................................................... 25
Propriedade de alinhamento ........................................................... 25
Controle com alinhamento predefinido .......................................... 28
Desenho de uma aplicação que aproveita este recurso ................... 28
3.7 Introdução ao desenho de primitivas ..................................................... 30
3.1 Conceito
O Gambas, como qualquer outra linguagem de programação bem desenhado, pode
trabalhar perfeitamente independente de qualquer biblioteca gráfica, criando programa de
console, um de seus ponto forte é a simplicidade para criar interface gráfica de usuário.

No mundo GNU/Linux existiu, ao longo da história, diversas bibliotecas gráficas que


facilitam a criação dessas interfaces. Ao nível mais baixo, no tradicional sistema X- Window não
só proporciona uma API para mostrar janelas, desenhar linhas, copiar mapas de bits e um pouco
mais. Sobre esse sistema, uma das primeiras bibliotecas que proporcionava controles completos,
telas com botões ou etiquetas, foi a MotifTM, tradicional do sistema UNIXTM. Seu clone livre,
Lesstif, o problema foi que chegou tarde demais no cenário para ter um papel relevante.

As interfaces criadas com essas duas bibliotecas sempre tiveram a fama, de ser
demasiadamente, feias e incômodas, especialmente aos olhos dos usuários do WindowsTM, e não
há como esquecer que deste sistema proprietário provinha boa parte dos usuários do desktop
GNU/Linux.
A companhia TrollTechTM, por sua parte, criou as bibliotecas QT, para o desenvolvimento
de aplicações gráficas com C++. Estas bibliotecas, em principio, eram distribuídas sob licença,
QPL, não totalmente compatível com o projeto da Free Software Fundadtion. Sua inclusão com
base no projeto do desktop KDE gerou uma grande agitação, e a rejeição de um setor da
comunidade, fazia ambos projeto (QT e KDE). Na atualidade, as bibliotecas QT em sua edição
não comercial, eram distribuídas sob licença GPL, o qual implica que os programas
desenvolvidos e compilados com o QT como base, tinham também que ser software livre
compatível com a GPL. Um programa que não cumprisse com esta norma, teria que ser
compilado com a versão comercial do QT, que a companhia antes citada vendia e dava suporte.
Em parte como rejeição a colaboração QT/KDE, e em parte para criar uma alternativa ao
popular desktop KDE, surgiu o projeto GNOME, que está baseado em bibliotecas GTK+.
GTK+ foi desenvolvido a principio com uma biblioteca gráfica escrita unicamente para o
popular programa de desenho The Gimp (GTK significa Gimp Tool Kit). No entanto mais tarde
este projeto dividiu-se para converter-se em uma biblioteca de propósito geral, especialmente
desenhada para desenvolvimento em linguagem C. Hoje em dia todo o projeto GTK+ está
dividido em vários blocos e níveis: Glib, utilidades de caráter geral sem relação com a interface
gráfica: Gobject para dotar, de certa orientação a objetos em linguagem C; Atk, que é um kit de
acessibilidade; Pango, para a gestão de fontes; Gdk, para o desenho de baixo nível; e,
finalmente, GTK, que proporciona os elementos da interface gráfica habitual. A licença do
GTK+ é LGPL, por isso tem sido utilizado em muitos dos projetos de software livre, em
programas gráficos privados que não desejam contar com suporte da versão comercial do QT
visto que é uma alternativa mais cômoda em seus desenvolvimentos, ou quando se decide reduzir
os custos por não ter que pagar pela biblioteca gráfica.
A marge destes pesos pesados, há nomes como FOX, FLTK ou WxWdgats, que também
conta como alternativas para o desenvolvimento de programas gráficos.
Por tanto, há muitas alternativas (toolkits) para desenvolver interfaces, e ao menos duas
delas (QT e GTK), servem como base para os dois desktops mais comuns (KDE e GNOME),
que por sua vez possuem um aspecto e funcionalidades diferentes. No entanto uma aplicação
KDE pode funcionar em um ambiente GNOME e vice versa, a custa, talvez perder
homogeneidade no ambiente.

Gambas esta decidido a ser neutro a respeito. Possue uma interface de alto nível, simples
para o desenho e programação habituais. Que não está ligado aos conceitos de QT, GTK+, nem a
nenhuma outra biblioteca gráfica subjacente.
No entanto na hora de implementar a interface é necessário empregar alguma biblioteca
de apoio por baixo, escritas em C ou C++. por tanto, existe dois componentes gráficos que
proporcionam ao programador a liberdade de selecionar: gb.qt e gb.gtk. Como podemos supor
por seus nomes, o primeiro usa código compilado com QT e o segundo código compilado com
GTK+.
A particularidade do gambas é que o código escrito para gb.qt funciona exatamente igual
se substituirmos este componente por gb.gtk e vice versa. Por tanto o programador em cada
momento pode escolher o que mais se adapte a suas necessidades por diversos motivos, por
exemplo:

* Integração com KDE, GNOME, O XFCE (este ultimo é um desktop rápido baseado em
GTK+).

* Aspecto final. Alguns programadores e usuários gostam mais de uma aplicação QT,
outros com GTK+.

* Questões de rendimento, uso de recursos.

* Licença, custos em software comercial.

* Necessidades especiais. GTK+ pode ser compilado sobre DirectFB, um sistema gráfico
alternativo ao X-Window, rápido e apto para sistemas embutidos. Talvez gb.qt possa
ser compilado no futuro sobre Qtopia, a biblioteca da TrollTechTM geralmente empregadas
em PDAs.

Iniciando no console
Vamos começar pelo caminho mais difícil para compreender que a interface gráfica não é
mais que outro acessório do Gambas, que é igual ao resto dos componentes, as classes a partir
dos quais criaremos objetos, neste caso Controles o Widgets.

Criaremos um projeto de console (sim, de console), chamado de Janela. Adicionaremos


um módulo modMain e uma referencia ao componente gb.qt.

 
    
Escrevemos agora o seguinte código:
PUBLIC SUB Main()

DIM hWin AS Window

hWin = NEW Window

hWin.Show()

END

Ao executa-lo, aparecerá uma janela solitária na tela, que desaparecerá quando


pressionarmos o botão Fechar do gestor de janelas.

As janelas são contêiner de primeiro nível. Um contêiner é um controle que permite


colocar outros em seu interior (botões, caixa de texto e etc.). O adjetivo de primeiro nível se
refere a quem não tem um pai, quer dizer, que não é submetido a outro controle de nossa
aplicação, senão diretamente ao desktop.

Neste programa temos vários efeitos, a primeira vista, estranhos. O primeiro é que
declaramos hWin como uma variável local, assim, parece que ao finalizar a função Main o
objeto deveria destruir-se. E não é assim já que a janela, por ser um controle, mantem uma
referência interna (os objetos Gambas só destruirão-se caso não exista uma referência em todo o
programa). Esta referência podemos dizer que corresponde a janela que está desenhada no
servidor gráfico, com a intermediação da biblioteca gráfica (neste caso QT). Por outro lado, o
programa deveria ter finalizado ao terminar a função Main() e não foi assim.

O segundo efeito se deve a que tanto o componente gb.qt como o componente gb.gtk são
chamados automaticamente através do método Main() do programa e fica em laço principal da
biblioteca gráfica, esperando que se produzam eventos gráficos (por exemplo, um click do mouse
sobre um controle).

Na prática, para criar um programa gráfico indicaremos diretamente no assistente que


desejamos criar um projeto gráfico, de forma que o ambiente de desenvolvimento inclui por
padrão, o componente gb.qt, e nos permite criar um formulário de inicio. Um formulário de
inicio é uma classe de inicio que herda as características da classe Form, que não necessita de um
método Main no programa porque o interpretador chama diretamente o formulário para mostra-
lo, e entra no laço de gestão de eventos.

O ambiente de desenvolvimento
Para criar um novo formulário nos localizamos na janela principal da IDE e, damos um
click com o botão direito do mouse, Escolheremos Novo | Formulário.
         

                 


    !                 
      ∀         ∀ # 
!      !   ∃       ∃ 
  %&    ∋#     
!                 
∀ (           

     


    

        ∗


 ∀       
       ∀∋  
 &     
  +     , 
    


)      
3.2 Manipulação básica dos controles
Apesar de que cada controle foi desenhado para cumprir uma função específica,
compartilha boa parte de sua interface de programação, de modo que aprender a manipular um
novo controle seria fácil para um programador que já trabalhou com outros controles.

Os controles herdam métodos, propriedade e eventos da classe Control, e todas as


características herdadas são aplicadas a todos eles.

Enquanto os contêiner, procede da classe Container (que por sua vez procede de Control)
e igualmente tem muitas características comuns.

Posição e tamanho
Todos os controles dispõe de uma série de propriedades que permitem modificar sua
posição e tamanho dentro de seu contêiner, o desktop no caso das janelas:
* Propriedades x e y: são de altura e
largura, determinam a posição do controle, ou
seja, seu ponto superior esquerdo. Aos
controles comuns, a posição indicada é relativa
a seu contêiner, no caso das janelas é relativo
ao canto superior esquerdo do desktop. Os
controles dispõe de outras propriedades, Left e
Top, que são sinônimos de X e Y,
respectivamente. Usar uma ou outra fica à
    −.    cargo do programador.
*Propriedade W e H: são de altura e largura, e determina a altura e largura do controle,
respectivamente. Dispõe de duas propriedades sinônimos Width e Height com o mesmo
significado.

Propriedade ScreenX e ScreenY: são de apenas leitura, e permite conhecer a posição de


qualquer controle relativo ao desktop, em lugar de seu contêiner pai.

Os contêiner dispõe, de outras propriedades ClientX, ClientY, ClienteWidth e


ClienteHeight, que determinam, respectivamente, o início e a dimensão da área útil para conter
os controles filhos. Por exemplo, o controle TabStrip, que dispõe de umas abas na parte superior,
dispõe suas filhas por baixo delas; e ScrollView, que pode mostrar barras de Scroll, tem suas
áreas reduzidas pelas barras.

Existem também uma serie de métodos para modificar os controles:

*Método Resize(W,H): como podemos trocar o tamanho de um controle modificando


sua altura e largura de uma só vez, em lugar de faze-lo em dois passos
modificando as Propriedades W e H, o que melhora o efeito gráfico do redimensionamento ante
ao usuário.

*Método Move (X, Y): move de uma só vez o controle a posição indicada, em lugar de
faze-lo em dois passos. Também dispõe de dois parâmetros adicionais, Move(X,Y,W,H),
com os quais alem de mover o controle ainda podemos redimensionar todos eles em um
só passo, gerando uma transição mais suave ante ao usuário, que se modificarmos as
propriedades uma por uma.

*Métodos MoveRelative e ResizeRelative: são similares ao Move e Resize,


respectivamente, mas neste caso as unidades não são pixels, e sim unidades relativas ao
tamanho da fonte por defoult do desktop. Com essa capacidade, o aspecto do formulário
será similar para usuários que tenham diferentes configurações de fontes (por exemplo,
grandes em um desktop de 1024x768 pequena em um desktop de 800x600.

As janelas (controles Window e Form) dispõe, de vários eventos relacionados com a


posição e tamanho. O evento Resize é gerado cada vez que o usuário redimensiona uma janela, e
Move quando se move.

Visibilidade
Todo controle pode encontrar-se em um destes dois estados em um dado momento:
Visible, quando aparece ante ao usuário; e Invisible, que mantem todas as suas propriedades, mas
não aparece para o usuário.

A propriedade Visible serve para conhecer o estado de visibilidade a qualquer momento.

Por exemplo, os métodos Show() e Hide() mostram e ocultam respectivamente o controle.

Com respeito as janelas, dispõem de dois eventos, Show e Hide, que disparam quando
se mostram ou deixa de estar visível.

Alem do mais, a abertura pela primeira vez de um formulário ou janela gera o evento
Open.
  
      ∀!    
   ∃    ∋#         /     #       
∃   ∋#      0    ∀  1 ∀  
             ∃  

Textos relacionados
Todos os controles podem ter diversas cadeias de texto, que aparecem de uma ou outra
forma.

Para todos eles, a propriedade ToolTip se encarrega de manter um texto, que aparecerá
como uma pequena janela flutuante, quando o usuário situa o mouse sobre a área ocupada pelo
controle.
Desta forma, podemos dar pequenas informações de ajuda para o usuário conhecer a
função do controle dentro do programa.
Muitos controles dispõe de um texto que mostra em seu espaço principal, como é o caso
dos label, caixas de textos ou os botões. Para todos estes controles, a propriedade Text é o que
determina o texto a mostrar.
Como o Gambas trabalha com codificação UTF-8 para a interface gráfica, pode ser que
seja necessário utilizar a função Conv$ para converter de outras codificações, na hora de
representar texto procedente, por exemplo, de uma base de dados ou de um processo em
execução em segundo plano.
Estes controles, por comodidade para programadores de outros ambiente, tem um
sinonimo para esta propriedade, denominado Caption,salvo em caso de janelas nas quais o
sinonimo é Title.

Cores
Para cada controle definimos duas cores: ForeGround, qu é a cor do primeiro plano em
que normalmente mostrará o texto do controle ou parte de suas linhas e desenhos; Bckground que
é a cor do fundo.
Por herança de outras linguagem de programação, dispomos de dois sinônimos BckColor
e ForeColor.
As cores no Gambas são valores numéricos inteiros, desde 0 até o hexadecimal FFFFFF,
de forma que cada componente da cor vem determinado por seus componentes de vermelho,
verde e azul.
A intensidade das cores primarias variam entre 0 (mínimo) e o 255 (máximo).
A gestão das cores é realizada no Gambas através da classe Color, que dispõe de vários
métodos estáticos, assim como de uma series de constantes.
As constantes da classe Color determinam uma serie de cores básicas em sua codificação
numérica:
O Método RGB recebe três parâmetro, os componentes de vermelho, verde e azul, nesta
ordem, com valores entre 0 e 255 para cada um, e devolve um número representando a cor.
O método HSV recebe três parâmetros, aos componentes de tonalidades (0-360),
saturação (0-255) e brilho (0-255), nesta ordem, e o devolvem traduzido em uma cor com sua
codificação numérica habitual.
Existem também umas propriedades que determinam as cores do sistema.
Por exemplo, dependendo do tema usado pelo usuário, os quadros de texto podem
aparecer de cor branca com letras pretas, e os formulários de cores cinza.
Estas propriedades permitem conhecermos as cores atuais para os elementos da interface
gráfica:

       2 ∗ 


   ∀ (    2 2 ∗ 
     ∀ (    2   
         32 ∗ 
         3  
          2 ∗ 
          

É pouco recomendado trocar as cores da interface por capricho ou questões de estética


particulares do programador. Cada usuário escolhe o tema que mais se adapta a seu gosto ou
necessidades visuais, e pode sentir-se incômodo se o fizermos usar outras cores. Por outro lado,
alguns temas podem interferir com suas cores coma que definimos em nossa aplicação,
resultando uma combinação difícil de ver, é incômodo ou desagradável. Só se troca as cores dos
controles quando é extremamente necessário por algum motivo de desenho (por exemplo, resaltar
de forma clara um texto ou uma etiqueta).

Mouse
O mouse é a interface por excelência de qualquer desktop atual, Vamos distinguir aqui
dois parágrafo.
Em primeiro lugar encontra-se a representação deste no desktop, o ponteiro, que
habitualmente aparece com uma forma de flecha, branca ou preta. A respeito, cada controle
dispõe de uma propriedade Mouse, a qual pode tomar como valores as constantes da classe
Mouse para trocar seu aspecto.
Se consideram constantes para que o mouse adote diversos aspectos, como pode ser um
relógio (espera), um cursor de texto, flechas em diversas ocasiões, etc.

As constantes da classe Mouse são as mesmas para o gb.qt e gb.gtk, não obstante, seus
valores numéricos são distintos. Por tanto um código bem escrito e escalável, não deve usar
valores numéricos para indicar um tipo de ponteiro, se não as constantes desta classe.
Cada controle dispõe, de mais, de uma propriedade Cursor, que aceita uma imagem e
permite desenhar um ponteiro totalmente personalizado (a partir de um arquivo .png pu .xpm,
por exemplo) para cada controle dependendo do servidor gráfico utilizado no sistema, é possível
que o cursor tenham também várias cores, não só em preto e branco como os tradicionais.

A classe estática Aplicação, possui uma propriedade Busy, que é um número inteiro. Se
seu valor for maior que zero, todos os controles e janelas da aplicação mostrarão o ponteiro como
um relógio (indicando ao usuário que tem que esperar), independentemente do cursor empregado
para cada controle. Se o valor passa a zero, retornam-se aos controles habituais.

Por outro lado, cada controle recebe evento do mouse, que podemos manipula-lo a partir
do programa. Os eventos MouseDown, MouseUp, MouseWheel, determinam, respectivamente, se
o usuário pressionou um botão, levantou, moveu a roda do mouse ou moveu o mouse de posição.

Dentro destes eventos, somente dentro deles, podemos empregar a classe Mouse, para
determinar que botão foi pressionado (esquerdo, direito ou central) mediante as propriedades
Mouse.Left, Mouse.Right ou Mouse.Middle, que tornam o valor true se o botão correspondente
foi pressionado ou levantado; a posição do mouse dentro do controle (Mouse.X e Mouse.Y ); a
posição do mouse em relação ao desktop (Mouse.ScreenX e Mouse.ScreenY); assim como a
movimentação sobre a roda do mouse (Mouse.Delta e Mouse.Orientation).

Na maior parte de controles, o evento MouseMove, é produzido só se ao menos um botão


do mouse for pressionado pelo usuário.

Este pequeno exemplo permite mover um botão de posição dentro de um formulário,


quando o usuário o arrasta enquanto mantem pressionado o botão esquerdo.

Criaremos um fomulário Form1 com um botão Button1, que inclua este código:

' Gambas class file

PRIVATE pX AS Integer

PRIVATE py AS Integer

PUBLIC SUB Button1_MouseDown()

IF Mouse.Left THEN

'Armazenamos posições iniciais

pX = Mouse.X

pX = Mouse.Y

END IF

END
PUBLIC SUB Button1_MouseMove()

IF Mouse.Left THEN

'********************************************************

'deslocamos o botão de acordo com a variação de X e Y

'********************************************************

Button1.Move(Button1.X - px + Mouse.X, Button1.Y - pY + Mouse.Y)

END IF

END

 ∋     ∀         ∀  
 4    ∀ #  ∋#  5 ∀   1      ∋# 
      
!    

O evento MouseDown é cancelável, o que significa que empregamos a instrução STOP


EVENT dentro de seu código, evitando que se propague e, por tanto, que o controle atue em
consequência, segundo seu funcionamento interno habitual (por exemplo, um botão não lançaria
o evento Click se cancelarmos o MouseDown).

Outros eventos comuns aos controles são DblClick (duplo click do mouse), Menu
(pressionar o botão direito) o Click (pressionar o botão esquerdo). Alguns controles podem não
dispor do evento Click.

Teclado
De forma similar ao mouse, o teclado controla-se com os eventos KeyPress e KeyRelease.
Estes não tem parâmetros.

A classe estática Key proporciona a informação necessária para controlar o teclado dentro
destes eventos, do mesmo modo que a classe Mouse dentro dos eventos do mouse.
O evento KeyPress é cancelável com a instrução STOP EVENT, de modo que podemos impedir,
por exemplo, que em uma caixa de texto imprimam determinados caracteres.
O seguinte exemplo, bloqueia um TextBox, de forma que só permita a entrada de
números, e pressionar as teclas Del (apagar) e BackSpace (apagar atrás) e o tabulador para passar
o foco a outro controle.
Para chamar o STOP EVENT quando o código da tecla pressionada não for nenhuma
-

das desejadas.
' Gambas class file
PUBLIC SUB TextBox1_KeyPress()

SELECT CASE Key.Code

CASE 48 TO 57 'Códigos ASCII dos números

CASE 65456 TO 65465 'teclado numérico

CASE Key.BackSpace 'voltar

CASE Key.Delete 'Apagar

CASE key.Tab 'Tabulador

CASE ELSE

STOP EVENT

END SELECT

PRINT key.Code 'mostra o código da tecla pressionada

END

3.3 Galeria de controles


Controles básicos

Tanto gb.qt como gb.gtk possuem uma serie de


controles básicos para desenvolver uma interface
gráfica. Em seguida detalharemos estes controles e suas
características principais.
*Label: é uma etiqueta simples que contem uma
linha de texto não muito longa. Sua única função é
mostrar um texto em uma posição dentro de um
formulário. A propriedade text é a que determina o
texto a mostrar a cada momento. Podemos modificar
tanto sua cor de fundo (Background), como a cor de
primeiro plano (ForeGround). Como os outros
controles, responde aos eventos do mouse, mas em um
dado momento, aliado a MousePress e
      
MouseRelease,
pode servir para implementar um botão personalizado.
*TextLabel: é muito similar ao Label, mas tem a particularidade de mostrar o texto formatado
em HTML. Desta forma, indicando uma cadeia com tags HTML na propriedade Text, poderemos ter
texto que combine negrito, itálica, subscrito e outras características de texto enriquecido.

TextLabel1.text = "<b> Texto com HTML</b><br>Dentro de uma <i>etiqueta."

%       1           607    (          
  #    ∀          !  8              
 ∗  #    ∀    0        ∃       ∀ ∃           
         

*TextBox: uma caixa de texto de uma só linha, na qual o usuário pode modificar, copiar,
cortar ou apagar o texto. O texto introduzido recebe modificação por código mediante a
propriedade Text. Alem do mais o método Select permite selecionar e ressaltar por código uma
parte do texto, e Selection dota de algumas propriedades para conhecer o texto que o usuário
selecionou.

5 ∀    ∋# 89:     1!    
;  <∀=
  ∃     ∃   ∃     
       )          1     
!       ∃  
    

*TextArea: trata-se de uma caixa de texto que é capaz de conter múltiplas linhas. E
permite também o retorno do carro. Como no caso do TextBox, o método Selection e a
propriedade Select determina o texto selecionado. Alem disso este controle dispõe de dois
métodos Undo e Redo que equivale ao comandos Fazer e Desfazer de qualquer editor de textos.
Quer dizer, eliminam as ultimas alterações do usuário ou o volta a situação anterior no texto.
*Botões: Gambas tem três tipos de botões. O primeiro, Button, é um botão normal,
dispõe de uma propriedade Text que indica o texto a mostrar, assim como uma propriedade
Picture para mostrar um ícone identificador. Este controle dispõe do evento Click que dispara
quando pressionamos o botão esquerdo do mouse (o direito se estiver configurado para
canhotos). Outro tipo de botão é o ToggleButton, que mantem o estado anterior quando
pressionado,quer dizer, uma vez fica no estado pressionado, e ao pressionar outra vez, volta ao
estado anterior. A propriedade Value serve para conhecer ou variar o estado: FALSE significa
'não pressionado' E TRUE 'pressionado'. O botão ToolButton, é similar mas só mostra um
pequeno ícone sem texto. Está desenhado para inserir em barra de ferramentas, habituais na parte
superior das interfaces, como acesso rápido a certas funções comuns. Pode atuar como um botão
normal, se sua propriedade Toggle estiver FALSE, ou como um interruptor (como um
ToggleButton) se Toggle for TRUE . mesmo assim dispõe de uma propriedade Border que se for
FALSE dará aparência plana ao botão e se for TRUE o mostrará como relevo, como um botão
normal.
%∀ (         >?) 5≅%0)      
     # Α            
!                    !   Β  
 ∀        ∋#
*CheckBox: mostra um texto determinado pela propriedade Text, junto com uma caixa
onde o usuário pode clicar para marcar ou desmarcar a opção.
A propriedade Value indica se o usuário marcou o CheckBox, o evento Change informa a
cada troca. Empregamos ele para opções de configuração que só dispõe de dois possível valores:
'Ativado ou Desativado', 'Sim ou Não', 'Verdadeiro ou Falso'.

*RadioButton: é similar ao CheckBox, mas tem uma particularidade de que todos os


RadioButton existentes dentro de um mesmo contêiner, estão internamente agrupados, e em cada
momento só pode haver um ativo. Quando o usuário ativa um deles, o resto se desativa, por isso
que o empregamos para selecionar uma opção que exclua as outras dentro de um menu de
opções.

*PictureBox: este controle tem a função de mostrar uma imagem. Responde ao evento do
mouse, podemos emprega-lo como botão personalizado. Sua propriedade Stretch permite adaptar
a imagem ao tamanho do PictureBox em cada momento, a propriedade Border determina sua
aparência plana ou com relevo e a propriedade Picture representa a imagem a mostrar.

Outros controles básicos miscelâneas


Outros controles que podem ajudar a desenhar a
interface e tem um proposito muito mais concreto são
os que estão representado na imagem a esquerda.

*ProgressBar: barra de progresso que mostra


uma porcentagem de forma gráfica. Serve para dar ideia
do avanço de um processo que dura muito tempo, de
forma que o usuário não sinta que a aplicação está
pendente enquanto trabalha em segundo plano.


   

* Slider: é similar ao ProgressBar no sentido de que mostra uma porcentagem, mas neste
caso o usuário é quem varia seu valor. Um bom exemplo é o uso para subir e baixar o volume em
uma aplicação que reproduza áudio. Os valores de volume se definem entre um valor máximo e
mínimo, e o usuário o troca a seu gosto. O evento Change assinala uma troca por parte do
usuário no valor da escala.

*MovieBox: apesar de seu sugestivo nome, não se trata de um reprodutor multimídia, e


sim algo mais humilde. Mostra uma animação em formato GIF ou MNG, é que o programador
deve preocupar-se da sucessão de frames do arquivo que vai mostrar. A propriedade Path
determina o arquivo a reproduzir, e Playing permite o controle da reprodução, com os valores
TRUE (reproduzir) ou FALSE (parado).
*ScrollBar: trata-se de uma barra de scroll para deslocar outro controle, habitualmente,
de forma que seja o usuário quem determina a posição deste. A propriedade Value indica o valor
escolhido pelo usuário, e o evento Change sinala cada mudança.

A diferença de outras interfaces gráficas, onde existe variantes horizontais e verticais para determinar controles,
em Gambas os controles Slider e ScrollBar determinam sua orientação automaticamente: se forem mais largos que altos
serão horizontais, e vertical em caso contrário.

Lista de dados
Existem três controle desenhados para mostrar listas de diferentes modos:

1. ListBox: é uma lista simples. Se adiciona ou eliminam elementos que são representado
como uma linha de texto cada um. O usuário tem capacidade de seleciona-los ou desceleciona-
los. A propriedade Mode determina se o usuário não pode selecionar nenhum, só um ou vários.

2. ListView: similar a ListBox, dispõe de capacidades adicionais. Pode representar um


ícone junto a cada elemento da lista, e cada um deles está identificado por uma chave única de
texto, que nos permite fazer buscas dos elementos por sua chave. Mesmo assim dispões de um
cursor interno que pode mover-se para diante e para trás, o que o faz apropriado para interagir
com uma aplicação de base de dados, onde posa representar um campo de uma tabela.

3. ComboBox: é um lista desdobrável. O usuário só ver o elemento selecionado a cada


momento e pode desdobrar a lista para selecionar outro.

Outros controles avançados


Continuamos mostrando outros controles mais avançados:

*TreeView: serve para representar elementos em uma árvore, de forma que cada nó pode
ter outros nós filhos.

*ColumnView: é similar ao anterior, mas cada nó pode dispor de varias colunas.

3.4 Diálogos
Gambas tem uma série de diálogos auxiliares para mostrar ou receber informações
interagindo com o usuário.

A classe Message
A classe Message se encarrega de mostrar uma janela modal ao usuário, na qual
podemos definir um texto, que será uma informação ou uma pergunta, e uma série de botões para
escolher uma opção. A classe Message é estática, e dispõe de uma série de métodos para mostrar
distintos tipos de mensagens, que serão reconhecido pelo usuário graças ao ícone que acompanha
a janela e que da uma ideia do caráter da mensagem. Nestes métodos teremos sempre, como
primeiro parâmetro, o texto a mostrar e, os seguintes, os botões, que são no máximo três. Se não
indicarmos o texto dos botões, aparecerá apenas um botão indicando OK para que o usuário
aceite a leitura da mensagem.
*Message.Info:
(Texto, Botão):
É utilizada para mostrar uma mensagem
meramente informativa. Só permite definir um botão,
que normalmente terá um texto tal como OK ou
Aceitar.      
A classe Message também pode ser chamada como uma função, de modo que o código:

Message.Info (Mensagem”)

É equivalente a:

Message (“Mensagem”)

*Message.Delet:
(Texto, Botão1, Botão2, botão3):

É utilizada para avisar que algo vai ser


eliminado (arquivo, registro de uma tabela .....), e é
solicitado ao usuário sua confirmação.


      

*Message.Error:
(Texto, Botão1, Botão2, botão3):

É utilizada para indicar uma mensagem de


erro.
     

*Message.Question:
(Texto, Botão1, Botão2, botão3):

É uma pergunta ao usuário, geralmente para


confirmar uma ação ou uma opção de configuração.

   





*Message.Warning:
(Texto, Botão1, Botão2, botão3):

Adverte o usuário que a ação que vai realizar


pode ser perigosa, por exemplo perda de dados de
uma tabela que poderiam ser úteis ainda.

       


Os métodos da classe Message devolvem um número inteiro que destaca o botão que o
usuário pressionou. O primeiro botão começa com o número 1. as mensagens são modais, o que
quer dizer que há interação da interface do usuário com o programa, assim como o fluxo deste,
fica bloqueado ate que se pressione um dos botões.
.......

DIM hRes AS Integer

hRes = Message.Warning ( " Formatar o disco Rígido", "Sim", "Não" )

IF hRes = 1 THEN Formatar o Disco()

END

.........

Depende do gestor de janelas do sistema, é possível


que as caixas de diálogo tenham um botão de fechar. De o
usuário fechar a mensagem deste modo, retornará o numero
do botão existente mais alto (no exemplo anterior é o 2),
portanto a opção menos perigosa, é que deve executar por
padrão, deveremos indicar o botão mais alto.

    

*Dialog.Filter: permite indicar filtros para os arquivos a mostrar. Se trata de uma matriz
de cadeias na qual podemos especificar, por exemplo, as extensões dos arquivos a escolher,
coringas como desejarmos.
*Dialog.Title: permite estabelecer um título para a janela, Que por padrão corresponderá
a ação e realizar (Select Font, Select Color, etc.).

.......

Dialog.title = " Selecionar Imagens a processar "

Dialog.Filter = [ " * . png " , " . jpg " ]

IF Dialog.Openfile (TRUE) THEN

Message.info ( " Ação Cancelada " )

ELSE

Processa_Imagens (Dialog.Path)

END IF

.........

Diálogos Personalizados
Além dos diálogos já mencionados, o programador pode criar outros personalizados.
Quando um formulário mostra-se de formo Modal, quer dizer, com os métodos ShowModal() ou
ShowDialog(), pode retornar um valor inteiro, que serve como indicação da aplicação escolhida
Pelo usuário. Vejamos um pequeno exemplo.

Críaremos um programa, com o formulário principal chamado FMain e outro formulário


FDialogo. Criaremos também três pequenos ícones no formato png e os copiamos para a
pasta
/usr/share/pixmaps do sistema, e os renomeamos a.png,
b.png e c.png de forma que os tenhamos disponível na
pasta do programa. O formulário principal Fmain terá
um PictureBox chamado pImage, e um botão
denominado btnSelect com o texto Icone. O formulário
Fdialogo disporá de três controles 
  

PictureBox, cada um dos contêiner um dos ícones png que criamos no projeto, e os
chamamos pic1, pic2 e pic3.

Com o código do formulário Fdialogo o que conseguiremos é que cada vez que o
usuário pressionar um dos controles PictureBox, o formulário que aparece de forma modal,
devolve um valor inteiro que identifica o ícone pressionado.
PUBLIC SUB Pic1_MouseDown()

ME.Close (1)

END

PUBLIC SUB Pic2_MouseDown()

ME.Close (2)

END

PUBLIC SUB Pic3_MouseDown()

ME.Close (3)

END

Enquanto o formulário FMain, ao pressionar o botão o conduziremos


a mostrar o formulário Fdialogo de forma modal e, em função do
valor retornado carregará uma imagem ou outra no PictureBox
chamado pImage:
     

PUBLIC SUB BtnSelect_Click()

SELECT CASE Fdialogo.ShowDialog ( )

CASE 1

pImage.Picture = Picture [ "a.png" ]

CASE 2

pImage.Picture = Picture [ "b.png" ]


CASE 3

pImage.Picture = Picture [ "c.png" ]

END SELECT

END

Ao executa-lo poderemos comprovar o resultado. Se o usuário fechar o formulário modal


pressionando o ícone do gestor de janelas, é retornado o valor por padrão, ou seja, zero, o que
equivale a cancelar a seleção. Pode apresentar outro problema mais complexo: a necessidade de
devolver outro tipo de valores, tais como cadeias e referências a objetos. Neste caso a solução do
número inteiro não é válida. Então tratamos de manter uma cadeia em uma variável do
formulário, esta se liberará quando fechar o formulário, o qual não nos serve. Por exemplo,
modificando o código anterior, da seguinte maneira, para que o formulário Fdialogo mantenha
uma cadeia com o valor escolhido.

PUBLIC Valor AS String

PUBLIC SUB Pic1_MouseDown()

Valor = "a.png"

ME.Close

END

PUBLIC SUB Pic2_MouseDown()

Valor = "b.png"

ME.Close

END

PUBLIC SUB Pic3_MouseDown()

Valor = "c.png"

ME.Close

END

E modificando o formulário principal para que tomo o valor da cadeia:

PUBLIC SUB BtnSelect_Click()

Fdialogo.ShowDialog()

IF Fdialogo.Valor <> "" THEN

pImage.Picture = Picture [ FDialogo.valor ]

END IF

END

Não conseguiremos que funcione, já que a sequência é a seguinte:

1. A chamada a Fdialogo.ShowDialog() cria uma instância da classe FDialogo.


2. Depois do usuário pressionar, está instância se destruirá e com ela o valor armazenado
na variável pública Valor.
3. Ao chamar a FDilogo.valor, se cria uma instancia de FDialogo, que tem a variável
Valor com seu valor por padrão (cadeia vazia).

Podemos resolver este problema por dois caminhos:

1. O primeiro consiste em declarar a variável Valor como estática. Assim a variável não
depende de cada instância, se não da classe, de forma que não se cria nem se destrói em cada
chamada a FDialogo.ShowModal().

2. O segundo consiste em aproveitar a propriedade


Persistent dos formulários. Mudando seu valor para
TRUE, um formulário não se destrói quando o usuário o
encerra pressionando o ícone de fechar do gestor de
janelas, nem se chamar o método Close(), simplesmente
oculta-se. Se estava de forma modal, o programa abandona
este modo e continua sua execução normal.
  
  
    
 

Sendo assim, definiremos a propriedade Persistente do formulário FDialogo a TRUE, e


modificaremos o código do formulário FMain para que destrua o formulário de forma explícita
depois de ter lido o valor que interessava.

PUBLIC SUB BtnSelect_Click()

Fdialogo.ShowDialog()

IF Fdialogo.Valor <> "" THEN

pImage.Picture = Picture [ FDialogo.valor ]

END IF

FDialogo.delete()

END

3.5 Menus
A criação de menus e realmente fácil já que um
assistente da IDE permite desenha-los. Pressionando o botão
direito do mouse sobre o formulário, e selecionar a opção
Editor de menu...
Os menus são criado em arvores, isto é cada menu de
primeiro nível, por exemplo os típicos menus da barra superior
de muitos programas como Arquivo, Editar, ajuda, etc., terão
menus filhos que ficarão um nível abaixo deste e, por sua vez se
estes tiverem filhos ficarão um nível mais abaixo.
 !  ∀ 

Tudo isso se controla com os botões com forma de flecha, as verticais permitem trocar a
ordem de aparição dos menus, e com as horizontais modificamos a profundidade destes.
As propriedades amais importantes são o
Nome, que é o nome do objeto menu e que
corresponderá com seu gestor de eventos, o Título
que é o texto que aparecerá na tela, um ícone a
escolher se o desejarmos, e um possível atalho de
teclado para acessa-lo sem necessidade do mouse.
Se deixarmos o nome de um menu em
branco, este parecerá como uma barra separadora em
lugar de uma entrada de menu normal.
Na figura 23 podemos ver um exemplo com
um menu principal que tem três opções, e seus
correspondentes submenus.
 #∃ 
    

O formulário terá o aspecto da figura 24. Se


pressionarmos sobre um dos menus que não tenham
filhos, a IDE nos levará diretamente ao evento Click do
menu, que é onde podemos criar o código que será
executado quando o usuário pressionar o menu.

   


PUBLIC SUB menuSelessionar_Click()

..............

END

Se o que desejamos é criar um menu daqueles


que aparece quando o usuário pressiona, por exemplo,
o botão direito sobre o formulário ou outro controle,
teremos que criar um menu de primeiro nível com sua
propriedade visible FALSE, e seus correspondentes
filhos. Depois é só lançar o evento do formulário o
controle que nos interessa, por exemplo ao
pressionarmos o botão direito sobre o formulário, que é
detectado mediante a gestão do evento Menu,
indicaremos a nosso menu invisível que deve mostrar-
se como um menu contextual:
PUBLIC SUB Form_Menu()

menuEditar.Popup()

END    


Ao executar o código, veremos o resultado ao
pressionar o botão direito do mouse sobre o
formulário.
Quanto a natureza dos menus, não são mais que
objetos, ainda que neste caso não provem da classe
Control, se bem que dispõem de algumas propriedades
comuns como Text ou Picture.
 %      ∃

Na hora de criar um menu, teremos que indicar


seu objeto pai, que poderá ser uma janela ou
formulário para os menus de primeiro nível, ou outro
menu para os filhos do primeiro. Os menus também
podem ser criados ou destruídos diretamente por
código
 %      ∃

PUBLIC SUB Form_Open()

DIM h1 AS Menu

DIM h2 AS Menu

DIM h3 AS Menu

h1 = NEW Menu(ME)

h1.Text = "Ações"

h2 = NEW Menu(h1) AS "h2"

h2.Text = "Enviar"

h3 = NEW Menu(h1) AS "h3"

h3.Text = "Deletar"

END

3.6 Alinhamento dos controles


Propriedade de alinhamento

Alguns contêiner dispõe de uma propriedade Arrangement que permite determinar como
se alinha os controles dentro de um contêiner. Por padrão, o valor da propriedade é None, o que
significa que as posições dos controles são livres, como é típico nas interfaces para WindowsTM
ou em algumas bibliotecas QT.
Em outras bibliotecas gráficas como GTK+, ou nas definições da linguagem XUL
desenhado pelo projeto Mozilla para aplicações web, o habitual é encontrar contêiner que
definirá onde se localizará cada filho, de forma que o programador só indicará o modo geral de
alinhamento e os controles se adaptarão em todo momento a qualquer alinhamento.

A principio desenhar interfaces desta maneira pode ser algo complicado para pessoas
inexperiente, no entanto uma vez que se consegue alguma perícia, Terá grandes vantagens. A
principal é que desenhar as janelas redimensionável com controles variados em seu interior.

Cada usuário pode aumentar ou diminuir o tamanho da janela, ou variar sua relação
altura/largura, e a aplicação seguirá mantendo um aspecto coerente em cada momento, dentro de
uns limites razoável de tamanho.

Gambas define várias possibilidades de alinhamento para os controles:

*None: Alinhamento livre, o contêiner não decide nada sobre a posição de seus filhos.

*Horizontal: todos os controles alinham-se da esquerda pra direita, ocupando todo o


espaço em vertical dentro do contêiner.

*Vertical: Todos os controles alinham-se de sima para baixo, ocupando todo espaço
horizontal dentro do contêiner.

*LeftRight: os controles tratam de alinharem-se da esquerda para a direita, e se faltar


espaço de cima para baixo.

*TopBottom: os controles tratam de alinharem-se de cima para baixo e se faltar espaço


da esquerda para a direita.

Alem da propriedade geral Arrangement, existe a propriedade Padding, que é uma


especie de queda livre na borda do contêiner, e uma propriedade Spacing, que determina o
espaço de separação entre um controle e outro.

Cada controle, por sua vês, dispõe da propriedade Expand. Se o controle está localizado
sobre um contêiner cuja propriedade Arrangemente é None, o valor Expand determina se este,
junto com o resto dos controles do contêiner que tenham a propriedade Expand = TRUE, tratam
de ocupar o espaço livre que fica dentro do contêiner.

Para comprovarmos o efeito de todas estas


propriedades criaremos um novo projeto gráfico
chamado Alinhamento, com um só formulário de
inicio FMain, em cujo interior criaremos dois botões e
um RadioButton. Ao executa-lo veremos o resultado
habitual: um formulário com três controles meio
desordenados.
  & 
Trocaremos o valor da propriedade
Arrangement a Horizontal e executaremos o programa.

Agora os controles estão alinhados na horizontal


e ocupando todo espaço vertical do contêiner
(Figura 29).
  & 

Ponhamos a borda do formulário


como Resizable, de modo que possamos
variar seu tamanho. Façamos varias provas
de execução trocando sua relação
aotura/largura.


∋     (

Como podemos observar, os controles seguem ocupando toda a largura do contêiner,


enquanto a largura extra fica livre. Colocaremos agora a propriedade Expand do controle
Button2 a TRUE, e voltamos a executa-lo. Com esta nova configuração, todo o espaço do
contêiner é aproveitado, modificando a largura do Button2.

 )
   
    

Se colocarmos a propriedade Expand do Button1 como TRUE, O espaço extra será


compartilhado por ambos controles.

 #
 
       
Para deixar espaço visível pela borda do contêiner, podemos dar um valor a propriedade
Padding, e para separar um puco cada controle utilizamos a propriedade Spacing. Os valores
indicados são pixeis de separação.

O efeito da propriedade Alinhamento não se aplica em tempo de


desenho, deste modo se por erro trocarmos para um valor não desejado,
podemos reverter sem perdermos a posição de cada controle dentro do
contêiner.

Controles com alinhamentos predefinidos

Os controles Form (Window), Panel, TabStrip e ScrollView permite definir as


propriedades de alinhamento, no entanto outros controles tem esta propriedade implícita e não é
modificável. Estes são os seguintes.

*Hbox: é um painel que sempre tem alinhamento horizontal.

*Vbox: é um painel com alinhamento vertical.

*Hpaenl: segue o modelo RightToleft.

*Vpanel: segue o modelo Vpanel

Alem destes, os controles Hsplit, e Vsplit são contêiner com um modo de trabalho
totalmente diferente: cada controle adicionado mostra-se separado por uma barra vertical, e no
caso do Hsplit, uma horizontal, no caso do Vsplit, que o usuário pode mover para aumentar um
ou outro controle em detrimento do tamanho do seu vizinho.

Desenho de uma aplicação que aproveita este recursos

Na hora de desenha uma aplicação redimensionável, o melhor é projetar áreas de trabalho


com várias funcionalidades e agrupa-las em diversos painéis horizontais e verticais. Suponhamos
um clone dos exploradores de arquivos habituais. Uma zona de trabalho estará formada os típicos
botões de menu, que permitem realizar as tarefas mais comuns e que representam um ícone e um
tooltip explicativo. Outra zona de trabalho pode ser a parte inferior, em que mostra dados de
estado. Por ultimo, a zona central mostra os arquivos e, por sua vez é uma zona de trabalho que
compreende outras duas: uma árvore com as pastas a esquerda, e uma zona maior a direita com a
vista em detalhes dos arquivos. Não criaremos neste exemplo o código correspondente, mas
seguiremos os passos necessários para criar a interface de um modo pratico, para conseguir que
cada usuário possa dispor de suas janelas como melhor o desejar.

Em primeiro lugar, há três grandes grupos de trabalho, que vão de cima para baixo.
Portanto, o melhor e definir um formulário com a propriedade Arrangement na vertical
  

  

Dentro desta criaremos três painéis com alinhamento horizontal, quer dizer, três contêiner
Hbox. Desenhamos a parte superior com os botoes e a parte inferior com a barra de estado, com
uma largura fixa, mas a parte central que contem o corpo da informação útil do programa, será
redimensionável. Por tanto, a propriedade Expand do painel central deverá ter o valor TRUE.

   
    ∀

A parte superior conterá diversos botões tipo


ToolButton, que podemos ressalta-los com distintos ícones. A
parte inferior disporá de etiquetas Label com borda Suken,
uma das quais, queremos Faze-la extensível, terá sua
propriedade Expand TRUE.

  ∗   



Dentro do corpo a esquerda ficará uma caixa vertical
(Vbox) com outros botões auxiliares. É o mesmo desenho que
a zona de botões principal, mas alinhada em vertical.

+∗ ∃     

Vamos agora colocar o contêiner Hsplit, de modo que


o usuário possa dispor de uma barra para modificar o
tamanho relativo da árvore e a zona principal de trabalho.
Dentro dela, a esquerda, colocaremos, o controle TreeView
para a árvore assim como o controle IconView para a zona
principal.

O controle Hsplit deve ter a propriedade Expand


TRUE, para que aproveite todo o espaço livre disponível
dentro do seu contêiner.
    

Já podemos executar o programa para ver a interface,


adicionando, algum código para encher a árvore e a vista de ícones.

Podemos modificar a altura e a largura da aplicação, a qual


se encarregará de manter, em todo momento, a relação dos vários
controles, sem que seja necessário adicionar nenhum código de
calculo de posição de nossa parte.

      


 

3.7 Introdução ao desenhos de primitivas


Alem dos controles já desenhado, o programador pode necessitar desenhar gráficos
personalizados. A classe estática Draw é empregado para desenhar sobre um controle, que pode
ser um formulário ou um controle DrawingArea.

Desenhar diretamente sobre um formulário não faz sentido, já que uma vez refresque a
interface, por exemplo quando passar uma janela ou minimizar e maximizar, se perde o desenho
Realizado sem que tenhamos controle sobre a situação.

DrawingArea dispõe de dois modos de trabalho: o primeiro como é o caso do formulário,


perde-se o desenho uma vez refrescado o controle, mas o evento Refresh nos informa para que
redesenhamos a parte eliminada; e o segundo que é ativado quando quando fazemos a
propriedade Cached igual a TRUE o controle guarda um cache do desenho realizado e não gera
eventos Refresh, sendo que redesenha automaticamente a zona clareada.
Como exemplo, criaremos um projeto
gráfico Desenhando, que contenha um
formulário FMain, e em seu interior um controle
DrawingArea chamado Tela e um botão
chamado Desenhar.

A propriedade Cached do controle


DrawingArea colocamos como TRUE.

O código do botão desenhar será o seguinte:



 & 

PUBLIC SUB Button1_Click()

Draw.Begin(Tela)

Draw.Line(0, 0, Tela.W, Tela.H)

Draw.Line(0, Tela.H, Tela.W, 0)

Draw.End

END

Ao executarmos o programa e
pressionar o botão, aparecerá uma cruz criada
por nosso código.

 %    , 

Este código contem, para qualquer desenho o método de trabalho seguinte:


*Em primeiro lugar, especificaremos a classe Draw que controle será empregado para
desenhar os elementos que indiquemos, para o qual se empregou o método
Draw.Begin() passando como parâmetro o controle desejado, em nosso caso o controle
Tela.

*Depois disto passamos a desenhar as primitivas que desejarmos. Aqui vemos empregado
o método Draw.Line(), o qual desenha linhas quando especificamos os pontos de origem
e destino

*Finalmente, sempre tem que chamar o método Draw.End() para que o cache do controle
DrawingArea desenhe-se na tela, e se liberem os recursos associados ao processo de
desenhar do controle.

A classe Draw permite desenhar diversos tipos de primitivas:

*Draw.Ellipse: elipse.

*Draw.Line: linhas

*Draw.Point: pontos.

*Draw.Polyline: varias linhas enlaçadas

*Draw.Polygon: poligonos.

*Draw.Rect: retângulos

Em todo momento podemos controlar diversos aspectos do desenho, utilizando o


seguinte:

*Draw.BackGround: cor de fundo do pincel.

*Draw.ForeGround: cor do primeiro plano do pincel.

*Draw.FillColor: cor para preencher elipse ou retângulo

*Draw.FillStyle: utiliza as constantes da classe Fill para determinar o padrão de desenho


(preenchimento, rajado na horizontal, linhas e pontos, etc.).

Alem das primitivas, podemos desenhar elementos complexos como:

*Draw.Text: Desenhar um texto em uma posição indicada e com a fonte selecionada por
Draw.Font.

*Draw.Image: desenha um gráfico armazenado em um objeto Image.

*Draw.Picture: desenha um gráfico armazenado em um objeto Picture.


Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 4: GESTÃO DE PROCESSOS ......................................................... 04


4.1 A ajuda oferecida por outros programas ............................................ 04
4.2 Gestão poder do processo .................................................................. 05
4.3 Exec ................................................................................................... 05
Palavra chave WAIT ................................................................... 06

 de processo .............................................................. 07
Redirecionando com TO ............................................................. 09
Matar um processo......... ............................................................. 10
Redirecionando para saída padrão de erros ................................. 12
Redirecionando para a saída padrão ............................................ 14
Evento KILL () a propriedade e valores ...................................... 15
Redirecionando para a entrada padrão no uso de CLOSE ........... 18
Notas finais sobre o objeto PROCESS ........................................ 19
4.4 SHELL ............................................................................................. 20
4.1 A ajuda oferecida por outros programas

O sistema GNU/Linux seguem a filosofia do UNIX. Parte dela consiste de criar pequenos
programas especializados em cada área, em lugar de gerar grandes aplicações monolíticas. Como
resultado o GNU/Linux dispõe de muitos utilitários de console capazes de realizar quase que
qualquer tarefa que necessitemos. Os programas com interface gráfica, habitualmente são
simples front-ends para aplicações de linha de comando. Podemos por como exemplo o
magnífico programa de gravação de Cds e DVDs K3B, uma aplicação simples, bonita e intuitiva
que no entanto, e só uma porta para aplicações como cdrecord ou mkisofs, poderosas ferramentas
de console. Reprodutores de video ou áudio como Totem ou Kmplayer, recobrem também
aplicações sem interface gráfica própria como o grande Mplayer.

Para aqueles que vem do ambiente Win32/VB, pode achar estranho, acostumados a
trabalhar só com os recursos suportado pelo próprio ambiente de programação ou adicionando
chamadas a API, isto é, trabalhando com bibliotecas do sistema quando o VB não possui os
recursos necessário, mas este modelo de segmentação em pequenas unidades oferece grandes
vantagens ao programador.
GNU/Linux permite desenvolver sem reinventar a roda. O shell bash, comum neste
sistema, junto com as ferramentas habituais de console que acompanham a qualquer distribuição,
proporciona todo o necessário para gravar um CD, reproduzir um video, gestionar serviços
LDAP, transmitir arquivos com ftp, http, smb, scp e nfs, enviar e receber email, administrar base
de dados, converter formatos de arquivos gráficos ou de texto e muitas tarefas mais. Não é
necessário, na maior parte de nossos desenvolvimentos, entrar nas complexidades das diversas
bibliotecas escritas em C, tratar de conversões de tipos entre uma linguagem de alto nível e C,
nem cair frequentemente em violações de segmento por um descuido em um ponteiro mal
gestionado. Basta consultar a documentação de um comando e chama-lo tal e como faríamos a
partir do console do sistema.
A depuração do programa também o torna amais fácil: basta testar o comando
em um terminal de texto e comprovar os resultados, antes de inclui-lo no código do
Gambas.

4.2 Gestão o poder de processos


A diferença do VB, onde a única coisa que podíamos fazer sem ajuda da API era lançar
um processo e perdermos o controle dele, Gambas permite sincronizar a execução dos
programas, comunicar-se com ele lendo e escrevendo pela entrada e saída estândar (stdin e
stdout), conhecer o estado (em execução ou finalizado) e receber suas mensagens de erro pela sua
saída estândar de erros (stderr).

4.3 EXEC
Existem dois comandos para lançar a execução de programas a partir do Gambas: EXEC e
SHELL. Um programa em execução se denomina processo e a partir de agora falaremos de
processos mais que de programas. A primeira instrução, EXEC, lança o comando que
indiquemos, acompanhado dos parâmetros que escrevemos:
[Variavel=] EXEC [ Comande ] [ WAIT ] [ FOR (READ | WRITE | READ |

WRITE) ][ TO String]

Para facilitar a escrita dos parâmetros, evitando os problemas que podem surgir com
caracteres especiais tais como os espaços, a sintaxes de EXEC indica que temos que passar o
comando e os parâmetros como uma matriz ou array de cadeias:

[ Command , parametro1 , parametro2 ....... ]

Desta forma, si tivermos que executar, por exemplo, o comando cat com um
nome de arquivo tal como meu arquivo.txt que tenha um espaço no meio, desaparece
a ambiguidade e a necessidade de indicar mais caracteres especiais como \, a fim de
eliminar a possibilidade de que o comando tome meu arquivo.txt como dois
parâmetros no lugar de um só.
Vamos Descobrindo pouco a pouco as diferentes opções. Command é o único
parâmetro obrigatório e representa o comando a executar. No modo de trabalho mais simples de
EXEC, este executa o comando que indiquemos e se libera dele. Agora vamos criar um projeto
de console com Gambas. Para ele, adicionaremos um módulo de inicio e, como único código
escrevemos: PUBLIC SUB Main()

EXEC [ "ls" , "-l" ]

END

Ao executa-lo lançamos o comando ls com o parâmetro -l obtemos uma lista


dos arquivos da pasta atual em formato longo. Observamos que a nossa matriz de
cadeias está indicada diretamente no comando; poderíamos ter feito o programa
também assim:

PUBLIC SUB Main()

DIM sCad AS NEW String [ ]

sCad.Add ( "ls" )

sCad.Add ( "-l" )

EXEC sCad

END

Porem o interpretador do Gambas é capaz de reconhecer uma matriz indicada


nas cadeias entre colchetes e, dessa forma, nos livramos de umas quantas linhas de
código.

Vimos até aqui tudo o que fazer para o Gambas executar um novo processo,
passando-lhes os parâmetros, e livrando-se dele. Executando-o de forma assíncrona, quer dizer, o
programa Gambas segue seu curso sem esperar que o processo filho finalize. Isto pode ser um
inconveniente se tivermos que sincronizar, ou esperar que o processo acabe, antes de continuar
com a instrução seguinte. Podemos realizar esta tarefa de três maneiras que vemos a seguir.

Palavra chave WAIT


Se adicionarmos o flag WAIT à instrução EXEC, o programa principal se deterá até que o
processo tenha finalizado de forma normal ou devido a alguma falha.

Vamos de novo ao primeiro exemplo com pequenas modificações. Vamos fazer uma
lista da pasta /dev, que contem grande quantidade de arquivos. Se os dois processos executam-se
de forma assíncrona,PUBLIC
obteremos resultados imprevisíveis.
SUB Main()

EXEC [ "ls" , "/dev" , "-l" ]

PRINT "OLÁ GAMBAS"

END
Em seguida, o compilamos e executamos várias vezes a partir de um terminal.
Observamos como na lista mais abaixo a frase OLÁ GAMBAS se introduz de forma caprichosa
entre a lista gerado pelo comando ls.

Aplicamos agora o flag WAIT:


PUBLIC SUB Main()

EXEC [ "ls" , "/dev" , "-l" ] WAIT

PRINT "OLÁ GAMBAS"

END

Podemos executar quantas vezes quisermos: já não existe o problema inicial, o programa
Gambas espera que termine de executar ls, e depois passa a seguinte linha de código.
Conseguimos sincronizar a execução de dois processos de forma simples, simplificando nosso
código, já que de outro modo teríamos, por exemplo, que colocar a lista em um arquivo, esperar
em um loop até que o arquivo deixasse de crescer e, em seguida, lê-lo e mostra-lo na tela.

O descritor de processo
Observamos que a princípio a sintaxe de EXEC, se indica um valor opcional
VARIAVEL=, que recebe algo de retorno ao chamar EXEC. Esta variável é um objeto da classe
Process, e o que se recebe é um descritor de processo que lançamos. Os objetos da classe
Process tem uma série de propriedades que nos permite conhecer o estado do processo, assim
como atuar sobre ele.
O que nos interessa agora é a propriedade State, que reflete o estado de execução. Quando
se lança um processo, o valor de State é Process.Running, quer dizer, processo em execução. Se
o processo já terminou, a variável poderá tomar os valores Process.Stopped, detido, finalizado,
ou Process.Crashed, se finalizou devido a um erro grave, habitualmente uma violação de
segmento.
Utilizando esta propriedade teremos a capacidade de sincronizar os dois processos de um
modo mais eficiente: poderemos realizar algumas tarefas em nosso programa Gambas, no entanto
esperamos que finalize o processo auxiliar. Tipicamente, o que faremos será dar algo de
feedback ou informação ao usuário de que deve esperar. Como exemplo, vamos baixar um
arquivo da internet quando o usuário pressionar um botão, e avisa-lo de que estamos trabalhando,
que a aplicação está pendente, e que deve esperar com paciência.
Vamos trabalhar com a aplicação auxiliar curl, que é um programa de linha de comandos
que permite precisamente o que queremos: baixar um arquivo de uma URL. Se não tivermos o
curl já instalado, aproveitaremos para faze-lo agora, já que está disponível para todas as
distribuições GNU/Linux habituais, assim como para FreeBSD. Para isso, consultamos a partir
do Synaptic, Yast, RpmDrake ou nosso gestor preferido de pacotes em outras distribuições.
O que baixaremos é um programa em Gambas chamado RadioGambas, cujo código
contem um bom exemplo de gestão de processos e que também podemos estudar. Alem disso nos
serve para escutar programas de rádio emitido pela internet, o qual não está mal. A URL é
http://gambas.gnulinex.org/radiogambas/radiogambas-1.0.1.tar.gz, ainda podemos consultar em
http://gambas.gnulinex.org/radiogambas a existência de uma versão mais recente.
Agora, vamos criar um programa gráfico e um
formulário, com um botão chamado Downloads e um label
chamado LblInfo.

O label terá como propriedade text o valor Inativo e o


botão terá o texto Downloads
 

  
  


 

O código do formulário será o seguinte:

PUBLIC SUB BtnDownloads_Click()

DIM hProc AS Process

DIM sUrl AS String

sUrl = "http://gambas.gnulinex.org/radiogambas/radiogambas-1.0.1.tar.gz"

hProc = EXEC ["curl", sUrl, "-o", User.home & "/RadioGambas.tar.gz"]

DO WHILE hProc.State = Process.Running

SELECT CASE LblInfo.Text

CASE "|"

LblInfo.Text = "/"

CASE "/"

LblInfo.Text = "-"

CASE "-"

LblInfo.Text = "\\"
CASE "\\"

LblInfo.Text = "|"

CASE ELSE

LblInfo.Text = "|"

END SELECT

WAIT 0.1

LOOP

LblInfo.Text = "Inativo"

Message.Info("Download Finalizado")

END

Executamos o processo com os parâmetros necessários para que o download seja para
nossa pasta pessoal com o nome RadioGambas.tar.gz, e recebemos uma descrição do processo.
Seguidamente, entramos em um loop que se executa enquanto o processo está ativo, quer dizer,
enquanto o estado do processo é Process.Running. E faz o loop trocar o valor do texto do Label
entre os valores “-”, “|”, “/”, e “\”, de forma que se gera a ilusão de uma hélice, com o qual o
usuário sabe que algo tá acontecendo por baixo. Com a instrução WAIT refrescamos a interface.
Ao terminar, informamos ao usuário e repomos os valores originais do label.

Voltamos a modificar o programa eliminando o código da hélice e executando


o programa de forma síncrona, como explicamos com o flag WAIT:

Hproc = EXEC [ “curl” , sUrl , “-o” , System.Home & “/RadioGambas.tar.gz” ]

WAIT
O funcionamento é igualmente eficiente: o programa baixa o arquivo em
ambos os casos, porem agora a interface do usuário fica bloqueada durante o
download, o que pode fazer pensar que nosso programa está congelado e gerar
algumas chamadas inúteis a nossos serviços de atendimento ao cliente, no pior dos
casos. É importante, por isso, avaliar em que caso é conveniente usar o WAIT nos
quais é melhor informar, de alguma maneira, ao usuário que mantenha a calma e as
mãos longe do telefone.

Redireção com TO

Nos exemplos anteriores com o comando ls, a saída aparecia diretamente no console, o
qual não é útil se quisermos processar a informação procedente do comando.

Podemos utilizar a palavra chave TO para conseguir dois proposito de forma simples:
esperar que o processo acabe antes de continuar o programa principal e receber em uma cadeia de
texto a saída do programa.
PUBLIC SUB Main()

DIM sCads AS NEW String[]

DIM Buf AS String

DIM Bucle AS Integer

EXEC ["ls", "/dev", "-1"] TO Buf

sCads = Split(Buf, "\n")

sCads.Remove(0)

sCads.Remove(sCads.Count - 1)

FOR Bucle = 0 TO (sCads.Count - 1)

PRINT Left(sCads[Bucle], 10)

NEXT

END

A instrução EXEC aguarda até que finalize o comando ls, armazenando em um buffer a
saída padrão do comando, que nos devolve na cadeia Buf. Em seguida, processamos a cadeia
separando-a em linhas com Split, eliminamos a primeira e ultima (sem informação útil) e
mostramos na tela só a parte da lista correspondente aos arquivos.

As saídas dos processos com várias linhas pode ser dividida facilmente
empregando a função Split, e utilizando como separador o retorno do carro \n.

Até aqui vimos como sincronizar os dois processos, mas ainda podemos ter mais controle
sobre ele.

Matar um processo
Alem das propriedades do objeto Process, este oferece um método de grande importância:
Kill, o qual permite matar ou acabar com o processo em qualquer momento. Suponhamos que o
nosso programa anterior, a rede é demasiadamente lenta e o usuário decide não esperar e
interromper o download. Graças a Kill podemos incluir essa possibilidade em nosso programa,
vamos ver como fazer isso.

Adicionamos ao programa anterior um botão


chamado BtnCancelar, com o texto Cancelar e com a
propriedade Enabled a FALSE, para que inicialmente
esteja inativo.

    

O novo código é o seguinte:


PRIVATE hCancelar AS Boolean

PUBLIC SUB BtnDownloads_Click()

DIM hProc AS Process

DIM sUrl AS String

hCancelar = FALSE

BtnCancelar.Enabled = TRUE

sUrl = "http://gambas.gnulinex.org/radiogambas/radiogambas-1.0.1.tar.gz"

hProc = EXEC ["curl", sUrl, "-o", User.home & "/RadioGambas.tar.gz"]

DO WHILE hProc.State = Process.Running

SELECT CASE LblInfo.Text

CASE "|"

LblInfo.Text = "/"

CASE "/"

LblInfo.Text = "-"

CASE "-"

LblInfo.Text = "\\"

CASE "\\"

LblInfo.Text = "|"

CASE ELSE

LblInfo.Text = "|"

END SELECT

WAIT 0.1

IF hCancelar = TRUE THEN

hProc.Kill()

message.Warning("Processo Cancelado")

BtnCancelar.Enabled = FALSE

RETURN

END IF

LOOP

LblInfo.Text = "Inativo"

Message.Info("Download Finalizado")

END

PUBLIC SUB BtnCancelar_Click()

hCancelar = TRUE

END
Dispomos de uma variável global, hCancelar, para saber se o usuário pressionou
o botão Cancelar. Ao iniciar o download, daremos a esta variável o valor FALSE. Se o usuário
pressionar o botão BtnCancelar, faz a variável tomar o valor TRUE. Em nosso loop, testamos o
valor de hCancelar e, se for TRUE, matamos o processo com Kill(), retornamos a interface o
estado inativo, informamos ou usuário e saímos da subrotina.

Ainda que temos sincronização do processo e podemos acabar com ele a qualquer
momento, o feedback que até aqui temos proporcionado ao usuário é algo pobre: a hélice não nos
serve para conhecer qual é o estado real do download, nem fazemos ideia quanto mais teremos
que esperar.

No entanto, curl está emitindo um informe pela saída padrão de erros stderr (o texto
impresso pelo console, se não estivermos familiarizado com esse termo) que podemos aproveitar.

Redirecionando a saída padrão de erros


Se estivermos familiarizado com a linguagem C, as mensagens são enviadas a saída
padrão com a função printf() e na saída padrão de erros mediante perror(), ambas incluída na
biblioteca padrão de C (glibc no sistema GNU/Linux).

Os processos podem enviar textos para o console por duas vias, a primeira é
utilizando a saída padrão e, a segunda, a saída padrão de erros. Os dois caminhos
separados são utilizados para diferenciar que tipo de mensagens são enviadas. Para a
saída estândar (stdout) são emitidas informações úteis. Como veremos no paragrafo
seguinte, o programa curl emite o arquivo recebido para a saída padrão salvo que
indiquemos expressamente onde salva-lo. Pela saída padrão de erros(stderr) se
emitem mensagens de estado, de advertência ou de erro. Curl aproveita esta saida
para indicar o estado do download ou para informar um erro na conexão.

Nos concentramos mais em curl, se aplicarmos o parâmetro -#, o programa vai mostrando
uma barra de progresso formada pelo símbolo # (almofadinha ou sustenido) e um indicador do
tanto por cento baixado.

Gambas permite recolher o conteúdo tanto da saída padrão como da saída padrão de erros,
se aplicarmos o flag FOR READ na hora de executa-lo.

Quando o processo filho envia uma cadeia pela saída padrão de erros (chamado de
perror(), se for escrito em C), nosso programa Gambas receberá um evento Error() procedente
da classe Process. A sintaxe deste evento é:
PUBLIC SUB Process_Error(sError AS String)

............

END

Na cadeia sError receberemos a mensagem de erro, a informação procedente do processo


filho. Vamos agora modificar o programa para receber a porcentagem de downloads e representa-
lo no Label no lugar da hélice.
PRIVATE hCancelar AS Boolean

PUBLIC SUB BtnCancelar_Click()

hCancelar = TRUE

END

PUBLIC SUB Process_Error(Err AS String)

DIM sCad AS String[ ]

Err = Trim(Err)

sCad = Split(Err, " ")

LblInfo.Text = sCad[sCad.Count - 1]

END

PUBLIC SUB BtnDownloads_Click()

DIM hProc AS Process

DIM sUrl AS String

hCancelar = FALSE

BtnCancelar.Enabled = TRUE

sUrl = "http://gambas.gnulinex.org/radiogambas/radiogambas-1.0.1.tar.gz"

hProc = EXEC ["curl", sUrl, "-o", User.home & "/RadioGambas.tar.gz", "-#"] FOR READ

DO WHILE hProc.State = Process.Running

WAIT 0.1

IF hCancelar = TRUE THEN

hProc.Kill()

message.Warning("Processo Cancelado")

LblInfo.Text = "Inativo"

BtnCancelar.Enabled = FALSE

RETURN

END IF

LOOP

LblInfo.Text = "Inativo"

Message.Info("Download Finalizado")

END

Nesta ocasião executamos o programa curl com o parâmetro adicional -#. cada vez que
nosso programa recebe um evento Error de Process, este é tratado em nosso código: tomamos a
cadeia e eliminamos com Trim() os possíveis caracteres especiais de controle que curl usa para
manter
o cursor sempre na mesma linha, de modo que da a impressão que a barra de progresso avança (se
trata dos chamados caracteres ANSI de controle), separamos a cadeia em várias sub cadeias, tomando o
caracter de espaço como base e nós ficamos com a última sub cadeia, que é a que contém os dados de
porcentagem, para representa-la no Label.

Redireção da saída padrão


Suponhamos agora que não desejamos guardar um arquivo, e sim mostra-lo diretamente seu
conteúdo na tela. Por exemplo, URL http://www.gnu.org/licenses/gpl.txt, dispomos de um arquivo que é
um texto plano, contem a licença GPL original em inglês.

A primeira opção seria salvar o arquivo e, uma vez finalizado o processo lê-lo e representa-lo na
tela, mas deste modo complicamos o código já que teríamos que criar um arquivo em algum local (por
exemplo, /temp), lê-lo e logo apaga-lo para não deixar restos no disco rígido. Como indicamos no
parágrafo anterior, o conteúdo da saída padrão também pode ser lido e curl envia o arquivo a saída
padrão salvo que, como em nosso exemplos anteriores, especificamos um arquivo onde depositar os
dados recebidos. A sintaxe do evento gerado para a recepção de dados procedentes da saída padrão de
processo, é diferente a dos erros ou informativos que vimos antes:
PUBLIC SUB Process_Read()

............

END

Neste caso não há nenhuma cadeia para receber a informação, pelo contrário, cada objeto
Process se comporta como um fluxo ou estream, o que em outras palavras significa que podemos
trabalhar do mesmo modo que faríamos com arquivos aberto com OPEN, podendo utilizar, por tanto,
READ ou LINE INPUT.

Temos que recordar também que Gambas provê uma palavra chave, LAST, que dentro do evento
represente de forma genérica o objeto que o gerou. Podemos, no entanto, ler o conteúdo da saída padrão
do processo utilizando LAST, como parâmetros das instruções relacionadas com leitura e escrita de
processos. Por exemplo, para ler uma linha completa faríamos:
PUBLIC SUB Process_Read()

DIM sCad AS String

LINE INPUT #LAST, sCad

PRINT sCad

END

Para nosso programa, adicionaremos uma


caixa de texto (TextArea), chamada TxtLicenca,
com seu texto inicial em branco e nela ponhemos o
conteúdo do arquivo gpl.txt, conforme o
recebemos.

O código é o seguinte:
   

  
PRIVATE hCancelar AS Boolean
PUBLIC SUB BtnCancelar_Click()
hCancelar = TRUE
END

PUBLIC SUB Process_Read()


DIM sCad AS String
LINE INPUT #LAST, sCad
TextLicenca.Text = TextLicenca.Text & sCad & "\n"
END

PUBLIC SUB Process_Error(Err AS String)


DIM sCad AS String[ ]
Err = Trim(Err)
sCad = Split(Err, " ")
LblInfo.Text = sCad[sCad.Count - 1]
END

PUBLIC SUB BtnDownloads_Click()


DIM hProc AS Process
DIM sUrl AS String
hCancelar = FALSE
BtnCancelar.Enabled = TRUE
hProc = EXEC ["curl", "http://www.gnu.org/licenses/gpl.txt", "-#"] FOR READ

DO WHILE hProc.State = Process.Running


WAIT 0.1
IF hCancelar = TRUE THEN
hProc.Kill()
message.Warning("Processo Cancelado")
LblInfo.Text = "Inativo"
BtnCancelar.Enabled = FALSE
RETURN
END IF
LOOP

LblInfo.Text = "Inativo"
Message.Info("Download Finalizado")
END

No evento Read() lemos uma linha e adicionamos ao conteúdo prévio de TextLicenca,


mais um retorno do carro para separar cada linha.

Evento Kill() e a propriedade Value


Além do método Kill(), a classe Process emite um evento Kill() quando um processo é
finalizado, de forma normal ou por um erro. A sintaxe desse evento é:

PUBLIC SUB Kill()

............

END
Até agora, em nosso gerenciador de downloads temos esperado em um loop até que o
processo acabe, mas podemos criar uma estrutura mais elegante, mais adaptada a programação
orientada a objetos, valendo-nos desse evento: em lugar de esperar em um loop, aguardamos
tranquilamente sem fazer absolutamente nada em nosso programa principal até receber o evento
Kill(), seja porque o download finalizou bem ou porque o usuário decidiu cancelar o processo:
PRIVATE hCancelar AS Boolean

PRIVATE hProc AS Process

PUBLIC SUB BtnCancelar_Click()

hCancelar = TRUE

hProc.Kill()

END

PUBLIC SUB Process_Error(Err AS String)

DIM sCad AS String[ ]

Err = Trim(Err)

sCad = Split(Err, " ")

LblInfo.Text = sCad[sCad.Count - 1]

END

PUBLIC SUB Process_Kill()

IF hCancelar = TRUE THEN

Message.Warning("Processo Cancelado")

ELSE

Message.Info("Download concluido")

END IF

LblInfo.Text = "Inativo"

BtnCancelar.Enabled = FALSE

END

PUBLIC SUB BtnDownloads_Click()

DIM sCad AS String

TextLicenca.Text = ""

hCancelar = FALSE

BtnCancelar.Enabled = TRUE

hProc = EXEC ["curl", "http://www.gnu.org/licenses/gpl.txt", "-#"] FOR READ

END

PUBLIC SUB Process_Read()

DIM sCad AS String

LINE INPUT #LAST, sCad

TextLicenca.Text = TextLicenca.Text & sCad & "\n"

END
Trocamos a declaração de hProc da função BtnDownload_Click() ao inicio de
nosso formulário, para que seja acessível também do evento BtnCancelar_Click(). Agora
lançamos um processo e já não esperamos em um loop. Se o usuário decidir cancelar o
download, matamos o processo no mesmo evento BtnCancelar_Click().

Tanto faz o programa terminar normalmente como devido a um cancelamento,


aproveitamos o evento Process_Kill() para informar ao usuário é devolver a interface a seu
estado inicial(com o botão BtnCancelar desabilitado, e o Label marcando Inativo).

O resultado final: menos gastos de recursos (o programa principal não tem que executar o
loop constantemente), menos linhas de código e melhor estruturação de todo o processo.

Por outro lado, a maior parte de programas de console (e gráficos) devolvem


ao sistema um código de erro quando finalizam. O costume é que se devolve um zero
se todo acorrer bem e outro valor quando algo falhou.

A partir do console podemos fazer um teste executando em uma janela de terminal estes
dois comandos consecutivos:
$ Ls -l /dev

$ echo $?

Ao executarmos echo $? obteremos um zero. O shell do sistema armazenou o ultimo


código de erro disponível na varável $? e, em seguida o mostramos na tela: tudo ocorreu bem.
Agora executamos o seguinte comando:
$ Ls -l /arquivo/que/não/existe

$ echo $?
Nesta ocasião obteremos um seis, um código de erro que no caso do comando ls implica
que o caminho não existia (sempre e quando não haja, por uma estranha razão que
desconhecemos, um arquivo em nosso sistema cujo caminho seja /arquivo/que/não/existe).

Se curl não poder acessar a URL, por qualquer circunstância, retorna um valor diferente
de 0. Nós podemos ler em nosso código esse valor, mediante a propriedade Value, e informar ao
usuário o erro. A seguir modificamos o código do programa anterior de forma que o evento Kill
fique assim:

PUBLIC SUB Process_Kill()

IF LAST.value <> 0 THEN

Message.Error("Erro no Download")

ELSE

IF hCancelar = TRUE THEN

Message.Warning("Processo cancelado")

ELSE
Message.Info("Download completo")

END IF

END IF

LblInfo.Text = "Inativo"

BtnCancelar.Enabled = FALSE

END

Testamos o programa desconectando o modem da internet desencaixando o cabo da rede ou


desabilitando a rede de nosso sistema. Observamos agora que podemos determinar com exito a
falha do download, alem de seu cancelamento.

Redirecionando a saída padrão, o uso de CLOSE

Os programas alem de emitir informações através de stdin e stderr, podem receber através
do console, conforme o usuário digite as ordens. Esta recepção realiza-se mediante a entrada
padrão stdin, e se tivermos conhecimento de C sabemos que podemos utilizar com funções
scanf() ou getchar(). Com o Gambas, podemos empregar o flag FOR WRITE para indicar ao
interpretador que estamos interessados em escrever dados para o processo filho. Uma vez
lançado o processo deste modo, podemos usar as funções normais de escrita de arquivos com o
descritor de processo (PRINT, WRITE).

Suponhamos um programa com uma área de


texto (TextArea) chamada TxtTexto, na qual
escrevemos qualquer coisa, e desejamos conhecer o
número de linhas (separadas com o retorno do carro),
que escrevemos. Podemos para isso usar o comando
wc (significa Word Counter e não outra coisa), com o
parâmetro -l (número de linhas) em nosso exemplo
chamaremos o programa, escreveremos o conteúdo
de TxtTexto ao processo, e receberemos o resultado
pela entrada padrão. Já que vamos ler e escrever,
podemos combinar os flags READ e WRITE. Para
isso criaremos um projeto gráfico com um botão
BtnContar, com o texto Contar, e um TextArea
chamado TxtTexto, com o texto em branco
inicialmente para que depois escrevamos um texto.
 





 
  
  
PRIVATE hProc AS Process

PUBLIC SUB Process_Read()

DIM sCad AS String

LINE INPUT #hProc, sCad

Message("O texto tem : " & sCad & " linhas")

END

PUBLIC SUB BtnContar_Click()

hProc = EXEC ["wc", "-l"] FOR READ WRITE

PRINT #hProc, TxtTexto.Text

CLOSE #hProc

END

Na função BtnContar_Click(), executamos o programa indicando ao


interpretador que desejamos acesso de leitura e escrita ao processo, escrevemos neste o
conteúdo da caixa de texto e, em seguida executamos CLOSE sobre o processo.

Seguidamente, executamos wc -l a partir de um terminal de linha de comandos.


Observamos que podemos ir escrevendo no processo tudo o que queremos, Assim criamos várias
linhas e depois pressionamos de uma vez as teclas Control + D.

É quando nos retorna o número de linhas que escrevemos. Ao pressionarmos o caracter


especial CONTROL + D, estamos indicando ao processo que encerramos o fluxo de dados e,
por tanto, procede que ele compute o recebido e devolva o resultado.

Quando no programa Gambas executamos CLOSE sobre um descritor de arquivos, o


resultado é similar: encerra-se a redireção entre a saída padrão do processo filho e nosso processo
principal, com o qual o primeiro fica informado de que recebeu todos os dados.

O resto do programa é trivial: no evento READ recebemos a cadeia que contem o número
de linhas e o mostramos ao usuário em uma mensagem.

Notas finais sobre o objeto Process


O objeto process tem um método Signal() usado pelo próprio depurador do gambas.
permite enviar sinal para o processo em curso, mas seu uso não é aconselhado, já que interfere
com o resto do código do interpretador.

O objeto dispõe também de uma propriedade id, que é um handle ou descritor de arquivo.
se conhecermos C ou C++, trata-se do identificador de processo ou PID do programa filho que
obtém-se através de uma chamada a fork(), que é a forma de criar novos processos em sistemas
UNIX. este valor pode servir para buscar o processo, por exemplo, executando ps -le, variar sua
propriedade com nice, ao utiliza-los juntos com posteriores chamadas a funções de C (veremos
em outro capitulo como faze-lo a partir do Gambas) para controle de processo.
Os eventos gerados ao trabalhar com processos são estáticos. Observamos que nos
sucessivos exemplos foi indicado Process_Read e Process_Kill. Se tivermos vários
processos em execução, sempre podemos diferenciar que processo gerou o evento mediante a
palavra chave LAST e atuar em consequência.

PRIVATE hProc1 AS Process

PRIVATE hProc2 AS Process

..........

PUBLIC SUB Process_Read()

IF LAST = hProc1 THEN ....

END

4.4 SHELL
SHELL é similar a EXEC, mas neste caso lança o shell ao interpretador de comando do
sistema, habitualmente o BASH, nas distribuições GNU/Linux e o passa o comando que
indiquemos. Isto permite, por exemplo, dar ordem ao sistema tais como export ou cd que são
parte do BASH e não comandos independentes.

Também dá via livre ao uso de tabulações | e operadores de redireção como > ou 2> entre
outras características do shell. A sintaxe de SHELL é a seguinte:

[Variável = ] SHELL Command [WAIT ] [ FOR ( READ | WRITE | READ WRITE) ]

É muito similar ao de EXEC, mas neste caso Commande é uma cadeia de texto e não
uma matriz de cadeias, já que este valor é passado diretamente ao shell do sistema que,
dependendo de suas características, interpretara os espaços e outros símbolos especiais de um
modo ou outro. Fica então, por tanto, como trabalho para o programador, adicionar o formato
adequado para que o shell interprete o comando corretamente.
Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 5: GESTÃO ........................................................................................ 04


5.1 Sistema de base de dados ................................................................... 04
5.2 Base de dados no Gambas .................................................................. 05
5.3 Gambas – Database – Manager, Gestor Gráfico ................................ 06
 Criar uma base de Dados ................................................................... 06
 Criar uma tabela ................................................................................ 09
 Gestionar dados em uma tabela ......................................................... 13
 SQL ................................................................................................... 14
5.4 Programação ....................................................................................... 00
Modelo de Base de dados ............................................................... 165
Conectando-se por código ................................................................ 14
Consulta de dados ............................................................................. 16
Apagar registros ................................................................................ 18
 Adicionar registros ............................................................................ 19
Modificar registros ............................................................................ 22
5.5 Outras características .......................................................................... 25
Estrutura das tabelas ..........................................................................25
Mais utilidades do gestor de base de dados ...................................... 28
5.1 Sistema de base de dados

Um sistema de base de dados é uma aplicação que permite armazenar e consultar


informações de forma simples. Quase todas as bases de dados atuais possibilitam a interação com
o usuário ou programador, através de uma linguagem chamada SQL. É importante antes de
trabalharmos com uma base de dados, conhecermos esta linguagem, mediante a qual podemos
consultar e modificar os dados, além de permitir criar a estrutura da mesma (tabelas, campos,
etc.).

Que está fora do alcance deste livro explicar a linguagem SQL, se bem que há
algumas noções básicas ao longo dos exemplos exposto.
Existem muitas bases de dados diferentes, mas podemos classifica-las em dois tipos.
No primeiro, existe um programa chamado servidor de base de dados, que gestiona a informação
armazenada. O servidor mantem diversas base de dados, controla as permissões de acesso de cada
usuário e permite varias conexões simultaneamente a partir de diversos equipamentos cliente. A
estrutura interna das bases é tarefa do servidor, não do programador da aplicação. Quer dizer, que
não devemos nos preocupar de aspecto tais como a localização dos arquivos que contem cada base
ou tabela. Um exemplo deste modelo é MySQL (http://www.mysql.org).
No segundo tipo, a base de dados não é mais que um arquivo alojado em nosso disco rígido.
O programador haverá de localizar a base, manter as cópias de segurança e realizar a manutenção
que proceda. Normalmente são bases de dados locais, acessível só a partir do próprio equipamento, e
habitualmente não permitem mais de uma conexão de forma simultânea, ao menos em modo
leitura/escrita. A mudança, consome muito menos recursos do PC, não requer a instalação e
administração de um servidor de base de dados e muito mais fácil de transportar (por exemplo, pode
ser gravado em um CD). Em GNU/Linux, a base de dados mais popular deste tipo, talvez seja o
Sqlite (hhttp://www.sqlite.org).
È hora de desenhar nossa aplicação, temos que ter presente dois aspectos fundamentais: o
volume de dados a manipular e a simplicidade de administração requerida. Para uma aplicação
pequena, como uma agenda pessoal, uma base de dados de Cds e DVDs, as informações relativa aos
alunos de uma classe ou a administração de um pequeno comercio, uma base de dados Sqlite é
suficientemente poderosa e com uma instalação simples. O cliente pode receber o programa e
instala-lo com pouca ajuda adicional, a base pode ser criada na primeira vez que o programa iniciar
sem configuração previa e o usuário pode fazer suas cópias de segurança periodicamente e leva-las
pra casa em um CD ou em um dispositivo USB para seguir trabalhando.
Para a administração, por exemplo, de um comercio grande, uma aplicação de gestão de
pesquisas científicas, a consulta de alunos e inscrições em um instituto, ou as informações de
pacientes de um hospital, uma base como o Sqlite é totalmente inadequada, tanto pelo volume de
dados armazenados, como pela necessidade da consulta e inserção de dados a partir de diversos
postos de trabalho de forma simultânea. Aqui teremos que estudar que servidor de base dados é a
mais adequada para nosso sistema. No ambiente de software livre, as duas opções mais aprovada e
de maior prestigio são. MySQL e PostgresSQL (http://www.postgresql.org/). ambos os sistemas,
possuem, versões com suporte comercial.

5.2 Base de dados e Gambas


Gambas tem estrutura de acesso a base de dados mediante drivers. Este são módulos de
código escritos por diversos programadores especificamente para comunicar-se com uma base de
dados determinada, o que permite acessar a distintas base utilizando o mesmo código. Como
veremos mais adiante, basta especificarmos o tipo de base de dados a utilizar, e o resto do código
funcionará, possivelmente, sem modificações, independente da base de dados utilizada.
Gambas pode manipular vários tipos de base de dados. Dispõe-se até o momento de três
drivers específicos: Sqlite, MySQL e Postgres. Alem de contar com um driver ODBC, o qual é um
padrão para comunicar aplicações com base de dados. Por tanto, podemos acessar com o Gambas a
qualquer base que suporte esse padrão. Isso permite entrar, por exemplo, a bases de
MS SQL Server ou Firebird.

Na hora de escolher um driver ou outro, levaremos em conta que os drivers específicos


estão optimizados e oferecem uma maior velocidade de transferência de dados. Só quando não
dispomos de um específico usaremos o ODBC.

Adentrando-nos na estrutura do Gambas para base de dados, qualquer aplicação que use
esta característica, necessitará do componente gb.db como dependência. Os drivers para cada
sistema de base de dados são também componentes, mas o programador não tem que marca-los
como dependência. Uma vez que indiquemos a que sistema nós vamos conectar, o interpretador
do Gambas tratará de carregar o driver específico.

Estes componentes especiais são:


.gb.db.sqlite: Sqlite versão 2 ou anterior.
.gb.db.sqlite3: Sqlite versão 3 ou superior.
.gb.db.MySQL: MySQL.
.gb.db.PostgreSQL: PostgreSQL.
.gb.db.odbc: genérico ODBC.

5.3 Gambas-database-manager, o gestor gráfico


Antes de explicar o modelo de programação para base de dados do Gambas, vamos
aprender a usar o Gestor de Base de Dados, que é uma aplicação escrita neste programa, o qual é
dotado de um ambiente gráfico de desenvolvimento de uma ferramenta para administrar, de
forma fácil, múltiplas bases de dados.

O Gestor de Base de Dados escrito em Gambas, é um programa extenso e é


Software Livre. Por tanto, o estudo do seu código nos pode ajudar resolver eventuais
dúvidas que surjam na hora de usar o componente gb.db.

Criar uma base


Vamos aprender a criar nossa própria base. Para isso desenvolveremos um projeto novo,
se bem que poderíamos abrir qualquer um já existente em nosso equipamento. Vamos até o menu
ferramentas e selecionamos o Gestor de Base de Dados. Tambem podemos executar
diretamente a partir do console, com o comando gambas-database-manger.

Depois de pressionarmos sobre a opção correspondente, nos pedirá uma contra senha.
Esta contra senha emprega-se para encriptar os dados de usuário e senhas que desejemos
administrar a partir deste programa, para evitar que
outro usuário possa lê-lo com facilidade consultando os
arquivos do ambiente Gambas. A contra senha que
introduziremos haverá de ter no mínimo 8 caracteres, e
será perguntada a cada vez que iniciarmos o programa,
para decriptar senhas já armazenadas em sessões

    prévias.
Após este passo, aparece o gestor em si,
que a princípio está vazio, já que não
configuramos nenhuma conexão.

     




Pressionamos o menu Servidor e


escolheremos a opção Novo servidor .... temos que
especificar, em seguida, os dados relativos a conexão
que desejamos estabelecer (figura 4).


   

O primeiro dado Tipo, se refere ao driver que


empregaremos: sqlite, sqlite3, mysql, postgres ou
odbc. Host é o nome do computador ou o endereço
IP do servidor de base de dados. Com exceção, para
as conexões sqlite o host será apenas a pasta onde
encontra-se alojada as bases de dados, quer dizer,
uma rota absoluta dentro do sistema de arquivos, por
exemplo, /home/usuário/base.

   

O terceiro e quarto dados são o nome de Usuário e a Senha para acessar ao sistema de
base de dados, que determina os diversos privilégios do usuário.

No caso excepcional de Sqlite as bases de dados são simplesmente arquivos, por isso as
permissões serão definidas pelos privilégios do usuário e seu grupo no sistema de arquivos
(leitura/escrita). Não procede neste caso, por tanto, especificar os dados de usuários/senha.

Suponhamos dois senários básicos. No primeiro trabalhamos sobre um servidor MySQL


em nosso computador, com um usuário chamado admin e uma senha para esse usuário. Em Tipo
indicaremos o driver mysql; como Host, tratase do próprio computador, podemos muito bem
indicar localhost ou 127.0.0.1, que é o endereço IP que sempre aponta para o próprio PC.
Introduziremos depois os dados, nome Usuário e Senha.

E quanto a instalação e administração de MySQL, é recomendado consultar a


documentação disponível na própria pagina desta base de dados:
http://dev.mysql.com/doc/
No segundo cenário, com o que trabalharemos adiante, criaremos uma base Sqlite. A
primeira coisa que temos que fazer é criar uma pasta nova para armazenar o arquivo que conterá
a base de dados. Para isso, a partir da linha de comandos, em nossa pasta pessoal, podemos fazer:

mkdir Bases

Também podemos criar a pasta com o


Konqueror ou Nautilus, se assim preferirmos.

Agora preencheremos os dados de conexão, tal e


como aparece na Figura 5.

    


 
Poderemos selecionar sqlite ou sqlite3 dependendo do gestor instalado em seu
sistema. Para novos desenvolvimento é recomendado dispor da versão 3 ou posterior,
ela está mais optimizada. Mas se tivermos que programar para sistema antigos (por
exemplo, um cliente que disponha do RedHat 7.0) talvez devêssemos usar sqlite em
suas versões anteriores, a fim de não ter que compilar e instalar novas bibliotecas no
sistema, questão que as vezes o cliente recusa ou impõe.

Uma vez incluídos os dados, pressionamos OK e


o novo servidor ficará refletido na arvore da esquerda do
gestor. Se criarmos diversos servidores, (diversas pastas,
algumas apontando a servidores MySQL ou Postgres,
por exemplo), irão adicionando-se a árvore.
  
 

Para conectarmos, faremos duplo click com o


botão esquerdo do mouse sobre os servidor (ou o botão
direito, no caso do mouse está configurado para destro),
e depois um click como o botão direito para que mostre-
se o menu contextual de opções (ou esquerdo para os
destros).
      

A opção Criar usuário ... tem sentido se


trabalharmos sobre (mysql ou postgres), e tenhamos
permissão de administração sobre o servidor. O uso desta
opção é trivial: pergunta-se o nome se usuário e senha e você
tem permissão de administração; é só pressionar OK para
adiciona-lo. Mais adiante veremos algum exemplo concreto.
Selecionamos a opção Criar base. Nos perguntará o nome 

da base, que no caso de Sqlite será o nome do arquivo dentro da
pasta que previamente adicionamos, o qual alojará a base de
dados que vamos criar. Sinalamos com o nome provas e
pressionamos OK. Já dispomos de uma base vazia, onde
criaremos as várias tabelas que alojarão os dados.
 

Criar uma tabela

Para criar uma tabela, como no caso do


servidor, damos um duplo click para abrir a base e
pressionamos o botão direito para obter seu menu
contextual (Figura 10).

Selecionemos Criate table... os dados


necessários são o Nome da tabela (ponhamos dados em
em nosso exemplo), e o Tipo.      


Este segundo parâmetro só faz sentido em


determinados sistemas servidores, como MySQL, nos
quais podemos indicar vários tipos de tabelas,
optimizada para uma ou outra área concreta. Salvo que
tenhamos necessidades especiais, podemos desejar os
dados com valores padrão.
     

Uma vez que pressionemos OK, o gestor aparece no lado direito mostrando-nos a
estrutura atual de nossa tabela dados (vazia), para que adicionemos a informação aos campos que
apareceram.

Portanto vamos manipular a aba Campos, com a qual criaremos uma tabela para
armazenar dados dos livros de nossa própria
coleção.

Os campos serão: identificador


único, um número que por sua vez
pegaremos no lombo (forrado) para busca-
lo rapidamente, o título do livro, o autor, a
faixa de adequação, o preço que pagamos
por ele e uma breve descrição.
  
Agora temos que pensar nos tipos de dados que armazenaremos em cada campo:

.Identificador: um número inteiro. (integer)

Título: uma cadeia de texto. (string)

Autor: uma cadeia de texto. (string)

Data: um dado tipo data. (date)

Preço: um número real. (float)

Descrição: uma cadeia de texto. (string)

Pensar nos tipos de dados evitará posteriores problemas na hora de fazermos


buscas, ordenar os dados por diversos parâmetros, assim como reduzir o tamanho da
base de dados e optimizar a velocidade de consulta. Este passo é muito importante e
vale a pena dedicar algum tempo.

Também temos que delimitar a chave principal, que é única e serve para identificar cada
registro armazenado. A chave única estará formada por vários campos ou um só. Em nosso caso
trata-se do campo referido ao número de identificação.

Alguns programadores em outros ambientes desenhavam tabelas sem chaves


únicas, já que alguns sistemas de base de dados o permitem. Como resultado
geravam-se tabelas propensas a erros, com dados duplicados, lentas de manipular e
muito difícil de administrar, conforme passavam o tempo e os dados aumentavam,
corrompendo-se as vezes por duplicações. Jamais devemos trabalhar deste modo: se
perde toda a vantajem que pode ter uma base de dados relacional, e impede a futura
expansão e manutenção de uma aplicação.

Para criarmos o primeiro campo, modificaremos o que o próprio gestor criou como
sugestão, indicando como Nome identificador e Tipo Integer (ou seja número inteiro).
Deixaremos os demais campos em branco.

Para o resto dos campos,


pressionemos o botão com o ícone
que representa uma folha em
branco, e que serve para adicionar
um novo campo. Se nos movermos
pelos diversos ícones, aparece um
texto de ajuda que nos indica o
significado de cada um.



  
Conforme os criamos, preenchemos os dados com estes valores:

.Nome: título; Tipo: String; Tamanho: 40; Valor por default: (nada).

.Nome: autor; Tipo: String; Tamanho: 40; Valor por default: (nada).

.Nome: data; Tipo: Data; Tamanho: (nada); Valor por default: (nada).

.Nome: preço; Tipo: Float; Tamanho: (nada); Valor por default: (nada).

.Nome: Descrição; Tipo: String; Tamanho: 40; Valor por default: (nada).

A interface deverá ficar com este aspecto:

Porem o interpretador do Gambas é capaz de reconhecer uma matriz indicada


nas cadeias entre colchetes e, dessa forma, nos livramos de umas quantas linhas de
código.

! ∀ 


 

O dado Length aplica-se apenas aos campos do tipo String, e refere-se ao número de
caracteres que podemos armazenar. Convém ajustar o máximo possível o valor para que a base
de dados não cressa em demasia e, tornando as buscas e modificações posteriores mais lentas.
Mesmo que só guardamos um caracter em um campo de um dado registro, os sistemas de base de
dados habituais, guardam todo o tamanho indicado (em nosso caso 40 caracteres para o título e
autor, e 200 para a descrição). Se multiplicarmos estes valores por um grande número de
registros, comprovaremos rapidamente a economia que se consegue com um desenho prévio dos
tamanhos.

O tamanho máximo de um campo de texto pode ser 200 ou 255 caracteres, em


muitos sistemas de base de dados. Por isso, devemos levar em conta os tamanhos
máximos se planejarmos trocar de gestor de base de dados no futuro, sobre uma
aplicação já desenvolvida.

Quando criamos um novo registro, o campo é preenchido, por padrão, com um valor, que
só modificaremos para casos específico. Isto é útil se um dado se repete muitas vezes, e só
trocmos ocasionalmente.
Como temos observado, Gambas realiza uma abstração dos tipos de dados, permitindo
escolher entre uns muito concretos: Boolean (dois valores, “sim” ou “não”), Integer (número
inteiro), Float (numero real), String (cadeia de texto) e Data (data/hora). Estes tipos abstraem os da
própria base de dados, (alguns sistemas contemplam, por exemplo, “inteiro grande” ou “inteiro de
um byte”) assemelhando-se aos do Gambas e simplificando a transação entre um sistema de bases de
dados e outros.

Se existe necessidade de optimizar os tipos de base de dados para um sistema em


especial, as tabelas devem ser criadas com as ferramentas próprias desse sistema de base de
dados, em lugar da ferramenta genérica do Gambas, mas ao trocar perde-se portabilidade.
Quer dizer será mais difícil trocar de sistema gestor no futuro, se for necessário por aumento
no volume de dados, tecnologia ou velocidade requeridas. Em geral, não é necessário
recorrer a optimizações deste nível, salvo em casos muito especiais.

Quanto a chave principal, podemos observar na Figura 14 que o primeiro campo,


identificador, tem uma chave amarela marcada. Podemos pressionar sobre o primeiro elemento de
cada campo para que apareça ou desapareça tal chave.
A soma de todos os campos que estão marcados com a chave, formam a chave principal ou
identificador único de cada registro. Mesmo em nosso caso, que só temos um.
Estes dados, até agora, encontram-se só em fase de desenho. Para gravar a nova tabela na
base de dados, pressionemos o ícone que tem o símbolo de um floppy (disquete). A partir deste
momento, a tabela aparece realmente na base, lista para adicionar dados ou consulta-los.

Os nomes de campos usados no exemplo não tem acentos. Em geral, não devemos
utilizar somente caracteres do alfabeto inglês e números. Usar símbolos como caracteres
com til, cedilha, separadores tais como o & ou espaço em branco, podem dar muitas dores
de cabeça ao programador no futuro, tanto para uma simples consulta de dados, como para a
migração futura a outro sistema de base de dados. É uma má política de desenho, devemos
evita-la sempre.

Uma vez criada a tabela, podemos editar os campos, adiciona ou deletar, seguindo o mesmo
procedimento.
Agora podemos passar a seguinte aba (Figura 15), Índices. Um índice serve ao gestor de
base de dados para organizar a informação, de forma que mais tarde cada consulta execute-se o mais
rápido possível. Por exemplo, pode ser que desejemos fazer frequentemente buscas indicando o
autor, para saber os dados de todos os livros que tenhamos em nosa coleção.
Para nossa pequena base de exemplo não utilizaremos índices, mas a gestão a partir deste
programa é trivial: basta que os criemos, eliminemos ou modifiquemos do mesmo modo que fizemos
para criar os campos da tabela. Cada índice pode ser formado por um ou mais campos, quer dizer,
pode compreender, por exemplo, o nome do livro + o autor. Para isso, a interface proporciona dois
botões. O primeiro, Novo índice, serve para adicionar um novo índice a tabela. Se for formado por
um só campo, basta que indiquemos o nome desse campo. Se for vários, pressionaremos o botão
novo campo de índice, tantas vezes quanto seja necessário, para indicar os demais campos que
formam esse índice.
O outro dado de relevância é o valor Unique, o qual determina se pode existir ou não dois
registros nos quais os dados dos campos pertencentes a um índice sejam iguais. Por exemplo, em
uma tabela que contenham os dados de Cds, pode interessar-nos ter um índice criado a partir dos
campos discografia e data. E pode haver vários discos editado por essa discografia em um mesmo
ano, portanto marcaremos o valor Unique como FALSE, para introduzirmos os dados de vários
discos cujos campos sejam coincidentes.
Pelo contrário, em uma base de
dados de alunos pode interessar-nos um
índice que compreenda o RG e seu
nome. Neste caso, não há duas pessoas
com o mesmo RG, logo esse índice é
único, e marcaremos a opção Unique
com o valor TRUE para proteger a tabela
de dados duplicados.
 
 !

Gestionar dados de uma tabela

A última aba da interface do gestor de base de dados, Dados, nos permite consultar e
modificar o conteúdo da tabela. Dispõe de três botões: Novo registro (folha em branco), Apagar
registro (cruz vermelha) e Salvar dados (floppy). Temos que ter em mente que, ao trabalhar com
esta interface, os dados que adicionamos, eliminamos, ou modificamos, só atualizam-se realmente na
base de dados quando pressionamos o botão para salvar.
Cada vez que pressionamos o botão Novo registro, cria-se uma linha, correspondente a um
novo registro na tabela atual. Podemos nos mover pelos diversos campos desse registro, para
adicionar os dados correspondentes. Também, se a tabela já contem dados, podemos modifica-los ou
podemos selecionar um registro e elimina-lo com a opção Apagar registro.
Neste último caso o registro se põe de uma cor distinta para assinalar que na próxima
atualização este registro desaparecerá, o qual nos permite assegurar-nos que a solicitação é correta
antes de pressionarmos o botão Salvar dados.

Introduzimos
registros de livros utilizando
esta interface, para que
disponhamos de alguns
dados com os quais
trabalharemos mais adiante.
Recordemos que é
importante pressionar o
botão Salvar dados quando
estivermos terminado a
!    edição.
SQL
Sem escrever um código Gambas, já podemos
começar a trabalhar com a linguagem SQL. Como
podemos observar, um dos botões do gestor de base de
dados do Gambas contem um texto SQL. O
pressionaremos para escrever sentenças nesta
linguagem. 
∀#∃
Nossa primeira consulta servirá para obter uma
lista de todos os dados da tabela que criamos. Para os que
não estão muito familiarizados com a linguagem SQL,
esclarecemos os seguintes pontos: a introdução da
consulta começa com a instrução SELECT, em seguida
determinaremos o que queremos consultar (em nosso
caso todos os campos, para o qual empregaremos o
símbolo *) e depois é só usar a palavra chave FROM para
#∃ ∃ ∀#∃
indicar onde obter esses dados.
O objeto Connection dispõe de uma série de propriedades para determinar o tipo de
sistema de base de dados, o nome da base, a localização do recurso (pode ser um endereço no
sistema de arquivos ou um endereço IP), e os dados do usuário que deseja conectar-se a este
sistema. Esta classe, junto com a classe Database, tem a informação necessária sobre nossa base
de dados. A classe User, nosso usuário e, por tanto nossas permissões de acesso.
A informação de uma base de dados é armazenada em diversas tabelas. Cada tabela pode
se imaginar como uma rede bidimensional, ao estilo de uma folha de cálculo. Na horizontal
temos os cabeçalhos que nos indicam a informação de cada campo de nossa tabela. Em Gambas,
o componente gb.db comporta uma classe Table, que define a estrutura de uma tabela, e a classe
Field que determina as características de um campo de uma tabela.
Na vertical vão se acumulando os diversos registros com informações. Quando se trabalha
com base de dados relacionais, o programador não se limita a folhear o conteúdo de uma tabela,
empregando instruções SQL, podemos consultar por intermédio de valores, somas, uniões
coerentes dos dados de várias tabelas, dados agrupados por uma chave ou ordenado segundo
algum critério, etc.
Em todo caso, o sistema de base de dados retorna sempre o resultado da consulta como
uma serie de registros que dispõem de vários campos, quer dizer, em um formato similar ao das
próprias tabelas. Os objetos da classe Result proporciona o acesso a esses registros e campos de
informações, como se tratasse de uma tabela.

Conectando-se por código


Vamos conectar-nos a base de dados Sqlite que criamos no primeiro parágrafo.
Dependendo de nossa versão de Sqlite, usaremos o driver chamado sqlite ou o sqlite3.
Para isso criemos um projeto gráfico
chamado MisLivro. Criaremos também um
formulário chamado Fmain, que será o de
arranque, e, dentro dele um controle
ColumnView chamado Tabela. Este
controle ColumnView mostrará os dados da
tabela que havíamos criado nos exercícios
 ∀ %% anteriores

Agora vamos criar uma função que se conecte com a base de dados dentro do código do
formulário:

PRIVATE hCon AS Connection

PRIVATE FUNCTION ConectarBase() AS Boolean

IF hCon <> NULL THEN RETURN FALSE

hCon = NEW Connection

hCon.Host = "/home/Usuário/Bases"

hCon.Name = "provas"

hcon.Type = "sqlite"

TRY hCon.Open

IF ERROR THEN

hCon = NULL

message.Error("Erro ao conectar com a base")

RETURN TRUE

END IF

RETURN FALSE

END

Definimos um objeto Connection que será acessível em todo o formulário e representa a


conexão a nossa base de dados. Depois escreve-se uma função ConectarBase que retorna
FALSE se tiver exito ou TRUE se falhou.
O código implica estes passos:
1. Se já existe uma conexão (se o objeto hCon não é nulo), retornamos imediatamente
indicando que já existe uma conexão FALSE, desta forma poderemos chamar sistematicamente
esta função de vários pontos sem nos preocuparmos se já existe a conexão ou não.
2. Criamos o objeto Conexão, que a princípio não está conectado a nenhuma base, e
proporcionamos as informações necessária para conectar: preenchemos a propriedade Host, que
no caso de uma base de dados com servidor poderia ser um endereço IP, mas no caso do Sqlite é
uma local dentro do sistema de arquivos (lembre de substituir /home/cientista/ pelo local
onde
Você armazenou a base do exemplo); depois indicamos o nome da base de dados (provas), que
no caso do Sqlite é também o nome do arquivo que está dentro da pasta /home/usuário/Bases; e
por ultimo indicamos o tipo de base de dados que nos conectaremos, e que será sqlite ou sqlite3
de acordo com o desenho que realizamos com o gestor de base de dados do Gambas. Se nossa
conexão for com um servidor MySQL, por exemplo, também teríamos que preencher as
propriedades Login e Password, com o nome de usuário e senha de acesso ao servidor.

Já que não estamos realmente conectados, podemos preencher todos estes


dados na ordem que desejarmos.

3. depois tratamos de abrir a conexão. Se não for possível (por exemplo, por um endereço
incorreto ou uma falha no servidor), disparará um erro, que capturamos com o comando TRY.
4. Se ocorrer um erro, fazemos nulo novamente a conexão falida e retornamos com o
valor de erro, que segundo decidimos é TRUE.
5. se pelo contrário tivermos exito, retornamos o valor correspondente (FALSE).
A seguir, vamos criar uma função a que chamamos para encerrar a conexão. O algorítimo
é simples, se não houver nenhuma conexão, retornamos, caso contrário, encerramos a conexão e
a fazemos nula para que em uma próxima chamada a ConectarBase perceba-se que não há uma
conexão ativa.

PRIVATE SUB EncerrarConexao()

IF hCon = NULL THEN RETURN

hCon.Close()

hCon = NULL

END

Consulta de dados

Ao abrir-se o formulário, abriremos a conexão, leremos os dados disponíveis, os


representamos em nosso controle Tabela e encerramos a conexão. Isto seria representado da
seguinte maneira:

PUBLIC SUB Form_Open()

DIM hResul AS Result

DIM chave AS String

tabela.Clear()

IF conectarBase() THEN RETURN


tabela.Columns.Count = 5

tabela.Columns[0].Text = "Título"

tabela.Columns[1].Text = "Autor"

tabela.Columns[2].Text = "Data"

tabela.Columns[3].Text = "Preço"

tabela.Columns[4].Text = "Descrição"

hResul = hCon.Exec("select * from dados")

DO WHILE hResul.Available

Chave = hResul["titulo"]

tabela.Add(Chave, Chave)

tabela[chave][1] = hResul["autor"]

tabela[chave][2] = hResul["data"]

tabela[chave][3] = hResul["preco"]

tabela[chave][4] = hResul["descricao"]

hResul.MoveNext()

LOOP

EncerrarConexao()

END

O código trata de abrir a conexão e se fracassar sai da função. Se todo for bem, define
cinco colunas em nosso controle Tabela, põe o título e os cabeçalhos de colunas e continua
preenchendo os diferentes registros. Para efetuar esta tarefa, primeiro consultamos os dados da
tabela, empregando o método Exec de nosso objeto hCon, o qual passamos como parâmetro a
consulta SQL e nos devolve um objeto de classe Result, que contem cada um dos registros
proveniente da consulta.
O objeto Reult tem um ponteiro interno que em cada momento aponta a um dos registros.
No estado inicial aponta ao primeiro registro, se é que haja algum. Este objeto dispõe de uma
serie de métodos para mover o ponteiro. MoveNext (o de nosso exemplo) o move ao registro
seguinte, se existir; MovePrevious, ao anterior; MoveFirst, ao primeiro; e MoveLast, ao ultimo.
Com o MoveTo podemos especificar um registro concreto a nos deslocar.
Quando estamos situados em um registro, Result nos permite obter os dados
correspondente a um campo, indicando o nome do campo como se o objeto fosse uma Array: em
nosso exemplo, hResul[“autor”] nos devolve o valor do campo autor no registro atual.
Se como resultado de um movimento do ponteiro teremos sobrepassado o último registro
ou estamos antes do primeiro, a propriedade Available toma o valor False, enquanto que se nos
encontrarmos apontando a um registro, Available toma o valor True. Igualmente, se não houver
registro resultante da consulta, Available valerá False. (Available significa Disponível em
inglês)
Com estes conhecimentos já podemos realizar um loop para preencher os dados de nossa
tabela.

Enquanto a propriedade Available for True, tomamos como chave o valor do campo
“titulo”, adicionamos uma linha em nossa tabela identificada por uma chave, e cujo texto (a
primeira coluna) é essa chave. Preenchemos o resto dos campos com os valores proveniente do
objeto hResul, e nos movemos ao registro seguinte. Após sair do loop, encerramos a conexão e
abandonamos a função.

Como último retoque antes de executar, ponhamos a propriedade Sorted do controle


ColummView a True, de modo que o usuário ao pressionar sobre a cabeça de uma coluna, pode
ordenar a lista pelo campo que preferir. Já que cada registro na lista está identificado pela mesma
chave que tem na tabela, posteriores operações de seleção/modificação por parte do usuário, não
vão ser afetada pela ordem de apresentação.

O resultado final é este:

 ! ∀& #

Aproveitar as possibilidades de ordenação dos controles, pode evitar


sobrecarregar o servidor ou o cliente de base de dados com múltiplas consultas.

Apagar registros
Podemos adicionar um código para apagar registros. Quer dizer que quando o
usuário pressionar a tecla Supr ou Del, o registro que está selecionado será apagado.

PUBLIC SUB Tabela_KeyRelease()

IF key.Delete THEN

IF tabela.Current = NULL THEN RETURN

IF tabela.Current.Selected = FALSE THEN RETURN

IF ConectarBase() THEN RETURN

TRY hCon.Exec("delete from dados where titulo=&1", tabela.Current.Key)

IF ERROR THEN

message.Error("Impossível apagar o registro")

ELSE

tabela.Current.Delete()
END IF

EncerrarConexao()

END IF

END

Aproveitando o evento Key_Release de nosso controle Tabela. Se a tecla pressionada é


Del ou Supr, primeiro comprovamos se existe algum elemento atual na tabela; se não existir,
saímos. Se o elemento atual não estiver selecionado, também saímos. Por outro lado, tratamos de
abrir a conexão, e se já existe executamos uma instrução SQL para apagar o registro cuja chave
coincide com a do registro de nosso controle ColumnView. (por exemplo, uma base de somente
leitura), informa-se ao usuário. Em caso contrário, apaga-se também o registro do controle para
refletir a troca. Encerramos a conexão e saímos da função.

Em geral é preferível usar KeyRelease e MouseRelease, os equivalentes Press, quando se


executa uma ação por parte do usuário. Deste modo, quando for pressionado o botão do mouse, temos
tempo para afasta-lo do controle e cancelar assim uma ação errada, antes de levantar o dedo do
mouse. De outro modo, no evento MousePress poderiamos apagar um registro sem tempo para reagir.

Como podemos observar, no caso seguinte a sentença SQL está construída de um modo
diferente. Temos dois parâmetros, o primeiro é parte da consulta e o segundo contem o elemento
concreto a que nos referimos:
hCon.Exec("delete from dados where titulo=&1", tabela.Current.Key)

O método Exec admite um número variável de parâmetros, e na hora de tratar a sentença


SQL, Gambas substitui os indicadores de parâmetros (&1, &2, &3 .... ) pelo parâmetro que
corresponde em ordem (&1 é o primeiro parâmetro extra, &2 o segundo...). Alem disso, formata
os dados segundo se necessite (por exemplo, este parâmetro corresponde a um dado do tipo
String, que na sintaxe Sqlite requer umas aspas antes e depois). Isto é muito útil para
trabalharmos com dados do tipo data/hora, cadeias de texto e números com decimais, já que a
sintaxe pode variar de um tipo de base de dados para outras, inclusive com a mesma base pode
trocar segundo os parâmetros regionais (formato de data, usar como um ponto para decimais,
etc.). Com este sistema podemos por simplesmente um dado tipo Cadeia, Booleano, Data ou
número, e o Gambas se encarrega de dar o formato adequado.

Deixar ao Gambas a tarefa de dar o formato aos diferentes tipos de dados, evitará muitos
problemas se trocarmos de sistema gestor de base de dados, ou pretendermos que a aplicação funcione
corretamente em vários PC que pode ter diferentes configurações.

Adicionar registros

Vamos adicionar agora um formulário chamado Fdados que nos servirá para introduzir
dados e também, no parágrafo seguinte, para editar os já existente. Este formulário terá cinco
etiquetas, com qualquer nome. Cinco caixas de texto chamadas TxtTitulo, TxtAutor, TxtData,
TxtPreco, e TxtDescricao, e dois botões chamados BtnAceitar e BtnCancelar. O
Aspecto
geral pode ser como o que vemos na figura da
direita (lembramos de por os textos nas
etiquetas de acordo com a caixa de texto
correspondente).

Este formulário receberá uma


referência da conexão com a base de dados,
por tanto, dispõe-se de uma referência a um
objeto Connection.
 ∋ % 
Por isso deveremos escrever no início do código deste formulário o seguinte:
PRIVATE hCon AS Connection

Assim mesmo dispor do método RunNew com o qual chamaremos a partir do formulário
principal, passando como parâmetro nossa conexão à base, e que mostrará o formulário de forma
modal, a espera da introdução de dados por parte do usuário.
PUBLIC SUB RunNew(Data AS Connection)

IF key.Delete THEN

hCon = Data

ME.ShowModal()

END IF

END

O código toma em hCon a referência ao objeto Connection que lhe passemos, e o


mostra. Enquanto o botão de cancelamento, é bem simples: basta indicar ao formulário que se
encerre da seguinte maneira:

PUBLIC SUB BtnCancelar_Click()

ME.Close()

END

E quanto ao botão Aceitar, com ele realizaremos a operação de inscrição de um novo


registro, utilizando a instrução SQL insert into:

PUBLIC SUB BtnAceitar_Click()

TRY hCon.Exec ("insert into dados values (&1,&2,&3,&4,&5)",

TxtTitulo.Text, TxtAutor.Text, Cdate ( TxtData.Text ),

Cfloat ( TxtPreco.Text ), txtDescricao.Text)

' END IF

ME.Close()

CATCH

message.Error ( "Impossível introduzir os dados solicitado" )

END
Indicamos ao objeto Connection que execute a sentença SQL, a qual, para as escritas que
seguem sempre o mesmo padrão:

Insert into [nome da tabela] values ([valor do campo 1], [valor do campo 2]..... )

Para evitar problemas de formato, seguimos empregando os parâmetros adicionais, como


no caso antes comentado.

Os dados de Título. Descrição e Autor passa diretamente, a ser dados tipo texto.

O dado do Preço converte-se a número real com CFloat(), e a Data converte-se a dado
data/hora com CDate().

Se ocorrer um erro, seja por falta de permissão de escrita ou por dados não convertíveis a
datas ou números, por exemplo, captura-se com TRY, e tratamos no bloco de código CATCH
onde indicamos a mensagem de erro ao usuário.

Se pelo contrário, tudo ocorrer bem, encerra-se o formulário após a escrita.

Voltamos agora ao formulário


principal (FMain), e adicionamos dois
botões para completar a interface: um
chamado BtnEscrita para podermos
escrever um novo registro, e outro
BtnSair, para encerrar o programa.
Como sempre o botão de sair é simples
de implementar (lembramos que agora
escrevemos o código em Fmain não em
Fdata):

 
(   ∀
∀ % 

PUBLIC SUB BtnSair_Click()

ME.Close

END

Para o botão de novo registro, como sempre, tratamos de conectar a base, mas nós saímos
se não logarmos. Em seguida chamamos o método RunNew de Fdata. Já que mostra de forma
modal, o código deste formulário fica aqui detido até que o usuário dê uma escrita ou encerre o
formulário.

Após isto, e de novo em nossa função, encerramos a conexão e chamamos o método


Form_Open(), que, como recordamos, encarrega-se de representar os dados na tabela, para
refletir assim a escrita dada pelo usuário.
PUBLIC SUB BtnEscrita_Click()

IF ConectarBase() THEN RETURN

FDados.RunNew(hCon)

EncerrarConexao()

Form_Open()

END

Já dispomos de uma interface básica para escrever. Podemos melhorar muitas coisas.
Entre outras, podemos bloquear os quadros de texto para que só aceite números e o sinal decimal
(no caso do preço), para que só aceite um dado válido como data (no caso do campo Data), e que
comprove o tamanho máximo no caso dos diferentes campos de texto. Também podemos ajustar
o formato da data a adequa-la para o usuário (aqui usamos diretamente o do Sqlite, quer dizer,
dois dígitos do mês/ dois dígitos do dia / dois dígitos do ano), transferir os dados de volta ao
formulário principal para que só se atualize o novo registro em vez de toda a tabela.

Modificar registros
Aproveitamos o formulário de escrita para as modificações. Quando o usuário der um
duplo click sobre um dos registros, aparecerá o formulário de escrita, com os dados do registro
atual, mas desta vez em modo modificação para que o usuário troque só o que queira.
Modificaremos o código do formulário de dados para dispor de um flag para indicar se trabalha
sobre uma escrita ou modificação, dispomos também de uma referência a um objeto da classe
Result, que apontará o registro atual a modificar. O inicio do código do formulário FData ficaria
assim:
PRIVATE Editando AS Boolean

PRIVATE hResul AS Result

PRIVATE hCon AS Connection

No mesmo formulário adicionaremos um novo método que, a diferencia de RunNew


prepara o formulário para entrar em modo edição. Para isso recebemos a referência ao objeto da
classe Result; ponhamos o flag Editando a TRUE; e ponhamos em cada caixa de texto o valor
correspondente. Após isto, o mostramos de forma modal.

PUBLIC SUB RunEdite(Data AS Date)

hResul = Data

Editando = TRUE

TxtTitulo.Text = hResul["titulo"]

TxtAutor.Text = hResul["autor"]

TxtData.Text = hResul["data"]

TxtDescricao.Text = hResul["descricao"]

ME.ShowModal()

END
Temos que modificar também o código do botão Aceitar para que distingua entre escrita
e modificações, e atue em consequência. No caso da modificação, inserir o valor de cada caixa de
texto no campo correspondente, e se existir um erro, trata-se na zona CATCH ao estar protegido
cada inserção com uma instrução TRY. Se houver exito, chama-se o método Update, que faz
com que os valores que introduzimos nos campos sejam realmente atualizados na base de dados.
Após isto, como no caso da escrita, se encerra a descarga do formulário.
PUBLIC SUB BtnAceitar_Click()

IF Editando THEN

TRY hResul["titulo"] = TxtTitulo.Text

TRY hResul["autor"] = TxtAutor.Text

TRY hResul["data"] = TxtData.Text

TRY hResul["preco"] = TxtPreco.Text

TRY hResul["descricao"] = TxtDescricao.Text

TRY hResul.Update()

ELSE

TRY hCon.Exec ("insert into dados values (&1,&2,&3,&4,&5)",

TxtTitulo.Text, TxtAutor.Text, Cdate ( TxtData.Text ),

Cfloat ( TxtPreco.Text ), txtDescricao.Text)

' END IF

ME.Close()

CATCH

message.Error ( "Impossível introduzir os dados solicitado" )

END

Adicionar novas funcionalidades a um elemento já existente, como um formulário, poupa


tempo e código, mas em certas ocasiões pode tornar o novo código complexo e difícil de manter.
Temos que manter o equilíbrio entre custo de programação e manutenção posterior em cada caso,
antes de criar uma nova estrutura de código ou modificar a existente.

De volta ao formulário principal, FMain, aproveitaremos o evento Activate do controle


ColumnView, que se gera ao dar um duplo click, para dar aceso a essa nova funcionalidade.
PUBLIC SUB Tabela_Activate()

DIM hResul AS Result

IF tabela.Current = NULL THEN RETURN

IF ConectarBase() THEN RETURN

hresul = hCon.Edit("dados", "titulo=&1",

Tabela.Current.Key)

FDados.RunEdite(hResul)

Tabela.Current[0] = hResul["titulo"]
Tabela.Current[1] = hResul["autor"]

Tabela.Current[2] = hResul["data"]

Tabela.Current[3] = hResul["preco"]

Tabela.Current[4] = hResul["descricao"]

EncerrarConexao()

END

Como podemos observar, agora o objeto Result não o obtemos com a chamada com o método
Exec e sim com um novo método: Edit. A razão é que as chamadas com Exec, que recebe como
parâmetro numa sentença SQL, é de somente leitura, quer dizer, é possível examinar o conteúdo
de um campo como por exemplo:

Cadeia = hResul [“titulo”]

Mas não é possível atualizar o conteúdo do campo, quer dizer, não podemos realizar:

hResul [“titulo”] = Cadeia

que é precisamente o que nos interessa.

O método Edit não recebe como parâmetro uma sentença SQL, e sim o nome da tabela
que desejamos modificar, e como segundo parâmetro e posteriores, um filtro e os parâmetros
desse filtro. É fácil entender o modo como trabalha se partirmos de uma instrução SQL e
obtermos a parte que nos interessa para o método Edit.

Suponhamos de uma tabela Artigos queremos modificar todos aqueles cujo campo preco
seja superior a 20. uma sentença SQL para obter esse conjunto de registros seria:

select * from Artigos where preco >20

Para construir a chamada com o método Edit teríamos:

Nome da tabela: Artigos


Filtro: Preço maior que uma determinada quantidade
Parâmetros do filtro: 20, neste caso

Portanto a chamada fica assim:

hCon.Edit ( “Artigos”, “preco>&1”, 20 )

Porque não empregamos sentença SQL diretamente? Porque as modificações tem sentido
só sobre uma tabela. Não podemos modificar, por exemplo, o resultado dos registros que
provenham da união de duas tabelas, ou da soma dos valores de um campo.

Após obter o registro com a chamada a Edit, passamos ao objeto Result editable
ao
formulário de dados, com a chamada seu método RunEdit, e este mostra-se de forma modal. Ao
fechar o formulário, a execução retorna ao nosso método no qual atualizamos o conteúdo de de
nosso controle ColumnView para refletir as mudanças feita na tabela.

A seguir, reproduzimos por completo o código de Fmain e Fdados por ser dois códigos
extensos que temos fatiado e modificado.

FMAIN
PRIVATE hCon AS Connection

PRIVATE FUNCTION ConectarBase() AS Boolean

IF hCon THEN RETURN FALSE

hCon = NEW Connection

hCon.Host = "/home/Usuário/Bases"

hCon.Name = "provas"

hcon.Type = "sqlite"

TRY hCon.Open

IF ERROR THEN

hCon = NULL

message.Error("Erro ao conectar com a base")

RETURN TRUE

END IF

RETURN FALSE

END

PRIVATE SUB EncerrarConexao()

IF hCon = NULL THEN RETURN

hCon.Close()

hCon = NULL

END

PUBLIC SUB Form_Open()

DIM hResul AS Result

DIM chave AS String

tabela.Clear()

IF conectarBase() THEN RETURN

tabela.Columns.Count = 5

tabela.Columns[0].Text = "Título"

tabela.Columns[1].Text = "Autor"

tabela.Columns[2].Text = "Data"

tabela.Columns[3].Text = "Preço"
tabela.Columns[4].Text = "Descrição"

hResul = hCon.Exec("select * from dados")

DO WHILE hResul.Available

Chave = hResul["titulo"]

tabela.Add(Chave, Chave)

tabela[chave][1] = hResul["autor"]

tabela[chave][2] = hResul["data"]

tabela[chave][3] = hResul["preco"]

tabela[chave][4] = hResul["descricao"]

hResul.MoveNext()

LOOP

EncerrarConexao()

END

PUBLIC SUB Tabela_KeyRelease()

IF key.Code = key.Delete THEN

IF tabela.Current = NULL THEN RETURN

IF tabela.Current.Selected = FALSE THEN RETURN

IF ConectarBase() THEN RETURN

TRY hCon.Exec("delete from dados where titulo=&1", tabela.Current.Key)

IF ERROR THEN

message.Error("Impossível apagar o registro")

ELSE

EncerrarConexao()

tabela.Current.Delete()

END IF

END IF

END

PUBLIC SUB Tabela_Activate()

DIM hResul AS Result

IF tabela.Current = NULL THEN RETURN

IF ConectarBase() THEN RETURN

hresul = hCon.Edit("dados", "titulo=&1", Tabela.Current.Key)

FDados.RunEdite(hResul)

Tabela.Current[0] = hResul["titulo"]

Tabela.Current[1] = hResul["autor"]

Tabela.Current[2] = hResul["data"]

Tabela.Current[3] = hResul["preco"]

Tabela.Current[4] = hResul["descricao"]

EncerrarConexao()

END
PUBLIC SUB BtnEscrita_Click()

IF ConectarBase() THEN RETURN

FDados.RunNew(hCon)

EncerrarConexao()

Form_Open()

END

PUBLIC SUB BtnSair_Click()

ME.Close

END

FDADOS
PRIVATE Editando AS Boolean

PRIVATE hResul AS Result

PRIVATE hCon AS Connection

PUBLIC SUB RunNew(Data AS Connection)

hCon = Data

ME.ShowModal()

END

PUBLIC SUB RunEdite(Data AS Result)

hResul = Data

Editando = TRUE

TxtTitulo.Text = hResul["titulo"]

TxtAutor.Text = hResul["autor"]

TxtData.Text = hResul["data"]

TxtPreco.Text = hResul["preco"]

TxtDescricao.Text = hResul["descricao"]

ME.ShowModal()

END

PUBLIC SUB BtnCancelar_Click()

ME.Close()

END

PUBLIC SUB BtnAceitar_Click()

IF Editando THEN
TRY hResul["titulo"] = TxtTitulo.Text
TRY hResul["autor"] = TxtAutor.Text
TRY hResul["data"] = TxtData.Text
TRY hResul["preco"] = TxtPreco.Text
TRY hResul["descricao"] = TxtDescricao.Text

TRY hResul.Update()

ELSE

TRY hCon.Exec ("insert into dados values (&1,&2,&3,&4,&5)",


TxtTitulo.Text, TxtAutor.Text, Cdate ( TxtData.Text ),

Cfloat ( TxtPreco.Text ), txtDescricao.Text)

END IF

ME.Close()

CATCH

message.Error ( "Impossível introduzir os dados solicitado" )

END

Mais utilidades do gestor de bases de dados

Executando gambas-database-manager
podemos encontrar algumas ferramentas
adicionais.
A primeira delas é obter o código para criar
as tabelas da base. Colocando sobre uma base e
pressionando o botão direito, podemos selecionar a
opção Criar código Gambas.....

Ao acessar este menu, nos perguntará o


nome do módulo e o procedimento para criar o
código. Após indicar estes valores, encerramos e
voltamos a abrir o projeto, dispomos de um novo
módulo com o nome indicado, o qual contem o
código para criar a tabela.

   ∃    


PROCEDURE Createdatabase(hCon AS Connection, sDatabase AS String)

'Generatede by Gambas database manager - 22/08/2005 10:26:43

DIM hTabela AS Table


hTable = hCon.Tables.Add("dados")
WITH hTabela

.Fields.Add("titulo", gb.String, 40)


.Fields.Add("autor", gb.String, 40)
.Fields.Add("data", gb.Date, )
.Fields.Add("preco", gb.Float, )
.Fields.Add("descricao", gb.String, 200)

.PrimaryKey = ["titulo"]

.Update
END
No inicio do programa, e após conectar com o servidor de base de dados, podemos então
comprovar se existe a(s) tabela(s), e chamar este código para que a(s) crie, com o qual facilita a
distribuição do programa em vários equipamentos, sem intervenção adicional manual para por
em funcionamento o seu sistema novo.

Gambas-database-manager permite também copiar dados entre bases. Podemos, por


exemplo, pressionar o botão direito sobre nossa tabela dados, pressionar sobre Copiar, acessar a
outra base de dados, seja Sqlite ou de outro tipo, pressionar Colar, e o gestor criará a tabela na
outra base, perguntando-nos se queremos copiar só a estrutura dos campos ou também os dados.
Desta forma, facilita-se a fase de programação ao poder dispor de varias bases de provas, assim
como a fase de migração de uma base a outra, ao poder copiar dados da base antiga a nova. Este
processo pode funcionar para tabelas individuais ou para base de dados completa.
Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 6: REDE ........................................................................................ 04


REDES--------------------------------------------------------------------------------04
6.1 Concepções ---------------------------------------------------------------04
6.2 Criando um servidor TCP ----------------------------------------------06
6.3 Um cliente TCP ----------------------------------------------------------11
6.4 Cliente e servidor locais -------------------------------------------------15
6.5 UDP ------------------------------------------------------------------------16
6.6 Resolução de nome ------------------------------------------------------18
6.7 Protocolo HTTP ----------------------------------------------------------20
6.8 Protocolo DTP ------------------------------------------------------------24
6.1 Conceitos

. A transmissão de dados através de uma rede padronizou-se faz tempo a nível de


software e hardware. Atualmente trabalha com o chamado modelo de camadas. Basicamente,
este modelo pretende isolar os diferentes níveis de complexidade de dados, de forma que a
substituição de uma camada não afete o trabalho em outra. Podemos ver hoje em dia que é
possível, por exemplo, conectar-se a internet através de um modem, uma linha ADSL ou cabo
ethernet em uma rede local e, sem maiores problemas, não necessitamos ter diversos
equipamento e não somos obrigados a dispor de um navegador ou um cliente de email diferente
para cada situação. Isto se deve ao modelo de capas. O navegador só entende de protocolos de
alto nível, como HTTP ou FTP, transfere a informação a um nível mais baixo e, finalmente, o
modem convencional, ADSL ou a placa de rede se ocupam de transportar esses dados.

Para não entrar em complexidades, já que é tarefa de um livro sobre redes, diremos que
há um par de níveis: os mais baixos, formados pelos hardware (modems, placas de rede...) e os
E os drivers ou módulos do núcleo, que controlam e fatiam a informação para emiti-la ou
recebe-la por este hardware; e alguns níveis acima, que são os que mais nos interessa na hora de
programar com o Gambas aplicações de rede, e que são independente do método de transporte
que haja abaixo.

O protocolo mais conhecido hoje em dia para distribuir informação entre equipamentos é
o protocolo IP, que determina que destino e que equipamento recebera cada fragmento de
informação que circula por uma rede. Cada equipamento tem um número designado, chamado
endereço IP é seu identificador único. Cada pacote IP de informação é similar a uma carta postal:
inclui dados do remetente(IP do equipamento de origem) e o destinatário ( IP do equipamento de
destino). Estes endereços IP tem a forma XXX.XXX.XXX.XXX; são grupos de três números que
variam entre 0 e 255, separados por um ponto (por exemplo: 192.168.0.23 ou 10.124.35.127).

Já que estes não difíceis de lembrar, foi estabelecido um sistema que permitia estabelecer
uma correspondência entre nomes e endereços IP este sistema denomina-se DNS e, na internet e
em quase todas as redes locais, encontram-se servidores DNS que recebem consultas acerca de
nomes de equipamentos e endereços IP e os traduzem em um sentido ou outro. Desta forma,
podemos, por exemplo, indicar ao navegador que mostre a página www.gnulinex.org, em lugar
de ter que indicar o endereço IP do equipamento que servindo esta página.

Os pacotes IP podem conter qualquer tipo de informação, porem acima deste nível foi
estabelecido outros dois também padrão, que são os protocolos TCP e UDP.

1. TCP utilizado para manter a conexão entre dois equipamentos: sabe-se em todo o
momento se o equipamento remoto está à escuta, se recebeu toda a informação corretamente ou
teremos que voltar a emitir, e adiciona-se um controle de erros para garantir que um ruido na
linha não deformou as mensagens. É o protocolo mais utilizado na internet, já que assegura que
recebemos uma pagina web ou um arquivo de forma completa e sem erros.

2.Enquanto o UDP, é um protocolo de transporte muito mais simples que o TCP, e não
verifica se realmente existe uma conexão entre os dois equipamentos e se a informação foi
realmente recebida ou não. É por tanto, menos confiável, mas em determinado tipo de
transmissões, como são as de áudio e video em tempo real, o importante não é que chegue toda a
informação (pode haver pequenos cortes ou erros), e sim que o fluxo seja constante e o mais
rápido possível. Por isso, também é empregado com frequência na internet, sobre tudo para os
servidores de streaming, que nos permite escutar rádio, ver programa de televisão ou realizar
videoconferência pela rede.

Na hora de estabelecer uma comunicação entre dois equipamentos, o modelo indica temos
de abrir um soket. É algo similar a uma bandeja de entrada ou saída, como a dos
administradores, que tem uma bandeja com os informes pedintes e outra com os que vão
terminando. O sistema operacional armazena informações na bandeja de entrada proveniente do
sistema remoto, até que nosso programa decida pegar os dados, então os processamos e os
deixamos na bandeja de saída. O sistema operacional se encarregará de enviar essa informação
quando for possível.

Sockets não é utilizado apenas para comunicação entre dois equipamentos, dentro de
nossa própria maquina muitos programas comunicam-se utilizando este sistema. Por exemplo,
este é o caso dos servidores gráficos ou X-Window: o servidor gráfico desenha o que lhe pedem
os programas clientes ou aplicações que tenham estabelecido um socket com ele.

Existe um tipo especial de socket, chamado local ou Unix, que só serve para comunicar
programas dentro de um mesmo sistema, e que está optimizado para realizar esta função com
grande velocidade e eficácia, permitindo uma comunicação interna várias vezes mais veloz e com
menor consumo de recursos que através de um socket TCP ou UDP.

Mais acima, temos os chamados protocolos de aplicações. Já não nos encarregamos do


transporte de dados, e sim do formato dos dados e da comunicação. Um dos protocolos mais
estendidos é o HTTP que entre outras coisas, é utilizado em toda internet para transmitir e
receber páginas web. Estabelece o modo de conectar, como autenticar-se, a forma dos dados a
transmitir ou receber paginas web, imagens, arquivos compactados...) e como finalizar a
comunicação.

Outros protocolos específicos são o FTP, especializado na transmissão e recepção de


arquivos; TELNET, para trabalhar com terminal de texto sobre um sistema remoto; SMTP, POP
ou IMAP, para o correio eletrônico ou os diferentes protocolos de mensageiros instantâneo, como
o JABBER.

Se esta breve introdução nos trouxe novidades, devemos estender nosso


conhecimentos na área de redes, aprendendo conceitos como as máscaras de rede e
divisões em subredes, os diferentes tipos de próxis, ruters e gateways, as conversões
NAT, em geral, tudo o que nos ajude a determinar por que não conseguimos conectar
com outro equipamento em um dado momento. O mundo das redes é extenso, porem
uma boa base nos evitará muitos problemas na hora de programar sistemas
distribuídos entre vários servidores e clientes.

6.2 Criando um servidor TCP


Nossa primeira tarefa consistirá em escrever o código de um servidor TCP: será um
servidor que aceita conexões remotas, lerá os dados enviados e devolverá um eco aos clientes.
Criaremos um programa de console chamado MeuServidor. Este programa terá um único módulo
chamado ModMain. E nas propriedades do projeto selecionaremos o componente gb.net.

Dentro do módulo ModMain teremos uma referência a um objeto ServerSocket. Estes


objetos comportam-se como servidores de socketes, quer dizer, encontram-se a escuta de petições
de clientes remotos em um porta determinada. As portam são numeradas de 1 a 65535 e cada
programa que atua como servidor dentro do sistema utiliza uma delas.

 

As portas com número de 1 a 1024, se consideram reservadas para serviços


conhecidos (HTTP, POP FTP, IMAP...) e podem ser utilizados por programas cujo
usuário é diferente de root em sistemas GNU/Linux. Tratar, por tanto, de abrir, por
exemplo, a porta 523 a partir de um programa executado por um usuário normal, dará
um erro.

Os serviços mais conhecidos como HTTP ou FTP já tem uma porta designada (por exemplo, 80
no caso de HTTP) de forma padrão, se bem que não há nenhuma razão técnica pela qual um
servidor web não possa atender, por exemplo, a porta 9854. Esto se deve unicamente a um
acordo internacional para que qualquer cliente que queira realizar uma petição web, saiba que,
salvo instruções especificas ao contrário, terá que conectar-se a porta 80 da maquina servidora.

Em sistema GNU/Linux podemos encontrar uma lista dos serviços mais


comuns e suas portas oficiais dentro do arquivo de texto /etc/services

Em nosso caso vamos utilizar a porta 3152. Na função Main do programa,deveremos criar
o objeto ServerSocket, especificar que seu tipo será internet (isso se for um socket TCP e não
UNIX), a porta que deve atender e indicar-lhe que comece com as petições.

 

PUBLIC SUB Main()

Servidor = NEW ServerSocket AS "Servidor"


Servidor.Type = Net.Internet
Servidor.Port = 3152
TRY Servidor.Listen()

IF ERROR THEN PRINT "Erro: oo sistema não permite atender a porta


especificada"

END

Observamos que na hora de criar o objeto servidor, indicamos que o gestor de eventos
será "Servidor", para poder escrever as rotinas nas quais tratemos as petições que chega dos
clientes.

Na hora de indicar ao servidor que ponha-se a escutar com o método listen, o fazemos
protegendo o código com uma instrução TRY, para gestionar o erro se o sistema não permitir
atender a porta indicada (por exemplo, se outro serviço já o estiver utilizando). Já podemos
executar o programa.

Como podemos observar, o programa passa a função Main e segue executando a espera
da conexão de um. Um programa normal de console, ao terminar esta função, teria finalizado
sem mais, mas neste caso o interpretador Gambas está atendendo as novidades que podem
aparecer no socket que foi criado e, por tanto, o programa segue funcionando, esperando petições
de clientes.

Qualquer programa Gambas que está vigiando um descritor, ou seja, um


arquivo, um processo ou um socket, não finaliza enquanto esse descritor se encontre
aberto. Esta técnica permite criar programa de console que não ficam em um loop
continuo a espera de novidades (seja uma modificação em um arquivo, uma entrada
de um cliente em um socket ou a leitura de dados de um processo filho), poupando
recursos e melhorando a eficácia do processo.

O programa servidor já está funcionando, enquanto não haja nada realmente útil. Pra
fazer, se testarmos ao conectarmos a partir do console com o programa telnet em nosso próprio
servidor, observaremos que conecta-se e imediatamente desconecta-se do servidor.

 




O funcionamento do objeto ServerSocket é precisamente este: recebe uma conexão de um
cliente e, se não especificarmos ao programa que desejamos atende-la, a fecha.
Quando um objeto servidor recebe uma petição de um cliente, dispara o evento
Connection. Dentro desse evento, e só dentro dele, podemos utilizar o método Accept. Esse
método devolve um objeto Socket que representa uma conexão com esse cliente. Na hora de
receber e enviar dados a esse cliente, faremos através desse objeto, desse modo, se diferencia a
cada cliente conectado com um servidor de forma única, evitando, por exemplo, que enviamos o
resultado de um calculo a um cliente equivocado.
Em nosso programa adicionaremos uma matriz de objetos onde guardaremos uma
referencia a cada cliente, e nessa matriz iremos adicionando os clientes que iram conectar-se.


 

 

  !∀


#∃  %&
∋ ( 

) ∗ +∀&
∗ ∀&

∋

PUBLIC SUB Main()

Servidor = NEW ServerSocket AS "Servidor"


Servidor.Type = Net.Internet
Servidor.Port = 3152
TRY Servidor.Listen()

IF ERROR THEN PRINT "Erro: O sistema não permite atender a porta


especificada"

END

Em nosso gestor de eventos Connection utilizamos o método Accept, para que sempre
aceitemos as conexões de entrada. No entanto, se o omitimos em casos determinados, a tentativa
de conexão do cliente encerra-se automaticamente. Observamos, por exemplo, que Accept nos
informa do endereço IP do cliente que trata de conectar-se através do parâmetro RemoteHostIP.
Se desejarmos, por exemplo, aceitar só conexões a partir de nosso próprio equipamento (que
sempre tem endereço 127.0.0.1), poderíamos modificar o código para que recuse as conexões de
outros endereços IP.

  !∀


#∃  %&
∋ ( 

,
#∃ )−./0∗1∗1∗.−∃
) ∗ +∀&
∗ ∀&
∋ ,

∋
Este objeto socket, por padrão recebem um gestor de eventos chamado Socket, pelo qual
os eventos dos clientes de socket se entendem no programa por um função chamada Socket_ +
Nome do evento.

Em nosso caso utilizamos os eventos Read produzido quando se recebe dados vindo dos
clientes, e o evento Close, que ocorre quando quando o cliente encerra a conexão.

No caso do evento READ, nos valemos da palavra chave LAST, que mantem uma
referência ao último objeto que disparou um evento (neste caso, o cliente de socket) para tratar os
dados que provem dele.

Para isso, leremos o socket como se fosse um arquivo, com a instrução READ, no qual
indicamos a cadeia que recebe os dados; leremos o comprimento dos dados disponíveis no sockt,
determinada pela função Lof() e, depois escreveremos esses mesmos dados no socket com a
instrução WRITE, de forma que o cliente receba um eco dos dados enviados, mas convertidos
em maiúsculas.

!
2 
DIM sCad AS String

TRY READ #LAST, sCad, Lof(LAST)


sCad = UCase(sCad)
TRY WRITE #LAST, sCad, Len(sCad)

END

Para Close, buscamos o objeto Socket dentro de nossa matriz e o eliminamos para que
desapareçam as referências a esse cliente dentro de nosso programa servidor.


!
DIM Ind AS Integer

Ind = Clientes.Find(LAST)
IF Ind >= 0 THEN Clientes.Remove(Ind)

END

Já dispomos de um programa servidor de socket com a capacidade para atender a


múltiplos clientes: podemos abrir várias janelas de terminal, executando nelas telnet 127.0.0.1
352, e enviar e receber dados do servidor. Cada vez que escrevemos uma cadeia e a enviamos
pressionando Return, receberemos a cadeia convertida em maiúsculas como resposta do
servidor.

Ainda que, o programa telnet foi criado pára controlar um equipamento de


forma remota, também é um cliente universal que pode servir para comprovar o
funcionamento de qualquer servidor que estejamos construindo, antes de dispor de
um cliente real.


 
 


6.3 Um cliente TCP


Agora que já dispomos
de um programa servidor,
vamos criar um programa
cliente.
Criaremos um programa
gráfico chamado MeuCliente,
com um formulário FMain, e
um referencial ao componente
gb.net como no caso anterior.

 




Dentro do formulário
Fmain colocaremos dois
controles TextBox, chamados
TxtIP e TxtPorta nos quais o
usuário colocará o endereço IP
e a Porta para conectar-se; um
botão chamado BtnConectar,
com o texto Conectar; um
TextBox chamado TxtDados, no
qual o usuário colocará os
dados a enviar ao servidor, e um
label chamado LblResultado,
 
no qual qual mostraremos o
resultado recebido do servidor.
Também colocaremos três labels de informações com qualquer nome para informar o
usuário. Ao lançar o formulário, colocaremos uns valores padrões e poremos o TextBox
TxtDados desabilitado, já que de inicio o programa não está conectado ao servidor. Também
colocaremos no início do código uma variável global do tipo Socket, que representa o cliente
com o qual nos conectaremos ao servidor.


 

PUBLIC SUB Form_Open()

TxtIP.Text = "127.0.0.1"
TxtPorta.Text = "3152"
TxtDados.Text = ""
LblResultado.Text = ""
TxtDados.Enabled = FALSE

END

O botão BtnConectar terá duas funções: se não estivermos conectados, tratará de


conectar, e se já o estamos, desconectará do servidor. Para isso, testamos se a referência ao objeto
Socket é nula ou não. Por outro lado verificamos se o dado do endereço IP é válido, assim como
o da porta indicada.

Para verificar o número da porta duas funções: Val(), devolve um número a partir de um
texto se este conter apenas caracteres numéricos. Caso contrário, devolve NULL. Se obtivermos
um número, verificamos se encontra-se entre os valores 1-65535. Para o endereço IP,
empregaremos o Net.format, que retorna um endereço IP a partir de um texto, se for possível
interpreta-lo como tal, caso contrário retorna uma cadeia vazia.

Uma vez dispondo dos dados, criamos o objeto Sockt e tratamos de conectar com o IP e
porta dados, e trocamos o texto do botão para Desconectar.

No caso de que já estivermos conectado tratamos de encerrar o sockt, se estiver aberto,


desabilitamos a caixa de texto de dados e levamos a NULL a referência ao objeto socket, para
destruí-lo.

2!
DIM nPorta AS Integer
DIM sIP AS String

LblResultado.Text = ""
TxtDados.Text = ""

IF Cliente = NULL THEN


TRY nPorta = Val(TxtPorta.Text)
IF ERROR THEN
Message.Error("Número da porta não é válido")
RETURN
END IF
IF nPorta < 1 OR nPorta > 65535 THEN
Message.Error("Número da porta não é válido")
RETURN
END IF

sIP = Net.Format(TxtIP.Text)

IF sIP = "" THEN


Message.Error("Endereço IP não é válido")
RETURN
END IF

Cliente = NEW Socket AS "Cliente"


Cliente.Host = sIP
Cliente.Port = nPorta
Cliente.Connect()

ELSE

TRY CLOSE #Cliente


Cliente = NULL
TxtDados.Enabled = FALSE

END IF

END

Entre a tentativa de conexão e a conexão real pode existir um intervalo de tempo. Para saber
quando realmente estamos conectado com o servidor, empregaremos o evento Ready, que é
produzido quando o servidor aceita nossa conexão. Neste evento, habilitaremos a caixa de texto
de dados para que o usuário possa escrever. Também pode acontecer que ocorra um erro (por
exemplo, uma conexão negada ou um nome de host não encontrado), em cujo caso dispara-se o
evento Error, que aproveitamos para devolver a interface a seu estado desconectado e eliminar o
socket recusado.

PUBLIC SUB Cliente_Ready()

TxtDados.Enabled = TRUE

END

PUBLIC SUB Cliente_Error()


TRY CLOSE #Cliente

Cliente = NULL

TxtDados.Enabled = FALSE
BtnConectar.Text = "Conectar"
Message.Error("Conexão encerrada")

END
Enquanto ao envio de dados, o realizaremos quando o usuário pressionar a tecla Return,
após escrever um texto na caixa TxtDados.

Para isso introduziremos o código dentro de evento KeyPress da caixa de texto.

PUBLIC SUB TxtDados_KeyPress()

IF key.Code = key.Return THEN


IF Len(TxtDados.Text) > 0 THEN
LblResultado.Text = ""
TRY WRITE #Cliente, TxtDados.Text, Len(TxtDados.Text)
END IF
END IF

END

Enquanto a recepção de dados do servidor, pode chegar-nos em vários fragmentos,


cada um dos quais receberemos no evento Read do objeto socket, e mostraremos no label
LblResultado.

Quando se envia ou recebe dados em uma rede, não podemos ter certeza que a
quantidade de dados será recebida de uma só vez, sempre temos que levar em conta a
possibilidade de unir fragmentos.

PUBLIC SUB Cliente_Read()


DIM sCad AS String

TRY READ #Cliente, sCad, Lof(Cliente)


LblResultado.Text = LblResultado.Text & sCad

END

Já temos o nosso cliente pronto. Executamos o servidor que criamos anteriormente em


uma ou várias instâncias deste programa para comprovar os resultados.

 
    



Na hora de criar clientes e servidores para produção, devemos levar em conta
a codificação dos caracteres empregados pelo servidor e o cliente. Muitos erros
podem provir por este fato, simplesmente, é que o Gambas e muitos outros
programas usam internamente UTF-8 como codificação predeterminada, sendo que
outros empregam ISO-8859-1 ou UTF-16. Este programa, por exemplo, pode ter
problemas com o ñ e vogais acentuadas. Podemos melhora-lo como exercício,
convertendo as cadeias enviadas e recebidas entre UTF-8 e ISO-8859-1 ou
ISO-8859-15.

6.4 Clientes e servidores locais


A margem dos sockets TCP, desenhados para conectar equipamentos remotos, nos sistemas
GNU/Linux, em geral qualquer sistema que siga a filosofia da família de sistemas UNIXTM,
dispõe de um outro tipo de sockets, que só permitem a conexão dentro do próprio equipamento e
cuja missão é optimizar a funcionalidade desses sockets quando o cliente e servidor encontram-se
na mesma maquina.

Estes sockets são uns arquivos especiais que o servidor cria dentro do sistema de
arquivos, e que os clientes tratam de abrir para leitura e escrita de dados. Temos de levar em
conta que localizar um destes arquivos especiais dentro de uma unidade de rede não serve não
serve para conectar dois equipamentos diferentes: um socket UNIX ou local, só podemos
empregar dentro de um mesmo equipamento.

Todo o explicado até aqui para clientes e servidores TCP, é aplicável aos clientes e
servidores locais, a única diferença se dá no modo inicial de configurar o serviço.

No caso do servidor, antes de conectar temos de especificar que o tipo de servidor é local
e um caminho para um arquivo que representará o socket servidor.

' Servidor TCP


PRIVATE Servidor AS ServerSocket

PUBLIC SUB Main()


Servidor = NEW ServerSocket AS "Servidor"

Servidor.Type = Net.Internet
TRY Servidor.Listen()
IF ERROR THEN PRINT "Erro: o sistema não permite atender a porta
especificada"

END
' Servidor TCP
PRIVATE Servidor AS ServerSocket

PUBLIC SUB Main()

Servidor = NEW ServerSocket AS "Servidor"

Servidor.Type = Net.Local
Servidor.Path = User.Home & "/meusocket"
TRY Servidor.Listen()
IF ERROR THEN PRINT "Erro: o sistema não permite atender a porta
especificada"

END

Se executarmo o código com essa modificação, observaremos que em nossa pasta pessoal
cria-se um arquivo especial do tipo socket chamado meusocket. Na parte cliente temos de
especificar que a conexão será em um endereço dentro do sistema de arquivo, no lugar de uma
porta TCP, para o qual se indica a porta especial Net.Local e a propriedade Path do socket.
Cliente = NEW Socket AS "Cliente"
Cliente.Path = User.Home & "/meusocket"
Cliente.Port = Net.Local
Cliente.Connect()

A partir daqui o resto do código funciona sem modificações.

É uma boa idéia expor a possibilidade de que servidores e clientes funcionem


com socket locais ou TCP, segundo os parâmetros de configurações da aplicação, a
fim de optimizar o funcionamento quando cliente servidor encontram-se na mesma
maquina. Os servidores X e os de fontes gráficas empregam este modo de trabalho.

6.5 UDP
O trabalho com UDP a princípio pode resultar algo mais complexo para o principiante.
Em UDP não existe uma conexão entre cliente e servidor, tão pouco chegam dados por uma
porta, ou envia-se, porem sem conhecer se do outro lado há alguem a escuta. Assim não existe
servidores ou cliente realmente, ainda que na prática faremos esta distinção na hora de desenhar
uma aplicação servidora ou cliente. Já que não existe conexão, cada pacote identifica-se com seu
endereço IP e porta de origem, e este dado será essencial na hora de devolver uma resposta ao
lado remoto.

Gambas da acesso ao protocolo UDP através dos objetos de classe UdpSocket, que
servem tanto para criar programas servidores como para programas cliente. Para iniciar o
trabalho com um objeto desta classe, temos de chamar o método Bind, no qual temos de indicar a
porta local a que estará ligado. Os servidores haverão de estar unidos a uma porta concreta
definida, e os clientes a qualquer porta, já que o interesse do cliente é enviar dados a um servidor
remoto em uma dada porta, independentemente da porta local que emprega para isso.
UdpSocket proporciona várias propriedades para identificar os pacotes. Quando chega
um pacote ao socket procedente de uma localização remota, gera um evento Read e nele
podemos consultar o IP de procedência com a propriedade SourcePort. Para enviar um pacote a
um sistema remoto, temos de lembrar previamente o valor das propriedades TargetHost, com o
IP de destino, e TargetPort, com a porta de destino.

Criaremos um servidor muito simples: atenderemos a porta UDP 3319, recebe os dados e
envia como resposta o texto ADIOS ao host que enviou o pacote. Para implementa-lo criaremos
um projeto de console chamado MeuServidorUDP, com um módulo ModMain e uma
referência ao componente gb.net. O código é o seguinte:

' Gambas module file

PRIVATE Meuservidor AS UdpSocket

PUBLIC SUB Servidor_Read()


DIM sCad AS String

READ #Meuservidor, sCad, Lof(Meuservidor)

Meuservidor.TargetHost = Meuservidor.SourceHost
Meuservidor.TargetPort = Meuservidor.SourcePort

WRITE #Meuservidor, "ADEUS", 5

END

PUBLIC SUB Main()

Meuservidor = NEW UdpSocket AS "Servidor"


Meuservidor.Bind(3319)

END

Dispomos de um objeto da classe Udpsocket que criamos na função Main. Fazemos uma
chamada a Bind para enlaça-lo com a porta UDP 3319. Como no caso do servidor TCP, o
interpretador Gambas fica a espera de algum evento no socket. Quando chega um pacote de
dados, se lê como no caso dos sockets TCP, fazendo uso da instrução READ, a seguir colocamos
as propriedades TargetHost e TargetPort com os valores correspondentes a procedência do
pacote de dados, e enviamos a cadeia ADEUS, que chegará ao host especificado. Quanto a parte
cliente, trata-se de outro programa de console que chamaremos MeuClienteUDP, com uma
referência ao componente gb.net, e que terá um único módulo chamado ModMain. O cliente
tratará de conectar com o servidor, enviará uma cadeia OLA e recebe uma cadeia completa
ADEUS, encerra o socket e acabará seu funcionamento.
' Gambas module file

PRIVATE MeuCliente AS UdpSocket


PRIVATE Buffer AS String

PUBLIC SUB Cliente_read()


DIM sBuf AS String

READ #MeuCliente, sBuf, Lof(MeuCliente)


IF Buffer = "ADEUS" THEN CLOSE #MeuCliente

END

PUBLIC SUB Main()

MeuCliente = NEW UdpSocket AS "Cliente"


MeuCliente.Bind(0)
MeuCliente.TargetHost = "127.0.0.1"
MeuCliente.TargetPort = "3319"
WRITE #MeuCliente, "OLA", 4

END

FALTA A PAGINA 216


O controle DnsCliente receberá o nome Cliente em nosso formulário, e a propriedade Async do
cliente DNS deve ficar como FALSE.

Estes controles especiais (DnsCliente, Socket, etc.) não são visíveis em tempo
de execução, nem são realmente controles, mas são adicionados ao formulário como
controles normais para facilitar o trabalho ao criar programas gráficos. O objeto
DnsCliente, em nosso caso chamado Cliente, cria-se ao desenvolver o formulário e
existe até que se destrua ao encerra-lo ou aplicar o método Delete nesse formulário.

O aspecto do desenho será similar ao da


Figura 8.

  

' Gambas class file

PUBLIC SUB BtnToIp_Click()

Cliente.HostName = TxtHost.Text
Cliente.GetHostIP()
Message.Info("IP: " & Cliente.HostIP)

END

PUBLIC SUB BtnToName_Click()

Cliente.HostIP = TxtHost.Text
Cliente.GetHostName()
Message.Info("Nome: " & Cliente.HostName)

END

No primeiro caso, será preenchida a propriedade HostName com o nome do host que
desejamos resolver; e chama o método GetHostIP() e depois lemos o IP desse host recém obtido
com o valor da propriedade HostIP.

No segundo caso é exatamente ao contrário: preenchemos a propriedade HostIP com o IP


que foi escrito pelo usuário na caixa de texto; chamamos o método GetHostName() o lemos e
mostramos o valor da propriedade HostName que obtivemos.

Em ambos os casos, se a resolução falha, a ler (HostIP no primeiro caso e HostName no


segundo) ficará em branco. Também podemos controlar o estado de erro com a propriedade
Status. Se vale zero, a resolução terminou corretamente; se tiver um valor menor que zero, houve
um erro, não se encontrou o IP ou o nome do host.

DnsCliente também permite o trabalho em modo assíncrono. Em ocasiões, uma resolução


pode demorar bastante tempo, no qual a interface do usuário fica bloqueada trabalhando em
modo síncrono

Para ativar o modo assíncrono, devemos por a propriedade Async do cliente DNS como
TRUE. Isto também requer modificar o código. Já não podemos chamar o método GetHostIP()
ou GetHostName() e imediatamente ler o valor das propriedades HostName e HostIp
respectivamente, já que enquanto o código do programa principal está executando, o cliente DNS
está trabalhando internamente. Temos duas opções: a primeira é esperar o evento Finished, que
dispara ao acabar o processo; e o segundo é testar o estado da propriedade Status, que valerá um
número maior que zero enquanto o processo está em curso, zero quando finaliza com exito ou um
valor menor que zero se houver um erro.

Este ´e um pequeno exemplo modificando o código inicial para trabalhar no modo


assíncrono. Se testarmos em um loop o estado da propriedade Status, repetindo-se enquanto é
maior que zero. Nesse loop poderíamos, por exemplo, controlar a pulsação de um botão
Cancelar para abortar o processo.

6.7 Protocolo HTTP


Em um nível acima de todo o explicado encontra-se o protocolo HTTP, no qual não trata-
se apenas de conectar a um servidor, e sim estabelecer um formato para a comunicação entre
clientes e servidores. O protocolo HTTP é um dos mais utilizados ao longo da internet, dado que,
entre outras coisas, é empregado para transmitir paginas web.

No protocolo HTTP, o cliente solicita ao servidor um documento em uma localização e


este o devolve seguindo uma série de convenções acerca da codificação de caracteres, controle de
erros, nível de compressão de dados, formato dos dados binários, etc.

O protocolo HTTP estabelece dois métodos principais para solicitar dados ao servidor. O
primeiro, e mais comum por ser utilizado para receber paginas web no navegador quando
escrevemos um endereço, por exemplo, é chamado método GET, no qual o cliente apenas solicita
um endereço URL, devolvendo o servidor o resultado. O segundo método, chamado POST, o
cliente não apenas solicita a URL, e sim envia uma série de informações adicionais que o
servidor processará antes de enviar o resultado. É o caso habitual quando preenchemos um
formulário com dados em uma pagina web e pressionamos o botão enviar.

Gambas possui no componente gb.net.curl, um cliente chamado HttpClient, que provê


acesso a servidores HTTP. O trabalho de negociação entre cliente e servidor, assim com a gestão
de formatos, é tarefa interna do cliente HTTP, a aplicação que desenvolvemos só terá de
preocupar-se em pedir um documento e recebe-lo.
O cliente HTTP pode funcionar de dois modos: o mais simples é o modo síncrono, nele
que recebemos diretamente o documento após a chamada e os métodos GET ou POST; no
segundo, assíncrono, o modo de trabalho se parece mais ao descrito para os sockets: com a
aplicação em funcionamento iremos recebendo fragmentos do documentos em cada evento
READ que uniremos em uma cadeia.

Criaremos um projeto de console para receber a pagina web


http://gambas.gnulinex.org/gtk/. Terá uma referência a gb.net e um módulo ModMain,com
este código:

' Gambas module file

PUBLIC SUB Main()


DIM Http AS HttpClient
DIM sCad AS String

Http = NEW HttpClient


Http.Async = FALSE
Http.TimeOut = 10
Http.URL = "http://gambas.gnulinex.org/gtk/"
Http.Get()

IF Http.Status < 0 THEN


PRINT "Erro ao receber a pagina"
ELSE
PRINT sCad
END IF

CLOSE #Http

END

Em primeiro lugar definimos e criamos um objeto da classe HttpClient chamado Http.


Colocamos sua propriedade Async a FALSE para que o processo seja síncrono, ou seja, que o
cliente fique bloqueado enquanto recebe a pagina. Já que o bloqueio poderá durar um tempo
excessivo, definimos também a propriedade TimeOut um tempo máximo em segundos antes de
dar por fracassada a conexão e recepção de dados.

Indicamos a URL que contem o nome do servidor e o documento dentro deste.

Chamamos o método Get com o propósito de receber a pagina, com que o programa é
interrompido até sua recepção até um erro ou até que se passem 10 segundos no máximo.

Lemos o valor da propriedade Status que, como no caso dos sockets ou do DNS, terá
valor maior que zero quando está ativo, zero em caso de exito, menor que zero em caso de erro.
Se houver um erro, se houver um erro, o indicamos pelo console. Se bem, lemos, como no caso
do socket ou qualquer outro fluxo, empregando a instrução READ e mostramos o conteúdo da
pagina recebida no console.
Finalmente, encerramos o cliente Http. Isto é necessário já que o cliente mantem viva a
conexão com o servidor enquanto for possível, para fazer desta maneira a recepção de múltiplos
documentos de um mesmo servidor.

O protocolo HTTP estabelece um tempo no qual o servidor mantem o socket


conectado com o cliente. Se necessitamos receber várias páginas de um servidor ou
realizar várias petições POST consecutivas, deveríamos empregar a instrução
CLOSE só após todo o processo, com o qual se ganhará em velocidade e se utilizarão
menos sos do sistema.

Enquanto o método assíncrono, o modo de proceder e exatamente o mesmo que com os


sockets: esperar o evento Read para ir lendo fragmentos do documento em processo de recepção,
ou o evento Error para entender possíveis problema de comunicação com o servidor.

A margem dos problemas físicos de comunicação, que detecta-se com o Error, o proprio
protocolo HTTP pode especificar códigos de erro nos quais a comunicação/recepção de dados foi
perfeita a nível físico, mas ocorreram erros lógicos. O caso mais comum é solicitar um
documento que não existe e em cujo caso gera-se o erro 404. outros erros comuns pode ser
elementos proibidos) ou o 500 (erro interno do servidor). Para detectar e tratar problemas, o
cliente HTTP proporciona duas propriedades que podemos controlar já que é numéricas e
devolve o código de erro (o valor 200 significa que tudo foi bem), e Reason, que é uma cadeia de
texto explicativa do erro, proporcionada pelo servidor.

O valor devolvido em Reason depende do programa utilizado como servidor


web e não é obrigatório. Por isso, a efeito de controle de erros, devemos empregar só
o valor do código e usar Reason só a efeito de informação do usuário.

Os fragmentos HTTP recebem-se empacotados, de forma que existe um encabeçamento


contendo meta informação do servidor e um corpo onde encontra alojado o documento em si, a
leitura mediante READ ou outros métodos para fluxos só acessam os dados do corpo. Para obter
as cabeceiras, que podem ser utilizado para comprovar dados tais como a data e hora do servidor,
o tipo de servidor informações específicas, podemos recorrer a propriedade Headers. Cada meta
informação é uma linha, pelo que Headers devolvem uma matriz de cada uma das quais é uma
informação procedente do servidor. Neste caso derivado do anterior mostra-se ao final do
encabeçado do servidor.
' Gambas module file

PUBLIC SUB Main()


DIM Http AS HttpClient
DIM sCad AS String

Http = NEW HttpClient


Http.Async = FALSE
Http.TimeOut = 10
Http.URL = "http://gambas.gnulinex.org/gtk/"
Http.Get()

IF Http.Status < 0 THEN


PRINT "Erro ao receber a pagina"
ELSE
READ #Http, sCad, Lof(Http)
PRINT sCad

PRINT "\n-------------Metadados------------------\n"
FOR EACH sCad IN Http.Headers
PRINT sCad
NEXT
END IF

CLOSE #Http

END

O protocolo HTTP permite ao cliente autenticar-se para receber determinadas páginas não
acessíveis ao publico em geral. O usuário tem de dispor de um nome de usuário e uma senha.
Para introduzir estes valores, há de especificar o nome de usuário e a propriedade User e a senha
na propriedade Password, antes de chamar os métodos Get ou Post.

Por outro lado existe diversos métodos de autenticação, desde os menos seguros (envio de
usuário/senha sem codificar) até alguns algorítimos mais seguros. Alem do usuário e senha, há de
especificar o método de autenticação preenchendo o valor da propriedade Auth com algumas das
constantes que representam os métodos suportados:

Neth.AuthBasic
Neth.AuthDigest
Neth.AuthGSSNegotiate
Neth.AuthNTLM

Ao desenvolver uma aplicação há de conhecer de antemão o método que necessitamos


para acessar o servidor.

O protocolo HTTP também contemplam o uso de cookies, que é a informação que o


servidor guarda no cliente e que é consultada de novo pelo servidor em ocasiões posteriores,
antes de enviar um documento ao cliente. Pode servir por exemplo, para saber se a página já foi
visitada anteriormente por esse cliente. Por padrão, o cliente HTTP não aceita os cookeis
proveniente do servidor. Se especificarmos um caminho a um arquivo com a propriedade
CookeisFile, estas ativarão-se e o cliente HTTP se nutrirá dos cookeis existentes neste arquivo
para devolver informações ao servidor. Se a propriedade UpdateCookies for levada a TRUE,
permitirá o acesso a escrita no arquivo de cookies, no qual os novos cookies serão guardados
entre execução e execução do programa.

Este componente encontra-se em fase de desenvolvimento e futuras versões suportarão o


uso de SSL e certificados.

6.8 Protocolo FTP


É igual ao protocolo HTTP, FTP encontra-se em largo uso na internet e está desenhado
especificamente para a transmissão e recepção de arquivos. A estrutura lógica de um servidor
FTP é muito próximo a de um sistema de arquivo locais: existe uma série de pastas com estrutura
em árvore e dentro de cada pasta existe arquivos.

O suporte de FTP no Gambas é atualmente muito simples e também bastante limitado.


Permite, essencialmente, subir e baixar arquivos do servidor. Com uma sintaxe muito similar a
do cliente HTTP, teremos que indicar a URL desejada e chamar os métodos Get() ou Put().

É igual no cliente HTTP, permite a gestão de nome de usuário e acesso ao servidor.

Futuras versões deste cliente darão acesso a lista de pastas e comandos personalizados.
No entanto, como foi indicado, no momento é uma classe muito limitada e pode ser empregada
como alternativa a uso direto de utilitários como ftp.curl ou wget para aplicações complexas que
necessita acesso a servidores FTP, dado que a gestão de processos externos é muito simples com
o Gambas.
Cópia literal

Extraído do livro "GAMBAS, programação visual com software Livre", da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal "Cópia literal". Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 7: XML ................................................................................................. 04


7.1 Escrevendo com XmlWriter .................................................................... 06
7.2 Leitura com o XmlReader ....................................................................... 11
Modelos de leitura ................................................................................ 11
Projeto inicial ....................................................................................... 12
Um exemplo de leitura ......................................................................... 13
7.3 XSLT ....................................................................................................... 20
O que é XSLT ...................................................................................... 20
Uma planilha de exemplo .................................................................... 20
Transformando documento com o Gambas ......................................... 22
7.4 Sobre o XML-PC ..................................................................................... 23
.Hoje em dia é comum ouvir falar de XML como a solução a todos os problemas de
gestão de informática na empresa. O certo é, que isso é sobrevalorizado, possivelmente devido as
campanhas de marketing realizadas por grandes companhia de software.

No entanto, também é certo que XML apresenta algumas vantagens importantes na hora
de trocar dados entre diferentes sistemas. Mas o que é XML?. Na ralidade não é nada novo nem
revolucionário, trata-se simplesmente de uma definição de formato para qualquer tipo de
documentos.

Quando necessitamos de escrever um arquivo de configuração, um documento com


registros extraídos de uma base de dados, transferir dados a um dispositivo, um formato para um
arquivo de texto ou uma folha de cálculo, sempre se expõe o mesmo problema ao programador: a
necessidade de um formato, documentado no caso ideal, que define o modo em que a informação
é inserida no arquivo ou em um fluxo de dados através de uma rede, com a intenção de que os
programas origem e destino desses dados possam ler e escrever cada parte do documento
corretamente. Muitas vezes escrever um arquivo com um determinado formato é uma tarefa
quase trivial, mas o processo inverso, a leitura, pode ser chato, já que temos que verificar erros,
cortar cadeias, comprovar a validade de cada fragmento, etc.
Por outro lado, cada aplicação desenvolveu seus próprios formatos para armazenamento e
leitura da informação que manipula, sendo muito difícil eliminar um elemento de um cadeia de
programas para substitui-lo por uma aplicação nova. Isto tem uma grande importância quando
uma empresa projeta passar de um sistema proprietário aos quais encontram-se acorrentados,
para outros livres onde obtém vantagens de preço, e capacidade de escolha de provedores de
serviços mais justos pelo investimento realizado. Os formatos proprietários prende literalmente o
cliente aos serviços e desejos de uma empresa provedora.
XML proporciona a solução em todos os aspectos comentados: é um padrão que define
como será inserido os dados e os campos de um arquivo ou fluxo. Alem disso as ferramentas de
gestão de documentos XML proporciona as funções necessárias para ler, escrever e verificar os
dados embebidos nesses documentos. Por último, trata-se de um padrão acessível a todos os
programadores e empresas de software, o que proporciona liberdade para manipular e
compreender o conteúdo dos dados.
O formato de um documento XML é similar a um escrito com HTML, no entanto há
diferenças substanciais. A primeira, e mais importante, já que XML é um formato de caráter
geral, pensado para trabalhar com qualquer tipo de dados, enquanto que HTML encontra-se
limitado a desenho de páginas web. A segunda diferença de HTML, é que onde há tags, como
novo parágrafo (<p>) que podemos deixar abertas, em XML absolutamente todas as tags devem
estar aninhadas, e cada tag aberta deve encerrar-se. Por outro lado, XML é sensível a maiúsculas,
quer dizer, seção que começa com a letra <p> não é igual outra que começa com a tag <P>.
O aspecto de um arquivo XML simples pode ser este:
<?xml version="1.0"?>

<dados>

<usuario>

<nome>Eric Smith</nome>

<socio>113</socio>

</usuario>

</dados>

Os documentos XML sempre começam, na atualidade, pela cadeia <?xml


versão="1.0"?>, na qual especifica-se que a seguir vem dados com o formato XML. No entanto
esta tag pode ser omitida, é conveniente adiciona-la, já que contem informações importantes.
Neste caso especifica-se a versão de XML utilizada, a 1.0 e que é a única que se usa na
atualidade. Se no futuro uma nova especificação internacional de XML ampliara ou modificara
XML, com uma versão 2.0 por exemplo, dispor dessa tag em um documento armazenado a
meses ou anos garantirá que os programas sigam sabendo como interpretar o conteúdo do
documento. Por outro lado, os documentos XML , por padrão, utilizam a codificação de
caracteres UTF-8. Se por qualquer razão tivermos que tratar com documentos XML que utilizem
outra codificação, também encontraremos essa informação na tag inicial:
<?xml version="1.0" encoding="ISO-8859-1"?>
Em seguida, chegamos ao corpo do documento XML. Há uma tag inicial que da nome ao
documento, neste caso simplesmente dados. Depois, neste documento vem os dados dos usuários
de uma associação: o nome e o número de associado. Todo ele entre tags abertas e fechadas, de
modo que o documento fica ordenado em ceções, com vários níveis de aninhamento.

XML permite que as tags disponham de atributos. Por exemplo, podemos prever que o
nosso arquivo de dados contemple um número de versão para o futuras aplicações, e que em cada
usuário adicione-se o ano que passou a fazer parte da associação. Os atributos tem a forma:
Definição="valor"
Ou seja, indicamos o nome do atributo, um sinal de igual e, encerramos entre aspas, o
valor desse atributo. O arquivo poderia ser assim agora:
<?xml version="1.0" encoding="ISO-8859-1"?>

<dados version="2.4">

<usuario>

<nome inicio="2005">Eric Smith</nome>

<socio>113</socio>

</usuario>

</dados>

Uma tag que se abre e fecha sem informações em seu interior, por exemplo <p></p>
para designar um paragrafo em um documento XML, pode simplificar-se escrevendo a tag de
abertura com a barra ao final do mome da tag: <p/>.

Estes são elementos mais básicos de XML, porem existe uma ampla gama de conceitos
que fica fora do alcance desse manual, e que mostra um grande número de possibilidades na
gestão de documentos XML: existe tags especiais para comentários, uma linguagem para
validação de dados DTD, folhas de estilos com formato XSL, espaços de nomes, etc. Um bom
ponto de inicio para aprender XML é a web http://www.w3schools.com/xml/default.asp, onde
encontram-se tutoriais a respeito, assim como links para outras fontes de informações.

7.1 Escrevendo com XmlWriter

A classe XmlWriter, contida dentro do componente gb.xml, utiliza um modo simples para
escrever um documento XML. Suponhamos que desejemos armazenar em um programa os dados
relativos a várias ligações telefônicas a internet. Nos interessará o nome da conexão, o telefone e
o DNS, primário e secundário, utilizados pelo provedor.

Vejamos um documento no qual teremos uma seção conexao para cada conexão, terá um
atributo mome e outro local, este último com dois valores: 1 se for local e 0 se for nacional.
Dentro da seção conexao haverá um campo para armazenar o número do telefone, assim como
outro para os DNS primário e secundário, os quais terá um atributo que determine se é o
primário.
Sempre é conveniente empregar nomes de tags que não contenham acentos ou
caracteres especiais de cada idioma (como ã ou ç), para evitar problemas se outros
programadores que devam retocar o programa ou o arquivo tenham teclados diferentes ou
empregam parsers de XML pobres que ignorem alguns aspectos da codificação.
Este será o aspecto do arquivo XML do exemplo:
<?xml version="1.0"?>

<conexoes version="1.0">

<conexao id="castuonet" local="0">

<telefone>1199212321</telefone>

<dns primario="1">127.0.0.2</dns>

<dns primario="0">127.0.0.3</dns>

</conexao>

<conexao id="limbonet" local="0">

<telefone>229943484</telefone>

<dns primario="1">127.0.10.10</dns>

<dns primario="0">127.0.20.42</dns>

</conexao>

</conexoes>

Antes de continuar, observemos o formato. Está separado em várias linhas, o retorno do


carro, e existe uma identificação para indicar os diversos níveis de a aninhamento. Isto não é em
absoluto necessário, e os parsers de XML ignoram os espaços em branco e tabulações, assim como os
retornos do carro. Isso é só uma ajuda para quem tem que consultar ou modificar o arquivo a partir de
um editor de texto comum. Só tem, portanto, sentido a nível humano, e um arquivo XML correto
poderia está contido em uma só linha de texto. Se estivermos acostumado a programar em C ou C++,
sabemos que com esta linguagem ocorre o mesmo: as diferentes linhas e tabulações dão ordem e
legibilidade ao programa, mas o mesmo poderia escrever-se em uma só linha. Em C, as chaves,
pontos e aspas são significativas, não é assim com os espaços e retorno do carro. Em XML o
significativo são as aberturas e fechamento das tags.

A seguir vamos criar um programa


de console chamado EscreveXML, e
dentro dele um único módulo ModMain
com uma função Main.
O programa terá uma referência ao
componente gb.xml.
Definimos e criamos um objeto
XmlWriter. Após isto, o abrimos com
Open para começar a escrita. Este método
aceita três parâmetros: o primeiro,
obrigatório, é o nome do arquivo a
escrever, que pode ser um local dentro do 
 
sistema de arquivos ou uma cadeia em branco, em cujo caso se escreverá na memória, logo
poderemos obtê-lo como uma cadeia para, por exemplo, enviar pela rede a um equipamento
remoto. O segundo é opcional, i indica que desejamos que tenha identificação (como se
comentou antes, para aumentar sua legibilidade), ou melhor, se escrevermos em uma só linha
para economizar espaço, o tornamos ilegível a partir de um editor não especializado. O terceiro,
também opcional, serve para, se desejarmos, especificar a codificação, diferente de UTF-8
' Gambas module file

PUBLIC SUB Main()

DIM Xml AS XmlWriter

Xml = NEW XmlWriter

Xml.OPEN ("", TRUE)

END

Indicar uma codificação diferente serve para que esta fique exposta no inicio
do documento, mas não realizará para nós a conversão das cadeias que escrevemos,
tarefa que teremos que fazer com funções como Conv$, se desejarmos.

Xml.StartElement ( conexoes )

Escrevendo a primeira seção. Temos de introduzir uma tag de abertura, tarefa que será
realizada com o método StartElement:

Xml.Attribute ( "versão", "1.0" )

Alem disso dispomos de um atributo "version" com valor "1.0":

Neste caso também podemos unir as duas instruções anteriores em uma só:
StartElemente aceita um parâmetro opcional, consistente em uma matriz de cadeia que sejam
pares de valores atributo-valor do atributo:

Xml.StartElemente ( "conexoes", [ "version", "1.0" ] )

Dentro desta sessão temos cada uma das conexão definidas. Começamos pela primeira
que, é igual a anterior, uma tag de captura, com um nome e dois atributos, neste caso:

Xml.StartElemente ( "conexoes", [ "id", "castuonet", "local", "0" ] )

Dentro desta região, dispomos de uma nova abertura de tag para o telefone:

Xml.StartElemente ( "telefone" )

Contem um texto que compreende o número do telefone:

Xml.StartElemente ( "1199212321" )
Após este passo encerramos a tag:

Xml.EndElemente ( )

No entanto, podemos resumir as três instruções anteriores em uma só, válidas para estes
elementos simples que tão só contem um texto dentro (ou nada) e logo se encerra.

Xml.Elemente ( "telefone", "1199212321" )

Para o DNS iniciamos uma tag "dns" com um atributo:

Xml.StartElemente ( "dns", [ "primario", "1" ] )

Incluimos um texto com o IP:

Xml.text ( "127.0.0.2" )

E finalizamos a seção:

Xml.EndElemente ( )

O mesmo para o segundo DNS:


Xml.StartElemente ( "dns", [ "primario", "0" ] )

Xml.text ( "127.0.0.2" )

Xml.EndElemente ( )

Finalmente encerramos a tag "conexao" que contem os elementos anteriores relativo a


essa conexão:
Xml.EndElemente ( )

Procedemos de igual maneira para a segunda conexão, e como podemos observar no


seguinte código:
Xml.StartElement ( "conexao", [ "id", "limbonet", "local", "1" ] )

Xml.Element ( "telefone" , "229943484" )

Xml.Startelement ( "dns", [ "primario", "1" ] )

Xml.text ( "127.0.10.10" )

Xml.EndElement ( )

Xml.Startelement ( "dns", [ "primario", "0" ] )

Xml.text ( "127.0.20.42" )

Xml.EndElement ( )

Xml.EndElement ( )

Finalmente encerramos o documento para que se escreva. Se não especificamos o nome


do arquivo, é o momento de fazer isso, em nosso caso, em memória.
A chamada a EndDocumento nos devolve uma cadeia com o documento XML, que o
mostramos no console.
PRINT Xml.EndDocumento ( )

O programa completo fica da seguinte maneira:

' Gambas module file

PUBLIC SUB Main()

DIM Xml AS XmlWriter

Xml = NEW XmlWriter

Xml.Open ( "", TRUE )

Xml.StartElement ("conexoes", ["versao", "1.0"] )

Xml.StartElement ("conexao", ["id", "castuonet", "local", "0"] )

Xml.Element ( "telefone", "1199212321" )

Xml.StartElement ( "dns", ["primario", "1"] )

Xml.Text ("127.0.0.2")

Xml.EndElement()

Xml.StartElement ( "dns", ["primario", "0"] )

Xml.Text ( "127.0.0.3" )

Xml.EndElement ()

Xml.EndElement ()

Xml.StartElement ("conexao", ["id", "limbonet", "local", "1" ] )

Xml.Element ("telefone", "229943484")

Xml.StartElement ("dns", ["primario", "1"])

Xml.Text ("127.0.10.10")

Xml.EndElement ()

Xml.StartElement ("dns", ["primario", "0"])

Xml.Text ("127.0.20.42")

Xml.EndElement ()

Xml.EndElement ()

PRINT Xml.EndDocument()

END
Ao executa-lo, veremos o documento no console. O modificamos para que escreva em um
arquivo em lugar do console, o qual nos servirá para lê-lo em um futuro exemplo de leitura de
arquivo XML. Para isso modificaremos o método Open:
Xml.Open ( User.Home & "/conexoes.xml" , TRUE )
eliminaremos a instrução PRINT no final:
Xml.EndDocument ( )

neste caso a chamada ao metodo EndDocument criará o documento em um arquivo dentro de


nossa pasta pessoal, chamado conexao.xml.
O arquivo aberto com Open não é gravado realmente do disco rígido até a chamada a
EndDocument. Por sua vez, o método Enddocument se encarrega de fechar todas as tags que
estiverem abertas, para garantir a coerência XML deste.

Exploremos a classe XmlWriter, observaremos que, alem dos métodos comentados, existe
outros para escrever comentários e elementos correspondente a DTD, entre outras características.
Também dispomos dos métodos práticos Base64 e BinHex, que converte uma cadeia para
codificações com este nome. Essas codificações permitem introduzir dados binários dentro de um
arquivo XML.

7.2 Leitura com XmlReader


Modelos de leitura
Ao menos existe três métodos para ler o conteúdo de um arquivo XML:

1. Um método se baseia em ler o arquivo do inicio ao fim. O litor vai gerando os


eventos conforme entra e sai dos diversos nós do documento, e os gestores de eventos
escrito pelo programador vão recebendo a informação. Esta forma de trabalho ainda não
está implementada no componente gb.xml, mas está previsto sua inclusão em futuras
versões.
2. Outro método consiste em carregar o documento completo na memoria, para
depois navegar por ele, com o qual obtém-se grande flexibilidade a custa de um consumo
considerável de recursos do sistema. Este método está parcialmente implementado no
Gambas através da classe XmlDocument, se bem que sua finalização não está prevista
para futuras versões, e não é recomendado seu emprego para leitura de arquivos XML.
No entanto, esta classe já é utilizada para transformações XSLT, como veremos mais
adiante neste capitulo.
3. Por último, o método que consome menos recursos e possui bastante
simplicidade de aprendizagem e uso, que dispõe de um cursor, que só move-se para
adiante, de nó em nó, e que em cada momento podemos emprega-lo para conhecer o
conteúdo e tipo de cada nó. Se já trabalhamos com a plataforma .NET(TM) ou MONO(TM),
nos será familiar a classe XmlReader e seus derivados, como XmlTextReader, que
trabalha da mesma maneira. Este modo de trabalho encontra-se perfeitamente suportado
no componente gb.xml através da classe XmlReader.
Apresentação inicial
No caso do gambas, a leitura de documentos é realizada utilizando objetos de classe
XmlReader.

O código de leitura se tornará mais complexo, ao levar em conta vários aspectos:

* Recusar cada arquivo inválido: pode haver arquivos com formatos não XML ou que
contenham dados sem sentido para nossa aplicação.

* Ignorar dados não conhecidos: é possível que um documento contenha dados que não
nos interessam, mas foram adicionadas ao arquivo por outra aplicação em previsão de
futuros usos (pode haver, por exemplo, uma tag <tarifa> dentro de cada conexão).
Também uma tag conhecida pode conter atributos desconhecidos.

* Ordem desconhecida: um arquivo XML não é relevante a a ordem que se escreve os nós
filhos de um nó, quer dizer, que estes dois exemplos deveriam ser dados por válidos:
<conexao>

<dns>...

<telefone>...

<dns>...

</conexao>

<conexao>

<telefone>...

<dns>...

<dns>...

</conexao>

Se a aplicação espera encontrar o nó telefone antes do nó dns, falhará ao tratar o primeiro


arquivo, que, no entanto, contem a mesma informação.

* Ignorar tags sem interesse para nossa aplicação: XML, como indicamos brevemente no
inicio, prever a possibilidade de adicionar comentários (similares aos comentários de
qualquer programa, sem uso para este mas que aumenta a legibilidade), nós DTD, etc.
Haveremos de passar sobre esses nós ignorando-os e sem pressupor se existe ou não.

Quanto mais possibilidades adicionarmos ao nosso código de salvar o


desconhecido, mais flexível será nosso leitor XML para permitir a leitura de dados
proveniente, talvez, de programas escritos por vários programadores com os quais
não temos contato, ou que tenham pensamentos bem diferentes acerca do conteúdo
do arquivo.
Um exemplo de leitura
Criaremos um projeto gráfico chamado LerXML, com um formulário Fmain, que terá a
propriedade Arrangement com o valor Fill, e um único controle TreeView em seu interior
chamdo Arvore. O programa conterá um referencial ao componente gb.xml.

 
Na Abertura do formulário leremos o arquivo XML. Com o método Form_Open que
ficará assim:
* Em primeiro lugar definimos o objeto XmlReader, o criemos e tratamos de abrir o
arquivo XML. Se o arquivo se o arquivo não existe, ou o formato for desconhecido, será
gerado um erro nesse ponto.

PUBLIC SUB Form_Open()

DIM Xml AS XmlReader

xml = NEW XmlReader

TRY Xml.Open ( User.Home & "/conexoes.xml" )

IF ERROR THEN

Message.Error("Falha ao abrir o arquivo")

RETURN

END IF

END
* Entramos em um loop no qual lemos cada nó avançando pelo conteúdo do arquivo. Nos
interessa encontrar o primeiro tipo Element e que seu nome seja conexoes. Se não for assim, o
arquivo não conteria dados de interesse e o recusaríamos Mas se for o correto, chamaremos uma
função PreencherArvore, onde trataremos estes dados.
DO WHILE TRUE

IF Xml.Node.Type = XmlReaderNodeType.Element THEN

IF Xml.Node.Name = "conexoes" THEN

PreencherArvore (Xml)

ELSE

Message.Error("O documento não contém dados de conexões")

Xml.Close()

END IF

END IF

END

Para cada interação do loop, empregamos o método Read, que situa o ponteiro interno no
nó seguinte do arquivo XML. Neste processo, pode ocorrer um erro se o ponteiro chegar em zona
onde o arquivo está corrompido, ou seja, que não cumpre a norma XML e, por tanto, não pode
ser lido.

TRY Xml.Read()

IF ERROR THEN

Message.Error("Formato XML inválido")

RETURN

END IF

Se chegarmos ao final do arquivo (após o ultimo nó), terminamos o loop. Esta


circunstância pode ocorrer porque a propriedade Eof do objeto XmlReader toma o valor TRUE.
IF Xml.Eof THEN BREAK

LOOP

Após a leitura do arquivo encerramos o objeto XmlReader.

TRY Xml.Close()

END

Vamos agora implementar o procedimento PrenecherArvore. Entramos de novo no loop


e, lemos o seguinte nó para nos situarmos dentro da conexao.

Neste caso seguiremos lendo por cada um dos elementos conexao que existe dentro da tag
principal conexones, e sairmos da função quando chegarmos ao final desta tag.
PUBLIC SUB PreencherArvore ( Xml AS XmlReader )

DO WHILE TRUE

TRY Xml.Read ( )

IF ERROR THEN RETURN

Se encontrarmos um nó do tipo Element que se chame conexao, chamaremos uma


função chamada PreencherItem para trata-lo. Mas e se o seu nome for desconhecido para nós, o
ignoraremos, todo o seu conteúdo para chegarmos ao nó seguinte do mesmo nível, com o método
Next.
IF Xml.Node.Type = XmlReaderNodeType.Element THEN

IF Xml.Node.Name = "conexao" THEN

PreencherItem ( Xml )

ELSE

TRY Xml.Read ( )

IF ERROR THEN BREAK

END IF

ELSE

IF Xml.Node.Name = "conexoes" AND Xml.Node.Type

= XmlReaderNodeType.EndElement THEN BREAK

END IF

END IF

LOOP

END

PrenecherItem é um procedimento mais complexo, no qual leremos o conteúdo de cada


conexão existente.

PUBLIC SUB PreencherItem ( Xml AS XmlReader )

DIM Limite AS Integer

DIM sNode AS String

DIM sLocal AS String

DIM sPrim AS String

Em primeiro lugar pegar os dados dos atributos da tag conexao. Para isso, vamos iterar
com a propriedade Attributes do nó. Ao longo do processo de iteração iniciado com o FOR
EACH, o ponteiro interno do leitor XML passará por cada um dos nó, que por sua vez são nós,
cujo nome e valor é o nome e o valor do atributo. Finalizado o processo de iteração, o ponteiro
volta ao nó sobre o qual estamos situados, e com os dados arrecadados, adicionamos ao nosso
controle TreeView, um nó com efeito para sua representação gráfica.

FOR EACH Xml.Node.Attributes

IF Xml.Node.Name = "id" THEN sNode = Xml.Node.Value

IF Xml.Node.Name = "local" THEN sLocal = Xml.Node.Value


NEXT

IF sNode <> "" AND sLocal <> "" THEN

IF sNode = "0" THEN

TRY Arvore.Add ( sNode, sNode & " (local)" )

ELSE

TRY Arvore.Add ( sNode, sNode & " (nacional)" )

END IF

END IF

Passamos agora ao interior do nó conexao para extrair informações de seus nós filhos,
quer dizer, os DNS e o número do telefone. Buscaremos nós do tipo Element e em função de seu
nome atuaremos de um modo ou outro.
TRY Xml.Read ()

IF ERROR THEN RETURN

DO WHILE TRUE

IF Xml.Node.Type = XmlReaderNodeType.Element THEN

SELECT CASE Xml.Node.Name

Para o caso do telefone, passaremos do nó atual (a tag telefone), o seguinte nó que


conterá o texto com o número do telefone, (poderíamos, no entanto, melhorar o algorítimo
contemplando a possibilidade de encontrar algo distinto a um nó de texto, coisa que não faremos
aqui para não complicar mais o código, que pode se tornar muito complexo para um
principiante).
CASE "telefone"

TRY Xml.Read ()

TRY Arvore.Add ( sNode & "-tel" , "tel: " & Xml.Node.Value , NULL, sNode )

Se o nó for do tipo "dns", teremos que comprovar o valor do atributo que indica se é
DNS primário ou não, e depois ler o texto que contem o IP:
CASE "dns"

sPrim = "0"

FOR EACH Xml.Node.Attributes

IF Xml.Node.Name = "primario" THEN sPrim = Xml.Node.Value

NEXT

TRY Xml.Read ()

IF sPrim = "0" THEN

TRY Arvore.Add ( sNode & "-dns2", "-dns2" & Xml.Node.Value, NULL, sNode )

ELSE

TRY Arvore.Add ( sNode & "-dns1", "-dns1" & Xml.Node.Value, NULL, sNode )

END IF

END SELECT
Uma vez lido o nó, passamos ao seguinte e continuamos lendo até encontrar um do tipo
EndElement, onde saberemos que encontramos o final do nó conexao.
TRY Xml.Read()

IF ERROR THEN BREAK

LOOP

ELSE

IF Xml.Node.Type = XmlReaderNodeType.EndElement THEN BREAK

END IF

TRY Xml.Read ()

IF ERROR THEN BREAK

LOOP

END

Este é o código completo exposto:

PUBLIC SUB PreencherItem ( Xml AS XmlReader )

DIM Limite AS Integer

DIM sNode AS String

DIM sLocal AS String

DIM sPrim AS String

FOR EACH Xml.Node.Attributes

IF Xml.Node.Name = "id" THEN sNode = Xml.Node.Value

IF Xml.Node.Name = "local" THEN sLocal = Xml.Node.Value

NEXT

IF sNode <> "" AND sLocal <> "" THEN

IF sNode = "0" THEN

TRY Arvore.Add ( sNode, sNode & " (local)" )

ELSE

TRY Arvore.Add ( sNode, sNode & " (nacional)" )

END IF

END IF

TRY Xml.Read ()

IF ERROR THEN RETURN

DO WHILE TRUE

IF Xml.Node.Type = XmlReaderNodeType.Element THEN

SELECT CASE Xml.Node.Name


CASE "telefone"

TRY Xml.Read ()

TRY Arvore.Add ( sNode & "-tel" , "tel: " & Xml.Node.Value , NULL, sNode )

CASE "dns"

sPrim = "0"

FOR EACH Xml.Node.Attributes

IF Xml.Node.Name = "primario" THEN sPrim = Xml.Node.Value

NEXT

TRY Xml.Read ()

IF sPrim = "0" THEN

TRY Arvore.Add ( sNode & "-dns2", "-dns2" & Xml.Node.Value, NULL, sNode )

ELSE

TRY Arvore.Add ( sNode & "-dns1", "-dns1" & Xml.Node.Value, NULL, sNode )

END IF

END SELECT

TRY Xml.Read ()

IF ERROR THEN BREAK

LOOP

ELSE

IF Xml.Node.Type = XmlReaderNodeType.EndElement THEN BREAK

END IF

TRY Xml.Read ()

IF ERROR THEN BREAK

LOOP

END ' fim de PreenchendoItem ***********************************************

PUBLIC SUB PreencherArvore ( Xml AS XmlReader )

DO WHILE TRUE

TRY Xml.Read ( )

IF ERROR THEN RETURN

IF Xml.Node.Type = XmlReaderNodeType.Element THEN

IF Xml.Node.Name = "conexao" THEN

PreencherItem ( Xml )

ELSE

TRY Xml.Read ( )

IF ERROR THEN BREAK


END IF

ELSE

IF Xml.Node.Name = "conexoes" AND Xml.Node.Type = XmlReaderNodeType.EndElement THEN

BREAK

END IF

END IF

LOOP

END ' fim de PrenecherArvore ***********************************************

PUBLIC SUB Form_Open()

DIM Xml AS XmlReader

Xml = NEW XmlReader

TRY Xml.Open ( User.Home & "/conexoes.xml" )

IF ERROR THEN

Message.Error("Falha ao abrir o arquivo")

RETURN

END IF

DO WHILE TRUE

IF Xml.Node.Type = XmlReaderNodeType.Element THEN

IF Xml.Node.Name = "conexoes" THEN

PreencherArvore (Xml)

ELSE

Message.Error("O documento não contém dados de conexões")

Xml.Close()

END IF

END IF

TRY Xml.Read()

IF ERROR THEN

Message.Error("Formato XML inválido")

RETURN

END IF

IF Xml.Eof THEN BREAK

LOOP

TRY Xml.Close()

END
Este código dará lugar, ao executar-se, à uma
visão em árvore dos dados contido no arquivo XML.

 
   
 

7.3 XSLT
O que é XSLT?
Acompanhando a XML, XSLT permite realizar conversões de formatos de documentos.
Com XSLT podemos, por exemplo, converter dados de um documento XML em um documento
HTML, ou casos mais complexos como gerar um documento PDF ou StarWriter a partir de
dados XML que nós criamos.

Graças s XSTL podemos separar de modo definitivo a informação de sua representação,


pelo qual empregamos extensivamente em aplicações web, que podem receber dados de uma
base remota em formato XML e converte-los, geralmente, a HTML para envia-los ao cliente com
uma representação agradável.
XSTL é baseado em documentos XML, chamados planilhas, que contem as instruções
necessárias para converter um determinado documento XML (com as tags e atributos próprios
desse documento) em outro com diferente formato.
XSLT é extenso para trata-lo aqui em profundidade no entanto, como sempre acontece
com os padrões abertos e quase nunca com os formatos proprietários, podemos encontrar fontes
específicas de informações adicionais, por exemplo, http://www.w3schools.com.

Uma exemplo de planilha


Cada planilha XSTL refere-se ao conteúdo de um determinado documento XML.
Suponhamos um documento XML como este, no qual encontra-se uma lista de sócios:
<?xml version="1.0"?>

<socios>

<socio>

<numero>1123</numero>

<nome>João L. Aguiar</nome>

<tipo>Horário</tipo>

</socio>

<socio>

<numero>2135</numero>

<nome>Salvador G. Serra</nome>
<tipo>Regular</tipo>

</socio>

<socio>

<numero>9554</numero>

<nome>alberto N. Parra</nome>

<tipo>regular</tipo>

</socio>

</socios>

As planilhas XSLT sempre começam com identificadores. O primeiro, de documento XML, já o


conhecemos, o segundo denota que o que vem a seguir é um documento XSTL.
<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

Após isso o código em si, o qual geraremos uma tabela HTML com os dados
<xsl:template match="/">

<html>

<body>

<hl>Lista de socios</hl>

<table border="1">

<tr>

<th><b>Nro.</b></th>

<th><b>Nome</b></th>

<th><b>Tipo</b></th>

</tr>

<xsl:for-each select="socios/socio">

<tr>

<td><xsl:value-of select="numero"/></td>

<td><xsl:value-of select="nome"/></td>

<td><xsl:value-of select="tipo"/></td>

</tr>

</xsl:for-each>

</table>

</body>

</html>

</xsl:template>

</xsl:stylesheet>
Como podemos observar, vamos embebendo as tags HTML; empregando for each para
tomar cada elemento do arquivo XML; e colocaremos em cada ponto da tabela um dos campos
escolhidos.

Transformando o documento com Gambas


Até aqui o que temos é um documento XML e uma planilha XSTL, mas agora
necessitamos de um motor que realize a conversão. Para isso salvaremos o arquivo com os dados
em nossa pasta pessoal como socios.xml, e a planilha como socios.xsl. Criamos um novo projeto
de console chamado TransformaXSLT, com um único módulo modMain e uma referência ao
componente gb.xml.xslt.

O código será tão simples como este:

' Gambas module file

PUBLIC SUB Main ()

DIM Documento AS NEW XmlDocument

DIM Planilha AS NEW XmlDocument

DIM Resultado AS NEW XmlDocument

Documento.Open ( User.Home & "/socios.xml" )

Planilha.Open ( User.Home & "/socios.xsl" )

Resultado = Xslt.Transform ( Documento, Planilha )

Resultado.Write ( User.Home & "/socios.html" )

END

Como vimos anteriormente, a classe XmlDocument carrega e verifica um


documento XML na memória. Neste caso carregamos dois documentos: o primeiro chamado
Documento, contem os dados dos sócios; o segundo, Planilha, é a folha XSLT que indica como
transforma-la em HTML. A única classe que usa o
componente gb.xml.xslt, chamada XSLT, é estática e
dispõe de um único método Transform, o qual
passamos como parâmetros o documento e a planilha,
e retorna um documento novo com o formato indicado,
neste caso uma página web. Escrevemos essa página
em um arquivo em nossa pasta pessoal, e saímos. Se
abrirmos a página obtida, veremos o resultado é uma
 
     página como mostra a figura da esquerda.
7.4 Sobre XML-RPC
Em futuras versões Gambas 2, disporemos também de um componente XML-RPC. Estas
referem-se ao uso de XML como sistema para comunicar dois processos entre duas maquinas
diferentes XML-RPC é um subconjunto de XML que define uma linguagem para chamar a
processos remotos.

Um servidor XML-RPC aceita chamadas remotas e seus métodos. O processo é muito


similar a chamar uma função local dentro de um programa: o cliente chama a função passando
alguns parâmetros (números inteiros, cadeias, estrutura de dados...); e o servidor processa a
chamada e devolve ao cliente o resultado, que também será uma cadeia, número, data, etc.

O componente XML-RPC possuem varias facilidades para implementar estes processos.

Por um lado o servidor dispõe de um parser, no qual definiremos os nomes dos métodos
que serão aceitos, assim como a correspondência entre estes e outros locais do programa
servidor. Deste modo, teremos apenas que passar o pedido do cliente ao parser, o qual se
encarrega de verificar o número e tipo dos parâmetros, e devolver um error ao cliente, ao chamar
a função local e devolver o resultado ao cliente.

O servidor poderá funcionar em solitário, implementando um pequeno serviço web para


atender as petições, ou pode funcionar de modo controlado pela aplicação servidora, a fim de
implementar CGIs que se sirvam desde apache, por exemplo.

Na parte cliente é possível definir a URL do servidor e a forma de cada método.


Igualmente poderá atuar de modo solitário, solicitando mediante uma petição web a chamada ao
servidor. Também pode criar o documento XML da petição, deixando à aplicação ou modo de
transporte e recepção dos dados com o servidor.
Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 8: HERANÇA ...................................................................................... 04


8.1 Linguagem orientada a objeto e herança ................................................ 04
8.2 Conceito necessários .............................................................................. 05
A classe pai ............................................................................................ 05
A classe Filha. Palavra chave INHERITS ............................................. 06
Funcionalidade estendida. Palavra chave SUPER ................................. 07
Modificando Funcionalidades ................................................................ 08

métodos especiais New e Free ................................................ 09
Limitações .............................................................................................. 13
8.3 Criar um novo controle com o Gambas ................................................. 13
 
o código ............................................................................ 13
Implementação básica ............................................................................ 14
8.4 Novo componentes com o Gambas ........................................................ 17
Preparando o código para a palavra chave EXPORT ........................... 17
Arquivos necessários, localização ......................................................... 18
Testando os novos componentes ........................................................... 19
8.1

Já tivemos uma breve introdução ao mundo da programação orientada a objetos. Podemos


dizer que um um objeto é uma ferramenta, por exemplo, um lava jato. A classe são os planos
dessa ferramenta, nos quais estão descritos as diversas escovas giratórias, os condutores de agua
e detergente, os secadores de ar quente, os diversos sensores, o processo automático passo a
passo desde a entrada do carro até sua saída, etc.
Quando criamos um objeto, o sistema nos devolve uma ferramenta, neste caso, um Lava
Jato pronto para funcionar, que foi criado a partir dos planos escrito na classe.
Mas, suponhamos que como vendedores de Lava Jato desejamos inovar e adicionar uma
fase de enceramento em nosso Lava Jato. Venderemos maquinas baratas sem o inserador e outras
mais caras com esta funcionalidade adicionada. a primeira opção será por mãos a obra com
novos planos a partir do zero, mas isto custa tempo e dinheiro. o melhor seria tomar os planos já
existente e a partir deles, adicionar novas funções.
Isto consiste precisamente a herança: partimos de uma classe já escrita, provavelmente
grande e com muitas funcionalidades, e a partir dela criamos outra classe (outros planos) que
tenha o mesmos rendimento do original, mais as novas funções que adicionamos, a qual herda,
(daí vem o termo), a funcionalidade de sua classe pai.
Além de adicionar novas funcionalidades, poderemos também modificar o
comportamento das já existente para adapta-las a nova máquina a criar, por exemplo, as escovas
do Lava Jato com cera terão de ser de um material que que não se dissolva com os produtos
químicos que compõem a nossa cera.

Em resumo, vamos explicar como criar classes que provenham de outras e que se comportem
em primeira instância como a original e que adicionem novas funções ao adicionarmos algumas das
existentes, eliminando assim a necessidade de escrever as mesmas linhas de código mais de uma vez.

8.2 Conceitos necessários


A classe pai

Comecemos criando os planos da máquina original, Quer dizer, criemos uma classe pai.
Vamos escrever uma classe a partir da qual criaremos objetos. Esses objetos armazena vários
números reais e, uma vez introduzidos, nos devolve a média aritmética de todos eles.

Agora, vamos criar um novo projeto de console, chamado EstudoHerança e dentro dele
um módulo de inicio chamado modMain, que nos servirá para os testes.

Além disso, criaremos também uma classe, chamada ClsCalculo, na qual escreveremos o
código principal a estudar.



   

Dentro de ClasCalculo escreveremos o código necessário: teremos uma matriz privada de


números reais, onde adicionamos cada um dos números a calcular e quatro métodos: o primeiro,
_New, é o construtor que serve para inicializar a matriz; o segundo, _Free, libera a matriz a
destruir o objeto; o terceiro, Add, adiciona um novo valor a série; e o quarto, Avarage, calcula as
média aritmética dos números armazenados:
' Gambas class file

PUBLIC _Numeros AS Float

PUBLIC SUB _new ()

_Numeros = NEW Float [ ]

END

Faltam paginas 260-261

Os métodos públicos que começam pelo


sinal de sublinhar nunca aparecem no sistema de
auto completar nem no sistema de ajuda, quando
criamos componentes. Já que no gambas não      

existe o conceito de C++ e outras linguagens de 
propriedade, variável ou função protegida (acessível só a partir da classe e suas classes filhas),
esta funcionalidade nos permite declarar variáveis publicas não visíveis a partir do código
principal, mantendo assim uma interface mais coerente com nosso propósito.

Os nomes das variáveis, propriedade ou funções que começam com um


sublinhado, não aparecem nem no sistema de auto completar nem na ajuda do
Gambas.

Nota para programadores de C++


No Gambas existe propriedades, métodos e variáveis privadas ou públicas,
mas não protegidas.

A classe filha. Palavra chave INHERITS

Suponhamos agora que desejamos ter uma nova classe que se comporte como a inicial,
mas tenha uma propriedade adicional, só de leitura, que nos devolve o número de elementos que
armazenamos.

Criaremos então uma nova classe chamada ClsCalculo2, na qual introduziremos no início
a palavra chave INHERITS seguida do nome da classe pai(ver figura 4).

Isto é todo o necessário para ter uma classe que herda todas as propriedades de sua classe
pai. Vamos modificar o código do método Main() de modo que criemos um objeto na segunda
classe, em lugar da original.





INHERITS ClsCalculo

PUBLIC SUB Main()

DIM Calculo AS NEW ClsCalculo2

Calculo.add [ 12.5 ]

Calculo.add [ 23.6 ]

Calculo.add [ 7.5 ]

PRINT Calculo.Average ( )

END

Se executarmosParaagora o programa,
escrever observaremos
uma classe queaso características
que herda resultado 14.6 deé exatamente
uma classeo mesmo:
pai, a
nova classe já dispõe
digitamos de todosseguido
INHERITS os métodos, propriedades
do nome e eventos
da classe pai, da original,
no início semdanecessidade
do código nova
de escrever
classe os códigos que os implementa.
' Gambas class file

INHERITS ClsCalculo

Notas para programadores de C++


No Gambas cada classe filho tem uma só classe pai, não existe o conseito de
[ Command , parametro1 , parametro2 ....... ]
herança múltipla.

Funcionalidade extendida. Palavra chave SUPER

A nova classse ClsCalculo temos pouco interesse por hora: fazer o mesmo
' Gambas class file

INHERITS ClsCalculo

PROPERTY READ Count AS Integer

PRIVATE FUNCTION Count_Read() AS Integer

RETURN SUPER._Numbers.Count

END

Estamos empregando uma nova palavra chave pela primeira vez, SUPER. Sabemos que
quando nos referimos a um objeto podemos utilizar, tanto o nome do objeto quanto a palavra
chave ME quando quando nos referimos ao objeto atual dentro da própria classe. Pois bem, neste
caso, a matriz _Números não se encontra dentro da classe ClsCalculo2, e sim dentro da classe
pai ClsCalculo.

Com a palavra chave SUPER não nos referimos ao objeto atual, e sim ao pai do objeto
atual.

Escrevamos agora o código


PUBLIC da função Main() para aproveitar as novas características de
SUB Main()
nossa classe filho: DIM Calculo AS NEW ClsCalculo2

Calculo.add [ 12.5 ]

Calculo.add [ 23.6 ]

Calculo.add [ 7.5 ]

PRINT "Elementos: " & Calculo.Count & " -Média: " & calculo.Average ()

END

Como é fácil adivinhar, o resultado da execução:

Elementos: 3 – Média: 14.6.

Modificando Funcionalidades

Até agora temos duas classes, uma das quais tem uma propriedade adicional Count,
continua sendo pouco útil já que poderíamos, sem mais, haver adicionado essa funcionalidade a
classe original ClsCalculo, evitando mantermos duas classes diferentes. Vamos agora explorar o
verdadeiro potencial do conceito de Herança, e as possibilidades de substituir os métodos e
propriedades originais por novas implementação que dão lugar a resultados distintos: neste caso,
a nova classe, quando o método Avarage é chamado, apagará todos os elementos da matriz para
ficar em branco. Desta forma, teremos duas classes com diferente funcionalidade partindo de
uma base comum: os objetos da classe pai acumula números sem fim, enquanto que após cada
A classe ClsCalculo2 fica assim:
' Gambas class file

INHERITS ClsCalculo

PROPERTY READ Count AS Integer

PRIVATE FUNCTION Count_Read() AS Integer

RETURN SUPER._Numbers.Count

END

PUBLIC FUNCTION Average() AS Float

DIM Nm AS Float

DIM Vl AS Float

DIM Total AS Integer

IF SUPER._Numbers.Count = 0 THEN RETURN 0

FOR EACH Vl IN SUPER._Numbers

Nm += Vl

Total += 1

NEXT

SUPER._Numbers.Clear()

RETURN Nm / Total
Agora existe um método Average dentro de ClsCalculo2, e este método
substituem na END
classe filho o método Average do pai. Quando chamamos o método
Average de um objeto da classe ClsCalculo2, o interpretador não chamará a função
original do pai, e sim a implementação da classe filho.

PUBLIC SUB Main()

DIM Calculo AS NEW ClsCalculo2


função Main()
Modificamos aCalculo.add [ 12.5 ]e testamos esse novo código para comprovar os resultados

Calculo.add [ 23.6 ]

Calculo.add [ 7.5 ]

PRINT "Elementos: " & Calculo.Count & " -Média: " & calculo.Average ()

Calculo.add [ 17.5 ]

Calculo.add [ 31.8 ]

PRINT "Elementos: " & Calculo.Count & " -Média: " & calculo.Average ()

END
Como resultados, obteremos no console duas linhas que mostra o funcionamento da nova
classe:
Elementos: 3 – Média: 14.6

Elementos: 2 – Média: 24.65

Se a primeira linha de Maina() definirmos que o objeto Calculo pertence a classe pai
ClsCalculo, eliminamos o uso de count, que não existe na classe pai, e voltamos a executar o
programa, observamos claramente
PUBLIC a diferença:
SUB Main()

DIM Calculo AS NEW ClsCalculo

Calculo.add [ 12.5 ]

Calculo.add [ 23.6 ]

Calculo.add [ 7.5 ]

PRINT " -Média: " & calculo.Average ()

Calculo.add [ 17.5 ]

Calculo.add [ 31.8 ]

PRINT " -Média: " & calculo.Average ()

END

Elementos: 3 – Média: 14.6


Neste caso, seElementos:
calcula primeiro a média dos três primeiros números e depois a média dos
2 – Média: 24.65
cinco números:

Podemos aproveitar mais da herança, empregando a palavra SUPER que vimos


anteriormente. A classe original ClsCalculo já tem o código necessário para calcular a média, e
na classe filha ClsCalculo2 se necessitarmos apagar os elementos da matriz uma vez calculada a
média. ' Gambas class file

INHERITS ClsCalculo
Podemos, então, modificar o código da nova função Average, para que chame a original e
PROPERTY
depois apague os elementos, READ Count
poupando, comoASsempre,
Integer tempo e código:

PRIVATE FUNCTION Count_Read() AS Integer

RETURN SUPER._Numbers.Count

END

PUBLIC FUNCTION Average() AS Float

DIM Nm AS Float
Nm = SUPER.Average()

SUPER._Numbers.Clear()

RETURN Nm

END

A nova função Average agora se limita a chamar a original, utilizando a classe pai, para
armazenar esse valor, apagar os elementos da matriz e devolver o valor.

Utilizando métodos especiais: _New e _Free

Vamos criar uma nova classe filho de ClsCalculo, que se comporte como a original, mas
que, neste caso, no momento de inicializar um objeto da classe, armazene alguns valores
Ponhamos como exemplo a medida de nosso temperatura.
Os objetos, ao inicializar-se, pode tomar os valores da
temperatura da semana anterior de forma automática para que, depois, o
programador introduza os da semana em curso e obtenha a média da
quinzena.

  
 Para isso criaremos a classe ClsCalculo3, com este código:

!     

' Gambas class file

INHERITS ClsCalculo

PUBLIC SUB _New()

SUPER.Add ( 12.5 )

SUPER.Add ( 15.3 )

SUPER.Add ( 18.4 )

SUPER.Add ( 19.12 )

SUPER.Add ( 21.15 )

SUPER.Add ( 20.4 )

SUPER.Add ( 19.5 )

END

PUBLIC SUB Main()

DIM Calculo AS NEW ClsCalculo3

Calculo.add [ 29.2 ]
Igualmente escrevemos e executamos o código de Main() para comprovar o resultado:
PRINT " -Média: " & calculo.Average ()

END
O resultado da execução é:

Média: 19.44625

Como era de se esperar, pelo explicado anteriormente, foi executado no modo _New() de
nossa classe filha. Se bem, neste método não só adicionamos elementos, porem não inicializamos
a matriz, o interpretador comportou-se como nos casos anteriores, deveríamos ter obtido um erro
em tempo de execução, ao ter no início a matriz com o valor Null.

O que ocorre na realidade, é que ao utilizarmos o método especial _New(), o interpretador


sempre executa o método original da classe pai e depois a nova implementação do filho.
O método especial _New() realizado na classe filhas, como no resto dos
métodos ou propriedades, em seu lugar se chama sempre o método original New()
do pai para que inicialize todo o necessário e, depois, se existe, se chama o método
_New() da classe filha. Deste modo, o objeto sempre está pronto e inicializado
quando começarmos a trabalhar com ele.

Adicionamos' agora
Gambasum
classdestructor
file _Free() personalizado para nossa classe herdeira, na
qual indicamos no valor da variável
INHERITS _Numbers:
ClsCalculo

PUBLIC SUB _New()

SUPER.Add ( 12.5 )

SUPER.Add ( 15.3 )

SUPER.Add ( 18.4 )

SUPER.Add ( 19.12 )

SUPER.Add ( 21.15 )

SUPER.Add ( 20.4 )

SUPER.Add ( 19.5 )

END

PUBLIC SUB _Free()

IF SUPER._Numbers = NULL THEN PRINT "NULO"

END

A execução do programa da agora como resultado o que vemos a seguir:

Média: 19.44625
NULO

Com isto se demostra que o interpretador, como no caso do _New, chamou primeiro o
Se nos surgir alguma dúvida a respeito da ordem de execução de algumas
funções, recordemos que sempre podemos executar o código passo a passo para
testa-lo.

Limitações

A principal limitação da herança em Gambas, é que uma classe não pode ser filha de
outra que, por sua vez, seja filha de uma terceira.

Entre outras palavras, só há um nível de herança e em nossos exemplo anteriores não


poderíamos criar uma ClsCalculo4 que fossem filhas de ClsCalculo2 ou ClsCalculo3, já que
estas estão utilizando a palavra chave INHERITS para indicar indicar que herda as propriedades
de ClsCalculo.

Isto implica, por exemplo, que não podemos criar classes que são herdeiras de Form,
pois a classe Form provem de Window.

8.3 Criar um novo controle com o Gambas


Apresentando o código

Vamos pensar na possibilidade de criar novos controles gráficos diretamente do Gambas,


aproveitando o conceito de herança.

Criar controles novos programados em Gambas, tem algumas vantagens fundamentais:


seu desenvolvimento é rápido ao trabalhar em uma linguagem de alto nível, e o mesmo código
serve tanto se programarmos com gb.gtk, como se fizermos com gb.qt, forçando duas
implementações para um mesmo controle com diferentes toolkits.

A desvantagem é uma velocidade menor do código ao ser interpretado, questão que terá
mais ou menos importância segundo a função do novo controle, assim como menor flexibilidade
na hora de moldar o controle, já que trabalhamos sobre os controles previamente criados com o
Gambas e não sobre um toolkit para C ou C++ como GTK+ ou KT, nem um baixo nível como
X-Windows.
Os controles criados com Gambas hão de partir de um controle já existente
não podemos
Para partir
criar um diretamente
controle da classe
com Gambas, base
temos control,
que ouum
partir de seja,
queum
já controle
exista. Secriado
estivermos
com INHERITS CONTROL não funcionará, já que o controle não
acostumado a trabalhar com outros ambientes RAD de BASIC ou C++, nos será familiar tem uma partir
de umrepresentação gráfica,
controle modelo com senão quepropriedades
algumas é simplesmente a basebásico
e métodos que define as propriedades,
para criar um novo.
métodos e eventos mínimos para outros controles reais (Label, Button...)
Para desenhar texto em diferentes cores, trazemos cada letra em separado, a partir de uma
cor inicial Color1 até uma cor final Color2, utilizando um algorítimo algo rudimentar: tomamos
a diferença de cor de duas cores e vamos aumentando e vamos aumentando o valor da cor atual
por cada nova letra que desenharmos.

Para trazer cada letra separada, teremos que usar a classe Draw sobre um controle base
que será DrawingArea.

Implementação básica
Criaremos um novo projeto gráfico, que chamaremos
ColorLabel, para nossos testes. Dentro dele, originaremos um
formulário FMain, também para nossos testes, assim como uma
classe chamada ColorLabel para implementar o controle. Igual a
um label normal, definiremos uma propriedade Text com o texto
a mostrar, e adicionaremos duas propriedades novas Color1 e
Color2 que contem o valor inicial e final do gradiente. Para
manipular as três propriedades, teremos três variáveis privadas: 




∀ !

hText para o texto, hColor1 para a primeira cor e hColor2 que
contem o valor inicial e final do gradiente. Para manipular as
três propriedades, teremos três variáveis privadas: hText para o

texto hColor1 para a primeira cor e hColor2 para a segunda cor.


' Gambas class file

Começamos agora a escrever


INHERITS o código da classe:
DrawingArea

PRIVATE hText AS String

PRIVATE hColor1 AS Integer

PRIVATE hColor2 AS Integer

PROPERTY Text AS String

PROPERTY Color1 AS Integer

PROPERTY Color2 AS Integer

PRIVATE FUNCTION Text_Read () AS String

END

PRIVATE FUNCTION Color1_Read () AS Integer

END

PRIVATE FUNCTION Color2_Read () AS Integer

END
Em quanto as funções de escrita, atribuir, temos de atribuir o valor que o usuário passa a nossa
propriedade, e temos de atualizar o controle cada vez que haja uma mudança, tarefa que
realizaremos em uma função chamada Redraw(), ainda por definir:
PRIVATE SUB Text_Write ( V1 AS String )

hText = V1

RedraW()

END

PRIVATE SUB Color1_Write ( V1 AS Integer )

hColor1 = V1

RedraW()

END

PRIVATE SUB Color2_Write ( V1 AS Integer )

hColor2 = V1

RedraW()

END

Chegamos ao ponto de implementar Redraw(), pa o qual recorreremos caracter por


caracter a cadeia armazenada na variável hText e iremos representar letra por letra usando as
PRIVATEPara
diversas cores do gradiente. SUB calcular
Redraw() a posição no eixo X de cada letra, nos servimos do

método Font.Width()DIMquexPos
determina o largura de cada letra com a fonte atual do controle:
AS Integer

DIM Lupe AS Integer

DIM hColor AS Integer

DIM hDiff AS Integer

ME.Clear

hColor = hColor1

IF Len ( hText ) THEN hDiff = ( hColor2 – hColor1 ) / Len ( hText )

Draw.Begin ( ME )

FOR Lupe = 1 TO Len ( hText )

Draw.ForeColor = hColor

Draw.Text ( Mid ( hText, Lupe, 1 ), xPos, 0 )

xPos = xPos + SUPER.Font.Width ( Mid(hText, Lupe, 1 ) )

hColor = hColor + hDiff

NEXT

Draw.End()

END
Finalmente, nos interessa que o controle se encarregue por si mesmo de redesenhar a
janela. Por padrão, todo o desenho sobre uma DrawingArea desaparece se a janela se oculta atras
de outra janela ou se minimizar. Para evita-lo, utilizamos a propriedade Cached de
DrawingArea, que se encarrega de manter um buffer e redesenhar as partes do controle exposta
ao usuário: PUBLIC SUB _New()

SUPER.Cached = TRUE

END
Experimentemos agora o controle. Nosso
formulário Fmain terá dimensões 225x240 pixeis e conterá
um botão chamado BtnTeste com o texto Teste.

O código do formulário consiste em criar um


controle ColorLabel em tempo de execução, situando-o
no formulário com um texto de exemplo e duas cores
  #∃

 
 definidas para o gradiente:
!
% 


Ao executarmos o programa e pressionarmos o botão, podemos ver o nosso controle em


funcionamento. Foi uma tarefa rápida e simples, se bem, que podemos depurar bastante o código
do controle. Por exemplo,
Podemos este controle
solucionar este terá problemas com caracteres com o código ASCII
problema
maior que 127,
convertendo a já que napara
cadeia codificação do Gambasque
uma codificação (UTF-8) ocupa mais de um byte, rompendo o
algorítimo que recolhe
empregue sempre um obyte
textopor
letra por(por
letra letra.exemplo,
(ISO-8859-1) e converte-lo a UTF-8 na hora de
representa-lo. Também poderíamos incluir uma
propriedade para alinhar o texto a direita, esquerda
ou centro, assim como melhorar o algoritmo para
obter o gradiente, separando os componentes RGB   

  %
!
%

de cada cor, podemos fazer todas elas se desejarmos
praticar
Este novo controle podemos reutilizar em todos os programas que
desenharmos adicionando a classe ColorLabel ao projeto.
8.4 Novos componentes com o Gambas
Quando abrimos as propriedades de um projeto e pressionamos a aba Componentes,
observamos os módulos adicionais que podemos utilizar em um programa escrito com Gambas,
os quais atribui novas funcionalidades.

No Gambas, temos também a possibilidade de criar novos componentes. Estes podem ser
de qualquer índole, o próximo exemplo será um componente que atribui um controle adicional: o
ColorLabel que criamos no parágrafo anterior.
Vamos criar aqui um novo componente gráfico, os componentes se limitam a
atribuir novas classes que não tem por que está relacionado com a interface gráfica.


%




Preparando o código. Palavra chave EXPORT

Dentro de um programa Gambas, temos diversos módulos, classes e formulários.


Implementar um controle em Gambas supõe-se não só criar um programa que exportará algumas
de suas classes. Preparar um programa Gambas para ser um componente, simplesmente implica
definir que classes serão visíveis e quais não.

Para indicar que um programa será visível a partir do programa principal, usa-se a palavra
' Gambas class file

EXPORT

INHERITS DrawingArea

PRIVATE hText AS String

.......

Agora e necessário compilar o programa para ter um executável com a nova classe
exportada.

Arquivos necessários, localizações

Cada componente necessita, em primeiro lugar, do programa em si, que terá sempre como
nome gb.x.gambas, onde x será o nome que nós escolhemos.

A partir de agora, vamos chamar o nosso novo componente controles e, por tanto, o nome
do componente dentro do sistema de arquivos será gb.controles.gambas.

É necessário também uma localização específica para este arquivo. Todos os


componentes do gambas estarão localizado localizados em uma pasta que, salvo se tivermos
Se tivermos
compilado Gambas compilado
a partir Gambas
dos fontes usando uma
empregando opção
uma – prefix
opção diferente
- - prefix de /usr será
específica,
no script configure (por exemplo, /usr/local/lib/gambas2 neste caso.
/usr/lib/gambas2.

Cp ColorLabel.gambas
Copiaremos nosso /usr/lib/gambas2/gb.controles.gambas
executável a essa pasta, para o qual, a partir do console e como root,
entraremos na pasta do projeto e copiaremos o projeto com o novo nome (se preferirmos,
podemos faze-lo com o Konqueror ou Nautilus):

gedit /usr/lib/gambas2/gb.controles.component
Os componentes em gambas necessitam também com extenção .componente para ser
reconhecido como tal. Vamos criar este arquivo a mão com qualquer editor de texto. Nós vamos
usar o gedit, mas pode ser qualquer um (lembremos que devemos ser root):
[Component]

Key=gb.controles

Name=Controle Adicional
O conteúdo desse arquivo incluem vários parâmetros que são exposto a seguir:
Author=Daniel Campos

Group=Adicionais

Controls==ColorLabel

Require=gb.qt
A chave [Componente] deve estar sempre no início do arquivo. E o valor Key é o nome
do componente sem extensão (neste caso, o arquivo gb.componente.gambas que copiamos, mas
sem a extensão gambas). E a chave Name indica o nome do componente que aparecerá como
descrição na hora de escolher os componentes de um projeto. Em Author colocamos o nome do
autor do componente.

Depois vem uma serie de chaves opcionais: Group só usamos se quisermos adicionar
controles gráficos e coloca-lo na aba de de controles onde queremos que ele apareça. O mesmo se
aplica para Controls, que se refere a cada um dos controles que queremos que apareçam nessa
aba. Require se utiliza se nosso componente depende de outros, neste caso, de gb.qt. Se
tivermos compilado para gb.gtk, teria que ser essa a dependência indicada.

Uma vez criado e salvo o arquivo gb.controles.component, temos finalmente que copiar
dois arquivo dentro de /usr/share/gambas2/info, que proporciona informação adicional para o
interpretador Gambas.

Dentro de nossa pasta de projeto serão criados também após compilar, dois arquivos
chamados .list e .info. Se desejarmos vê-los, lembramos que os arquivos que começam com um
ponto são ocultos, po tanto, temos de usar ls -a no console ou ativar a opção de exibir arquivos
Cp .inf /usr/share/gambas2/info/gb.controles.gambas.info
ocultos no Konqueror ou Nautilus. terão de ser copiados para sua localização definitiva com o
nome do componenteCpneste
.list caso,
/usr/share/gambas2/info/gb.controles.gambas.list
gb.controles.info e gb.controles.list):

Testando o novo componente

Após estes passos encerramos a IDE do Gambas se


estiver aberta e o executamos novamente, criando um projeto
gráfico chamado MeuTeste no qual adicionaremos um
formulário.

 &
% 
 


Dentro de Projeto |
Propriedades, selecionaremos a abade
Componentes.

Procuramos e marcamos o componente


gb.controles e pressionamos aceitar para tê-lo
como dependência.
 ∋

 
%
 ∀

 
%

  (
Agora na janela de seleção de controles aparece
uma nova aba Adicionais, tal e como havíamos
indicado no no arquivo componente (Figura 12).

Ao pressionarmos nesta nova aba, aparecem os


controles disponíveis, neste momento os controles não
tem ícones, até o momento não há a possibilidade de
que esses controles tenham um ícone próprio, aparece
apenas um desenho representativo (Figura 13)

No entanto, se posicionarmos o ponteiro do


mouse ele aparecerá o ToolTip indicado-nos o novo
controle.

Como fazemos com outros controles, o arrastaremos ao formulário e o colocaremos como


desejarmos.

PUBLIC SUB Form_Open()

ColorLabel1.Font.Size = 16
Finalmente, no código do formulário
ColorLabel1.Text indicamos
= "Holá deste o texto e as cores do Label.
componente"

ColorLabel1.Color1 = color.Blue

ColorLabel1.Color2 = color.Black

END

Ao executa-lo observamos o resultado. Já dispomos de um novo componente reutilizável


em muitos projetos e que podemos distribuir entre diversos equipes seguindo os passos indicado.
Um componente escrito em gambas é só um programa que exporta classes,
por tanto, podemos depurar facilmente fazendo os testes como em qualquer outro
projeto para, depois, distribuir como componente.
Cópia literal

Extraído do livro “GAMBAS, programação visual com software Livre”, da editora EDIT
LIN EDITORIAL S.L, dos autores Daniel Campos Fernández e José Luis Redrejo.

LICENSA DESTE DOCUMENTO

É permitido a cópia e distribuição da totalidade ou parte desta obra sem fins lucrativo.
Toda cópia total ou parcial devera expressar o nome do autor e da editora e incluir esta mesma
licença, adicionando se é uma cópia literal “Cópia literal”. Se é autorizada a modificação e
tradução da obra sem fins lucrativo sempre se deve constar na obra resultante a modificação o
nome da obra original o autor da obra original e o nome da editora e a obra resultante também
deverá ser livremente reproduzida, distribuída, ao publico e transformada em termos similares ao
exposto nesta licença.

Tradução


(Antonio Sousa)
INDICE

CAPITULO 9: ACESSO À API .............................................................................. 04


9.1 Declarando uma função externa ............................................................. 05
Como denominar uma biblioteca ......................................................... 06
O uso dos aliases .................................................................................. 06
Tipo de dados ....................................................................................... 07
9.2 Funções auxiliares .................................................................................. 07
9.3 Um exemplo com libaspell .................................................................... 08
9.4 Obtendo informações sobre a biblioteca ................................................ 11
9.5 Resumo ................................................................................................... 13

APENDICE: Marca registradas ............................................................................ 14


Na realidade, o título Acesso a API foi pego dos programadores de Visual Basicatm, nos
quais é muito frequente realizar chamadas a funções externas, alojadas em bibliotecas também
externas, escritas em linguagem C. Talvez, neste caso não seja correto, já que o Linux é só um
núcleo e o resto de bibliotecas(incluindo a standard de C, glibc) não são propriamente a API do
sistema operacional, e sim campos de utilitários que são adicionados ao sistema. Mas, de toda
forma, serve de introdução para o assunto que vamos tratar.

Um sistema GNU/Linux pode contar com diversas bibliotecas, mas na prática sempre
vamos encontrar no mínimo a biblioteca C(glibc), possivelmente uma biblioteca para desenho de
interface de textos(ncurses) e, se tivermos um sistema gráfico instalado, as bibliotecas de X-
Window e outras como glib, gtk+, atk, etc. Alem destas, todo sistema pode ter instaladas
bibliotecas como libcurl, para acesso a diversos protocolos de rede, o libaspell para correção
ortográfica.
A partir do Gambas podemos tirar proveito das funções fornecidas por estas bibliotecas,
declarando funções externas e indicando qual biblioteca deve procurar. O programa contem,
ainda, alguns elementos para gestão de memória e ponteiros. As bibliotecas acessível serão
aquelas escritas em linguagem C, que sigam o padrão definido (ABI), ou bem escritas em outras
linguagens e respeitam este padrão (por exemplo, escritas com FreePascal). Não é possível até
este momento, acessar as funções de bibliotecas escritas em C++, ao menos de forma direta.

Se bem que no sistema WindowsTM é comum recorrer a este modo de trabalho, temos que
lembrar que no Linux muitas tarefas podemos resolver simplesmente chamando processos
externos, trabalho geralmente mais simples, e menos problemático na depuração dos programas.
Por tanto, recorrer a gestão de processos quando possível, no lugar deste sistema, é em geral,
uma boa ideia.

Temos que levar em conta que a chamada a funções externas, com o Gambas ou de
outras linguagens interpretadas (Mono, por exemplo), requer um trabalho de conversão de tipos,
entre outras tarefas, o qual consome um tempo de processamento. empregarmos chamadas com a
finalidade de incrementar a velocidade do programa, pose não ser uma boa idéia em todos aos
casos.

9.1 Declarar uma função externa


A declaração deve ser declarada no inicio de uma classe ou módulo, do mesmo modo que
as variáveis globais. A principio declaramos a função como PUBLIC ou PRIVATE, para definir
seu âmbito, limitado a classe onde se aloja ou a todo o programa. por padrão são privadas

A seguir a palavra chave EXTERN indica que vamos declarar uma função externa,
seguida por seu nome, que será o nativo da função na biblioteca.

Devemos lembrar que em C distingue entre letras maiúsculas e minúsculas e, por tanto,
declarar Printf em lugar de printf para a biblioteca glibc, dará como resultado um erro.

Como no resto das funções de Gambas, indicaremos os parâmetros a receber entre


parênteses, e depois o tipo de dado devolvido. Opcionalmente, podemos indicar a final a
biblioteca em que se encontra a função, assim como um aliás, o nome alternativo, a utilizar a
partir deste programa, para chamar a função, a utilizar, por exemplo, em certas funções cujo
nome coincide com palavra chaves da linguagem Gambas. Suponhamos a função strcmp da
biblioteca padrão de C, que definimos nesta linguagem como:

Int strcmp ( const char *s1, const char *s2);

Na declaração em gambas será:


EXTERN strcmp ( s1 AS STRING, s2 AS STRIN ) AS INTEGER IN “libc:6”
Como denominar a biblioteca

Após a palavra chave IN, é colocada entre aspas o nome da biblioteca. A principio
podemos indicar um nome simples, como llibc ou libgstreamer. Neste caso, Gambas tratará de
encontrar a versão mais moderna instalada no sistema. As vezes, pode nos interessar uma versão
exata de uma biblioteca, em cujo caso usamos o símbolo : após o nome e em seguida, o número
da versão, por exemplo libgl:1.0.7667 ou libc:6.

No sistema GNU/Linux, dentro das pastas /lib ou /usr/lib encontramos a maioria das
bibliotecas que existe em nosso sistema. A partir dos nomes desses arquivos, podemos deduzir o
nome da biblioteca, por exemplo:
/usr/lib/libglib-2.0.so.600.4 --> "libglib-2.0.600.4"

Como podemos observar, o número da versão é sempre o número que encontra-se após o
texto .so. No nome do arquivo, o valor 2.0 após o texto libglib- é parte do nome e não da versão.
Especificar o número da versão pode ser problemático quando se trata de instalar um programa
em diferentes equipamentos com diferentes distribuições GNU/Linux instaladas, que podem ter
versões ligeiramente diferentes desta biblioteca ou trocar após uma atualização do sistema. Por
tanto, temos que aplicar esta característica só em casos imprescindível, ou talvez empregar um
número maior e deixar o menor e a revisão a escolha do Gambas.

Se em um módulo ou classe definirmos varias funções de uma mesma biblioteca, não é


necessário especifica-la em todas as declarações, basta indica-la como biblioteca padrão com a
palavra chave LIBRARY e Gambas a empregará em todas as declarações na qual não se
especifique a biblioteca.
LIBRARY "libglib-2.0"

EXTERN g_utf8_strlen ( p AS String, Mx AS Integer ) AS Integer

EXTERN g_utf8_strlen ( Buf AS String, Mx AS Integer) AS Pointer

EXTERN getchar ( ) AS Integer IN "libc:6"

Aqui, as duas primeiras funções pertencem a libglib, e a terceira a libc.

Ao uso dos aliases

Suponhamos a função freea() da biblioteca padrão de C, que se emprega para liberar


memória designada, por exemplo, com malloc(). O problema para declarar em Gambas é que já
existe free como palavra chave reservada, por tanto, teremos que indicar um nome alternativo:

EXTERN FreePointer ( ptr AS Pointer ) IN "libc:6" EXEC "free"


Na hora de escrever códigos Gambas, chamaremos sempre a função FreePoiter, que
internamente chamará a original free da biblioteca C.

Tipos de dados

Existe uma correspondência entre os tipos de dados básicos do Gambas e os de C. Temos


que ter cuidados com os falsos amigos, um tipo Long de C não é um tipo Long de Gambas, e sim
um tipo Integer, e um tipo Float de C não é o Float de Gambas, e sim o Single. As seguintes
correspondências são aplicáveis a Gambas sobre GNU/Linux 32 bits/intel.

Correspondência de dados entre C e Gambas

O tipo Pointer de Gambas é empregado para definir um ponteiro. Em Gambas-


GNU/Linux-32 bits é exatamente igual a Integer, mas em outras arquiteturas poderia mudar, por
isso é recomendado utilizar sempre o tipo de dado Pointer no lugar de Integer para referir-se a
ponteiros. O tipo de dados String só deve empregar-se como cadeia a passar como Parâmetro não
pode ser modificado ou resignado pela função de C, em outro caso deve utilizar um dado tipo
Pointer. Por último indicar que é possível passar dados tipo Object, mas não é permitido usar
tipo Variant.

9.2 Funções auxiliares


Gambas possui alguns utilitários para trabalhar com chamadas a funções.

Ponteiro = Alloc ( Tamanho AS Integer [ , Quantidade AS Integer ] )

A função Alloc é similar a homônima de C: reserva um bloco de Tamanho*Quantidade


bytes. Neste caso, o interpretador do Gambas leva, alem de um, contador de designações e
liberações de memória realizadas, indicando, com uma mensage de advertência no final do
programa, se ficou memória sem liberar.
Novo Ponteiro = Realloc ( Antigo ponteiro AS Pointeer , Tamanho AS Integer ,

Quantidade AS Integer )

Realloc toma um ponteiro, previamente designado com Alloc ou Realloc, e troca seu
tamanho, mantendo os dados armazenados; devolve um ponteiro a nova área da memória
designada; e igual a Alloc, mantem a quantidade de designações/liberações de memória.
Free ( Ponteiro )
Free libera um ponteiro designado com Alloc ou Realloc, do mesmo modo que a função
homônima de C, porem mantendo a quantidade de designações e liberações de memória.

Muitas bibliotecas possuem funções próprias para designar ou liberar memória.


Pode ser necessário que empregamos estas funções em lugar de Alloc ou Free, se assim
requerer o programa, para manter a coerência do código e funcionalidade da biblioteca.

Cadeia = StrPtr ( Ponteiro AS Pointer )

Quando uma cadeia há de tratar-se como um ponteiro, esta função nos permite obter uma
cópia como tipo de dado String, sempre e quando ponteiro aponta a uma cadeia terminada em
caractere \0 ( nulo), que é o habitual quando se trabalha com C.

Gambas permite escrever e ler em memória como se fosse outro fluxo qualquer. Assim,
utilizando um ponteiro como parâmetro, podemos utilizar as instruções habituais em arquivos:
READ, WRITE, etc. Entre outras possibilidades oferecidas por esta solução, este é o modo atual
em que o programador pode acessar aos dados de uma estrutura C.

Está previsto que a versão 2 do Gambas será adicionado o componente gb.api.

Este componente está sendo desenvolvido para dar suporte direto ao trabalho com
estrutura de dados e a retro chamadas, que aumentaram e simplificarão as capacidades do
Gambas para manipular bibliotecas externas.

9.3 Um exemplo com libaspell


Aspell proporciona uma API para correção de texto, permite analisar palavras, indicar se
são válidas segundo o dicionário empregado, e oferece palavras alternativas as consideradas
incorretas, entre outras capacidades.
Dispomos de mais informações sobre o libaspell em sua página oficial:
http://aspell.sourceforge.net/

Para poder utilizar esse código, teremos que instalar Aspell no sistema, assim como o
dicionário de nosso idioma.

Por exemplo, em um sistema gnuLinex 2004 em casteliano ( baseado no Debian ), os


pacotes instalados no sistema são:

aspell aspell-es
aspell-bin libaspell15
libaspell-dev
Criaremos um novo projeto gráfico chamado Aspell, com u
único formulário Fmain no qual conterá um botão chamado
BtnCheck, um TextBox chamado TxtIn, um ListBox chamado
Lista e ProgressBar chamado Pbar.

No início do código do formulário definimos as funções do


libaspell.
   
   

' Gambas class file

LIBRARY "libaspell"

EXTERN new_aspell_config () AS Pointer

EXTERN aspell_config_replace ( Cfg AS Pointer, Var AS String, Value AS String )

EXTERN new_aspell_speller ( Cfg AS Pointer ) AS Pointer

EXTERN aspell_error_number ( Err AS Pointer ) AS Integer

EXTERN aspell_error_message ( Err AS Pointer ) AS Pointer

EXTERN to_aspell_speller ( Err AS Pointer ) AS Pointer

EXTERN aspell_speller_check ( V1 AS Pointer, V2 AS String, V3 AS Integer ) AS

Integer

EXTERN aspell_speller_suggest (V1 AS Pointer, V2 AS String, V3 AS Integer) AS

Pointer

EXTERN aspell_word_list_elements ( V1 AS Pointer ) AS Pointer

EXTERN aspell_string_enumeration_next ( Ptr AS Pointer ) AS Pointer

EXTERN delete_aspell_string_enumeration ( Ptr AS Pointer )


Ao ao pressionarmos o botão BtnCheck, realizaremos o processo de análise do
texto introduzido no EXTERN delete_aspell_config
quadro TxtIn, ( Cfg AS
para ir mostrando Pointer ) na lista.
o resultado

PUBLIC SUB BtnCheck_Click ()

DIM spell_config AS Pointer

DIM possible_err AS Pointer

DIM spell_checker AS Pointer

DIM suggestions AS Pointer

DIM elements AS Pointer

DIM MyStr AS String[]

DIM Bucle AS Integer

DIM BuffErr AS String


DIM sum AS Float

DIM hPtr AS Pointer

DIM sPtr AS String

Em primeiro lugar, dividimos o texto introduzido pelo usuário em palavras, separadas por
espaços.

Pbar.Value = 0

Lista.Clear ()

MyStr = Split ( TxtIn.Text, " " )

Sum = 1 / MyStr.Count

Realizamos as primeiras chamadas a biblioteca libaspell. Para isso tomamos uma


configuração por padrão e aplicamos dois atributos: o texto estará codificado como UTF-8 e
usaremos o dicionário que corresponde com a linguagem do sistema.

Aplicamos a configuração e, se ocorrer um erro (por exemplo, que o dicionário não se


encontra disponível), indicamos a mensagem correspondente:
spell_config = new_aspell_config ()

aspell_config_replace ( spell_config, "lang", Application.Env [ "LANG" ] )

aspell_config_replace ( spell_config, "encoding", "utf-8" )

possible_err = new_aspell_speller ( spell_config )

IF aspell_error_number ( possible_err ) <> 0 THEN

Message.Error ( StrPtr ( aspell_error_message ( possible_err ) ) )

RETURN

ELSE

spell_checker = to_aspell_speller ( possible_err )

END IF

Depois entramos em um loop no qual analisaremos, palavra por palavra, o texto


introduzido. Em caso de erro, se dará uma lista de sugestões. Porem se estiver tudo correto, se
indicará com um OK.
FOR Bucle = 0 TO MyStr.Count - 1

Pbar.Value = Pbar.Value + Sum

MyStr [ Bucle ] = Trim ( MyStr [ Bucle ] )

IF Len ( MyStr [ Bucle ] ) > 0 THEN

IF aspell_speller_check ( spell_checker, MyStr [ Bucle ], - 1 ) THEN

Lista.Add ( "OK ->" & MyStr [ Bucle ] )


ELSE

BuffErr = " ( "

suggestions = aspell_speller_suggest ( spell_checker, MyStr [ Bucle ], - 1 )

elements = aspell_word_list_elements ( suggestions )

DO WHILE TRUE

hPtr = aspell_string_enumeration_next ( elements )

IF hPtr = 0 THEN

BREAK

ELSE

sPtr = ""

INPUT #hPtr, sPtr

BuffErr = BuffErr & sPtr & " "

END IF

LOOP

delete_aspell_string_enumeration ( elements )

BuffErr = BuffErr & ")"

Lista.Add ( "ERROR -> " & MyStr [ Bucle ] & BuffErr )

END IF

END IF

WAIT 0.001

NEXT

Finalmente liberamos os elementos necessários empregados na correção do texto

delete_aspell_config ( spell_config )

Pbar.Value = 1

END

9.4 Obtendo informações sobre a biblioteca


Normalmente, as bibliotecas possuem uma multidão de constantes e macros definidas em
seus arquivos de cabeceira. Na hora de trabalharmos com elas devemos conhecer seus valores, já
que não podemos recorrer diretamente aos #includ de C. Vejamos este exemplo, que utiliza
constante GTK_WINDOW_TOPLEVEL:
#include < gtk/gtk,h >

int main ( void )

GtkWindow *win ;

gtk_init ( 0, 0 ) ;

win = gtk_window_new ( GTK_WINDOW_TOPLEVEL ) ;

gtk_main ( )

Com a finalidade de conhecer o valor desta constante, ou qualquer outra utilidade fornecido por
uma biblioteca, podemos recorrer ao menos a quatro métodos:

1. consultar a documentação da própria biblioteca, que neste caso (GTK+), é bastante


completa. Podemos encontra-la no seguinte endereço:

http://developer/doc/API/2.0/gtk/gtk-Standard-Enumerations.html#GtkWindowType

2. Indicar que GTK_WINDOW_TOPLEVEL tem o valor 0 como parte da numeração


GtkWindowType.

3. Procurar nos arquivos de cabeceira da biblioteca, que possamos inserir a partir da rota
/usr/includ/gtk-2.0/gtk/gtkenums.sh.

4. Compilar um pequeno programa em C que mostre o valor problemático que não


alcançamos a averiguar por outros sistemas.

#include < gtk/gtk,h >

#includ < stdio.h >

int main ( void )

prinf ( "%d\n" , GTK_WINDOW_TOPLEVEL ) ;

Já podemos passar a um pequeno programa em Gambas. Para isso, criamos um programa de


console com um só módulo modMain que contenha este código:
' Gambas class file

LIBRARY "libgtk-x11-2.0"

CONST GTK_WINDOW_TOPLEVEL AS Integer = 0

EXTERN gtk_init ( Argv AS Pointer, Argc AS Pointer )

EXTERN gtk_main ( )

EXTERN gtk_window_new ( wType AS Pointer ) AS Pointer

EXTERN gtk_widget_show ( wid AS Pointer )

PUBLIC SUB Main ( )

DIM win AS Pointer

gtk_init ( 0, 0 )

win = gtk_window_new ( GTK_WINDOW_TOPLEVEL )

gtk_widget_show ( Win )

gtk_main ( )

END

Se executarmos podemos observar que o resultado é equivalente ao do programa em C.

9.5 Resumo
Neste momento, Gambas autoriza o acesso a funções de bibliotecas externas. A única
condição é que sigam a ABI padrão. As próprias instruções do programa permitem reservar e
liberar memória. No futuro, através de gb.api (não detalhado aqui, pois se encontra em pĺeno
desenvolvimento e sua interface pode mudar), a gestão de estruturas e retro chamadas serão
possíveis.

Mas não é recomendado u uso dessa funcionalidade, sal que seja plenamente
justificado. Gambas possui componentes para tarefas variadas que cobrem boa parte das
necessidades que se encontram nos programas habituais, e sua capacidade de gestão de processos
esternos o faz apropriado para quase todas as tarefas. Trabalhar com chamadas a funções externas
pode tornar o programa mais difícil de depurar, sensível a troca de sistema (atualizações de
bibliotecas, troca da versão da distribuição...) e limita a portabilidade do código (por exemplo, de
um sistema GNU/Linux, onde se desenvolve, para uma plataforma FreeBSD ou, no futuro na
versão Windows do Gambas).
Marcas registradas

Os seguintes termos são marcas registradas no Estados Unidos ou outro país

* Linux é uma marca registrada de Linus Torvalds.

* Debian é uma marca registrada de Software in the Public Interest, Inc.

* SUSE é uma marca registrada de SuSE AG.

* Mandriva é uma marca registrada da Mandrakesoft S.A e Mandrakesoft Corporation.

* GNOME é uma marca registrada da fundação GNOME.

* KDE, K Desktop Evironamente, é uma marca resgistrada de KDE e V.

* Microsoft, Windows e Visual Basic são marcas registradas da Microsoft Corporation.

* gnuLinex é uma marca registrada da Junta de Extremadura.

* Java e todos os nomes e logos baseados em java são marcas registradas da Sun
Microsystems, Inc.

* UNIX é uma marca registrada do The Open Group.

* Mozilla e Firefox são marcas registradas da The Mozilla Organization.

* Apple e Macintosh são marcas registradas de Apple Computer Corporation.

Outras empresas, produtos e nomes de serviços podem ser marcas registradas ou


serviços de outras companhias.

Potrebbero piacerti anche