wxPerl:Perl的另一个GUI
wxPerl是什么?
如果你不仅仅使用Perl创建CGI脚本,你迟早需要为你的应用程序创建某种前端。你可能使用Curses库,但如果你需要一个漂亮的GUI,你可能会使用Tk。Tk绝对是Perl可用的最稳定、文档最完善和最广泛使用的GUI。然而,越来越多的人正在使用其他GUI,如Gtk和Win32::GUI。这主要是因为Tk没有最流畅的界面,与人们使用的环境完全匹配。Tk有一个类似Motif的界面,而Gnome用户会希望使用Gtk的外观,Windows用户会希望使用Windows的外观。当然,当你在Win32机器上使用Tk时,它看起来更像Windows,当你在Gnome下运行时,它看起来更像Gtk,但它仍然是一个不同的界面。
最近,我发现了一个新的Perl GUI。不,不是未维护的Qt和FWTK模块,而是wxPerl,它由Mattia Barbon开发。wxPerl是wxWindows(http://www.wxwindows.org)的Perl绑定,wxWindows是一个针对C++的跨平台GUI库。当我提到跨平台时,我确实是指跨平台:有Windows、Gtk、Motif和Macintosh的wxWindows。wxWindows自1992年以来一直在开发,版本2(当前版本)自1997年以来一直在开发。这不是从某个平台移植过来的GUI,它的根源在于某个平台:wx代表Windows和X——它被设计成跨平台。而且,它已经存在了一段时间,因此有机会成为一个稳定的产物。
wxPerl的方法
wxPerl拥有一套非常丰富的标准小部件(在Wx术语中称为“控件”),从简单的按钮到复杂的HTML窗口和字体对话框。这使得它成为创建功能齐全的应用程序的优秀GUI。所有需要的控件都“现成可用”,如果你仍然想要创建一个复杂的小部件,那么你可以轻松地做到这一点。这与Tk等工具有很大的不同,例如,在Tk中定义新小部件非常痛苦。
编写wxPerl的方式不同。它不比Tk或Gtk接口更好或更差,但它是一种完全不同的方法。主要原因是库的来源,它是一个C++库。它不太像Perl,但它更面向对象。
当你想创建一个新的wxPerl应用程序时,你开始创建一个新的从Wx::App
继承的类。这个子类必须至少有一个名为OnInit
的方法,它定义了应用程序使用的窗口(在Wx术语中称为“Frames”)。如果你想有一个默认的窗口,你可以使用默认的类。如果你想向窗口添加控件,你可以从默认的窗口类派生,并添加控件。
这是一个比Tk和Gtk使用的方法更面向对象的方法。但不幸的是,它缺少Tk使用的命名参数方法,这使得Tk看起来更像Perl。
目前,wxPerl有一个很大的缺点:它的文档非常差。也就是说:wxWindows有很多文档。如果你努力足够,你可以使用这些文档来为wxPerl编写文档。但这需要相当多的努力。但阅读这个wxPerl教程的第一部分后,你可能会感兴趣,并找到自己的wxPerl方式。
你好,世界!
像每个教程一样,这个教程也有自己的“Hello World!”应用来入门并创建第一个应用程序。查看这个脚本
=1= #!/usr/bin/perl -w
=2= use strict;
=3= use Wx;
=4=
=5= ###########################################################
=6= #
=7= # Define our HelloWorld class that extends Wx::App
=8= #
=9= package HelloWorld;
=10=
=11= use base qw(Wx::App); # Inherit from Wx::App
=12=
=13= sub OnInit
=14= # Every application has its own OnInit method that will
=15= # be called when the constructor is called.
=16= {
=17= my $self = shift;
=18= my $frame = Wx::Frame->new( undef, # Parent window
=19= -1, # Window id
=20= 'Hello World', # Title
=21= [1,1], # position X, Y
=22= [200, 150] # size X, Y
=23= );
=24= $self->SetTopWindow($frame); # Define the toplevel window
=25= $frame->Show(1); # Show the frame
=26= }
=27=
=28= ###########################################################
=29= #
=30= # The main program
=31= #
=32= package main;
=33=
=34= my $wxobj = HelloWorld->new(); # New HelloWorld application
=35= $wxobj->MainLoop;
就像每个优秀的Perl应用程序一样,这个程序也是从-w
和use strict
开始的。然后我们使用use Wx
,这是主要的wxPerl模块。在第9行,我们开始创建名为HelloWorld的包,它(第11行)继承自Wx::App
,就像所有的wxPerl应用程序一样。这个新应用程序现在需要有一个OnInit
方法来定义框架(第18行)并定义哪个框架是顶层窗口(第24行)。最后,我们调用Show
方法(第25行),这使得创建的$frame
可见。
框架是通过几个参数创建的。第一个是父窗口。如果我们创建两个框架,那么第二个框架可以通过使用$frame
作为第二个框架构造函数的第一个参数,被指定为第一个框架的子窗口。但在我们的例子中,我们只有一个窗口,所以父窗口是undef
。
在这个时候,我们不关心第二个参数(窗口ID,-1表示默认值),但第三个和第四个参数更有趣。它们分别定义了屏幕上的位置和大小。这些参数作为数组引用传递,也可以分别是预定义的wxDefaultPosition
和wxDefaultSize
。
在定义了HelloWorld包之后,我们必须通过定义主包(第32行)来创建主程序。这个包从我们定义的HelloWorld包创建一个新的Wx对象(第34行),然后调用其上的MainLoop
方法(第35行)。
主循环是唯一类似于Tk和Gtk GUIs的东西。定义新的Wx::App
子类的方法完全不同。
当你运行这个第一个示例时,它看起来像这样
填充空窗口。
所以这是一个简单的例子,创建了一个标题为“Hello World!”的空窗口。不是很令人兴奋,对吧?现在我们想在窗口中看到更多的控件。让我们看看如何添加一个无用的按钮和屏幕上的文本。
在我们能够在窗口内创建一些东西之前,需要了解一些关于wxPerl应用程序(和wxWindows应用程序)一般工作原理的背景信息。正如你在前面的例子中所看到的,要创建一个应用程序,我们需要从Wx::App
派生。要创建我们自己的内容,即在
一个框架中,我们首先需要从Wx::Frame
派生,并在新创建的Wx::App
子类的OnInit
方法中创建该派生框架的实例。
要将控件放入我们派生的框架中,你首先需要在其中创建一个Panel,因为控件只能放置在Wx::Panel
的实例上。为了能够访问和修改Panel以及你希望在框架中放置的其他项目的属性,你需要将这些项目作为框架的对象。
这些都是很多(可能)令人困惑的信息。让我们看看这个例子
=1= #!/usr/bin/perl -w
=2= use strict;
=3= use Wx;
=4=
=5= ###########################################################
=6= #
=7= # Extend the Frame class to our needs
=8= #
=9= package MyFrame;
=10=
=11= use base qw(Wx::Frame); # Inherit from Wx::Frame
=12=
=13= sub new
=14= {
=15= my $class = shift;
=16= my $self = $class->SUPER::new(@_); # call the superclass' constructor
=17=
=18= # Then define a Panel to put the button on
=19= my $panel = Wx::Panel->new( $self, # parent
=20= -1 # id
=21= );
=22= $self->{txt} = Wx::StaticText->new( $panel, # parent
=23= 1, # id
=24= "A buttonexample.", # label
=25= [50, 15] # position
=26= );
=27= $self->{btn} = Wx::Button->new( $panel, # parent
=28= 1, # id
=29= ">>> Press me <<<", # label
=30= [50,50] # position
=31= );
=32= return $self;
=33= }
=34=
=35= ###########################################################
=36= #
=37= # Define our ButtonApp class that extends Wx::App
=38= #
=39= package ButtonApp;
=40=
=41= use base qw(Wx::App); # Inherit from Wx::App
=42=
=43= sub OnInit
=44= {
=45= my $self = shift;
=46= my $frame = MyFrame->new( undef, # Parent window
=47= -1, # Window id
=48= 'Button example', # Title
=49= [1,1], # position X, Y
=50= [200, 150] # size X, Y
=51= );
=52= $self->SetTopWindow($frame); # Define the toplevel window
=53= $frame->Show(1); # Show the frame
=54= }
=55=
=56= ###########################################################
=57= #
=58= # The main program
=59= #
=60= package main;
=61=
=62= my $wxobj = ButtonApp->new(); # New ButtonApp application
=63= $wxobj->MainLoop;
你可以看到,我们再次定义了一个名为ButtonApp的Wx::App
的子类(第39行)。但这次创建的框架不是Wx::Frame
实例,而是一个MyFrame
实例。这个MyFrame
是我们定义在第9行的新的Wx::Frame
子类。
基本上,我们只需要重写Wx::Frame
的new
构造函数。我们想要扩展Wx::Frame
类,所以我们的构造函数首先调用其SUPER
类构造函数,然后在之后定义其扩展。我们的扩展包括一个新的Panel
(第19行),它上面有一个StaticText
(第22行)和一个Button
(第27行)。就像原始的Wx::Frame
类一样,我们的构造函数也返回$self
(第32行),这完成了MyFrame
的定义。
正如您所看到的,我们将Button
和StaticText
对象定义为MyFrame
的属性。现在这并不是必需的,但如果我们想给这个脚本添加一些交互,这在下一个例子中将会做,我们需要访问这些对象。由于它们现在被存储为MyFrame
的属性,因此无论我们在哪里有MyFrame
对象的访问权限,都可以访问Button
和StaticText
。所以这里只是存储方式的问题,因为我们实际上在这个例子中没有用它做任何事情。
当你运行这个例子时,它会看起来像这样
添加交互
但它做了什么?嗯……它还没有做任何事情。但是,没有交互的GUI应用程序是毫无用处的。所以我们将实现一些交互。我在上一个例子中已经解释过:如果你想改变定义对象的属性,那么你必须将它们定义为Frame对象的属性。这样你就可以始终访问任何对象的任何属性,无论是StaticText、Button还是Menu。
考虑以下代码
=1= #!/usr/bin/perl -w
=2= use strict;
=3= use Wx;
=4=
=5= ###########################################################
=6= #
=7= # Extend the Frame class to our needs
=8= #
=9= package MyFrame;
=10=
=11= use Wx::Event qw( EVT_BUTTON );
=12=
=13= use base qw/Wx::Frame/; # Inherit from Wx::Frame
=14=
=15= sub new
=16= {
=17= my $class = shift;
=18=
=19= my $self = $class->SUPER::new(@_); # call the superclass' constructor
=20=
=21= # Then define a Panel to put the button on
=22= my $panel = Wx::Panel->new( $self, # parent
=23= -1 # id
=24= );
=25=
=26= $self->{txt} = Wx::StaticText->new( $panel, # parent
=27= 1, # id
=28= "A buttonexample.", # label
=29= [50, 15] # position
=30= );
=31=
=32= my $BTNID = 1; # store the id of the button in $BTNID
=33=
=34= $self->{btn} = Wx::Button->new( $panel, # parent
=35= $BTNID, # ButtonID
=36= ">>> Press me <<<", # label
=37= [50,50] # position
=38= );
=39=
=40= EVT_BUTTON( $self, # Object to bind to
=41= $BTNID, # ButtonID
=42= \&ButtonClicked # Subroutine to execute
=43= );
=44=
=45= return $self;
=46= }
=47=
=48= sub ButtonClicked
=49= {
=50= my( $self, $event ) = @_;
=51= # Change the contents of $self->{txt}
=52= $self->{txt}->SetLabel("The button was clicked!");
=53= }
=54=
=55= ###########################################################
=56= #
=57= # Define our ButtonApp2 class that extends Wx::App
=58= #
=59= package ButtonApp2;
=60=
=61= use base qw(Wx::App); # Inherit from Wx::App
=62=
=63= sub OnInit
=64= {
=65= my $self = shift;
=66= my $frame = MyFrame->new( undef, # Parent window
=67= -1, # Window id
=68= 'Button interaction example', # Title
=69= [1,1], # position X, Y
=70= [200, 150] # size X, Y
=71= );
=72= $self->SetTopWindow($frame); # Define the toplevel window
=73= $frame->Show(1); # Show the frame
=74= }
=75=
=76= ###########################################################
=77= #
=78= # The main program
=79= #
=80= package main;
=81=
=82= my $wxobj = ButtonApp2->new(); # New ButtonApp application
=83= $wxobj->MainLoop;
这个例子基本上与上一个例子相同,但主要区别是添加了一些交互。在上一个例子中,当你尝试点击按钮时,什么也没有发生。这次点击按钮将改变StaticText
对象的文本。让我们看看代码中有什么变化。
首先,我们使用Wx::Event
并导入EVT_BUTTON
。EVT_BUTTON
是按钮事件的处理器子程序。有许多其他事件处理器可用,但我们现在只需要这个。
在第31行,我引入了一个变量来保存按钮ID,名为$BTNID
。我仍然可以使用在上一个例子中使用的硬编码的1
,但使用这个变量可以使我引用的地方更清晰。例如,它在第40行的EVT_BUTTON
调用中是必要的。这是我们定义当按钮被点击时要做什么的地方。它接受$self
对象、$BTNID
和一个子程序引用作为参数。在第48行我们定义了那个子程序。
wxPerl中的事件回调始终接受两个参数:第一个是它所属的对象(导致事件发生),第二个是事件本身。在我们的例子中,我们不需要第二个参数,但我们确实需要第一个,因为我们想改变StaticText
对象的文本。这是我们看到将StaticText
对象定义为MyFrame
对象属性的用途的地方。我们现在可以简单地调用该属性的SetLabel
方法(第52行)。
在我们按下按钮之前,窗口将看起来与上一个例子中的窗口一样。在我们按下按钮之后,应用程序窗口将看起来像这样
结论
我展示了一些wxPerl的工作方式。更确切地说,我展示了如何使用wxPerl。很明显,这与其他GUI的方式不同。我承认,一开始我自己也认为这是一种不自然的Perl编程方式,更不用说Perl GUI编程了。但经过一些练习,我有一种感觉,这实际上比Tk或Gtk使用的方式更自然。当然,这完全取决于个人喜好。而且,个人喜好是无法衡量的。
在下一个wxPerl教程中,我将向您展示如何创建菜单,展示更多的事件处理,我甚至还会添加一些更高级的控件。但目标将是相同的:向您展示wxPerl隐藏的美丽之处!
标签
反馈
这篇文章有什么问题吗?请在GitHub上打开一个问题或拉取请求来帮助我们。