The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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 that import Qt::app will create $Qt::app if it does not already exist.

The reason we import instead of use Qt::app is to allow the creation of modules which subclass Qt::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 to resize. The resize function saw that it was passed one Qt::Size instead of two numbers, and called the resize function that accepts const 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 call continue 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(const QString &))

'member(const \QString)'

SLOT(member(const QString *))

'member(const QString)'

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>