Tcl/Tk
Curso On-Line de Programação

Associando eventos

Eventos são ações realizadas pelo usuário e interceptadas pelo programa. Em Tk eventos são chamados binds( aderências ). Os binds são usados para verificar as teclas pressionadas pelo usuário e os movimentos do ponteiro do mouse.

Também podemos simular eventos, de modo que o programa se comporte como se o usuário tivesse realizado uma determinada ação.

O uso mais frequente de binds em programas comerciais é a verificação dos dados digitados pelo usuário, ou para criar teclas de atalho para botões e menus.

A seguir estudaremos os comandos usados para conectar eventos aos widgets Tk e para simulá-los, quando for necessário.
 

Comando Descrição
bind tag Retorna uma lista  de todas as sequências, para as quais existem binds correspondentes à tag dada.
bind tag sequência Retorna o script correspondente à sequência e à tag dadas.
bind tag sequência script Adere o script à sequência para a tag dada. Se script for uma string vazia, a bind é apagada. Se o primeiro caractere do script for +, então o script será anexado ao script atualmente associado à sequência e à tag dadas.
bindtags janela [lista_de_tags] Ajusta a ordem de precedência das tags para a janela de acordo com a lista dada. Se a lista estiver vazia, a ordem será ajustada para a ordem padrão.
event add <<virtual>> sequência sequência... Configura para que o evento virtual <<virtual>> seja disparado, se qualquer das sequências dadas ocorrer.
event delete <<virtual>> [sequência...] Apaga a sequência dada, ou todas caso nenhuma sequência seja especificada, da lista de sequências que disparam o evento virtual dado.
event generate janela evento [-when quando] [opção valor...] Provoca um evento, na janela dada, como se ele tivesse sido gerado pelo sistema. Várias opções estão disponíveis e serão listadas a seguir. A opção -when indica quando o evento deve ser disparado: now( imediatamente ), tail( coloca no final da fila de eventos ), head( coloca no início da fila de eventos ) ou mark( como head, mas coloca entre os eventos gerados anteriormente ).
event info <<virtual>> Retorna a lista de sequências que disparam o evento virtual. Se o evento não for especificado, retorna a lista de todos os eventos virtuais definidos.

O argumento sequencia é uma lista de um ou mais tipos de evento. Um evento pode ser desde um caractere ASCII a uma string na forma <modificador-modificador-tipo-detalhe>, ou <<nome>>, para um evento virtual.

Veja a lista de modificadores possíveis: Any, Control, Shift, Lock, Double, Triple, Button1, Button2, Button3, Button4, Button5, B1, B2, B3, B4, B5, Meta, M, Mod1, Mod2, Mod3, Mod4, Mod5, M1, M2, M3, M4, M5, Alt.

Os tipos de eventos possíveis são: Activate, ButtonPress, Button, ButtonRelease, Circulate, Colormap, Configure, Deactivate, Destroy, Enter, Expose, FocusIn, FocusOut, Gravity, KeyPress, Key, KeyRelease, Motion, Leave, Map, Property, Reparent, Unmap, Visibility.

Os detalhes suportados são: para botões, um número de 1 a 5, para teclas, um símbolo definido em /usr/include/X11/keysymdef.

As tags podem ser: uma janela interna, e se aplicará somente a esta janela, um toplevel, e se aplicará a todas as suas janelas internas, uma classe de janelas, e se aplicará a todos os widgets desta classe, ou all e se aplicará a todas as janelas.

Dentro do script de um evento, você pode fazer referência à tecla pressionada, à janela onde o evento ocorreu, às coordenadas do mouse no momento em que o evento ocorreu etc. A seguir são apresentadas Os códigos, que atuam como se fossem variáveis, definidas por Tk para referenciar cada uma dessas informações:
 

