Sunday 20 December 2009

Back to the drawing board....

With the release of Qt 4.6, it's time to go back to the drawing board and get everything rebuilt and re-examined.
Apparently 4.6 has lots of exciting new features, and much improved WebKit support - which was already working rather well in 4.5.
Whether I can eek out all this through the wrapper, we'll have to wait and see.

First things first... I need to build it :-)

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".


Sunday 20 September 2009

BBObject* <> QVariant

All list widgets are generally able to store user data with each item. This usually helps the user keep track of how a particular item relates to their own data model - for example, you might have a TItem type which holds some useful information that you'd rather the widget tracked instead of you having to create your own TList and manually match up a list item to the item in the TList.

First, a quick note about how objects and the GC are handled in BlitzMax.
Each object instance has a form of reference counting attached to it. As objects lose their references (i.e. nothing is using it any more), they are eventually removed from the system.
If we store the object in a BlitzMax container (basically anywhere BlitzMax controls assignment of an object), all is well. But, outside of BlitzMax, we must manually keep track of object references by first retaining, and later releasing that object. When an object has no more references, it will be flagged for deletion, and at some point, removed by the GC.

Qt uses QVariants to hold user data, which is great for numbers and strings, but not so useful for BlitzMax Objects. QVariants are stored on the stack, and therefore, are memory-managed by C++ itself. This is a problem if we want to store a BlitzMax object in one, because there's no trigger to tell us that the QVariant object is going to be removed - at which point we can release our own object.

So I came up with an alternate solution in which each list type widget also contains a map of related user-data. Each entry in this map has a unique Long identifier. It is this identifier which is store in the widget item user-data. On request, the id is retrieved and looked-up against the map, which retrieves the appropriate object.
Whenever an item is removed, the map also requires an update to remove any mapped user data from it.
On deletion of a widget, the user-data is automatically un-referenced because the item map will be flagged for collection, and therefore also its contents.

This seems to work well in the few widgets that I've implemented so far.
For something like a table, I may have to introduce a slightly more complex alternative, but hopefully it will hold up there too.

Monday 31 August 2009

Which came first...

I'm quite enjoying the flow I'm using to write this language binding - that of implementing an example first, and then going to the modules and adding all the necessary API to allow the example to 1) compile, and 2) run properly.
In some ways it's a little like test-driven-design, where you write your tests before you write the code that the tests, test. It's certainly nice to get to that point where you run the little application and everything is finally working as it should be.

Since the module API matches so closely with the Qt API/class structure, porting the examples is generally just a case of removing semi-colons, and converting -> into .
(well, okay, I'm obviously taking liberties here, because you need to instantiate the objects more verbosely, but otherwise that's it!)

An example? Okay...

Some C++ :
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(fromStartCheckBox);
leftLayout->addStretch(1);

The equivalent BlitzMax :
Local leftLayout:QVBoxLayout = New QVBoxLayout.Create()
leftLayout.addLayout(topLeftLayout)
leftLayout.addWidget(caseCheckBox)
leftLayout.addWidget(fromStartCheckBox)
leftLayout.addStretch(1)

As you can see... the two are very similar.

You may be wondering why I require the Create() method call on the object instantiation.
I know, I could easily implement the New() method on the Type and have it create an instance of Qt's QVBoxLayout, but this complicates things if ever I need to create a BlitzMax QVBoxLayout object for an already existing C++ QVBoxLayout. (writing bindings can get your head in a spin!).
So, I generally have to "constructors" for each Type. A generic Function, and a Method. The nice thing about the method too, is that you can more easily implement your own subclasses of a given Type. (see any of the examples, where the main window/gadget does exactly that!)

:-)

Wednesday 26 August 2009

The perils of pointers

I hope you're all sitting comfortably? Good, then we'll begin...

In today's lesson, we are going to learn why writing language bindings can, on occasion, make you wish you had a normal leisure-time activity - like hunting wabbits.

If you are using a framework/toolkit in everyday C++ you aren't (usually) going to come across an issue which has caught me on several occasions, which all to do with how a pointer is perceived when you pass it down from your higher-level language. For most of the time, everything "just works", but there are times when you are passing in, say a QPaintDevice*, and things start to get just a little weird (insofar as strange error messages that you aren't expecting).
When I say "just works", I mean that you'll declare your function parameter as said pointer type, and the call down to the framework will do its thing, no questions asked.
However, you pass it into a different function, and suddenly it is reporting errors that don't quite make sense. Many hours later and you can't see anything wrong with the code.

