Introduction to PyMUI

Following text refers to PyMUI version >= 0.3

The first thing to know about PyMUI is its base on the Python language.
So before to continue here, I strongly suggest that you understand basics of this language first.

The second is PyMUI is designed to work with Python 2.x serie only. The work to make it compliant with 3.x is too huge to make it now.

The least but not the last is to know some basics on MUI. I let you refert on documentation comming with the MorphOS SDK.

Now, we starting on a successful installation of PyMUI on your system. To make it sure, run the Python interpreter console and type this command:

>>> import pymui
>>>

If the prompt comes immediately after without any errors as showed here, we can say there is a pymui module installed somewhere :-).

Ok, let's go! Starting with the so-boring Hello world!.

Create a simple application

A feature using an interactive console is to see in realtime how things work.
So we're going to create our first PyMUI application by typing it directly in the Python console…

So first, if it not done yet (see Introduction chapter) we start by importing the PyMUI module that contains all function, objects and so on, to use PyMUI. Little difference from the Introduction chapter, we're going to import the module contents in your global variables space.

>>> from pymui import *
>>>

Then, creating an Application object, with just some simple information like the name of the application, a simple description on the purpose of it and the base name (name used by MUI to store application preferences in ENV:).

>>> app = Application(Base='PyMUI_HelloWorld', Title='Hello World PyMUI Application', Description='A simple example of PyMUI')
>>>

Now we have our application created: PyMUI have created a new Python object, an instance of the Application class, by this call. A real MUI object has been created also in the same time. By printing on console the string representation of our application object we can see the address in memory of them:

>>> app
<Application at 0x12345678, BOOPSI at 0x87654321>
>>>

Note: you can use also print repr(app) or just app.. it's equivalent here, in this context.

It's the moment to add something more visual than just an application object… A window for exemple?
We're going to do it as for the application. Just use the class named Window like this:

>>> win = Window('Hello, World!')
>>>

As you can see we've given the window title as argument. This first argument is mandatory and it's always the title.
At this moment we've an application and a window… But! Where is the window???

By default PyMUI doesn't attach new windows object automatically to the application, and as MUI requires that a window is attached to an application before setting MUIA_Window_Open attribute to TRUE, it's normal to have nothing on the screen. So, let's do it:

>>> app.AddChild(win)
>>> win.OpenWindow()
>>>

Note: for fun, we could call the window method OpenWindow before attaching it: the result was a beautifull Python exception to remember you that our object is not attached yet!

Ouaahouu! We got in the middle of the screen… a… hummm… yes, a tiny window, so tiny that you may have missed it!

What's happening?

Simple, nothing has been added to this window, there are others objects, so nothing to display!
So for the moment we're going to close this empty window, add a simple button as our root object and re-open it.

>>> win.CloseWindow()
>>> bt = SimpleButton('Bye Bye!')
>>> win.RootObject = bt
>>> win.OpenWindow()
>>>

Cool! we got it! And with our smart button inside.
Now try to play a bit with this window: what's happening? The display is not refreshed… this is due to the fact we don't handle yet all MUI (or Intuition) events comming from our application.
In a classic C code, we shall write a clever event loop to listening to events from the application and call yourself Wait() if nothing happens.

PyMUI simplify all this code, the Application class gives a method that implement this loop:

>>> app.Run()

As you can see, the prompt doesn't come immediatly. We're in the loop and this one handle all basic MUI events now.
You can play safely with your window: click on the title and drag it to move it, resize it and click on the button.
But there is a probleme: the window close gadget doesn't do anything! How to close the window? How to quit this loop?

We doesn't have explained to your window that the close gadget should quit the application yet. So to leave the loop, just press CTRL-C in the console. This force the loop to exist and raise a Python Keyboard Interrupt exception (it's normal).
Now, if you play with the window again, you can see the it doesn't refresh as before the loop, as we can expect.

That could be good to permit the window to be closed and to leave the application (as we have only this window), if the close gadget is pressed and released. In the same time we want to do this if our simple button is also pressed and released. How to do that?

It's time to introduce the notion of Notification.
As MUI, almost all PyMUI classes are derivated from a top class named Notify.
This class gives some methods to add notifications: this is the way to do some actions when some events happen, like when a button is pressed or when an object attribute is changed (see chapter Methods and Attributes).

