March 2018 Archives

Four backends now for GUIDeFATE...but the struggle continues

Just as you are thinking you got the hang of something, believing you know how to work the system, imagining you have a problem licked, then you wake up to find out it was just a dream. That has been my experience in developing GUIDeFATE (A contrived acronym for the project - Graphical User Interface Design From A Text Editor).

GUIDeFATE is a tool that simplifies GUI design by choosing only a few useful elements that make a usable graphical application and making those elements accessible to the beginner developer. Handy too if you want a quick and dirty desktop application in Perl. A tool that converts a string into a GUI, using one of the many backends that exist. So I have been making this over the past month or so, using the often woefully outdated resources found on the internet. I had a rudimentary Wx Backend after about a week, by another week I had Tk, and was persuaded to put the project up on Github. Soon Gtk was also covered easy as pie in 3 days. Even started working on Win32...(I only have Linux machines at home, fortunately also have more skilled help in this matter in the form of mpersico)...then CRUNCH...

A fellow redditor daxim suggested Qt. Great idea, I thought. Qt well supported on Linux and other platforms. And Gtk was a breeze to handle. Significantly Qt and Perl have had a close relationship. This would be a walk in the park I figured. I figured wrong. Just to get Qt development setup on my system was a struggle. Installs from CPAN failed, installs from repositories are no longer updated. ppas claiming to offer this also fail. Switched to Kubuntu, wrecking some of my settings, no joy. Near defeat, daxim once again gently guided me to a reasonable install of Qt4. Problems over? No. Just beginning.

Qt

Qt is sophisticated, powerful, all-encompassing. Getting control of this sophistication and power needs huge brains which I lack and a dextrous use of Qt Designer, which defeats the object of GUIDeFATE. Secondly, Qt internal design appears to have evolved in a parallel universe, isolated from other paradigms of GUI design, even though on the surface looking similar. Sort of like Convergent evolution that gave flies and birds wings and legs. To crowbar this interface into GUIDeFATE was not easy (for me).

Key to this is how GUIDeFATE allocates actions (eg on button click, menu select etc) to widgets...it merely takes a reference to subroutine in main:: that has not been yet created. An example from a button widget generator from the front end sends a reference to a list of parameters for each widget like this: -

addWidget(["btn",$bid, $label,[$locationX,$locationY,[$width,$height], \&{"main::btn".$bid}]);

GUIDeFATE's middleware for each backend stores this referenced list in a list of widgets before it sets up content in new(). This means that the developer can create his own functions easily as long as he knows the id of the widgets, the autogenerator can generate the skeleton for these functions and Bob's your uncle. All the other backends quite easily accepted a dereferenced function as parameter in their widget.

Not so Qt. Qt uses SLOTs and SIGNALss. A powerful system, SLOTS are attached to the Qt::MainWindow, which is a Parent of the Qt::Widget container (in my case) containing all the widgets. So the SLOTS are in a different place to the generator of the SIGNAL. There is no way to add slots dynamically (as far as I can see), and you have to declare them at the beginning...difficult if you dont know what the functions are going to be. Then you have select a SIGNAL to be associated with a widget and connect the two. A simple Perl example is here.

Fortunately there is SignalMapper. This allows SIGNALs to be mapped to a single SLOT in signal mapper. Then comes the complexity of extracting signals from different events by a dispatcher which distributes that signal to the correct function.

Setting up the SignalMapper in new(): -

$self->{SigMan} = Qt::SignalMapper($self);
$self->connect($self->{SigMan}, SIGNAL 'mapped(QString)', $self, SLOT 'mapAction(QString)');

Adding a Signal to the widget, and giving the signal mapper clues about the source for the dispatcher to work on:-

$self->connect($canvas->{"btn".$id}, SIGNAL 'clicked()', $self->{SigMan}, SLOT 'map()');
$self->{SigMan}->setMapping($canvas->{"btn".$id}, "btn".$id);

And creating a dispatcher: -

sub mapAction{
   my $widgetIndex=getItem(shift);                   #find the widget parameters
   my @widget=@{$widgets[$widgetIndex]};             #using the clue (the Id)
                                                     #provided by SignalMapper
   my $wType=shift @widget;                          #remove the type,
                                                     #which allows you locate
   if ($widgetIndex !=-1){                           
      if     ($wType eq "mb")   { &{$widget[3]};}    # the function referenced
      elsif  ($wType eq "btn")  { &{$widget[4]};}    # and call that function
      elsif  ($wType eq "combo")  { &{$widget[4]};}
   }

}

So learnt a new paradigm, and finally GUIDeFATE 0.08 has another backend.

Four backends supported.png

One thing I am struggling with is understanding packages and the differences between "use ", "use parent " and "use base ", and other ways of absorbing other modules into the code. It takes a lot of guesswork and fiddling and trying different combinations for me to get these backends to work. It may be the way their Perl interfaces and the C libraries work. More likely it is something to do with my lack of understanding, but it sure leads to messy code, and scrambled brains.

About Saif

user-pic An Orthopaedic Surgeon, A Tissue Engineering Scientist, Lecturer, (and Hobbyist Programmer, Electronics Engineer and Roboticist)