ulis, 12-02-05. Comment garder son interface vivante.
Pourquoi
Il peut arriver qu'une interface contrôle un widget qui nécessite un calcul conséquent. Dans ce cas, il ne faut pas que l'interface reste bloquée jusqu'à la fin du calcul. Surtout si le calcul est devenu inutile car l'utilisateur a changé les paramètres du widget.
Comment
Pour la condition 1, il faut que le widget enregistre les paramètres au début de son calcul et vérifient régulièrement (à chaque boucle de calcul par exemple) qu'ils n'ont pas varié.
Pour la condition 2, le plus simple est de permettre à la boucle des évènements de traiter les messages en attente. Le danger étant que ces messages relancent (récursivement) la procédure courante jusqu'à dépasser la capacité de la pile des appels de procédure. Il faut donc trouver un moyen de limiter le niveau de récursivité induit.
Un problème annexe est d'éviter de refaire plusieurs fois un même calcul même s'il est redemandé (à tort) à travers l'interface.
Le code
# ---------------
# parameters
# ---------------
set radius 64
set px 0.25
set py -0.25
set ::TRACE 0
# ---------------
# procs
# ---------------
# compute_px
# ---------------
proc compute_px {px} \
{
update
if {$::TRACE} { puts "compute_px $px" }
if {$px == $::currentpx} { if {$::TRACE} { puts "px current" }; return }
after 0 compute $::px $::py
}
# compute_py
# ---------------
proc compute_py {py} \
{
update
if {$::TRACE} { puts "compute_py $py" }
if {$py == $::currentpy} { if {$::TRACE} { puts "py current" }; return }
after 0 compute $::px $::py
}
# compute px py
# ---------------
proc compute {px py} \
{
if {$::TRACE} { puts "compute $px $py" }
if {$px == $::currentpx && $py == $::currentpy} { if {$::TRACE} { puts "current" }; return }
set ::currentpx $px
set ::currentpy $py
set radius $::radius
set dx [expr {$px * $radius}]
set dy [expr {$py * $radius}]
for {set y -$radius} {$y < $radius} {incr y} \
{
for {set x -$radius} {$x < $radius} {incr x} \
{
if {$px != $::px} { if {$::TRACE} { puts "($x,$y) px $px $::px" }; return }
if {$py != $::py} { if {$::TRACE} { puts "($x,$y) py $py $::py" }; return }
set r [expr {($x * $x + $y * $y) / ${::radius^2}}]
if {$r > 1} { continue }
set r [expr {(($x - $dx) * ($x - $dx) + ($y - $dy) * ($y - $dy))/ ${::radius^2}}]
set c [expr {127 + int(128 * (1 - $r))}]
if {$c < 0} { set c 0 }
_img_ put [format #%2.2x%2.2x%2.2x $c $c $c] \
-to [expr {$x + $radius}] [expr {$y + $radius}]
update
}
}
}
# ---------------
# packages
# ---------------
package require Tk
package require Img
# ---------------
# interface
# ---------------
wm title . "Keeping alive"
set diameter [expr {$radius * 2}]
set radius^2 [expr {double($radius * $radius)}]
set pi/2 [expr {asin(1)}]
set currentpx ""
set currentpy ""
# image
# ---------------
image create photo _img_ \
-width $diameter -height $diameter
# canvas
# ---------------
canvas .c -bd 0 -highlightt 0 \
-width $diameter -height $diameter
.c create image 0 0 -anchor nw -image _img_
pack .c
# label frame
# ---------------
labelframe .lf -text "glint relative position" -relief groove
# scales
# ---------------
label .lf.lgx -text \nx
scale .lf.sgx -orient horizontal -variable ::px \
-from -1.0 -to 1.0 -resolution 0.05 -length 200
label .lf.lgy -text \ny
scale .lf.sgy -orient horizontal -variable ::py \
-from -1.0 -to 1.0 -resolution 0.05 -length 200
# place & display
# ---------------
pack .lf
grid .lf.lgx .lf.sgx
grid .lf.lgy .lf.sgy
update
# events
# ---------------
.lf.sgx config -command compute_px
.lf.sgy config -command compute_py
wm protocol . WM_DELETE_WINDOW exit
# ---------------
# go
# ---------------
compute $px $pyVoir aussi
Questions/réponses
Le calcul est fait dans la procédure compute. Celle-ci reçoit les paramètres px et py. Les variables globales ::px et ::py contiennent les valeurs courantes (mises à jour par l'interface) de ces paramètres.
A chaque boucle de calcul, la procédure compare px et ::px, py et ::py. S'il y a une différence c'est que l'utilisateur a demandé un nouveau calcul et il faut aussitôt arrêter celui qui est en cours (la procédure retourne aussitôt vers l'appelant).
--
A la fin de chaque boucle de calcul de la procédure compute, l'instruction update est appelée. Cela permet à la boucle des évènements de traiter les mouvements des widgets échelles.
La récursivité est contrôlée par le nombre maximum d'appels rapprochés des procédures compute_px et compute_py. Les échelles allant de -1 à +1 par pas de 0.05, il y a donc -au plus- 40 appels rapprochés de chacJL