Variável Descrição
%% Um sinal %.
%# O campo serial do último evento.
%a O campo acima.
%b O número do botão.
%c O campo contador.
%d O campo detalhe.
%f O campo focus.
%h O campo altura.
%k O campo código da tecla.
%m O campo modo.
%o O campo override_redirect.
%p O campo place.
%s O campo estado.
%t O campo time.
%w O campo largura.
%x O campo x.
%y O campo y.
%A O caractere ASCII.
%B O campo largura da borda.
%E O campo send_event.
%K O símbolo da tecla pressionada como texto.
%N O símbolo da tecla pressionada como um número decimal.
%R A janela principal( root ). 
%S Identificador sub_window.
%T O campo tipo.
%W O nome( caminho ) da janela onde o evento ocorreu.
%X O campo x_root.
%Y O campo y_root.

Os eventos suportam as seguintes opções:
 

Opção Código Evento válido
-above janela %a Configure
-borderwidth largura %B Configure
-button número %b ButtonPress, ButtonRelease
-count número %c Expose
-detail detalhe %d Enter, Leave, Focus
-focus booleano %f Enter, Leave
-height altura %h Configure
-keycode número %k KeyPress, KeyRelease
-keysym nome %K KeyPress, KeyRelease
-mode modo %m Enter, Leave, Focus
-override booleano %o Map, Reparent, Configure
-place onde %p Circulate
-root janela %R KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion
-rootx coordenada %X KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion
-rooty coordenada %Y KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion
-sendevent booleano %E Todos os eventos
-serial número %# Todos os eventos
-state estado %s Todos os eventos
-subwindow janela %S KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion
-time inteiro %t KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion, Property
-x coordenada %x KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion, Expose, Configure, Gravity, Reparent
-y coordenada %y KeyPress, KeyRelease, ButtonPress, ButtonRelease, Enter, Leave, Motion, Expose, Configure, Gravity, Reparent

Associando teclas de atalho

Normalmente associamos teclas de atalho a botões e menus. Para indicar a tecla de atalho em um botão sublinhamos a tecla correspondente através da opção -underline:

# Cria uma janela
toplevel .t
# Cria um botao com a primeira letra do rotulo sublinhada
button .t.b -text OK -underline 0 -command {destroy .t}
# Dispoe o botao na janela
pack .t.b
# Configura o evento pressionar as teclas Alt( esquerda ) + O( minusculo ) para que invoque o comando default do botao
bind .t <Key-Alt_L><Key-o> {.t.b invoke}
O exemplo acima configura a tecla de atalho ALT+O para o botao OK.

Para indicar a tecla de atalho para um menu devemos utilizar a opção -accelerator do menu:

# Cria a janela
toplevel .w1
   
wm protocol .w1 WM_DELETE_WINDOW {exit}
 