This is where you need to take a very large step back and ponder the problem at hand.
You should ask yourself. Is the pointer I'm passing into the function *really* a QPaintDevice, or is it actually a subclass, like say, a QWidget, in which case you may actually want to be defining the parameter as a QWidget* instead.
Why does it matter sometimes and not others?
I believe it's something to do with internal offsets inside instance of the QWidget, which although is also a QPaintDevice, is really a QWidget at heart.
Normally this is not a problem, because C++ *knows* what you are passing around. So when you say, "take this pointer as a QPaintDevice", it knows that yes it can, because it sees that it is a QWidget also.
But, when your pointer is appearing suddenly into the glure code from the higher-level language, C++ can't determine what the object really is. You tell it "this is a QPaintDevice", and it makes a whole raft of assumptions.

As you may imagine, I've been here, and solved an issue I've been having.

QPainter() takes a QPaintDevice* as a parameter. This might be a QWidget, or even a QImage (which isn't a widget), or other QPaintDevice subclasses.
When I was passing in a widget of some kind, up would pop a strange error :

  • QPainter::begin: A paint device can only be painted by one painter at a time.

Which would have been fine if I'd already assigned the widget to a QPainter...

Many hours later, and some last-resort Googling, it dawns on me that I perhaps, for widgets, instead of this :

  • QPainter * bmx_qt_qpainter_create(QPaintDevice * device)

I rather have this :

  • QPainter * bmx_qt_qpainter_createwithwidget(QWidget * widget);

And you know what... the error went away...


... even though a QWidget is a subclass of QPaintDevice.


Well, I've rambled on for too long again... back to 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

Wednesday 29 July 2009

The Plan Of Action

One of the fun things when creating a language binding for a over-sized library is deciding what to do first. With wxWidgets, I think I started with getting some basic functionality working and then randomly implemented widgets as I came across them.

For QtMax, I think a good course of action is to work through implementing the various examples provided with Qt. This should result in a working API along with a fairly rich set of documented, working examples.

I'm currently working through the widget examples, since the UI-side is probably one of the most used features of the library.
Other than losing my way a little with QPainter (and subsequently slinking over to the qt-interest mailing list to ask for assistance), it's coming along very well. As of this post, we have working examples of lineedit, analogclock and translucentclock. Although, to be fair, the translucent clock example comes from the Qt magazine (Qt Quarterly). But it looked cool, so I couldn't resist ;-)

Saturday 25 July 2009

Qt did MaxGUI

I forgot to mention, last time, that in the beginning of 2007 I knocked together a basic MaxGUI module using Qt as a backend. It was mostly done as some C++ practise, as I was essentially picking it up (C++) as I went along learning the API itself.

Interestingly, it mostly worked, but at the time, being GPL only on Windows, I wasn't overly keen on supporting it - and it's not like there aren't already enough MaxGUI modules floating around as it is!

One advantage to the "MaxGUI wrap" thing, is of course that one only needs to wrap a very small portion of the API, rather than every single method and class. The disadvantage is obvious - in that you only get a nibble of the whole Qt cake.

But you know me... experiment, tinker, kludge...

.. perhaps I'll resurrect it enough to have MaxIDE working in Qt...

Thursday 23 July 2009

In the beginning...

...there was BlitzMax + MaxGUI.

Nice as MaxGUI is, it's not really designed for big applications, and is a bugger to get everything looking good across platforms - which is the whole point of a write-once-compile-for-three-platforms application.

...then came BlitzMax + wxWidgets, otherwise known as wxMax. wxMax is a BlitzMax language-binding of the wxWidgets API, written by someone who should probably know better!
As a cross-platform applicaiton framework, it is very good indeed. It covers more than simply the GUI, with tools such as PDF generation.

Qt, is very similar to wxWidgets, but for a couple of facts.
The first, is that it renders its own UI, rather than trying to use the native gadgets (which wxWidgets does). As long as it looks the same, then how things are rendered isn't too important.
The second, is that Qt already has Cocoa support (in Qt 4.5), and has cross-platform support for WebKit (the engine that Safari uses to render web pages).
Otherwise, the two are very similar in functionality.

So why Qt, if wxWidgets is already good enough?

Well, why not?
There's no harm in having more choice :-)

Now that Qt is LGPL, it's also easier to develop applications for it that are intended to run on all three platforms (Linux, Mac and Win32). Previously, Win32 was GPL or Commercial only.

Anyway, I'll hopefully be writing about my adventures here...

Stay tuned ;-)