So we're going to add 2 notifications doing the same thing: leaving the application.
One is triggered when the close gadget is released, the second when its the button.
To do that we're going to use a window method (inherited from 'Notify' class): Notify.
This method connect an attribute change of the object to an action, conditionned on a trigger value.

>>> win.Notify('CloseRequest', True, lambda e: app.Quit())
>>> 

This is for the window close gadget. Now for the button:

>>> bt.Notify('Pressed', False, lambda e: app.Quit())
>>> 

Restart the application now and try to close the window or press the button:

>>> app.Run()
 
(after closing the window for example)
 
>>> 

That's it for you first PyMUI application!

You know now:

  • how to create some PyMUI objects like an application, a window and a simple button.
  • how to attach them together.
  • how to do some actions on events by using notifications.
  • how to run an application and leave it.

This is the full code of your application. It can be saved in a file and run directly by the Python interpretor, without using the interactive console, as for a real project:

Download full sample: helloworld.py

#!python
 
from pymui import *
 
# Application, window and root contents...
app = Application(Base='PyMUI_HelloWorld', Title='Hello World PyMUI Application', Description='A simple example of PyMUI')
win = Window('Hello, World!')
bt = SimpleButton('Bye Bye!')
 
# Attach everything
app.AddChild(win)
win.RootObject = bt
 
# Add notifications
win.Notify('CloseRequest', True, lambda e: app.Quit())
bt.Notify('Pressed', False, lambda e: app.Quit())
 
# And run...
win.OpenWindow()
app.Run()
 
# Not needed but for safe
del app

Using classes and OOP

Creating an instance of a MUI class using PyMUI is quite simple. As this C classes have been wrapped as normal Python classes, just call the class name with right initializing parameters (or not) to obtain an instance of this class and create the MUI object related.
This is the way to create basic objects like Application, Window, Text and so on.

Attributes

In general, parameters are attributes value to initialize your objects. If these attributes are MUI attributes, they are only available at creation if they have the I flags. See the class documentation about initial parameters and more.
A trick to know: if you run an interactive Python console, have loaded a pymui module class, use the isg property of a MUI class attribute to know avaibles ISG flags for this one. For example, what are ISG flags for the MUI attribute DragBar of class Window?

>>> from pymui import Window
>>> Window.DragBar.isg
'i..'
>>>

As we can see this attribute can only be set during the instance creation only.

Note: ISG flags is not the only property of MUI attribute in a PyMUI class. Refer to MAttribute for more properties and information.

Now, you may have noticed an important thing: naming convention for MUI stuffs, like attributes, in PyMUI doesn't take care about the MUIx_xxxx prefix inside classes.
These names are used in C to declare attributes, methods, constants and structures: for the first three, they are available with the same naming convention as constants in the submodule pymui.defines. But in classes, only the last part of the name has been kept.
That's why the MUIA_Window_DragBar MUI Window attribute is found as DragBar in PyMUI Window class. As this name is an python attribute of Window class, this convention is more intuitive.

But don't forget that normal Python attributes remains, so all classes attributes are not necessary related to MUI attributes… PyMUI classes are just normal Python classes ;-)

To finish on attributes, MUI ones are mapped as Python properties, so they may be get and/or set and have documentation. Use them like any properties:

>>> win = Window('A Title') # creating a Window instance object
>>> win.Title # get the title
'A Title'
>>> win.Title = 'Another Clever Title' # set the title
>>> win.Title
'Another Clever Title'
>>>

Methods

MUI classes wrapped in PyMUI may have many others MUI attributes like DragBar, they could also have wrapped MUI methods.

These methods are declared inside the wrapped MUI class using MMethod class (also a subclass of property like MAttribute). As you think, when used from a instance of the wrapped class, they give callables that accept zero or more parameters and push them to the C wrapped MUI method using an internal DoMethod() call.

Most of MUI classes methods have been wrapped. Some exceptions remains: some methods doesn't have sens in PyMUI like ones to create memory pools or sometime they just missing because I've not wrapped them :-D.
This PyMUI callables work like C MUI one, but they can also be aliased to add more powerfull features.

Take an example with the Application method AboutMUI: this method is declared as follow in C header libraries/mui.h file:

#define MUIM_Application_AboutMUI           0x8042d21d /* V14 */
(...)
struct  MUIP_Application_AboutMUI           { ULONG MethodID; Object *refwindow; };

