http://qs321.pair.com?node_id=415908

I'm not sure in which part of the site to post this, as it's threefold... So I'll just throw it in Meditations so we can also meditate about how to categorize the post :)

A Cool use of Perl: An example (and to proof that it can be done) of a very simple application that uses HTTP::Daemon and HTML and should thus be portable and easy to make.

The meditative part of this thread: is this a good or a bad idea? It's portable, but some platforms have native widgets that would be nicer to create a GUI in.

And finally, a small quest for Perl wisdom: what's a portable way to find out if the user has left the "site" or closed the browser window? So far, I've told users of these little applications to hit ^C when done, but that's not pretty.

#!/usr/bin/perl -w # Pizza calculator, an example of how to build a portable web based # GUI application. use strict; use HTTP::Daemon; use HTTP::Status; use CGI::Simple; sub res { HTTP::Response->new( RC_OK, OK => [ 'Content-Type' => 'text/html' ], shift ) } my %pages = ( index => sub { res qq[ <html><body> <h1>Pizza cost calculator</h1> <form method=post action=calc> Total cost: <input name=cost><br> Number of eaters: <input name=ppl><br> <input type=submit value=Submit> </form> </body></html> ]; }, calc => sub { my ($request) = @_; my $cgi = CGI::Simple->new( $request->content ); res sprintf q[ <html><body> <h1>Result</h1> Cost per eater: %.2f<br> <a href=index>Again!</a> </body></html> ], $cgi->param('cost') / $cgi->param('ppl'); }, ); my $daemon = HTTP::Daemon->new(LocalAddr => '127.0.0.1') or die; my $ppid = $$; if (my $pid = fork) { while (my $client = $daemon->accept) { while (my $request = $client->get_request) { my ($page) = $request->url->path =~ m[^/(\w+)$]; $page ||= 'index'; if (exists $pages{$page}) { $client->send_response( $pages{$page}->($request) ); } else { $client->send_error(RC_NOT_FOUND); } } $client->close; } } elsif (defined $pid) { my $url = $daemon->url; my @browsers = qw(firefox konqueror mozilla opera start); { @browsers or die "Couldn't start a browser."; my $b = shift @browsers; my $r = system("$b $url"); redo if $r; } } else { die "Couldn't fork!"; }

Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

