Pourquoi les appels à waitVariable ne sont-ils pas indépendants même si une variable différente ref est utilisée?

Je suis nouveau sur Tk et j’aimerais savoir si le problème en question est un comportement normal de Tk ou non.

En bref: j’ai un script Perl / Tk (Tk version 804.028) qui utilise deux widgets Tk :: ExecuteCommand (v1.6). De tels objects ont une méthode execute_command qui utilise le rappel de fileevent défini pour lire la sortie standard de la commande exécutée et retourne une fois celle-ci terminée. Il est résolu avec l’utilisation de waitVariable. Mais il semble que si deux ExecuteCommand ont démarré ensemble, ils ne retournent que lorsque le plus lent revient. Je peux m’attendre à ce que le plus rapide revienne immédiatement, une fois terminé.

J’ai réalisé un petit script de test Perl / Tk pour démontrer le problème:

#!/usr/bin/perl use ssortingct; use warnings; use Tk; use Tk::ROText; my $MAIN = new MainWindow -title => "TEST"; my $text = $MAIN->Scrolled('ROText')->pack(qw/-expand 1 -fill both/); sub pr { # Write into ROText widget $text->insert('end', join '', @_); $text->yview('end'); } pr "Tk version ", Tk->VERSION, "\n"; my @v = (100, 200); sub doo { # Button callback my ($rv, $txt) = @_; pr "B4 wait: $txt, ref=$rv, val=", $$rv, "\n"; $MAIN->waitVariable($rv); pr "Aft wait: $txt, ref=$rv, val=", $$rv, "\n"; } $MAIN->Button(-text => 'Do 0', -command => [\&doo, \$v[0], "Do 0" ] )->pack(qw/-expand 1 -side left -fill both/); $MAIN->Button(-text => 'Stop 0', -command => [sub {++$v[0]; pr "Stop 0\n";} ] )->pack(qw/-expand 1 -side left -fill both/); $MAIN->Button(-text => 'Do 1', -command => [\&doo, \$v[1], "Do 1" ] )->pack(qw/-expand 1 -side left -fill both/); $MAIN->Button(-text => 'Stop 1', -command => [sub {++$v[1]; pr "Stop 1\n";} ] )->pack(qw/-expand 1 -side left -fill both/); MainLoop(); 

Cela dessine un widget ROText et 4 boutons ([Do 0] [Stop 0] [Do 1] [Stop 1]) (voir la photo ci-jointe). En cliquant sur un bouton Do, appelle la fonction doo , qui attend que le scalaire affecté ait changé. Les variables sont modifiées lorsqu’un bouton Stop est enfoncé.

Si vous appuyez sur les touches dans l’ordre [Do 0] [Stop 0] [Do 1] [Stop 1], la sortie semble correcte (voir lignes 2-7). Mais si les “tâches” démarrent en parallèle, les deux rappels ne se terminent que si les deux sont arrêtés. Donc, appuyer sur les boutons dans [Do 0] [Do 1] [Stop 0] [Stop 1] (voir les lignes 8-13) donne un résultat étrange (voir photo).

entrer la description de l'image ici

Mon attente pour le deuxième test était que la première fonction de rappel retourne immédiatement après avoir appuyé sur le premier bouton Stop. Donc, je pense que la sortie devrait être:

 B4 wait: Do 0, ref=SCALAR(0x9970560), val=101 B4 wait: Do 1, ref=SCALAR(0x9970bfc), val=201 Stop 0 Aft wait: Do 0, ref=SCALAR(0x9970560), val=102 Stop 1 Aft wait: Do 1, ref=SCALAR(0x9970bfc), val=202 

Il fonctionne sur une machine Linux.

Est-ce que je manque quelque chose? Merci d’avance!

METTRE À JOUR

Pour contourner ce problème de waitVariable, j’ai réécrit ce widget pour utiliser des callbacks (merci à Tantalus!). Maintenant, execute_command revient immédiatement. Il y a deux rappels, un pour Annuler, un pour Terminer. Maintenant, l’appelant est informé par ces rappels. Quoi qu’il en soit, je lis quelque part (je ne trouve pas la source maintenant) qu’attendre longtemps dans un rappel n’est pas une bonne idée dans Tk. La nouvelle solution est conforme à cela.

Merci pour votre aide!

$ widget-> waitVariable (\ $ name)

$ widget-> waitVisibility

$ widget-> waitWindow

Les méthodes d’attente tk attendent que l’une des choses se produise, puis elle revient sans prendre d’autres actions. La valeur de retour est toujours une chaîne vide. waitVariable attend une référence à une variable perl et la commande attend la modification de cette variable. Ce formulaire est généralement utilisé pour attendre qu’un utilisateur ait fini d’interagir avec une boîte de dialog qui définit la variable en tant que partie (éventuellement finale) de l’interaction. waitVisibility attend une modification de l’état de visibilité de $ widget (indiqué par l’arrivée d’un événement VisibilityNotify). Ce formulaire est généralement utilisé pour attendre qu’une nouvelle fenêtre apparaisse à l’écran avant de prendre des mesures. waitWindow attend que $ widget soit détruit. Ce formulaire est généralement utilisé pour attendre qu’un utilisateur ait fini d’interagir avec une boîte de dialog avant d’utiliser le résultat de cette interaction. Notez que créer et détruire la fenêtre à chaque fois qu’une boîte de dialog est requirejse rend le code modulaire mais impose une surcharge qui peut être évitée en retirant la fenêtre et en utilisant waitVisibility.

Alors que les méthodes d’attente tk attendent, elles traitent les événements de manière normale, de sorte que l’application continuera à répondre aux interactions des utilisateurs. Si un gestionnaire d’événement appelle à nouveau tkwait, l’appel nested à tkwait doit se terminer avant que l’appel externe puisse se terminer.

Accent mis à moi.

Vous ne devriez pas attendre dans une boucle d’événement. Ce qui se passe, c’est que vous produisez une stack d’appels qui implique d’être dans la boucle d’attente précédente. Par exemple, si vous ajoutez use Carp; en haut, puis changez votre fonction pr comme ceci:

 sub pr { # Write into ROText widget $text->insert('end', Carp::longmess(join '', @_)); $text->yview('end'); } 

alors vous verrez le waitVariable apparaître – nous ne pouvons pas revenir en arrière tant que nous ne sums pas de retour dans cette boucle, et vous vous retrouvez avec des boucles dans les boucles.

Pour faire ce que vous voulez faire sans tout inverser dans les événements, vous pouvez essayer Coro qui peut inverser les événements comme celui-ci.

De plus, dans les perls modernes, qw n’implique pas de parenthèses, donc vos appels de pack besoin de parenthèses autour des listes qw/.../ .