As you can see in C there is two things that describe a method:

  1. the ID, an integer generally given in hexadecimal: 0x8042d21d here.
  2. a structure to pass parameter to the class dispatcher called by DoMethod().

This stucture gives you also information about parameters to give to the DoMethod() call.

On PyMUI, as we've just seen, MMethod class is used, see its documentation for usage.
That gives for you sample method:

class Application(Notify):
    (...)
    AboutMUI = MMethod(MUIM_Application_AboutMUI, [ ('refwindow', c_MUIObject) ])
    (...)

You see it's a class property so, accessible by AboutMUI name, that will execute DoMethod on instance using MUIM_Application_AboutMUI MUI method ID and accepts only 1 argument, a c_MUIObject.

The Python prototype of the callable given by app.AboutMUI1) is given by the list after the ID: [ ('refwindow', c_MUIObject) ].
It's a list of 2-tuple: first item is a the parameter key name as in normal Python class method, second item is a PyMUICType of the argument (Please read the PyMUI C types design and reference to know what to use).
So if we try to write the callable as Python normal class method it could be something like that:

class Application(Notify):
    def AboutMUI(self, refwindow):
        self.DoMethod(MUIM_Application_AboutMUI, refwindow)

In fact it's a bit more complex but the idea is here.

Now, in real world its gives:

>>> app = Application()
>>> win = Window('Test')
>>> app.AddChild(win)
>>> win.Open = True
>>> app.AboutMUI(win) # This open the AboutMUI window relative to our window 'win'
>>>

To be continued

Overloading

Overloading class methods is the main and best design to use when you need to add some class features or use some specials behaviours.

In MUI C version this is done by creating a new MCC (MUI Custom Class) and a dispatcher routine catching incoming BOOPSI messages when DoMethod() is called on instance of this MCC.
This is simple, but too complex again: with PyMUI you not longer need to create MCC/dispatcher yourself. Everything is done internally.

First, remember that all class methods can be overloaded in Python using its intrasec OOP paradigm. But using that, MUI doesn't take care of your Python side when it try to call a method on your PyMUI instance object.
We need to have a way for calling your Python code when a DoMethod is called… whatever where is called (internal MUI library, your code, public MCC, etc).

PyMUI module contains a decorator function named muimethod: use it to decorate your PyMUI MCC classes.
This decorator accepts only one argument, the method id, that could be an integer (the MUIM_xxx) or an insance of MAttribute, as returned by Window.Title for example.
Finally, to declare our MCC class as an MCC and not as a simple Python class we need to use MCC=True declaration in the class declaration body.

Decorated methods have a fixed prototype: (self, msg). msg is an instance of MethodMsg class could be used to call execute an equivalent to DoSuperMethod() and obtains method arguments.

Trying a example: we're going to overload MUIM_AskMinMax method of Area class to fix some dimensions of instance of our new MCC.

class MyArea(Area):
    MCC=True
 
    @muimethod(Area.AskMinMax)
    def mAskMinMax(self, msg):
        # Ask to the MUI super class (here Area) to fill the C Msg structure
        msg.DoSuper()
 
        # The MinMaxInfo structure contains in the msg
        minmax = msg.MinMaxInfo.contents # it's a pointer => use "contents" to obtain the pointer buffer.
 
        # Fix our object size
        minmax.DefWidth = 320
        minmax.DefHeight = 240
 
        # That's all ;-)

We've created a new MCC class and instances of this class have automatically a default size set to 320×240 pixels.
Just create and use the instance as usual: obj = MyArea()

As you can see the msg object has a method named DoSuper: it's a direct call to the MUI C DoSuperMethod function. Class, object and msg parameters needed by it are contained in the msg instance object and given for you by PyMUI, so don't care about it!

Then, the msg object is also a versatile object with some attributes depending on the MUI method C prototype given by MUIP_xxxx structure.

The attribute MethodID is always available, then for other ones refers to related MUIP_xxx stucture, or better, the related PyMUI class MMethod declaration: each pair (key, type) gives the attribute name (key) and the type of it when get/set.

Finally, the overloaded method can return a value, converted by the PyMUICtype type given by MMethod declaration (rettype value). Default is 'None converted a the zero integer value (as in our example).

1) where app is instance of Application

en/dev/pymui/docs/tutorial.txt · Dernière modification: 2010/01/21 12:08 par Yomgui