July 2012 Archives

Windows in Prima

Windows are the main element in any Prima application. Windows belong to the Prima hierarchy, but at this time we are interested on windows simply being windows.

Prima offers two basic types of windows: the MainWindow and the Window. The main difference is that destroying a MainWindow will destroy the application, causing the closing of any related windows, dialogs, while closing a Window does not destroy the application.

The creation of both types of window requires some minimal setting. Many others can be used to get the look and feel and the behavior intended.

The settings are of two types:

  • Parameters indicate properties of the new window, as size, the text to be shown on top, the menu to be shown, color of the canvas, status of windows as e.g. maximized or minimized, border colors, and so on
  • Events indicate the way the window would react on events. The one almost always present is the paint event, which shows the operations that are performed when the window is created and then painted

So to create a Window or a MainWindows, we could:

use Prima qw(Application MsgBox);

my $mw->Prima::MainWindow->create(
        #Parameters
        text => "the main window of the application",
        size => [100,100],
        windowState => ws::Maximized,
        menuItems => [[ "~File" => [
                        [],# division line
                        [ "E~xit" => sub {$::application -> destroy;}    ]
                    ]]],
        # events
        onPaint => sub {
                    my ($self, $canvas) = @_;
                    $canvas-> clear;
                    $canvas-> color(cl::Red);
                    $canvas-> line( 0, 0, 800, 800);
            },
        onSize => sub {
                    message "Window Resized";
            },
        onMouseClick => sub {
                    message "Mouse Clicked";
            },
);

Changing the Prima::MainWindow to Prima::Window, we could create a window that does not propagate to others the destroy, if the case.

Parameters and events are set using the pairs shown. For the event the pair is the

onEvent => sub { 
        #code here
        }

So, when you will be looking in the reference material what are the supported events, you will find a list and will know that for each Event the corresponding window creation event manager will be onEvent.

As we see already with this small snippet, code can become quite colored, as we have to put the subroutines needed to manage events in the body of window creation.

In real world this can become a problem, mainly when a need to make some change occurs later.

As we will later remove the direct definition of a menu, we can make something similar with event management to simplify and decouple the code.

We could put the action onEvents in a separate named subroutine, and then use the name in windows creation.

The point is that this works only if the window is created in a package as the derived window. In this way, there is no issue in overloading the name and in having clear who is the owner of the subroutine.

So this would not work:

use Prima qw(Application MsgBox);

sub Exit { $::application->destroy;}

my $mw->Prima::MainWindow->create(
        #Parameters
        text => "the main window of the application",
        size => [100,100],
        windowState => ws::Maximized,
        menuItems => [[ "~File" => [
                        [],# division line
                        ########## Named Function Exit#######
                        [ "E~xit" => "Exit"    ]
                    ]]],
        # events
        onPaint => sub {
                    my ($self, $canvas) = @_;
                    $canvas-> clear;
                    $canvas-> color(cl::Red);
                    $canvas-> line( 0, 0, 800, 800);
            },
        onSize => sub {
                    message "Window Resized";
            },
        onMouseClick => sub {
                    message "Mouse Clicked"
            },
);

while this

package MyMainWindow;
use Prima qw(Application MsgBox);
use vars qw(@ISA);
@ISA = qw(Prima::MainWindow);

sub Exit { $::application->destroy;}

#######The window is created as MyMainWindow
my $mw->myMainWindow->create(
        #Parameters
        text => "the main window of the application",
        size => [100,100],
        windowState => ws::Maximized,
        menuItems => [[ "~File" => [
                        [],# division line
    ##########This time the Exit is invoked and the application exits. 
                        [ "E~xit" => "Exit"    ]
                    ]]],
        # events
        onPaint => sub {
                    my ($self, $canvas) = @_;
                    $canvas-> clear;
                    $canvas-> color(cl::Red);
                    $canvas-> line( 0, 0, 800, 800);
            );,
        onSize => sub {
                    message "Window Resized";
            },
        onMouseClick => sub {
                    message "Mouse Clicked"
            },
);

would work.

Object Oriented Learning of Prima

Object orientation is a great benefit for programming but also a key for learning.

Prima offers a very large set of objects to the needs of development. As the human brain can group together a quite limited number of objects (four to seven depending on individuals), having a large number of objects can make the whole stuff unmanageable. That is the reason that makes our brain starting with one topic, then a first level of some different groups of things, and then puts at the bottom of each one, the objects that can be a kind of that. This is the way we can manage large sets of objects, buy putting them in a hierarchy. This way the mind works inspired the pyramid principle by B. Minto, as a way to produce documents while validating the ideas.

So, to get off of the numerosity that affects Prima as any other real world tool, we have to depict what type of objects we have, at first.

Prima deals with graphics, so the first decomposition is about the objects that are concerned in the process of building a Prima application:

  • Windows are the main objects we deal in Prima, the ones we expect to see starting an application;
  • Menus are used to give the user the ability to use the application in some useful way, putting the application functionalities available to the user;
  • Widgets add many ways for the user to interact with the application and for the developer to give back results to the user. Examples of widgets are the already seen combo box, the scroll bar, a button;
  • Standard dialogs are used to perform some common tasks as opening a file, o putting a message on the screen. In this sense they implement a single functionality, generally using under the hood, more than a single widget.
  • Images and shapes also play an important role in Prima; A part of Prima deals with image loading and management and with direct production of drawing.

I would remark again that menus are a separate concept and that what is seen on screen is combination of a window and a menu.

Coming back to our pyramid, in Prima there are about 30 widgets and 10 standard dialogs, but actually that doesn't matter, knowing the common way they are used in an application as widgets or standard dialogs. We would only further expect some specific parameter or event coming naturally from the specific function of the particular standard dialog or widget.

A brief introduction to Prima

Prima is a graphical toolkit actually developed and maintained by Dmitry Karasik, now at version 1.34. If offers a large set of feature that makes it a good choice for anyone looking for an event driven environment.

Prima basic concept is that of building an application as a process of creating windows, adding any needed menus, childs, images, properties, and relationships and then starting the whole.

The basic use directives to use Prima are:

use Prima;
use Prima::Application;
use Prima::ComboBox;

and so on or using the qw syntax:

use Prima qw (Application ComboBox);

When we have available the modules we could start creating a window that will be the main window of our application.

$mw = Prima::MainWindow->create (
        # here there are some options, mainly that more common:
        size => [400,400],
        menuItems => [[ "~File" => [
        [],# division line
        [ "E~xit" => "Exit"    ]
        ]]],
        # events
        onPaint => sub {
        my ($self, $canvas) = @_;
        $canvas-> clear;
        $canvas-> color(cl::Red);
        $canvas-> line( 0, 0, 800, 800);
        );

After we could add an object to the created window $mw using the insert:

$mw->insert('ComboBox',
        name => 'ComboBox',
        size => [ 100, 100],
       );`

and after this work we run the:

run Prima;

to get all built and executed.

Options used in the main window for this conceptual example, are size and menuItems. About the size there no much to say at this time but I would already spend some word on menuItems.

As shown the menuItems gets the menu structure. In real examples, as I will show later, it is a better and suggested solution to separate the menu in a subroutine that returns the menu and change the menuItems to:

menuItems => menu(),

The snippet contains also an example of events. In this case we have the onPaint event, which is triggered when the windows is painted. When the event occurs the sub will be executed, at least at the beginning when the windows is created, causing the line to be drawn on the screen.

About Fabio D'Alfonso

user-pic A blog about Perl and Prima. I would write about Perl and Prima the way I would like to read about them.