2017-01-05 20 views
5

Jestem nowicjuszem w TK i chciałbym się dowiedzieć, czy problem dotyczy normalnego zachowania Tk, czy nie.Dlaczego wywołania waitVariable nie są niezależne, nawet jeśli użyto innej zmiennej ref?

W skrócie: Mam skrypt Perl/Tk (Tk wersja 804.028), który używa dwóch widgetów Tk :: ExecuteCommand (v1.6). Takie obiekty mają metodę execute_command, która używa zdefiniowanego wywołania zwrotnego fileevent do odczytania stdout polecenia wykonanego i powraca po jego zakończeniu. Jest rozwiązywany za pomocą funkcji waitVariable. Ale wygląda na to, że jeśli dwa ExecuteCommand zaczęły się razem, zwracają się tylko wtedy, gdy powraca wolniej. Mogę się spodziewać, że szybciej powróci natychmiast po zakończeniu.

zrobiłem mały test skrypt Perl/Tk wykazać problem:

#!/usr/bin/perl 

use strict; 
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(); 

Rysuje widget ROText i 4 przyciski ([DO 0] [Stop 0] [uwagi 1] [stop 1]) (patrz załączony rysunek). Kliknięcie przycisku Do wywołuje funkcję doo, która czeka aż zmieniony przypisany skalar. Zmienne zmieniają się po naciśnięciu przycisku Stop.

Po naciśnięciu przycisków w kolejności [Do 0] [Stop 0] [Do 1] [Stop 1] wyjście wydaje się być w porządku (patrz wiersze 2-7). Ale jeśli "zadania" rozpoczęły się równolegle, oba wywołania zwrotne kończą się, gdy oba zostaną zatrzymane. Zatem naciśnięcie Przycisków w [Do 0] [Do 1] [Stop 0] [Stop 1] (patrz wiersze 8-13) daje dziwny wynik (patrz zdjęcie).

enter image description here

Moje oczekiwanie na drugim badaniu było to, że pierwsza funkcja zwrotna zwraca natychmiast po naciśnięciu przycisku pierwszy przystanek. Więc myślę, że wyjście powinno być:

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 

Działa na komputerze z systemem Linux.

Czy brakuje mi czegoś? Z góry dziękuję!

UPDATE

Aby obejść ten problem, ja waitVariable przepisał ten widget zamiast użyć wywołania zwrotne (dzięki Tantala!). Teraz execute_command natychmiast zwraca. Istnieją dwa wywołania zwrotne, jeden dla anulowania, drugi dla zakończenia. Teraz dzwoniący jest informowany przez te wywołania zwrotne. W każdym razie gdzieś czytam (nie mogę znaleźć źródła), że czekanie w callbacku długo nie jest dobrym pomysłem w Tk. Nowe rozwiązanie jest z tym zgodne.

Dziękuję za pomoc!

Odpowiedz

3

$ widget> waitVariable (\ $ name)

$ widget> waitVisibility

$ widget> waitWindow

TK metody wait czekać na jeden z kilku rzeczy się wydarzy , a następnie powraca bez podejmowania żadnych innych działań. Wartość zwracana jest zawsze pustym ciągiem. waitVariable oczekuje odwołania do zmiennej perl, a polecenie czeka na modyfikację tej zmiennej. Ta forma jest zwykle używana do czekania na zakończenie interakcji użytkownika z oknem dialogowym, które ustawia zmienną jako część (ewentualnie końcową) interakcji. waitVisibility czeka na zmianę w stanie widoczności widgetu $ $ (na co wskazuje pojawienie się zdarzenia VisibilityNotify). Ten formularz jest zwykle używany do czekania, aż nowo utworzone okno pojawi się na ekranie przed podjęciem jakiejś akcji. waitWindow czeka na zniszczenie widgetu $. Ten formularz jest zwykle używany do czekania na zakończenie interakcji użytkownika z oknem dialogowym przed użyciem wyniku tej interakcji. Zauważ, że tworzenie i niszczenie okna za każdym razem, gdy wymagane jest okno dialogowe, powoduje, że kod jest modułowy, ale nakłada się na niego, czego można uniknąć, cofając okno i używając waitVisibility.

Podczas oczekiwania na metody oczekiwania tk przetwarzają zdarzenia w normalny sposób, więc aplikacja będzie nadal reagować na interakcje użytkownika. Jeśli funkcja obsługi zdarzeń ponownie wywoła tkwęta, zagnieżdżone wywołanie do tkwait musi zakończyć się przed zakończeniem wywołania zewnętrznego.

Podkreśl moje.

+0

Świetnie! Dzięki! Czy znasz sposób implementacji funkcji waitVariable, która działa niezależnie? Próbowałem zaimplementować przy użyciu odpytywania, ale wydaje się, że $ widget-> after (ms) również nie może obsłużyć dwóch timerów niezależnie. – TrueY

+0

Przepraszamy. Dodałabym to, co zrobiłem. – ikegami

+0

Wreszcie znalazłem cytowany tekst w dokumentacji Tk, a nie w Perlu/Tk. Postanowiłem zrezygnować z użycia funkcji waitVariable, a zamiast nich nie są używane wywołania zwrotne ... Dzięki! Świetna pomoc! – TrueY

2

Nie powinieneś czekać w pętli zdarzeń. To, co się dzieje, polega na tym, że tworzysz stację wywoławczą, która wymaga przebywania w poprzedniej pętli oczekiwania. Na przykład, jeśli dodasz use Carp; na górze, a następnie zmienić funkcję pr tak:

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

wtedy zobaczysz waitVariable pokazuje się - nie możemy tam wrócić, dopóki jesteśmy z powrotem w w tę pętlę, a skończysz z pętlami wewnątrz pętli.

Aby zrobić to, co chcesz zrobić bez odwracania wszystkiego na zdarzenia, możesz spróbować Coro, która może odwrócić takie zdarzenia.

Także we współczesnych perłach, qw nie oznacza nawiasów, więc twoje wywołania pack wymagają nawiasów wokół list qw/.../.

+0

Dzięki! W rzeczywistości jest to podobne, co dzieje się w Tk :: ExecuteCommand. – TrueY

+0

Dałeś świetny pomysł, aby usunąć waitVariable! IMHO Coro jest nieco przesadzone do użycia tutaj, ponieważ równoległy przebieg może być obsługiwany przez otwarte (lub lepsze IPC :: Open3) i fileevent. Dzięki za to! Nawias dodany! :) – TrueY