如何使用 Tkx 反复提示用户?
使用 Perl Tkx,我想从用户那里获取一些输入,关闭窗口,也许稍后再做一次。对于用户输入,我只显示一些按钮,用户可以单击其中之一。这就是我现在所拥有的:
sub prompt_user {
my $answer;
my $mw = Tkx::widget->new("."); ## the main window is unavailable the second time
$mw->g_wm_title("Window Title"); ## line 40
$mw->g_wm_minsize(300, 200);
my $label = $mw->new_label( -text => "Question to the user?");
$label->g_pack( -padx => 10, -pady => 10);
my $button1 = $mw->new_button(
-text => "Option One",
-command => sub { $answer = 0; $mw->g_destroy; },
);
$button1->g_pack( -padx => 10, -pady => 10);
my $button2 = $mw->new_button(
-text => "Option Two",
-command => sub { $answer = 1; $mw->g_destroy; },
);
$button2->g_pack( -padx => 10, -pady => 10);
Tkx::MainLoop(); ## This blocks until the main window is killed
return $answer;
}
用户单击其中一个按钮,窗口关闭,prompt_user() 返回 0 或 1(取决于用户单击的按钮),然后继续执行。直到我尝试再次提示用户。然后我得到一个错误:
can't invoke "wm" command: application has been destroyed at MyFile.pm line 40
我只是想要一种方法来放置一堆按钮,让用户单击一个按钮,等待查看单击哪个按钮,也许稍后再执行一次。有没有办法可以等待按钮单击的响应而不破坏主窗口?也许创建一个子窗口?
我是使用 Tkx 的新手,谷歌搜索显示了很多简单的示例,如上面的代码(使用 MainLoop/g_destroy),但我找不到任何重新创建窗口的示例。我确实看到了有关对话框或消息框的内容,但这些不适合我的需要。我想在按钮上放置文本,并使用任意数量的按钮(所以我不想局限于是/否/取消,并且只有 3 个选项)。
更新 这是我能够使用的
# hide the main window, since I'm not using it
my $mw = Tkx::widget->new(".");
$mw->g_wm_withdraw();
# function to prompt the user to answer a question
# displays an arbitrary number of answers, each on its own button
sub prompt {
my $prompt = shift;
my $list_of_answers = shift;
# Note: the window name doesn't matter, as long as it's './something'
my $answer = Tkx::tk___dialog( "./mywindowpath", # window name
"Prompt", # window title
$prompt, # text to display
undef, # tk bmp library icon
undef, # default button
@$list_of_answers); # list of strings to use as buttons
return $answer;
}
# use the button to ask a question
my $index = prompt("Who was the best captain?",
[ "Kirk", "Picard", "Cisco", "Janeway", "Archer" ] );
Using Perl Tkx, I want to get some input from the user, close the window, and maybe do it again later. For user input, I'm just displaying some buttons, and the user gets to click on one of them. Here's what I have now:
sub prompt_user {
my $answer;
my $mw = Tkx::widget->new("."); ## the main window is unavailable the second time
$mw->g_wm_title("Window Title"); ## line 40
$mw->g_wm_minsize(300, 200);
my $label = $mw->new_label( -text => "Question to the user?");
$label->g_pack( -padx => 10, -pady => 10);
my $button1 = $mw->new_button(
-text => "Option One",
-command => sub { $answer = 0; $mw->g_destroy; },
);
$button1->g_pack( -padx => 10, -pady => 10);
my $button2 = $mw->new_button(
-text => "Option Two",
-command => sub { $answer = 1; $mw->g_destroy; },
);
$button2->g_pack( -padx => 10, -pady => 10);
Tkx::MainLoop(); ## This blocks until the main window is killed
return $answer;
}
So the user clicks on one of the buttons, the window closes, prompt_user() returns 0 or 1 (depending on which button the user clicked), and execution continues. Until I try to prompt the user again. Then I get an error:
can't invoke "wm" command: application has been destroyed at MyFile.pm line 40
I just want a way to put up a bunch of buttons, let the user click one, wait to see which one is clicked, and maybe do it again later. Is there a way I can wait for a response to the button click without destroying the main window? Maybe create a subwindow?
I'm new to using Tkx, and googling shows lots of simple examples like the above code (using MainLoop/g_destroy), but I couldn't find any examples of recreating windows. I did see stuff about a Dialog Box or Message Box, but those won't suit my needs. I want to put text on the buttons, and use an arbitrary number of buttons (so I don't want to be limited to yes/no/cancel, and only have 3 options).
Update
Here's what I was able to use
# hide the main window, since I'm not using it
my $mw = Tkx::widget->new(".");
$mw->g_wm_withdraw();
# function to prompt the user to answer a question
# displays an arbitrary number of answers, each on its own button
sub prompt {
my $prompt = shift;
my $list_of_answers = shift;
# Note: the window name doesn't matter, as long as it's './something'
my $answer = Tkx::tk___dialog( "./mywindowpath", # window name
"Prompt", # window title
$prompt, # text to display
undef, # tk bmp library icon
undef, # default button
@$list_of_answers); # list of strings to use as buttons
return $answer;
}
# use the button to ask a question
my $index = prompt("Who was the best captain?",
[ "Kirk", "Picard", "Cisco", "Janeway", "Archer" ] );
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我不太熟悉 Tkx,但 Tk 并不能很好地工作。一般来说,Tk 应用程序是异步的。您应该根据回调(有点像 JavaScript)重写您的应用程序。
基本上,这种逻辑:
应该重写为:
你的程序基本上不应该有主循环。相反,程序末尾对
Tkx::MainLoop
的调用将成为主循环,您应该通过处理事件来完成所有处理。话虽如此,有一些机制可以模拟阻塞。阅读
vwait
的文档。不过,我认为即使这也需要运行Tkx::MainLoop
,因此它不一定避免重构整个程序。关于如何创建和销毁窗口的问题,有两种解决方案:
使用主窗口(
.
),但最后不要销毁它。相反,隐藏它并摧毁它所有的孩子。然后您可以稍后通过取消隐藏来重用.
。隐藏
.
并且不使用它。相反,创建其他窗口(顶层)并使用它们。由于顶层是.
的子级,因此可以安全地销毁它们,而不会搞砸 Tk。I'm not really familiar with Tkx but Tk doesn't really work well that way. In general Tk applications are asynchronous. You should re-write your application in term of callbacks (kind of like javascript).
Basically, this kind of logic:
should be re-written to something like:
Your program should basically not have a main loop. Instead the call to
Tkx::MainLoop
at the end of your program becomes the main loop and you should do all processing by handling events.Having said that, there are some mechanisms available that emulates blocking. Read the documantation for
vwait
. Though, I think even that requires a runningTkx::MainLoop
so it does not necessarily avoid refactoring your whole program.On the question of how to create and destroy windows there are two solutions:
Use the main window (
.
) but don't destroy it at the end. Instead hide it and destroy all its children. You can then later reuse.
by unhiding it.Hide
.
and don't use it. Instead create other windows (toplevels) and use them. Since toplevels are children of.
they are safe to destroy without screwing up Tk.