Как отключить кнопку Perl, пока не будет выполнена внешняя команда?
sub push_button2
{
$but2->configure(-state => 'disabled');
open(launch, "+>C:\\integration\\check_label\\launch_check_label.bat") or die "Couldn't open file launch_check_label.bat$!";
print launch "$ARGV[1]:\n";
print launch "perl C:\\integration\\check_label\\check_label.pl $ARGV[0] $ARGV[1] $text";
system("start C:\\integration\\check_label\\external_command.bat");
$but2->configure(-state => 'normal');
}
Вышеупомянутый фрагмент кода не сработал, он отключил кнопку только на несколько миллисекунд, и кнопка была активна мгновенно, даже до запуска командного файла.
2 ответа
В вашем подходе есть пара вопросов. Во-первых, последовательность "кнопка выключения", "делать что-то, не относящееся к TK", "кнопка включения" не работает, и кнопка всегда остается включенной. Причина в том, что изменения отображения выполняются только в случае возврата или обратного вызова update()
или же idletasks()
называется. Чтобы продемонстрировать это, в следующем примере кнопка никогда не отключается:
use Tk;
my $mw = tkinit;
my $b; $b = $mw->Button(-text => 'Click me!', -command => sub {
$b->configure(-state => 'disabled');
# $mw->idletasks; # or alternatively: $mw->update;
sleep 10; # simulate some task
$b->configure(-state => 'normal');
})->pack;
MainLoop;
Но если idletasks()
или же update()
вызов активирован, тогда все работает как положено.
В примере сценария есть недостаток: в то время как sleep()
(или любая занятая задача) активна, обновление графического интерфейса невозможно. Приложение кажется замороженным для пользователя. Даже обновления окон больше не происходят. Если это проблема, тогда весь скрипт должен быть реструктурирован неблокирующим образом. В этом случае можно было бы начать новый процесс в фоновом режиме (как вы уже делаете это с помощью start
команда), но затем вы должны знать, когда фоновый процесс завершен, и снова включить кнопку. Один простой подход состоит в том, чтобы использовать какой-то "файл сигналов" и периодически проверять, был ли этот файл сигналов создан из фонового процесса. Вот пример, который работает в системах Unix (но должен работать и в Windows, если в system()
вызов):
use Tk;
my $mw = tkinit;
my $b; $b = $mw->Button(-text => 'Click me!', -command => sub {
$b->configure(-state => 'disabled');
# no idletasks/update necessary now
system("(rm -f /tmp/signal_file; sleep 10; touch /tmp/signal_file) &");
my $repeater; $repeater = $mw->repeat(1000, sub {
if (-e "/tmp/signal_file") {
unlink "/tmp/signal_file";
$b->configure(-state => 'normal');
$repeater->cancel;
}
});
})->pack;
MainLoop;
Этот подход не очень элегантен, потому что требует периодической проверки. Более элегантный способ может быть написан с Tk::IO
модуль, но возможно, что это работает только на платформах Unix:
use Tk;
use Tk::IO;
my $exec_fh;
my $mw = tkinit;
my $b; $b = $mw->Button(-text => 'Click me!', -command => sub {
$b->configure(-state => 'disabled');
# no idletasks/update necessary now
$exec_fh = Tk::IO->new(-linecommand => sub {
# empty, we're not interested in the output
}, -childcommand => sub {
$b->configure(-state => 'normal');
});
$exec_fh->exec("sleep 10");
})->pack;
MainLoop;
Дальнейшие хорошие чтения Tk::fileevent
Pod и, возможно, вы можете дать AnyEvent
попытка
Вы не закрыли launch
перед выполнением вашего system
команда.
Если вывод на launch
дескриптор файла буферизуется (обычно до 4 КБ), тогда ваш файл, вероятно, пуст во время system
команда запускается. Если твой system
В этом случае команда сразу же вернется, и ваша кнопка будет немедленно активирована.
Как раз перед вашим system
команда, вы должны:
close(launch);
(Кроме того, традиционно используется верхний регистр для файловых дескрипторов.)