wm title .w1 "HTML Browser"
wm resizable .w1 1 1
# Centraliza a janela na tela
set x [expr {([winfo screenwidth .w1] - 640)/2}]
set y [expr {([winfo screenheight .w1] - 480)/2}]
#
# Cria os paineis
#
# Painel principal
frame .w1.fr0 -borderwidth 2 -relief raised
# Painel do menu
frame .w1.fr0.fr1 -borderwidth 2 -relief raised
# Painel da barra de ferramentas
frame .w1.fr0.fr2 -borderwidth 2 -relief raised
# Painel da barra de navegacao
frame .w1.fr0.fr3 -borderwidth 2 -relief raised
# Painel da janela do browser
frame .w1.fr0.fr4 -borderwidth 2
# Painel da barra de status
frame .w1.fr0.fr5 -borderwidth 2 -relief raised
#
# Cria os componentes dos paineis
#
# Cria os menus
menubutton .w1.fr0.fr1.file -text "Arquivo" -underline 0 -menu .w1.fr0.fr1.file.menu
menubutton .w1.fr0.fr1.edit -text "Editar" -underline 0 -menu .w1.fr0.fr1.edit.menu
menubutton .w1.fr0.fr1.options -text "Opcoes" -underline 0 -menu .w1.fr0.fr1.options.menu
menubutton .w1.fr0.fr1.help -text "Ajuda" -underline 1 -menu .w1.fr0.fr1.help.menu
# Menu Arquivo
menu .w1.fr0.fr1.file.menu
.w1.fr0.fr1.file.menu add command -label "Abrir" -accelerator "Ctrl+O" -command {bell}
.w1.fr0.fr1.file.menu add separator
.w1.fr0.fr1.file.menu add command -label "Sair" -accelerator "Ctrl+R" -command {exit}
# Menu Editar
menu .w1.fr0.fr1.edit.menu
.w1.fr0.fr1.edit.menu add command -label "Procurar" -accelerator "Ctrl+F" -command {bell}
# Menu Opcoes
menu .w1.fr0.fr1.options.menu
.w1.fr0.fr1.options.menu add cascade -label "Tamanho da fonte" -menu .w1.fr0.fr1.options.menu.font
.w1.fr0.fr1.options.menu add cascade -label "Nivel de indentacao" -menu .w1.fr0.fr1.options.menu.indent
# Menu Ajuda
menu .w1.fr0.fr1.help.menu
.w1.fr0.fr1.help.menu add command -label "Conteudo" -accelerator "F1" -command {bell}
.w1.fr0.fr1.help.menu add separator
.w1.fr0.fr1.help.menu add command -label "Sobre" -command {bell}
menu .w1.fr0.fr1.options.menu.font
.w1.fr0.fr1.options.menu.font add radiobutton -label "Pequena" -value 0 -variable Size -command {bell}
.w1.fr0.fr1.options.menu.font add radiobutton -label "Media" -value 4 -variable Size -command {bell}
.w1.fr0.fr1.options.menu.font add radiobutton -label "Grande" -value 12 -variable Size -command {bell}
menu .w1.fr0.fr1.options.menu.indent
.w1.fr0.fr1.options.menu.indent add radiobutton -label "Pequena" -value 0.6 -variable Indent -command {bell}
.w1.fr0.fr1.options.menu.indent add radiobutton -label "Media" -value 1.2 -variable Indent -command {bell}
.w1.fr0.fr1.options.menu.indent add radiobutton -label "Grande" -value 2.4 -variable Indent -command {bell}
#
# Instala os componentes
#
# Instala os paineis
pack .w1.fr0 -expand 1 -fill both
pack .w1.fr0.fr1 .w1.fr0.fr2 .w1.fr0.fr3 -fill x
pack .w1.fr0.fr4 -expand 1 -fill both
pack .w1.fr0.fr5 -fill x
    
# Instala os componentes da barra de menu
pack .w1.fr0.fr1.file .w1.fr0.fr1.edit .w1.fr0.fr1.options -side left
pack .w1.fr0.fr1.help -side right
# Redimensiona a janela
wm geometry .w1 640x480+$x+$y
# Configura a tecla de atalho para o menu Arquivo->Sair como sendo Ctrl( esquerdo ) + R( minusculo )
bind .w1 <Key-Control_L><r> {exit}


O fragmento de código acima, adaptado do código fonte do meu browser TkShip, cria uma janela com uma barra de menu e configura a tecla de atalho do menu Arquivo->Sair para CTRL+R.

Copie e cole o código acima na linha de comandos do wish e veja como funciona.
 

Validando a entrada do usuário

Validar a entrada digitada pelo usuário é uma dos usos mais importantes para os binds. Tk 8.3 ou superior inclui o suporte a validação de dados digitados no widget entry, entretanto, versões mais antigas do Tk, e que acompanham a maioria das distribuições do Linux, só podem validar a entrada digitada através de binds. De fato, tudo o que as opções de validação do Tk 8.3 fazem é configurar binds para o widget entry, como faremos nesta seção.

Mas antes de prosseguir-mos, muitas pessoas me perguntam como trocar o comportamento default do X-Window/Windows de TAB para ENTER, para mudar o foco entre os widgets de uma janela. O fragmento de código a seguir fará exatamente isso:

# Configura a tecla ENTER para que realize a mudanca de foco entre os widgets de uma janela
bind Toplevel all <Key-Return> {focus [tk_focusNext %W]}


Agora vejamos como configurar os binds de um widget entry para que seja realizada a validação dos dados:
 

