|
|||
TkFrame o TkRoot) y después
crearías los componentes que los rellenen, como botones o etiquetas.
Cuando todo estuviese listo para iniciar la GUI, llamarías a Tk.mainloop. El
motor de Tk en ese momento tomaría el control del programa mostrando componentes y
ejecutando código tuyo en respuesta a los eventos de la GUI.
require 'tk'
root = TkRoot.new { title "Ex1" }
TkLabel.new(root) {
text 'Hello, World!'
pack { padx 15 ; pady 15; side 'left' }
}
Tk.mainloop
|
tk, creamos un
marco a nivel raíz usando TkRoot.new. Después creamos un componente etiqueta
como hijo del marco raíz, estableciendo varias opciones
de la propia etiqueta. Finalmente, ajustamos el tamaño del marco raíz y entramos en el bucle principal de eventos de la GUI.
Es una buena costumbre especificar la raíz explícitamente, pero podrías no hacerlo
---además de otras opciones adicionales---y reducir el código a tres líneas:
require 'tk'
TkLabel.new { text 'Hello, World!' }
Tk.mainloop
|
Tk delante de él. Por ejemplo,
los componentes Label, Button, y Entry pasarían a ser las clases TkLabel,
TkButton, y TkEntry. Se crean instancias de los componentes
mediante new, tal y como harías con cualquier otro objeto. Si no especificas
un padre específico para un componente determinado, se añadirá por defecto al marco
raíz. Normalmete especificaremos los padres de los componentes, así
como muchas otras opciones---color, tamaño, y demás. También necesitaremos ser
capaces de obtener información de nuestros componentes mientras
nuestro programa se esté ejecutando estableciendo callbacks y compartiendo datos.
Hash. Se puede hacer lo mismo en
Ruby, pero también puedes pasar las opciones usando un bloque de código; el nombre
de la opción se usa como nombre de método dentro del bloque y los argumentos de cada
opción aparecen como argumentos en la llamada del método. Los componentes reciben
un padre como primer argumento, seguido opcionalmente por una hash con las opciones o
el bloque de código con las opciones. Por lo tanto, las dos formas siguientes son
equivalentes.
TkLabel.new(parent_widget) {
text 'Hello, World!'
pack('padx' => 5,
'pady' => 5,
'side' => 'left')
}
# or
TkLabel.new(parent_widget, text => 'Hello, World!').pack(...)
|
padx y pady en nuestros
ejemplos) se asume que están en pixels, pero pueden especificarse en
diferentes unidades usando los sufijos ``c'' (centímetros),
``i'' (pulgadas), ``m'' (milímetros), o ``p'' (puntos).
command (mostrada en
la llamada a TkButton en el siguiente ejemplo) recibe un objeto Proc,
que será llamado cuando el callback se ejecute. Aquí se usa
Kernel::proc
para convertir el bloque {exit} a Proc.
TkButton.new(bottom) {
text "Ok"
command proc { p mycheck.value; exit }
pack('side'=>'left', 'padx'=>10, 'pady'=>10)
}
|
TkVariable. Mostraremos ésto en el siguiente ejemplo. Fíjate cómo
se configura TkCheckButton; la documentación dice que la opción
variable recibe una var reference como argumento. Para hacer
ésto, creamos una referencia a variable Tk mediante TkVariable.new.
Al acceder a mycheck.value obtendremos la cadena ``0'' o
``1'' dependiendo de si el cuadro de selección está activado o no. Puedes usar
el mismo mecanismo para cualquier componente que soporte una var reference, como los
radio buttons y los campos de texto.
mycheck = TkVariable.new
TkCheckButton.new(top) {
variable mycheck
pack('padx'=>5, 'pady'=>5, 'side' => 'left')
}
|
configure, que recibe una Hash o un bloque de código del mismo modo que
new. Podemos modificar el primer ejemplo para cambiar
el texto de la etiqueta en respuesta a pulsar en un botón:
lbl = TkLabel.new(top) { justify 'center'
text 'Hello, World!';
pack('padx'=>5, 'pady'=>5, 'side' => 'top') }
TkButton.new(top) {
text "Cancel"
command proc { lbl.configure('text'=>"Goodbye, Cruel World!") }
pack('side'=>'right', 'padx'=>10, 'pady'=>10)
}
|
Cancel se pulse, el texto de la etiqueta
cambiará inmediatamente de ``Hello, World!'' a ``Goodbye, Cruel
World!''
También puedes solicitar a los componentes valores concretos de alguna opción mediante
cget:
require 'tk'
|
||
b = TkButton.new {
|
||
text "OK"
|
||
justify 'left'
|
||
border 5
|
||
}
|
||
b.cget('text')
|
» |
"OK"
|
b.cget('justify')
|
» |
"left"
|
b.cget('border')
|
» |
5
|
require 'tk'
class PigBox
def pig(word)
leadingCap = word =~ /^A-Z/
word.downcase!
res = case word
when /^aeiouy/
word+"way"
when /^([^aeiouy]+)(.*)/
$2+$1+"ay"
else
word
end
leadingCap ? res.capitalize : res
end
def showPig
@text.value = @text.value.split.collect{|w| pig(w)}.join(" ")
end
def initialize
ph = { 'padx' => 10, 'pady' => 10 } # common options
p = proc {showPig}
@text = TkVariable.new
root = TkRoot.new { title "Pig" }
top = TkFrame.new(root)
TkLabel.new(top) {text 'Enter Text:' ; pack(ph) }
@entry = TkEntry.new(top, 'textvariable' => @text)
@entry.pack(ph)
TkButton.new(top) {text 'Pig It'; command p; pack ph}
TkButton.new(top) {text 'Exit'; command {proc exit}; pack ph}
top.pack('fill'=>'both', 'side' =>'top')
end
end
PigBox.new
Tk.mainloop
|
| Sidebar: Geometry Management | |||||||||||||||||
En el código de ejemplo de este capítulo
verás referencias al método pack
en los componentes. Es una llamada muy importante, desde el momento en que
no la pones---quítala y no podrás ver el componente. pack
es un comando que le dice al gestor de geometría que coloque el componete
de acuerdo a las restricciones que le especifiquemos. Los gestores de geometría
reconocen tres comandos:
pack es el comando mas usado, será el que utilicemos
en nuestros ejemplos.
|
|||||||||||||||||
bind del componente.
Por ejemplo, supongamos que hemos creado un botón que muestra una imagen.
Nuestra intención es que la imagen cambie cuando el ratón del usuario esté
sobre el botón.
image1 = TkPhotoImage.new { file "img1.gif" }
image2 = TkPhotoImage.new { file "img2.gif" }
b = TkButton.new(@root) {
image image1
command proc { doit }
}
b.bind("Enter") { b.configure('image'=>image2) }
b.bind("Leave") { b.configure('image'=>image1) }
|
TkPhotoImage. Después creamos un botón (nombrado de forma muy clara como
``b''), el cual muestra la imagen image1. A continuación asociamos el evento
``Enter'' para que la imagen mostrada por el botón cambie dinámicamente a
image2, y el evento ``Leave'' para volver a la imagen
image1.
Este ejemplo muestra los sencillos eventos ``Enter'' y ``Leave''. Pero
la cadena indicando el evento evento que se le pasa bind puede estar compuesta
de varias subcadenas separadas por dashes, siguiendo el orden
modificador-modificador-tipo-detalle. Los modificadores están listados en
la referencia de Tk e incluyen Button1, Control, Alt,
Shift, y demás. Tipo es el nombre del evento (tomado
de los convenios para nombres de X11) e incluye eventos como
ButtonPress, KeyPress, y Expose. Detalle es
un número entre 1 y 5 para botones o un keysym para entrada
por teclado. Por ejemplo, un binding que se ejecute cuando se suelte el botón 1
del ratón mientras la tecla control está pulsada, podría especificarse del siguiente modo:
Control-Button1-ButtonRelease
Control-ButtonRelease-1
El evento en si mismo puede contener ciertos campos como el tiempo del evento
y las posiciones x e y. bind puede pasar estos elementos al callback,
usando event field codes. Se usan siguiendo la especificaciones de
printf. Por ejemplo, para obtener las coordenadas x e y
en un movimiento de ratón, deberás especificar la llamada a bind
con tres parámetros. El segundo parámetro es el Proc para el callback, y el tercero
es el event field string.
canvas.bind("Motion", proc{|x, y| do_motion (x, y)}, "%x %y")
|
require 'tk'
class Draw
def do_press(x, y)
@start_x = x
@start_y = y
@current_line = TkcLine.new(@canvas, x, y, x, y)
end
def do_motion(x, y)
if @current_line
@current_line.coords @start_x, @start_y, x, y
end
end
def do_release(x, y)
if @current_line
@current_line.coords @start_x, @start_y, x, y
@current_line.fill 'black'
@current_line = nil
end
end
def initialize(parent)
@canvas = TkCanvas.new(parent)
@canvas.pack
@start_x = @start_y = 0
@canvas.bind("1", proc{|e| do_press(e.x, e.y)})
@canvas.bind("2", proc{ puts @canvas.postscript({}) })
@canvas.bind("B1-Motion", proc{|x, y| do_motion(x, y)}, "%x %y")
@canvas.bind("ButtonRelease-1",
proc{|x, y| do_release (x, y)}, "%x %y")
end
end
root = TkRoot.new{ title 'Canvas' }
Draw.new(root)
Tk.mainloop
|
TkCanvas, TkListbox, y
TkText pueden configurarse para usar barras de scroll
de modo que puedas trabajar en un pequeño subconjunto de un ``gran dibujo''.
La comunicación entre una barra de scroll y un componente
es bidireccional. Mover la barra de scroll implica que la vista del componente
cambie; pero si la vista del componente cambie por otros medios,
la barra de scroll tiene que cambiar también para reflejar la nueva
posición.
Ya que no hemos trabajado mucho con listas todavía, nuestro ejemplo de scrolling
usará una lista de texto con scrolling. En el siguiente fragmento de código,
empezaremos creando una TkListbox normal y corriente. Después crearemos la
TkScrollbar. El callback de las barras de scroll (establecido con command)
llamarán al método yview del componente lista, que cambiará el valor
de la porción visible en la dirección y.
Después de que el callback se configure, hacemos la asociación inversa: cuando
la lista se de cuenta de la necesidad de hacer scroll, estableceremos el rango apropiado
en la barra de scroll usando TkScrollbar#set.
Usaremos el mismo código en un programa completamente funcional en el
siguiente capítulo.
list_w = TkListbox.new(frame, 'selectmode' => 'single')
scroll_bar = TkScrollbar.new(frame,
'command' => proc { |*args| list_w.yview *args })
scroll_bar.pack('side' => 'left', 'fill' => 'y')
list_w.yscrollcommand(proc { |first,last|
scroll_bar.set(first,last) })
|
File.new
usaba un bloque para asegurarse de que el archivo se cerraba después de usarlo? Podemos hacer algo similar con el método busy, tal y como se muestra
en el siguiente ejemplo.
Este programa también muestra algunas operaciones simples de TkListbox---añadir
elementos a la lista, configurar un callback para cuando
el botón del ratón se suelte,[Probablemente desearás que sea cuando el botón se suelte,
no cuando se pulse, ya que el componente queda seleccionado cuando el botón se pulsa.] y obtener
la selección actual.
Hasta ahora hemos usado TkPhotoImage solamente para mostrar iconos directamente,
pero también podrías usarlo para hacer zoom, submuestras, y mostrar porciones de imágenes.
Aquí usaremos la característica de hacer submuestras para escalar la imagen
al visualizarla.
require 'tk'
def busy
begin
$root.cursor "watch" # Set a watch cursor
$root.update # Make sure it updates the screen
yield # Call the associated block
ensure
$root.cursor "" # Back to original
$root.update
end
end
$root = TkRoot.new {title 'Scroll List'}
frame = TkFrame.new($root)
list_w = TkListbox.new(frame, 'selectmode' => 'single')
scroll_bar = TkScrollbar.new(frame,
'command' => proc { |*args| list_w.yview *args })
scroll_bar.pack('side' => 'left', 'fill' => 'y')
list_w.yscrollcommand(proc { |first,last|
scroll_bar.set(first,last) })
list_w.pack('side'=>'left')
image_w = TkPhotoImage.new
TkLabel.new(frame, 'image' => image_w).pack('side'=>'left')
frame.pack
list_contents = Dir["screenshots/gifs/*.gif"]
list_contents.each {|x|
list_w.insert('end',x) # Insert each file name into the list
}
list_w.bind("ButtonRelease-1") {
index = list_w.curselection[0]
busy {
tmp_img = TkPhotoImage.new('file'=> list_contents[index])
scale = tmp_img.height / 100
scale = 1 if scale < 1
image_w.copy(tmp_img, 'subsample' => [scale,scale])
tmp_img = nil # Be sure to remove it, the
GC.start # image may have been large
}
}
Tk.mainloop
|
TkWidget usado, no en la
instancia de tu clase.
Perl/Tk: $widget = $parent->Widget( [ option => value ] )
Ruby: widget = TkWidget.new(parent, option-hash)
widget = TkWidget.new(parent) { code block }
|
Perl/Tk: -background => color
Ruby: 'background' => color
{ background color }
|
Perl/Tk: -textvariable => \$variable
-textvariable => varRef
Ruby: ref = TkVariable.new
'textvariable' => ref
{ textvariable ref }
|
TkVariable para asociar una variable Ruby al valor de un componente.
Luego podrás usar los métodos de acceso de value en TkVariable
(TkVariable#value y TkVariable#value=) para
operar sobre el contenido del componente directamente.