Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
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.
É 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
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:
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.
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.
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
./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.
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
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:
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:
!
∀ #
∃
!
%
& ∋
∀
(
)
∗
+
,(
−
.
+
∀
−
(
,
,
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.
.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 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.
* 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.
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.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.
A lista dos componentes disponíveis para o programador pode se ver na aba Componentes,
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
http://wiki.gnulinex.org/gambas/210
3 - http://fsf.org/licensing/licenses/gpl.html
http://gugs.sindominio.net/licencias/
http://wiki.gnulinex.org/gambas/
http://wiki.gnulinex.org/gambas/6
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.
É 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
.O projeto pode conter outros arquivos de dados, documentos, textos, etc., sem código
BASIC para ser executado pela aplicação.
. 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.
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á.
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.
PRIVATE CONST Pi = 3.141592
'No meu teste só funcionou como está no exemplo
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 é:
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
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
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 ( )
Final = Calcula_Media ( 4, 8 )
PRINT Final
END
END
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).
Short: representa um numero inteiro com valores possível entre -32.768 e +32.767
.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.
É 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:
Operando1 = 3.5
Operando2 = 2
.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
.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
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:
Colunas = 2
Linhas = 6
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 [ ]
Nomes.Add ( "Manoel" )
Nomes.Add ( "João" )
Nomes.Add ( "Antonio" )
PRINT Nomes.Count
Nomes.Remove (1)
Nomes.Clear
. 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:
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:
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
Nome = "Manoel"
Apelidos = "Alvares Gomes"
Vejamos agora a lista das funções disponíveis para manipular cadeias de texto:
. 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:
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:
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:
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.
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 Expressão THEN
.........
ENDIF
..............
.............. ]
[ ELSE
.............. ]
ENDIF
Alguns exemplos de uso:
.................
PRINT "Bebê"
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 é:
...........
CASE 0 TO 2
PRINT "Bebê"
CASE 2 TO 12
PRINT "Menino"
CASE 18
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:
..........
NEXT
DIM n AS Integer
FOR n = 10 TO 1 STEP -1
PRINT n
NEXT
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 é:
..........
NEXT
Vejamos um exemplo usando as matrizes dinâmicas que vimos neste capitulo:
DIM Matriz AS String [ ]
DIM Elemento AS String
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.
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:
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.
.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.
CLOSE [ # ] Arquivo
.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.
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.
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.
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 )
.........................
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:
PRINT Linha
WEND
FINALLY 'Sempre vai ser executado, inclusive se houver erro
END
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.
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.
Nascimento = Year(Data)
END
Patas = Numero
END
END
RETURN Patas
END
Este é o código para escrever no arquivo de classes Homem.cls:
' Gambas class file
INHERITS SerVivo
Nome = Cadeia
END
Apelido = Cadeia
END
END
Macaco.nascido(CDate("2/2/1992"))
Macaco.PPatas(3)
PRINT Macaco.Idade()
PRINT Macaco.DPatas()
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.
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.
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.
!
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.
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.
É 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
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+.
* 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.
Escrevemos agora o seguinte código:
PUBLIC SUB Main()
hWin.Show()
END
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).
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.
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.
*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.
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.
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:
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).
Criaremos um fomulário Form1 com um botão Button1, que inclua este código:
PRIVATE pX AS Integer
PRIVATE py AS Integer
IF Mouse.Left THEN
pX = Mouse.X
pX = Mouse.Y
END IF
END
PUBLIC SUB Button1_MouseMove()
IF Mouse.Left THEN
'********************************************************
'********************************************************
END IF
END
∋
∀
∀
4
∀
#
∋#
5 ∀
1
∋#
!
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()
CASE ELSE
STOP EVENT
END SELECT
END
%
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'.
*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.
* 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.
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.
*TreeView: serve para representar elementos em uma árvore, de forma que cada nó pode
ter outros nós filhos.
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):
*Message.Error:
(Texto, Botão1, Botão2, botão3):
*Message.Question:
(Texto, Botão1, Botão2, botão3):
*Message.Warning:
(Texto, Botão1, Botão2, botão3):
END
.........
*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.).
.......
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.
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
ME.Close (2)
END
ME.Close (3)
END
CASE 1
CASE 2
END SELECT
END
Valor = "a.png"
ME.Close
END
Valor = "b.png"
ME.Close
END
Valor = "c.png"
ME.Close
END
Fdialogo.ShowDialog()
END IF
END
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().
Fdialogo.ShowDialog()
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.
#∃
..............
END
menuEditar.Popup()
DIM h1 AS Menu
DIM h2 AS Menu
DIM h3 AS Menu
h1 = NEW Menu(ME)
h1.Text = "Ações"
h2.Text = "Enviar"
h3.Text = "Deletar"
END
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.
*None: Alinhamento livre, o contêiner não decide nada sobre a posição de seus filhos.
*Vertical: Todos os controles alinham-se de sima para baixo, ocupando todo espaço
horizontal dentro do contêiner.
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 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.
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.
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.
∀
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.
Draw.Begin(Tela)
Draw.End
END
Ao executarmos o programa e
pressionar o botão, aparecerá uma cruz criada
por nosso código.
*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.
*Draw.Ellipse: elipse.
*Draw.Line: linhas
*Draw.Point: pontos.
*Draw.Polygon: poligonos.
*Draw.Rect: retângulos
*Draw.Text: Desenhar um texto em uma posição indicada e com a fonte selecionada por
Draw.Font.
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.
É 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
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.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:
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()
END
sCad.Add ( "ls" )
sCad.Add ( "-l" )
EXEC sCad
END
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.
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()
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.
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.
sUrl = "http://gambas.gnulinex.org/radiogambas/radiogambas-1.0.1.tar.gz"
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.
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()
sCads.Remove(0)
sCads.Remove(sCads.Count - 1)
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.
hCancelar = FALSE
BtnCancelar.Enabled = TRUE
sUrl = "http://gambas.gnulinex.org/radiogambas/radiogambas-1.0.1.tar.gz"
CASE "|"
LblInfo.Text = "/"
CASE "/"
LblInfo.Text = "-"
CASE "-"
LblInfo.Text = "\\"
CASE "\\"
LblInfo.Text = "|"
CASE ELSE
LblInfo.Text = "|"
END SELECT
WAIT 0.1
hProc.Kill()
message.Warning("Processo Cancelado")
BtnCancelar.Enabled = FALSE
RETURN
END IF
LOOP
LblInfo.Text = "Inativo"
Message.Info("Download Finalizado")
END
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.
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
hCancelar = TRUE
END
Err = Trim(Err)
LblInfo.Text = sCad[sCad.Count - 1]
END
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
WAIT 0.1
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.
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()
PRINT sCad
END
O código é o seguinte:
PRIVATE hCancelar AS Boolean
PUBLIC SUB BtnCancelar_Click()
hCancelar = TRUE
END
LblInfo.Text = "Inativo"
Message.Info("Download Finalizado")
END
............
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
hCancelar = TRUE
hProc.Kill()
END
Err = Trim(Err)
LblInfo.Text = sCad[sCad.Count - 1]
END
Message.Warning("Processo Cancelado")
ELSE
Message.Info("Download concluido")
END IF
LblInfo.Text = "Inativo"
BtnCancelar.Enabled = FALSE
END
TextLicenca.Text = ""
hCancelar = FALSE
BtnCancelar.Enabled = TRUE
END
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().
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.
A partir do console podemos fazer um teste executando em uma janela de terminal estes
dois comandos consecutivos:
$ Ls -l /dev
$ echo $?
$ 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:
Message.Error("Erro no Download")
ELSE
Message.Warning("Processo cancelado")
ELSE
Message.Info("Download completo")
END IF
END IF
LblInfo.Text = "Inativo"
BtnCancelar.Enabled = FALSE
END
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).
END
CLOSE #hProc
END
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.
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.
..........
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:
É 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.
É 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
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.
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.
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.
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.
mkdir Bases
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.
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.
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.
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).
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.
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.
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.
!
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.
Agora vamos criar uma função que se conecte com a base de dados dentro do código do
formulário:
hCon.Host = "/home/Usuário/Bases"
hCon.Name = "provas"
hcon.Type = "sqlite"
TRY hCon.Open
IF ERROR THEN
hCon = NULL
RETURN TRUE
END IF
RETURN FALSE
END
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.
hCon.Close()
hCon = NULL
END
Consulta de dados
tabela.Clear()
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"
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.
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.
IF key.Delete THEN
IF ERROR THEN
ELSE
tabela.Current.Delete()
END IF
EncerrarConexao()
END IF
END
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)
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).
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
ME.Close()
END
' END IF
ME.Close()
CATCH
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]..... )
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.
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.
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
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.Update()
ELSE
' END IF
ME.Close()
CATCH
END
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:
Mas não é possível atualizar o conteúdo do campo, quer dizer, não podemos realizar:
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:
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
hCon.Host = "/home/Usuário/Bases"
hCon.Name = "provas"
hcon.Type = "sqlite"
TRY hCon.Open
IF ERROR THEN
hCon = NULL
RETURN TRUE
END IF
RETURN FALSE
END
hCon.Close()
hCon = NULL
END
tabela.Clear()
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"
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
IF ERROR THEN
ELSE
EncerrarConexao()
tabela.Current.Delete()
END IF
END IF
END
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()
FDados.RunNew(hCon)
EncerrarConexao()
Form_Open()
END
ME.Close
END
FDADOS
PRIVATE Editando AS Boolean
hCon = Data
ME.ShowModal()
END
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
ME.Close()
END
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
END IF
ME.Close()
CATCH
END
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.....
.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.
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.
É 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
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.
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 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.
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.
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.
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
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
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.
TxtIP.Text = "127.0.0.1"
TxtPorta.Text = "3152"
TxtDados.Text = ""
LblResultado.Text = ""
TxtDados.Enabled = FALSE
END
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.
LblResultado.Text = ""
TxtDados.Text = ""
sIP = Net.Format(TxtIP.Text)
ELSE
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.
TxtDados.Enabled = TRUE
END
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.
END
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.
END
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.
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.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
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()
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:
Meuservidor.TargetHost = Meuservidor.SourceHost
Meuservidor.TargetPort = Meuservidor.SourcePort
END
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
END
END
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.
Cliente.HostName = TxtHost.Text
Cliente.GetHostIP()
Message.Info("IP: " & Cliente.HostIP)
END
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.
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.
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.
CLOSE #Http
END
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.
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.
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
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.
É 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
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.
<dados>
<usuario>
<nome>Eric Smith</nome>
<socio>113</socio>
</usuario>
</dados>
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>
<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.
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">
<telefone>1199212321</telefone>
<dns primario="1">127.0.0.2</dns>
<dns primario="0">127.0.0.3</dns>
</conexao>
<telefone>229943484</telefone>
<dns primario="1">127.0.10.10</dns>
<dns primario="0">127.0.20.42</dns>
</conexao>
</conexoes>
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:
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:
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:
Dentro desta região, dispomos de uma nova abertura de tag para o telefone:
Xml.StartElemente ( "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.text ( "127.0.0.2" )
E finalizamos a seção:
Xml.EndElemente ( )
Xml.text ( "127.0.0.2" )
Xml.EndElemente ( )
Xml.text ( "127.0.10.10" )
Xml.EndElement ( )
Xml.text ( "127.0.20.42" )
Xml.EndElement ( )
Xml.EndElement ( )
Xml.Text ("127.0.0.2")
Xml.EndElement()
Xml.Text ( "127.0.0.3" )
Xml.EndElement ()
Xml.EndElement ()
Xml.Text ("127.0.10.10")
Xml.EndElement ()
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 ( )
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.
* 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>
* 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.
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.
IF ERROR THEN
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
PreencherArvore (Xml)
ELSE
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
RETURN
END IF
LOOP
TRY Xml.Close()
END
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 ( )
PreencherItem ( Xml )
ELSE
TRY Xml.Read ( )
END IF
ELSE
END IF
END IF
LOOP
END
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.
ELSE
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 ()
DO WHILE TRUE
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"
NEXT
TRY Xml.Read ()
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()
LOOP
ELSE
END IF
TRY Xml.Read ()
LOOP
END
NEXT
ELSE
END IF
END IF
TRY Xml.Read ()
DO WHILE TRUE
TRY Xml.Read ()
TRY Arvore.Add ( sNode & "-tel" , "tel: " & Xml.Node.Value , NULL, sNode )
CASE "dns"
sPrim = "0"
NEXT
TRY Xml.Read ()
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 ()
LOOP
ELSE
END IF
TRY Xml.Read ()
LOOP
DO WHILE TRUE
TRY Xml.Read ( )
PreencherItem ( Xml )
ELSE
TRY Xml.Read ( )
ELSE
BREAK
END IF
END IF
LOOP
IF ERROR THEN
RETURN
END IF
DO WHILE TRUE
PreencherArvore (Xml)
ELSE
Xml.Close()
END IF
END IF
TRY Xml.Read()
IF ERROR THEN
RETURN
END IF
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.
<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>
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.
END
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.
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.
É 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
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.
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.
END
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
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
A nova classse ClsCalculo temos pouco interesse por hora: fazer o mesmo
' Gambas class file
INHERITS ClsCalculo
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.
Calculo.add [ 12.5 ]
Calculo.add [ 23.6 ]
Calculo.add [ 7.5 ]
PRINT "Elementos: " & Calculo.Count & " -Média: " & calculo.Average ()
END
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
RETURN SUPER._Numbers.Count
END
DIM Nm AS Float
DIM Vl AS Float
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.
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
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()
Calculo.add [ 12.5 ]
Calculo.add [ 23.6 ]
Calculo.add [ 7.5 ]
Calculo.add [ 17.5 ]
Calculo.add [ 31.8 ]
END
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:
RETURN SUPER._Numbers.Count
END
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.
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:
!
INHERITS ClsCalculo
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
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.
Adicionamos' agora
Gambasum
classdestructor
file _Free() personalizado para nossa classe herdeira, na
qual indicamos no valor da variável
INHERITS _Numbers:
ClsCalculo
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
END
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.
Isto implica, por exemplo, que não podemos criar classes que são herdeiras de Form,
pois a classe Form provem de Window.
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
END
END
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
hColor1 = V1
RedraW()
END
hColor2 = V1
RedraW()
END
método Font.Width()DIMquexPos
determina o largura de cada letra com a fonte atual do controle:
AS Integer
ME.Clear
hColor = hColor1
Draw.Begin ( ME )
Draw.ForeColor = hColor
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.
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.
%
Para indicar que um programa será visível a partir do programa principal, usa-se a palavra
' Gambas class file
EXPORT
INHERITS DrawingArea
.......
Agora e necessário compilar o programa para ter um executável com a nova classe
exportada.
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.
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):
&
%
Dentro de Projeto |
Propriedades, selecionaremos a abade
Componentes.
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
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.
É 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
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.
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.
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.
Tipos de dados
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.
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.
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.
Para poder utilizar esse código, teremos que instalar Aspell no sistema, assim como o
dicionário de nosso idioma.
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.
LIBRARY "libaspell"
Integer
Pointer
Em primeiro lugar, dividimos o texto introduzido pelo usuário em palavras, separadas por
espaços.
Pbar.Value = 0
Lista.Clear ()
Sum = 1 / MyStr.Count
RETURN
ELSE
END IF
DO WHILE TRUE
IF hPtr = 0 THEN
BREAK
ELSE
sPtr = ""
END IF
LOOP
delete_aspell_string_enumeration ( elements )
END IF
END IF
WAIT 0.001
NEXT
delete_aspell_config ( spell_config )
Pbar.Value = 1
END
GtkWindow *win ;
gtk_init ( 0, 0 ) ;
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:
http://developer/doc/API/2.0/gtk/gtk-Standard-Enumerations.html#GtkWindowType
3. Procurar nos arquivos de cabeceira da biblioteca, que possamos inserir a partir da rota
/usr/includ/gtk-2.0/gtk/gtkenums.sh.
LIBRARY "libgtk-x11-2.0"
EXTERN gtk_main ( )
gtk_init ( 0, 0 )
gtk_widget_show ( Win )
gtk_main ( )
END
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
* Java e todos os nomes e logos baseados em java são marcas registradas da Sun
Microsystems, Inc.