# Cria a janela
toplevel .t
entry .t.e -justify right
pack .t.e -side left -expand 1 -fill x
# Configura o tipo de dado que pode ser digitado
# Esta forma nao e elegante, voce deve evita-la
# bind .t.e <Key> {
#     if {((%N < 44) || (%N > 57) || ([string length [%W get]] >= 10)) && ((%N >= 32) && (%N <= 128))} {
#         bell
#         break
#     }
# }
# Esta e a forma elegante de configurar a validacao dos dados
#    A parte ([regexp -nocase {[0-9]} %A] == 0) determina que somente valores numericos podem ser aceitos
#    A parte ([string length [%W get]] >= 10) determina que somente 10 digitos devem ser aceitos
#    A parte ((%N >= 32) && (%N <= 128)) e necessaria para permitir os caracteres ASCII de controle como
#        DELETE e BACKSPACE
bind .t.e <Key> {
    if {(([regexp -nocase {[0-9]} %A] == 0) || ([string length [%W get]] >= 10)) && ((%N >= 32) && (%N <= 128))} {
        bell
        break
    }
}
# Faz com que todo o texto dentro do widget seja selecionado, quando ele receber o foco
bind .t.e <FocusIn> {
    .t.e selection range 0 end
}


Agora se desejar-mos que os dados sejam formatados após a digitação, deveremos criar uma função que formate o texto para nós. Como a função abaixo, que formata um numero na forma #.##, útil para valores monetários:
 

# float2cur valor
# Formata um valor numerico para a forma #.##
proc float2cur {{valor 0.00}} {
    set vetor {}
    set vetor [split $valor .]
    set p1 ""
    set p2 ""
    set val ""
    if {[llength $vetor] == 2} {
        if {[lindex $vetor 0] != ""} {
            set p1 [lindex $vetor 0]
        } else {
            set p1 "0"
        }
        if {[lindex $vetor 1] != ""} {
            set x1 [format {%1$-2s} [lindex $vetor 1]]
            regsub " " $x1 0 x1
            regsub " " $x1 0 x1
            set p2 $x1
        } else {
            set p2 "00"
        }
    } else {
        if {[lindex $vetor 0] != ""} {
            set p1 [lindex $vetor 0]
        } else {
            set p1 "0"
        }
        set p2 "00"
    }
    if {[string length $p2] > 2} {
        set x2 [string range $p2 0 1]
        if {[string index $p2 2] > 5} {
            if {$x2 < 99} {
                if {[string index $x2 0] == 0} {
                    set x2 [string index $x2 1]
                    incr x2
                    set x2 [format {%1$02u} $x2]
                } else {
                    incr x2
                }
            } else {
                set x2 "00"
                incr p1
            }
        }
        set p2 $x2
    }
    append val $p1 . $p2
    return $val
}
# Cria a janela
toplevel .t
entry .t.e -justify right
pack .t.e -side left -expand 1 -fill x
# Configura o tipo de dado que pode ser digitado
bind .t.e <Key> {
    if {(([regexp -nocase {[0-9]} %A] == 0) || ([string length [%W get]] >= 10)) && ((%N >= 32) && (%N <= 128))} {
        bell
        break
    }
}
# Faz com que todo o texto dentro do widget seja selecionado, quando ele receber o foco
bind .t.e <FocusIn> {
    .t.e selection range 0 end
}
# Configura para que a informacao digitada seja formatada quando o widget perder o foco
bind .t.e <FocusOut> {
    set temp [float2cur [%W get]]
    %W delete 0 end
    %W insert 0 $temp
}


Experimente o fragmento de código acima copiando-o e colando-o na linha de comandos do wish. Os dados serão formatados quando o widget entry perder o foco.

Para uma descrição detalhada de todos os comandos disponíveis em Tk, consulte a documentação on-line, ou o Tcl/Tk Reference Guide, ou ainda o Tcl/Tk Electronic Reference.

Para maiores informações envie e-mail para info@souzamonteiro.com.


http://www.souzamonteiro.com
info@souzamonteiro.com

Copyright(C) 2000 by Roberto Luiz Souza Monteiro