Replies are listed 'Best First'.
Re: GUI with HTTP::Daemon
by dimar (Curate) on Dec 19, 2004 at 00:09 UTC

    Since about 1996 some were convinced that a Web Browser could serve as the ideal 'cross platform solution' for generating language-agnostic GUI front-ends. Ironically, it was about that time that several companies sprang up, offering 'virtual desktop' software that you could run over the internet, complete with DHTML-based widgets that looked just as good as (and in some cases even better) than the GUI components your typical Windows (TM) user has come to expect. (you might be very surprised by what can be rendered in a typical browser, see eg http://www.bindows.net/ )

    The dotBomb explosion and browser incompatibility hassles helped to wipe most of these ventures off the map, but the fundamental idea is still feasible, and still remains critically overlooked by *most* developers:

    The 'web' browser sitting on most computers is perhaps the most flexible and most amazingly underused component for GUI-based development.

    The single most enduring circumstance that prevents serious progress in this area is the lack of a language-neutral and credible mechanism for interprocess communication between the browser GUI front-end and perl. (or whatever your language of choice is).

    OOP enthusiasts will parrot the virtues of "loose coupling" until they are blue in the face, but the sad fact is, we are still in the archaic "stone age" when it comes to options for GUI development. We shall not emerge from this primitive state until it is possible for a GUI developer to create a front-end with absolutely *no need to know what 'language' will be used for the 'back end'*. The technology exists to make this possible today, but not enough people 'get it'. That is especially sad since great programming skills and great design skills tend to be mutually incompatible skill sets.

    HTTP: ubiquitous but too limited by the stateless 'request - response' model. WebServices: as bad as HTTP. OLE::COM: way too complicated, and not platform independent. CORBA: same problem as OLE. XPCOM: complicated, and does not seem to be catching on.

      The 'web' browser sitting on most computers is perhaps the most flexible and most amazingly underused component for GUI-based development.
      The single most enduring circumstance that prevents serious progress in this area is the lack of a language-neutral and credible mechanism for interprocess communication between the browser GUI front-end and perl. (or whatever your language of choice is).
      You're dead-on right about everything you've said, except this bit. I think the biggest hurdle is still the fact that developers create really ugly interfaces in html. Too much focus on functionality and not enough time spent making it pretty.

      Nobody wants to see an html page flash and render every time they hit a submit button, we want to a seamless clean interface that is just there and just works. Gmail is a great example.

      I personally have found that this is the biggest reason I like the google mail interface. It works like a real application. I click on a message and it comes up. I click on reply or forward and I can start typing right away.
Re: GUI with HTTP::Daemon
by Errto (Vicar) on Dec 18, 2004 at 21:51 UTC

    I'll tackle the meditative part first. For a long while I thought that desktop web applications were utterly pointless, because a) the GUI is there anyway, and b) embedded HTTP daemons cost so much in overhead (for example, I started playing with embedded Tomcat recently; cool concept but enormous RAM hog and a 10MB installation (which is a lot if you're offering something for download)). Then I saw the Google Desktop Search and that changed my opinion. So now my answer is "if it's the sort of thing that would make sense in a normal web app, it makes sense as a desktop web app."

    For the SOPW, I usually prefer not to do it that way. I once tried capturing the body.onunload event in Javascript but I couldn't make it work reliably. So instead I have a big link or button on the page that says "Close Me" and that button calls a Javascript function which first sends a ping back to the server (using IFRAME or XmlHttpRequest) with a URL that indicates the client is exiting and then calls window.close().

      the GUI is there anyway

      Is it? It may be in Windows, but X has no widgets (controls) of its own. It doesn't even manage windows itself. In X, you need a toolkit in order to easily program GUI applications. There are many toolkits, and it's hard to pick one. It's even harder to pick one with portability in mind. On the other hand, almost every platform has a browser, and they're all compliant enough to program a coherent application in.

      embedded HTTP daemons cost so much in overhead (for example, I started playing with embedded Tomcat recently; cool concept but enormous RAM hog and a 10MB installation (which is a lot if you're offering something for download)).

      Perhaps that's what you're used to, but with Perl, I think things are different. You already need to carry a heavy interpreter around, and adding LWP to that doesn't add much to disk space and memory usage. On my box, Perl with HTTP::Daemon takes 5 MB of memory. That is of course more than the 3 MB a bare interpreter is, but it is less than, for example, having Tk loaded (6.5 MB). (Perl + Gtk2 = 14 MB.) And you probably have LWP loaded anyway :)

      if it's the sort of thing that would make sense in a normal web app, it makes sense as a desktop web app

      That's still not a clear line you draw. When does something make sense in a web application? E-mail was once for client side MUAs, but sites like Hotmail and Gmail have rapidly changed that.

      So instead I have a big link or button on the page that says "Close Me" and that button calls a Javascript function which first sends a ping back to the server (using IFRAME or XmlHttpRequest) with a URL that indicates the client is exiting and then calls window.close().

      Then how do you shut down if the more tech savvy user hits a hotkey to close the browser, or even kill the browser? Or if the browser crashes, or if the user surfed to another site?

      Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

        Perhaps that's what you're used to, but with Perl, I think things are different. You already need to carry a heavy interpreter around, and adding LWP to that doesn't add much to disk space and memory usage. On my box, Perl with HTTP::Daemon takes 5 MB of memory. That is of course more than the 3 MB a bare interpreter is, but it is less than, for example, having Tk loaded (6.5 MB). (Perl + Gtk2 = 14 MB.) And you probably have LWP loaded anyway :)

        I think your're totally right there. In my present similar situation at work, if I knew with confidence that even a small percentage of my desktop users had Perl on their machines, I would be all over HTTP::Daemon. As it is, they don't and I'm struggling to find a good alternative.

        E-mail was once for client side MUAs, but sites like Hotmail and Gmail have rapidly changed that.

        I agree with this as well. I think Gmail is a real slap in the face to people who say "the web browser as GUI is dead, it has passed the limit of its potential, let's just wait for XAML or XUL or whatever's next" and it has inspired me to start doing more creative things in my dynamic HTML work. I'm just saying the obvious candidates are those things that are similar to what you'd typically see in a web site.

        Then how do you shut down if the more tech savvy user hits a hotkey to close the browser, or even kill the browser? Or if the browser crashes, or if the user surfed to another site?

        To my knowledge, body.onunload is the only portable way to do that. Other approaches are browser specific. For example, if your browser is MSIE on Win32, then you can launch it with something like (untested):

        my $browser = Win32::OLE->new('ShDocVw.InternetExplorer'); $browser->{Visible} = 1; $browser->Navigate("http://localhost:$port/someurl");
        and then periodically query the $browser object to make sure it's still there (I forget how exactly but the MSDN site has all the docs).

Re: GUI with HTTP::Daemon
by jplindstrom (Monsignor) on Dec 19, 2004 at 13:47 UTC
    When it comes to web based GUIs, XUL::Node looks really interesting.

    The thing with a portable web GUI is that it can't really be that portable anyway. If you build a GUI on top of the browser you need to use pretty browser specific things to make the GUI capable and, most importantly, usable enough to compete with the features of a proper GUI toolkit.

    If the program is something people use for long periods of time on a daily basis, this is very important. After all, if web browsers are so great environments to work in, where are all the web based IDEs we all love and cherish? :)

    Re: detecting application exit: First, try to catch the onUnload event Errto talked about. But that doesn't work in all browsers if the user closes the window with the X button (it works pretty well in recent IEs I think), so it may be interesting to also ping the server from client (the web browser) at regular intervals to make sure the client is still there.

    /J

Re: GUI with HTTP::Daemon
by kings (Sexton) on Dec 19, 2004 at 23:43 UTC
Re: GUI with HTTP::Daemon
by bbfu (Curate) on Dec 19, 2004 at 20:37 UTC

    And finally, a small quest for Perl wisdom: what's a portable way to find out if the user has left the "site" or closed the browser window? So far, I've told users of these little applications to hit ^C when done, but that's not pretty.

    Idle musings... Doesn't pretty much every browser now support Connection: Keep-Alive? Is there a way to consistently induce this behavior and, if so, couldn't you then just watch for the socket closing in the HTTP::Daemon server and assume that means the window was closed?

    bbfu
    Black flowers blossom
    Fearless on my breath

      Doesn't pretty much every browser now support Connection: Keep-Alive?

      Yes, but the browser usually closes the socket when it's done rendering a page, IIRC.

      Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

        Ah, that may well be the case. I thought that the browser kept it open until the site was left or the window closed but I can certainly see how it might make sense to close it when done rendering. Oh well.

        Hrm. If the browser does indeed close the connection after rendering the page, what about opening your own connection manually via javascript? Are javascript sockets automatically closed when the page is closed / unloaded? I would think they would be, so that means you could perhaps simply open a connection to a "keep alive" server, stick it in a globally scoped variable, and then just forget about it. I haven't done enough with javascript sockets to know if this is feasible. Alternatively, you could perhaps use a hidden frame that never finishes rendering to accomplish the same effect.

        bbfu
        Black flowers blossom
        Fearless on my breath

Re: GUI with HTTP::Daemon
by qbxk (Friar) on Dec 29, 2005 at 05:48 UTC
    Hey Juerd! I recently posted a module that encapsulates this exact concept - we really had the exact same idea here, see A different approach to generating a GUI for the post, or ServerApp for my code (which is still young and wants for features) It let's you forget about all the "server stuff" and just map your functions to the requesting urls, you can define a match by regex, string or a more complicated function that decides... see the forthcoming documentation, or the code itself for now.