April 2018 Archives

Websocket Extension for GUIDeFATE - Dialogs and File Operations

GUIDeFATE, your favourite Quick-and-Dirty GUI designer for newbies is now acquiring a Web-socket interface. Now this is certainly not capable of competing with those genius applications Mojolicious, Catalyst, Dancer etc. Having only discovered Web-sockets a few weeks ago my yield is going to be decidedly sketchy. Of course a desktop interface is quite different to a web app...the machine running the interface is the same as the one the user is sitting at, and the program running the graphical output is the same as the one that is handling user interactions. When there is a client and server involved, a certain of communication is required between the two, both must of course be able to understand each other, even though they may be coded in different languages. GFweb (GUIDeFATE's Web-socket module) handles 1) the generation of the user interface 2) the initiation of a listening socket, 3) handles the communications between the two.

Dialog/Message boxes over websockets

So in the previous iteration of the GFweb we managed simple comms, button presses and interface updates. Things like the calculator and Rock-Paper-Scissors-Lizard-Spock work well. Buttons are connected to functions on the server, and each function completes an update to the state of the client immediately. Dialog boxes and File operations have been more tricky for me to implement. In the desktop modules it has been possible to call a function that triggers a dialog box and that function that triggers the dialog box returns the result...

 # function showDialog returns the result of the dialog box,
 # so can be inserted directly into, say, an if statement e.g. 

    "This will wipe existing text...proceed?",
    "OKC","!")){ etc.

Now there probably is an easy way to do this in frameworks like Mojolicious etc. But as I have taken up wheel re-inventing, and already have started learning about Net::Websocket::Server, I have to figure out how to make a comparable interaction. This minimalist server can not trigger the dialog box and then proceed according to the result of that input. At least I have not figured out how to do this. So while under normal operations one triggers the client to create the dialog box first waits for user response and reacts to it. in the web app form, the routine that triggers the the dialog box and the one that reacts to it are distinct. The solution? Before triggering the dialog box, the programmer defines potential responses, by writing appropriate routines, and inserting these into a dispatch hash table. It is only after this point that the dialog box is triggered. The server later reacts to the actions of the user when announced by the client.

# the following lines add actions to each potential response to
# the Dialog.  Allowed responses Ok, Cancel, Yes, No and is
# defined by the parameter in showDialog e.g. OKC= Ok and Cancel,
# YNC is Yes No and Cancel
$frame->dialogAction(  "OK",       # action if ok clicked
     sub{$frame->appendValue("TextCtrl1","\nClicked OK\n"    )   } );
$frame->dialogAction(  "Cancel",   # action if cancel clicked
     sub{$frame->appendValue("TextCtrl1","\nClicked Cancel\n")   } );
# now trigger the dialog box
$frame->showDialog("The Dialog Title",
                   "The dialog Message goes here",

File interactions

Handling files is another issue. So a file selector box is relatively easy to trigger, the "onchange" parameter can then be used trigger the server to be ready to receive a file, and then the client proceeds to send the file as binary data. Simple.

Websockets can do binary transfers, but while the websocket protocol seems to allow very large files, with Net::Websocket::Server I could only transfer files less than 64k for some reason or the other. There is probably a way to increase the maximum transfer size. But the solution I did eventually was to create a BinaryBuffer object in JavaScript that takes the file, splits it into 64,000 byte chunks and handles the handshaking with the server until the file is reassembled on the server. Here is the screen capture log of the transfer process: -

Websocket File upload.png

Immediately after the file is loaded, the dispatch table is again queried to allow any routines that need to follow after file has been uploaded. The code is as follows, as before predefining actions for each of the potential reponses before triggering the file selector: -

# the following allows the user to upload a file to the server
# the server stores the file in a folder called dataFiles in the
# directory of the running application
$frame->dialogAction(  "Cancel",  # action if cancel clicked
     sub{$frame->appendValue("TextCtrl1","\nClicked Cancel\n")   } );
$frame->dialogAction(  "File",    # action to run after file loaded.
         # In this example add the filename to the text box
         # and set the Imagepanel to have this picture
        my $file=shift; #the name of the file is passed as parameter
        $frame->appendValue("TextCtrl1","\n Loaded File: $file \n"),
     }  );
 # now trigger the file selector dialog box
$frame->showFileSelectorDialog("Pick a picture File",1,"");

File downloading is decidedly easier...an <a href="file-to-download" download>DOWNLOAD </a> sorts it out.

Still a bit of work to go before I put it on CPAN...not least of which is that I wish to create a Gardening Database viewer. The code is available in the test folder. I want to allow the developer able to define the listening port/host (currently localhost and 8085...useless apart for testing purposes) and also fix my implementation (currently generates an HTML file followed by a call to the system browser to load that HTML while the server is running in the background) so that it detects whether on localhost of called from across the network. I also need to start rethinking the namespaces of GUIDeFATE's accessory modules as pointed out by grinnz on reddit...maybe too late. I have tried shifting things in my setup, can't seem to get things working when I do.

Now I know what you are thinking..."Why on earth bother?". Sure this is crude, probably not very secure, and pretty deficient compared to most widely used frameworks. The answer is simple. GUIDeFATE is simple. It is sort of WYSIWIG, and will allow someone with little or no knowledge of graphical back-ends or HTML or Websockets to create a quick-and-dirty GUI application in pure Perl.

A new wobbly wheel for GUIDeFATE

A couple of weeks ago a little misunderstanding about what GUIDeFATE was about led to an interesting comment. This tool had been designed to simplify GUI design using a text editor, and met the challenges of having a number of weakly supported potential targets to produce a semi consistent interface. Essentially producing a GUI for Perl applications has never been easy. A number of back-ends exist ( e.g Wx, Tk, Gtk, Win32, Qt etc) all with different approaches, most with rather outdated documentation, often not universally easy to install. A newbie like myself was never going to find this straight forward. So a tool was born that abstracts out the complexities of GUI design, producing near identical interfaces in multiple back-ends, all from a textual representation. As each interface was accommodated and announced, came reports of other folk being unable to use said interface, followed by a new module to use another GUI toolkit; so now we have a tool that can now accommodate 5 desktop UI systems.Surely that must cover all bases? Apparently not.

In response to a blog out-lining the difficulty I had had with the Qt paradigm of SLOTS and SOCKETS for interaction between the UI and application logic, and the need for a custom dispatcher, was a comment relating to WebSockets. Now GUIDeFATE was intended to be desktop UI focused, so clearly WebSockets are not the Sockets at issue. But Ron suggested I try out other people's efforts before re-inventing the wheel, thus finding this particular wheel rolling in a different direction. I started finding out about Mojolicious, Dancer, Catalyst. These are clearly the efforts of geniuses, with multiple dependencies and difficult to crowbar into an existing toolkit without duplication and “re-inventing the wheel”. I requested advice.

Flexibility is proportional to complexity

To connect to my less efficient brain requires much greater simplicity. So I quickly abandoned all these powerful frameworks. I chose the simplest possible websocket client, with the simplest available server, unashamedly ripping off the code from the examples provided. Attached them to quite possibly the simplest UI generator in the world... GUIDeFATE, of course☺. The result is GFweb.pm. A module for GUIDeFATE that generates and starts both the server and the client for a web-app-like interaction with a UI nearly identical to the desktop applications and with nearly completely unchanged programming logic. The extra effort required to transform a desktop app to a web application? Just change the small string selecting which back-end you want and the address/port to use.

my $gui=GUIDeFATE->new($window,"web","q");

The wheels come off..

Websocket rpsls.png

Admittedly the tool keeps things simple: Only a handful of widgets, with limited interaction potential: hopefully just enough for a quick and dirty graphical application, simple enough for a noob like me with little knowledge of JavaScript or WebSockets. The reinvented wheel is decidedly wobbly. File selection and message boxes are not yet done...my problem is how to handle the client side responses which the server-side programming logic is waiting for to further process. I will figure it out, or somebody will point me in the right direction...it's inevitable. This experimental version is uploaded into GitHub. The applications need to be started in a folder containing the dependent files (preferably from a terminal) and popups need to be enabled (the application currently also starts a logging window to enable debugging).

About Saif

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