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. 

 if($frame->showDialog("Sure?",
    "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",
                   "OKC","!");

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.
     sub{  
         # 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"),
        $frame->setImage("Image2","dataFiles/$file")
     }  );
 # 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.

Leave a comment

About Saif

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