2

我正在开发一个 Perl Tkx 应用程序,但无法获取有用/正确的错误消息。我正在尝试使用 croak,所以它会准确地告诉我失败的位置,但每次它发出 croak 时,它只会说错误位于“C:/Perl64/lib/Tkx.pm 第 347 行”。

我编写了一个非常简单的脚本来演示正在发生的事情:


#!/usr/bin/perl

use strict;
use warnings;

use Carp;
use Tkx;

my $mw = Tkx::widget->new(".");
my $b = $mw->new_button(
    -text => "Hello, world",
    -command => sub { croak; },
);
$b->g_pack;

Tkx::MainLoop();

当我运行此脚本并单击按钮时,我得到以下弹出框:

呱呱弹窗

如何让 Tkx 处理类似于在常规 Perl 脚本中使用 croak 的错误?有没有办法强制呱呱打印到 STDOUT 或 STDERR?


编辑:

Michael Carman 提出了一个很好的观点,即通常使用 GUI 控制台将被抑制,用户永远不会看到发送到 STDOUT 或 STDERR 的错误,但是如何找到一种方法来修改“应用程序错误”对话框中的文本以说点有用的?那可能吗?

文本不一定对最终用户有用,但至少应该是程序员可以理解的,这样当最终用户报告问题时,他们知道在哪里寻找问题。


编辑2:

好的,似乎使用dieorconfess代替croak会向“应用程序错误”窗口打印更有用的错误消息,但是一旦我单击“确定”,程序就会继续运行并且实际上并没有死。我怎样才能“杀死它直到它死去”并确保它保持死亡状态?


可能的解决方案:

这是基于迈克尔·卡曼(Michael Carman)的回应的可能解决方案。我稍微修改了一下,因为我认为 messageBox 看起来更好一点:)

#!/usr/bin/perl

use strict;
use warnings;

use Tkx;
use Carp;

my $mw = Tkx::widget->new(".");
my $b  = $mw->new_button(
    -text    => "Hello, world",
    -command => sub { die "uh-oh"; },
);
$b->g_pack;

Tkx::eval(<<'EOT');
proc bgerror {message} {
    tk_messageBox -title "Application Error" -message $message -icon error -type ok
    destroy .
}
EOT

Tkx::MainLoop();

到目前为止,这是最好的解决方案,但赏金还剩 5 天,所以请继续提供这些答案!

4

2 回答 2

4

croak从调用者的角度报告错误。用于die从代码的角度报告错误。

croak通常在编写模块时使用,以便您可以报告代码使用方式的问题。(例如,用于参数验证) GUI 编程是这种情况的一种镜像。您不是为其他人编写一个库来使用,而是将您的代码注入到一个由其他人编写的库中。当您将代码引用传递给-command调用者时,就会变成 Tkx,因此最好从代码的角度报告任何错误。

Tkx 捕获回调中的致命错误并通过您看到的“应用程序错误”对话框报告它们。GUI 应用程序与控制台断开连接的情况并不少见,这意味着 STDOUT 和 STDERR 已关闭,写入它们的任何消息都将丢失。如果没有对话框,您的应用程序将简单地消失,用户将不知道原因。

例如

use Tkx;

my $mw = Tkx::widget->new(".");
my $b  = $mw->new_button(
    -text    => "Hello, world",
    -command => sub { die "uh-oh" },
);
$b->g_pack;

Tkx::MainLoop();

当我运行它(并按下按钮)时,对话框消息是

uh-oh at c:\temp\foo.pl line 9.

如果您需要完全控制 Tk 如何处理错误,您可以覆盖默认处理程序。问题是你必须在 Tcl 中完成。这是创建带有错误消息的对话框并在应用程序关闭时退出应用程序的最小版本。

Tkx::eval(<<'EOT');
proc bgerror {message} {
    tk_dialog .error "Error" $message [] 0 Close
    destroy .
}
EOT
于 2015-01-06T18:57:03.503 回答
0

以下脚本将错误消息打印到 STDOUT:

use strict;
use warnings;

use Carp;
use Tkx;

my $mw = Tkx::widget->new(".");
my $b = $mw->new_button(
    -text => "Hello, world",
    -command => \&error_test,
);
$b->g_pack;

Tkx::MainLoop();

sub error_test {
    eval {
        confess;
    };
    print "$@\n";
}

输出:

 at ./p.pl line 20.
    eval {...} called at ./p.pl line 19
    main::error_test() called at /home/hakon/perl5/lib/perl5/Tkx.pm line 347
    eval {...} called at /home/hakon/perl5/lib/perl5/Tkx.pm line 347
    Tkx::i::DoOneEvent(0) called at /home/hakon/perl5/lib/perl5/Tkx.pm line 56
    Tkx::MainLoop() called at ./p.pl line 16

编辑

以下可用于将错误消息打印到 Tk GUI 的文本字段:

use strict;
use warnings;
use Carp;
use Tkx;

my $mw = Tkx::widget->new(".");
my $b = $mw->new_button(
    -text => "Hello, world",
    -command => \&error_test,
);
$b->g_grid(-column => 0, -row => 0);
my $text = $mw->new_tk__text(-width => 100, -height => 30);
$text->g_grid(-column => 0, -row => 1);

Tkx::MainLoop();

sub error_test {
    eval {
        confess;
    };
    print "$@\n";
    $text->insert("end", "$@\n");
}

编辑

要在显示错误消息后销毁窗口,您可以使用:

use strict;
use warnings;
use Carp;
use Tkx;

my $mw = Tkx::widget->new(".");
my $b = $mw->new_button(
    -text => "Hello, world",
    -command => \&error_test,
);
$b->g_pack;

Tkx::MainLoop();

sub error_test {
    eval {
        confess;
    };
    Tkx::tk___messageBox(-message => "$@");
    $mw->g_destroy;
}
于 2015-01-06T17:10:33.163 回答