NAME
Creating a PerlQt application - The Perl part
DESCRIPTION
PerlQt is meant to be a very direct mapping of Qt methods to PerlQt. Programming is more than methods, however. This document will show the basic differences you will need to be aware of as you translate programming techniques from C++ Qt to PerlQt.
BASICS
Including PerlQt in your application
Including PerlQt is very simple. The top of almost every PerlQt application will be the same.
#!/usr/bin/perl -w
use Qt;
import Qt::app;
- #!/usr/bin/perl -w
-
This the #! business which starts Perl on your system. I always recommend using the -w option, because relying on PerlQt to find your errors will leave you stranded. If you intend to distribute your app, make sure it uses /usr/bin/perl as the path for perl.
- use Qt;
-
This causes the PerlQt module to be loaded and the Qt library to be linked to Perl. From that line on, you can call any PerlQt method or access any PerlQt constant.
- import Qt::app;
-
This line imports the $app variable into your namespace. The $app variable is equivalent to the
qApp
variable in Qt. In PerlQt,qApp
is stored as$Qt::app
.The reason we
import Qt::app
instead of using $Qt::app directly is thatimport Qt::app
will create $Qt::app if it does not already exist.The reason we
import
instead ofuse Qt::app
is to allow the creation of modules which subclassQt::Application
and create $Qt::app, without worrying about coding order.If you want to create your own
$Qt::app
, this is how you do it.use Qt; BEGIN { $Qt::app = Qt::Application->new(\@ARGV) } import Qt::app;
Creating objects
Now that we have included PerlQt into our program, we can call any Qt method we want. Since most of the methods require an object, it would help if we created one.
$widget = Qt::Widget->new;
Please note that every class in Qt has had it's initial Q replaced with Qt:: (QWidget
=> Qt::Widget
).
In Perl, new
is a method, and Qt::Widget
, the class, can be treated as an object. It's weird, but it works. However, if you're inclined, you are also allowed to use more C++-compatible syntax.
$widget = new Qt::Widget;
I use the first way just to show a good example, since the second can lead to ambiguous function-calls under bizzare circumstances.
Calling methods
At this point, we want to call methods on our $widget. I'm going to resize this widget a few times, demonstrating the old perl motto that There Is More Than One Way To Do It.
$widget->resize(100, 100);
resize $widget 100, 100; # Yes, this works
$widget->resize(Qt::Size->new(100, 100));
$widget->resize(new Qt::Size(100, 100));
I chose this example in particular because it demonstrates a few important principals.
- $widget->resize(100, 100);
-
This is a standard method-call in perl. Parentheses are required on methods called with
->
that are called with arguments. On methods without arguments, no parentheses are required. That allows you to access object attributes easily with code like$widget->size
instead of$widget->size()
if that strikes your fancy. - resize $widget 100, 100;
-
I admit this is weird, and could start looking like tcl after a while. It's interesting and perfectly valid, though. It would also make for an good graphical IRC client interface.
- $widget->resize(Qt::Size->new(100, 100));
-
If you look at the Qt documentation,
QWidget::resize
has two prototypes.virtual void resize ( int w, int h ) void resize ( const QSize & )
The
QSize
class is supported in PerlQt. We created a new Qt::Size object, and passed it toresize
. Theresize
function saw that it was passed oneQt::Size
instead of two numbers, and called theresize
function that acceptsconst QSize &
. - $widget->resize(new Qt::Size(100, 100));
-
You can also use a more C++-like syntax. In C++ however, you would never allocate a new QSize object and pass it to
resize
since that would leak memory. In PerlQt, that object will be deleted automatically when the function returns. See "Object destruction"
SUBCLASSING
In order to add functionality to your program, it is often a good idea to subclass a Qt widget. If you want to create a custom dialog-based program, you would subclass Qt::Dialog
. If you wanted to write PerlExcel, you would subclass Qt::TableView
. By subclassing, you gain all the functionality of the class you inherit, and can change that functionality by overriding virtual methods from that class.
Creating a class
In Perl, classes are known as packages, because they are declared with the keyword package
. In reality, package
creates a namespace into which you place your methods, and it is through some perl magic that it gains object-oriented status. I prefer calling them classes nonetheless.
There are two ways to define your class. You can define it in its own file "MyClass.pm" and use
it in your application, or you can define it in the main application file.
This is a class defined in MyClass.pm which inherits Qt::Widget:
package MyClass;
use Qt;
@ISA = qw(Qt::Widget);
# your code here
1;
The 1
at the end of the file is required by Perl, and must be at the end of the file. To use that class from your application, add use MyClass
to your application after use Qt
.
This is a class defined in the main application file which inherits Qt::Dialog:
package MyClass;
@ISA = qw(Qt::Dialog);
# your code here
package main;
The package main
does the reverse of package MyClass
and puts you into the global namespace with normal variable access again.
Note: variables declared at the class level with
my()
will be visible from all classes in a file, but not outside a file.
That was it, you now have a class called MyClass
into which you can write some code.
Constructor
In Perl, the constructor is a method called new
. There is some tradition for using $self
in Perl as the equivalent to this
in C++, and that's what I will demonstrate. This is not required, and you are free to call it $this
, $that
, or $my_special_friend
.
sub new {
my $self = shift->SUPER::new(@_);
# your constructor code here
return $self;
}
The first line of the constructor allows PerlQt to create your object. The one I've shown you passes all the arguments to your constructor to the superclass constructor. If you want to hide some of those arguments from your superclass and use them yourself, this is what you need to do:
sub new {
my $class = shift;
my $argument = shift; # repeat for each argument
my $self = $class->SUPER::new($class, @_);
# your constructor code here
return $self;
}
If you don't want to pass the arguments passed to you to your superclass, omit @_
and replace it with your own arguments. Every constructor must return $self
in order to work. The variable $self
and the function &new
have no special signifigance to Perl, so there is no reason for Perl to do that for you. Your constructor must return the object it created.
If you want to create a destructor, create a method called DESTROY, and put your destruction code in it. You usually don't need a destructor, because Perl and Qt automatically deallocate everything associated with your object when it's destroyed.
The object
When you call &SUPER::new
, you are calling your superclass constructor. If you use multiple-inheritance, it will call $ISA[0]->new
. Your superclass constructor will create the C++ version of itself, and return a blessed perl hash reference. You cannot inherit two Qt classes properly, since $self
can only represent one Qt object at a time.
All objects in Perl are blessed references. Blessed means it represents a package and methods can be called on it. References are Perl pointers, and can refer to a variable or function. In this case, it refers to a perl associated list, or hash. This allows us to have named member-variables.
sub new {
my $self = shift->SUPER::new(@_);
$self->{button} = Qt::PushButton->new("OK", $self);
return $self;
}
We have saved a pushbutton in $self->{button}
.
Virtual methods
A large part of the Qt API involves overriding virtual methods. When a widget is resized or hidden, Qt calls a virtual method on your object. If you've reimplemented that virtual method, you get the opportunity to respond to that event.
PerlQt allows you to override Qt virtual functions. The resizeEvent
method is called whenever a widget is resized.
sub resizeEvent {
my $self = shift;
my $e = shift;
printf "Old size: %s, %s\n",
$e->oldSize->x, $e->oldSize->y;
printf "Current size: %s, %s\n",
$self->size->x, $self->size->y;
$self->SUPER::resizeEvent($e);
}
That reimplementation of resizeEvent
prints out the old widget size and the current widget size. It also calls the superclass resizeEvent
since our implementation didn't actually do anything useful.
Declaring signals and slots
The Qt callback mechanism is called signals and slots. It is a way for objects to communicate with each other. When a button is pushed, it emits a signal called clicked()
. If you want to know when the button is pushed, you create a slot and connect the clicked()
signal to it. To see how connect()
works in PerlQt, read "Signals and slots".
Slots are just normal methods which have been registered with Qt. Signals are methods created by PerlQt and given the name you specify. When you emit
(call) that signal, every slot connected to it gets called.
When you create a class, you will need to create slots to pick up signals such as clicked()
from Qt::PushButton, and returnPressed()
from Qt::LineEdit. You can also choose to emit signals, so other classes can be told what you are doing.
use Qt::slots 'buttonWasClicked()',
'returnWasPressed()';
use Qt::signals 'somethingHappened()';
That declares two slots, which you can implement in your class, and a signal which can be connected to slots in C++ or Perl.
sub buttonWasClicked {
my $self = shift; # you can use $self
# lets emit a signal
emit $self->somethingHappened;
}
sub returnWasPressed {
my $self = shift; # you can use $self
# your code here
emit $self->somethingHappened;
}
The emit
keyword does nothing, it's syntactic sugar to make it clear you are emitting a signal.
Signals and slots can have arguments. PerlQt limits signals and slots created by Perl to three arguments. See "Signals and slots".
WORKING IN PERL
Right off the bat, Perl eliminates pointers and references. That makes for a great deal of simplification. Also, Perl has garbage collection which means it will automatically free memory for a variable when it's no-longer needed. That eliminates the need for destructors usually.
Constants
Global constants in C++ Qt, like AlignLeft
, are accessed as Qt::AlignLeft
in PerlQt. They are constant functions, not variables. Class constants like QScrollBar::Vertical
are predictably accessed as Qt::ScrollBar::Vertical
in PerlQt.
Please note that if you inherit a class that has a constant, you must still use its fully-qualified name. Perl does not allow function inheritance like that. Of course, you could always try calling it as a method and use method inheritance ($scrollbar->Vertical
).
NULL pointer
PerlQt supports two-way NULL
pointer conversion to undef
. If you pass undef
to a function accepting a pointer argument, that function receives a NULL
pointer. If a function returns a NULL
pointer, Perl receives undef
.
Object destruction
There are several types of objects in PerlQt when it comes to object destruction.
There are the normal objects, which need to be destroyed when their Perl variable is destroyed.
Widgets are destroyed by their parent. I call this behavior suicidal, because these objects will delete themselves even if I lose track of them. The perl variable for these objects can be destroyed, but the C++ object will still be there. It is important that these objects have been given a parent or have been added to an object which will destroy them from C++.
Virtual objects are from classes which have been implemented in PerlQt with virtual functions. These objects keep a reference to the Perl object, so that even if you, the programmer, have destroyed every reference to the object you can find, PerlQt still keeps one which you cannot get rid of. A virtual object must either delete itself, or be suicidal in order for it to be destroyed.
Every object in PerlQt has three methods to handle object destruction.
- $object->delete
-
This calls the C++ delete operator on the object pointer, and causes immediate object destruction.
- $object->continue
-
An object that would normally be deleted at the end of a method can be told to stick around with
continue
. If you pass an object created in Perl to a method which deletes that variable, you must callcontinue
to stop PerlQt from deleting it. - $object->break
-
If you create a suicidal object like a dialog box, and don't give it a parent which will destroy it, you can make it get deleted when Perl destroys its object with
break
.
Signals and slots
PerlQt allows programmers to create signals and slots in Perl which are usable from Perl and C++. If your subclass inherits Qt::Object
, PerlQt will automatically do the Q_OBJECT
voodoo which Qt does, and you don't have to worry about any of that.
The SIGNAL() and SLOT() macros in C++ are replaced with simple quotes in PerlQt.
In C++: QObject::connect(sender, SIGNAL(didSomething(int)), receiver, SLOT(sawSomething(int)));
In PerlQt: $receiver->connect($sender, 'didSomething(int)', 'sawSomething(int)');
Now, there are a multitude of argument-types which can be passed through a signal, and here is a short translation
- SLOT(member(Object))
-
Sorry, you cannot connect to a signal or slot that sends an object by value.
- SLOT(member(Object *))
-
'member(Object)'
- SLOT(member(const Object *))
-
'member(const Object)'
- SLOT(member(Object &))
-
'member(\Object)'
- SLOT(member(const Object &))
-
'member(const \Object)'
- SLOT(member(int, long, double))
-
'member(int, long, double)'
- SLOT(member(const char *))
-
'member(const string)' OR 'member(cstring)'
- SLOT(member(SV *))
-
'member($)'
Yes, you can pass perl scalar variables as signal/slot arguments.
These are the functions you use for signals and slots.
- use signals LIST
-
Declare all of the signals in LIST. Each signal in LIST will cause a function to be imported into your namespace by that name which calls all the slots connected to it. Each signal name can be listed only once, multiple prototypes are not supported.
- use slots LIST
-
Declare all of the slots in LIST. Each slot is a normal method defined in Perl. When it is declared as a slot, you can
connect()
signals from Qt or Perl to it. Each slot name can be listed only once, multiple prototypes are not supported. - $receiver->connect($sender, $signal, $slot)
-
The
connect()
method from Qt works the same way in Perl. Just replace the SIGNAL() and SLOT() macros from Qt with quotes.In Qt terminology,
$sender
emits$signal
,$receiver
has$slot
. - emit $sender->signal()
-
The emit keyword is imported into your namespace whenever you
use signals
. It is provided for syntactic sugar to identify that you aren't calling a normal function, but are emitting a signal.
SEE ALSO
Qt has a massive amount of documentation which I will not attempt to duplicate. Read it.
http://www.troll.no/
Perl also has some great manual pages describing object-oriented practices which are particularly relevant.
perlobj Perl objects
perltoot Perl OO tutorial
Included with PerlQt is alot of sample code. Look in the tutorials and examples directories of your PerlQt directory.
BUGS
Much of PerlQt is untested. Signals and slots are limited to 3 arguments. Alot of datatypes are still not supported.
AUTHOR
Ashley Winters <jql@accessone.com>