Capítulo 10

anterior sumario proximo

Faltam ao Tcl/tk alguns widgets interessantes como um frame que possa ser visualizado em parte, controlado por scrollbars. Felizmente, podemos criar nós mesmos essas facilidades, mesmo sem recorrer ao uso de uma linguagem baixo-nível (C/C++). Apesar de um canvas possuir muito mais funcionalidade, que será sub-utilizada aqui, sua habilidade em conter outros widgets será explorada para produzir o efeito desejado.

Este tclet apresenta um frame "rolável", usando o procedimento descrito no texto.

O canvas é escolhido para essa função por suportar os protocolos dos scrollbars, permitindo tratar uma área maior que a parte visível, e por permitir ser colocado no seu interior outros widgets. A idéia é colocar um frame no interior do canvas e neste último "empacotar" toda a nossa interface (a parte que deverá ser rolável). A figura abaixo mostra a estrutura hierárquica da interface. No nosso exemplo teremos apenas botões, cada um com um rótulo diferente, com comandos que mostram esse mesmo rótulo em um label, este último empacotado separadamente, isto é, fora do canvas.

Brinque um pouco com o tclet ao lado, rolando e clicando em botões e observando que o label na parte acima, não rola juntamente com os botões e conterá o rótulo do último botão pressionado.

IMAGE
A figura mostra como criar um frame que é parcialmente visível, e que pode ser usado para conter vários outros objetos.

Para criar o frame rolável, segundo a hierarquia da figura, faremos:

frame .c
canvas .c.cv \
  -yscrollcommand [list .c.yscroll set]
scrollbar .c.yscroll -orient vertical \
  -width 10 -command [list .c.cv yview]
pack .c.yscroll -side right -fill y
pack .c.cv -side left 
pack .c -side bottom
set f [frame .c.cv.f -bd 0]
.c.cv create window 0 0 -anchor nw -window $f
onde a variável f conterá o nome do frame que nos interessa, aonde poderemos colocar nosso objetos. Evidentemente, poderíamos criar também um scrollbar horizontal, se desejássemos que o nosso frame fosse rolável na direção "x" igualmente.

Para adicionar os botões, usamos uma lista (chamada labels nesse exemplo) com todos os rótulos desejados e faremos um loop criando e empacotando esses botões:

	set i 0
	foreach label $labels {
		pack [button $f.b$i -text $label -width $max \
			-font {Helvetica -12 bold}\
			-command ".estado config -text \"$label\""] \
			-expand 1 -fill both
		incr i
	}
A variável max contém a largura da maior string dos labels e deve ser determinada previamente. O código para essa função fica como exercício.

O tamanho do frame é determinado pelo seu conteúdo, mas podemos desejar que o nosso canvas tenha a mesma largura que aquele e a sua altura seja um múltiplo da altura de um botão. Quermos também que o tamanho total do canvas seja limitado pelo tamanho total desse frame, para que ele não role fora dos limites. Essa última propriedade é obtida pela opção -scrollregion. Para modificar essas propriedade, contudo, devemos esperar que o frame (ou algum de seus widgets-filhos) seja visível, ou o tamanho retornado será nulo. Vejamos como fica o código:

  
	tkwait visibility $f.b0
    set width [winfo  width $f]
	set single [winfo height $f.b0]
	set height [winfo height $f]
    .c.cv config -scrollregion "0 0 $width $height"
    .c.cv config -yscrollincrement $single
	.c.cv config -width $width -height [expr 5*$single] 
A altura é determinada como 5 vezes a altura de um botão, mas a região "rolável" é a altura total do frame contido. O yscrollincrement determina em quanto deve ser adicionado ou subtraido a parte visível (na direção "y"), para não mostrar uma "fração de botão" quando rolarmos o canvas.

criando formulários para data entry

Um formulário de data entry usando um canvas como container.

As técnicas de rolagem com canvases descritas acima podem ser usadas com formulários para entrada de dados ou consultas, como mostrado no tclet ao lado. Observe o que mudou em relação ao exemplo acima e diga qual seria sua implementação. O canvas nesse caso rola nas duas direções, porque algumas caixas de entrada (entries) são maiores que o espaço que pretendemos ocupar. Os "botões" ao lado esquerdo, são na realidade simples labels com a opção -relief raised e uma borda de 2 (no lugar do default 0). É interessante agrupar várias entradas menores numa mesma linha, e deixar um espacejamento entre as diversas linhas. Nada impede-nos de usarmos outro geometry manager no lugar do pack (que tal o grid?), o que deixaria muito mais simples a implementação. Lembre-se que o frame usado, no interior do canvas pode ter qualquer coisa 'empacotado" no seu interior sem problema! Refaça esse exemplo tentando melhorar o design.

um container tipo agenda

Um formulário com múltiplas páginas com "orelhas".

Com um canvas podemos também criar widgets com múltiplas páginas como o tclet ao lado. As "orelhas" são simplesmente linhas brancas e pretas desenhadas no canvas, nas posições desejadas. Associada a cada "orelha" temos um frame onde são colocados os widgets referentes àquela página. Simplesmente mostrando ou obscurecendo cada um destes frames teremos o efeito desejado. O programa do tclet foi retirado de um programa real, com a funcionalidade removida, bem como sem os toplevels de ajuda e um tk_optionMenu pois estes não são permitidos em um tclet. Mas serve, de qualquer modo, para ilustrar a construção de uma interface com múltiplos frames selecionáveis.

Outro widget construido artificialmente nesta interface é o frame com um label, ou LabelFrame. Neste, criamos um frame interno, com um outro com uma pequena borda e um label colocado sobrepondo-se sobre a borda deste frame para produzir o título desejado. Um outro frame interno é devolvido, aonde poderemos colocar nossos widgets.

Apesar desse exemplo ter sido implementado inteiramente em tcl (script puro), existem bibliotecas contendo este tipo de widget como o Tix e o Blt (esta última com uma melhor implementação, a meu ver). O uso destas em tclets, todavia, é restrito por problemas de segurança¸ o que nos faz recorrer aos scripts.


rpragana Thu Aug 19 12:12:46 EST 1999