Capítulo 6

anterior sumario proximo

Widgets devem interagir entre si para realizarem sua funcionalidade completa. Por exemplo, um objeto tipo text, listbox ou canvas, usualmente é empregado em conjunto com um ou mais scrollbars. Estes permitem visualizar partes daqueles objetos que de outra forma seriam invisíveis, quando o tamanho do que queremos mostrar é maior que o espaço disponível na tela. Para mostrar o conceito, suponha que temos duas listas (listboxes) situadas lado-a-lado, uma delas com ítens textuais que queremos escolher e passar para a outra ou devolver (desselecionar). Esse tipo de comportamento é útil, por exemplo, num programa que escolherá quais arquivos a empacotar na criação de um arquivo .tar.gz.

Inicialmente, queremos criar bitmaps para colocar nos botões (melhor que deixar um texto, neste caso). Uma forma possível de fazer isso é editar o bitmap com algum editor gráfico e salvar no formato xbm, que é simplesmente um texto que pode ser incluido em um programa C, ou também no tcl, o que nos interessa. Escolhemos dimensões 16x16 pixels para estas imagens. Para deixá-los disponíveis em um widget tcl/tk, precisamos primeiro criar o objeto image, com o seguinte comando:

image create bitmap left -data "<dados do arquivo .xbm>"

onde left é o nome da imagem criada. A figura pode ser incluida com o editor de textos, entre aspas, onde está o comentário agora. Use o programa bitmap (que vem com a distribuição do X11) para criar os arquivos left.xbm e right.xbm, e depois inclua-os no seu programa como mostrado. De forma similar, procedemos com a imagem right, a seta desenhada no outro sentido. Assim, podemos criar os botões com imagens no lugar dos textos:

button .b1 -image right
button .b2 -image left

Eventualmente, falta atribuir comandos a serem executados por estes botões, mas vamos discutir primeiro o restante da interface visual. Os listboxes devem, para melhor simplicidade de operação, permitir seleção de múltiplos ítens, o que é conseguido com a opção -selectmode extended. No final da definição da interface, carregaremos um deles com a lista de estados brasileiros, que serão nossos dados a selecionar. Vejamos:

listbox .lb1 -selectmode extended 
listbox .lb2 -selectmode extended
pack .lb1 -side left 
pack .lb2 -side right 
pack .b2 .b1 -side bottom -expand 1 -padx 10

.lb1 insert end pernambuco paraiba "rio grande do norte" ceará alagoas piauí \
     sergipe bahia pará amazonas maranhão tocantins goias "distrito federal" \
     "minas gerais" "mato grosso" "mato grosso do sul" "rio de janeiro" \
     "espírito santo" "roraima" "acre" "são paulo" paraná \
     "santa catarina" "rio grande do sul" amapá rondônia

Observe que os comandos pack devem ter uma certa ordem, como já vimos, para garantir o posicionamento dos botões no centro da interface. A opção -expand 1 faz com que o espaço disponível seja distribuido igualmente entre os botões.

Agora vejamos a execução dos comandos dos botões. O botão superior (.b1), quando pressionado, deverá retirar os ítens selecionados no listbox da esquerda (.lb1) e inseri-los no listbox da direita. O comando (definido com -command {<comando a executar>} no momento de criação do widget ou pelo uso do comando .b1 config -command ... ) que nos devolve a lista de ítens selecionados no listbox da esquerda é:

.lb1 curselection

Precisamos tomar cuidado com a ordem que isto é feito, pois o comando que nos devolve a lista de ítens selecionados, os devolve como uma lista de índices numérico, que serão modificados à medida que o listbox é modificado. Assim, devemos ordenar os índices, em ordem descrescente, para que as mudanças de posíção dos ítens não alterem os que ainda restam a serem movidos. Então, usamos o comando lsort, que ordena uma lista obedecendo vários critérios, com as opções para índices numéricos e em ordem decrescente:

    set itens [lsort -integer -decreasing [.lb1 curselection]]

O restante da ação (comando) é imediata. Basta repetirmos o ato de movimentar um ítem, para cada ítem da lista. Usamos a estrutura de controle foreach do tcl para varrer a lista. Para inserir um ítem no listbox .lb2, o comando insert, seguido do índice (posição) deste ítem e em seguida o texto (conteúdo) do ítem. Para remover o ítem do listbox antigo (.lb1), o comando delete.

    
    foreach i $itens {        
         .lb2 insert 0 [.lb1 get $i]
         .lb1 delete $i 
    }

Experimente selecionar ítens no listbox da esquerda, movendo-os para a direita com a seta de cima. Para selecionar vários ao mesmo tempo, arraste o mouse com o botão <1> pressionado. Para retirar um único ítem da seleção, segure a tecla Control e clique no mouse normalmente, com esta tecla pressionada. Depois mova de volta alguns ítens.

