Showing posts with label signals. Show all posts
Showing posts with label signals. Show all posts

Monday, 19 October 2009

Piggyback

Usually when I'm wrapping a C++ class which requires me to callback into BlitzMax, I will subclass it, and override the specific methods to make those callbacks.
But sometimes, that's just not possible, because the object we want to have access to those callbacks has been created by something else - i.e. not by us!

It's always been a bit of a conundrum for me when I've come up against a class like this, as to what to do. Fortunately, I've recently conjured up a reasonably elegant solution which also happens to fit rather well into Qt's Signal/Slot model.

Essentially what I'm doing is piggybacking onto the object that I want, with a subclassed QObject of my own. We intercept the returned object, do a quick check to see if we are already piggybacking it, and if not, we attach ourselves to it, and pass it on.
During the attach phase, we plug into the object's destruction method, which we will use to detach and kill our own object. So in essence, once we are piggybacking, the object we've attached to takes ownership of our object. Fire-and-forget, if you will :-)

As is always the case, it is probably better to explain all this with an example.

For this example, we'll take a look at the QAction wrapper, which also happens to be the reference implementation.
Our wrapper class has a static method ('link') for checking if the object is already attached :

void MaxQAction::link(QAction * a) {

BBObject * handle = qfind(a);

if (handle == &bbNullObject) {

MaxQAction * action = new MaxQAction(a);

}

}


If we don't find the object in our internal map, we make the attachment. The MaxQAction() constructor creates a BlitzMax QAction object instance, and binds it to the original object. It then connects all the object's signals to our wrapper class slots.

When we receive a potentially new QAction, we simply call 'link', and the attaching is handled silently in the background :

QAction * action = group->addAction(a);

MaxQAction::link(action);

return action;


Most importantly, all this is done behind the scenes, so that all you need to do is write your BlitzMax code, and it will all "just work".


Thursday, 6 August 2009

Slots & Signals for BlitzMax

One of the very cool features of Qt is its Signal/Slot mechanism. It's also known as the Observer Pattern, in the Design Patterns book.
A signal is a function which is called as the result of some kind of event or action. For example, the click of a push button. A slot is a function which you may connect to a signal, so that whenever the signal is actioned, the slot function is called.

This is all well, good and easy to implement in C++ as part of Qt, because there are build tools which are there to generate glue framework code for you.
But of course, having to run extra utilities when trying to build your BlitzMax application wouldn't be very useful. One should simply be able to knock some code together and run it.

After mulling over this obvious problem for a while, I came up with what I think is quite a clever system which works in a functionally similar way to the C++ code....
On the low-level side, every C++ signal needs to be connected to a BlitzMax utility function.
On the BlitzMax side, the base QObject holds a list of all connections to any of its own signals. Instead of connecting a callback function pointer (like we would with wxMax), we rather pass in the name of the method to connect the signal to. Using reflection we can map the method name to the actual method which we will later call.
When the signal is actioned, every method (slot) which has connected to the signal is called in turn.
It all works seamlessly :-)

The sliders example is a very good demonstration of the system in action, and even shows the ability to create your own BlitzMax-based signal method.

Although we aren't really using the Qt signal/slot framework properly - since the real signal and slot connections are hard-coded into the modules - it functions in exactly the same way, which is really all we need to worry about ;-)

Tuesday, 4 August 2009

Big, Isn't it?

On a scale thing, Qt stands up there with the likes of wxWidgets for the number of classes and methods which make up the API.

I try not to think about it too much, because it's just too scary! Better to instead focus on specific functionality one Type at a time. Which is where I think my current method of gradually building up the library of fully-functional examples is helping immensely.
Before I know it, all of the really interesting stuff will be working ;-)

I'm still juggling with the issues of being able to "connect" to objects that I have been created by Qt, rather than by me. The biggest problem of course is connecting BlitzMax to a signal, which needs to be instantiated in C++. I believe I have a good method for this when I can create my own objects, but for Qt-specific objects, I'm still wandering around in the dark.
Two options I can see for this :
  1. Rather than subclass the original classes, we instead use a "wrapper" class, which itself contains all the slots/connect glue. It would need to connect itself to the "parent" class's destructor signal in order that it could clean both itself and the BlitzMax object up. All signalling would pass through this wrapper class (one per QObject super class)
  2. For Qt-generated objects, we check if we already have a connection with them in BlitzMax. If not, we attach a "signal handler" object to it, and have it connect to the destcructor signal of the object. The signal handler would simply set up the various signal/slots as per the wrapper class in 1.
Of course, now I've written all this down, No 1 seems to be the obvious course I should have taken from the start...

:-p