Experimente também rolar o conteúdo do listbox segurando o botão do meio (botão <2>) do mouse. Usuários de mouser "tipo Microsoft", com dois botões somente, terão que clicar os dois botões ao mesmo tempo, e certificar-se que o servidor X11 está com emulação de três botões habilitada.

Como exercício, modifique esta interface, colocando um botão extra para executar algum comando que use todos estes paràmetros (selecionados no listbox da direita) e codifique-a com o visual tcl. Se voce quiser executar algo com o shell do Unix, use o comando exec como prefixo do que você utilizaria normalmente.

incorporando scrollbars numa interface

O tk nos fornece um mecanismo simples para conectar scrollbars a outros widgets, uma vez que aqueles foram projetados exatamente para controlar a visualização de widgets como canvas, text, ou listbox. É mais fácil entender com um exemplo, (na realidade uma receita bem simples): se tivermos um listbox .lb e um scrollbar .sb que será usado para deslocar a parte visível do listbox, faremos:

.lb config -yscrollcommand {.sb set}
.sb config -command {.lb yview}

e de forma similar com a direção "x", substituindo yscrollcommand por xscrollcommand e yview por xview. Evidentemente, o scrollbar será criado com -orient vertical no primeiro caso e -orient horizontal neste último.

Modifique os dois listboxes do exemplo visto acima para conter scrollbars ao lado de cada um (com -orient vertical) para poder rolar a lista de estados do Brasil.

escondendo complexidade com procedures

O tcl permite-nos criar também procedures, como qualquer linguagem moderna, com argumentos passados por valor ou por referência, ou também com valor default para um argumento, e número de argumentos variável, determinado no momento da sua execução. Uma procedure pode também retornar um valor.

Declaramos uma procedure com o comando proc, seguido do nome da procedure, seguido da lista de arguentos, e seguido finalmente do corpo (body) da procedure.

No exemplo acima, podemos substituir os comandos atribuidos aos botões acima por uma procedure, que moverá os ítens selecionados de uma listbox para a outra. Os argumentos para esta procedure serão os nomes dos listboxes origem e destino, para generalizarmos:

proc moveSelection { orig dest } {
    set itens [lsort -integer -decreasing [.lb2 curselection]]
    foreach i $itens {
        $dest insert 0 [$orig get $i]
        $orig delete $i
    }
}

onde orig e dest são os nomes dos widgets desejados. Como estes são também comandos, sua invocação será feita pela subsituição com $. E assim, poderemos definir os botões acima como:

button .b1 -image right -command {moveSelection .lb1 .lb2}
button .b2 -image left -command {moveSelection .lb2 .lb1}

Os programas criados pelo visual tcl definem uma procedure main e a executa logo após definir toda a interface. Este comando é similar a um main na liguagem C, com argc o número de argumentos recebidos na linha de comando e argv uma lista de todos os parâmetros, exceto o argv0, que é uma variável adicional. Exemplo: no comando (dado na linha do shell) meuprog recife pernambuco brasil, teremos $argv0 contendo o nome (completo, com path) do meuprog, $argc será 3 e lindex $argv 0 será recife (o elemento de índice 0 da lista; argv conterá a lista recife pernambuco brasil. Além dessas variáveis pré-definidas, temos >env, um array indexado pelos nomes das variáveis do environment, o mesmo acessível pelo shell do unix. Exemplo: $env(PATH) será o nosso PATH atual. Lembre-se que isso não é nenhuma particularidade do visual tcl mas do próprio core do tcl. Qualquer programa terá estas facilidades definidas.

Subsituindo um argumento na definição da procedure por uma (sub-)lista, podemos introduzir um default para o argumento não fornecido na chamada desta procedure. Por exemplo, a procedure seguinte imprime o nome dado como argumento entre separadores (separador default "/"):

proc entreseps { nome {sep /} } { 
    puts $sep$nome$sep
}

Executando entreseps nina teremos a saida /nina/, enquanto que entreseps nina \" teremos a saida "nina". Observe que as aspas, por ser um caracter especial do tcl, deverá ser quotada com a barra invertida.

Podemos processar também um número variável de argumentos se denominarmos o último argumento na definição da procedure args. Veja uma função (procedure) que imprime cada argumento numerado numa linha diferente (o puts avança uma linha cada vez que é executado, a menos que forneçamos a este a opção -nonewline):

proc printargs { args } {
    for {set i 0} {[lindex $args $i] != ""} {incr i} {
       puts "$i: [lindex $args $i]"
    }
}

Vá para o capítulo 1 e use o tclet no final deste para testar a funcão. Ou melhor, use uma console com o tclsh ou wish rodando, introduza esta procedure e teste-a fornecendo vários argumentos.


rpragana Sat Apr 3 19:24:29 EST 1999