6

On the Apple II BASIC in the 1980s, you would use "HGR", to get to a screen, "HCOLOR" would set a color, and "HPLOT" would plot points. You could also manipulate the screen byte data, and you wouldn't have to hand off program control to any event handler. This ridiculously rudimentary functionality seems to be missing today from every language and every library.

So I wrote a small library to do this in C using the X Window API: HGR(x,y) puts up an XWindow, HCOLOR(i) sets the color, and HPLOT(i,j) plots a point, plus you extract the window's bitmap and can modify it and display the modified thing. This was not straightforward, and I don't want to go through it again.

I am using Perl for some things now, and I need such a tool for Perl, something where you can call a subroutine that preferably returns without spawning a new thread, having output a window, where you can add graphical stuff to the window, and query the events when your program feels like it. This is the functionality of 1980s BASIC, so it shouldn't be hard. But the tools I saw didn't do it:

  • GD: As far as I could see, this makes graphics files, not graphics windows. It seems you need to pipe it to another program.
  • Gtk2: I have to fork out a separate thread to catch the events, otherwise it won't display the window. I don't want to fork anything, and I mostly don't care about any events, not unless I specifically ask to see the stuff piled up in the queue later.
  • Qt: Here you are also expected to hand off control to the event handler, no handing off, no display. In the C++ version, I could get Qt to display and draw without a handoff.
  • PerlTk: Again with the stupid handoff.
  • OpenGL: Here, I don't know how to catch X Window events, like a keypress, when I feel like looking at what happened.

Does anyone know how to put up an XWindow from a running application, without forking out a GUI application process, where you can draw primitive things in the window like points and lines, and where you can query your windows' events on your own schedule? I briefly looked at the X Window API for Perl, it's as horrendous as C, and interfacing the C code with Perl is also horrendous. If necessary, I'll do it. But maybe not. Is there an HGR for Perl already?

If you are wondering what I mean, no event loop graphical library, see this linked paper, which unfortunately is for C. Such a thing is extremely useful for scientific stuff, outside of introductory programming classes.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • It probably can't be done without forking another process in some way. You'll notice that the library in the linked paper takes that approach for XWindows. Probably the best way to go is to write an app that accepts drawing commands from `stdin` (and possibly sends events to `stdout`) and then write an interface to it in Perl (and possibly other languages). – cjm Sep 30 '12 at 05:47
  • @cjm: You don't need to fork, the paper is old. My C HGR has no fork and it works. The reason to fork in old systems is to deal with refresh events (where your window is obscured and unobscured) and I had problems with that 15 years ago, but they went away with recent versions of X. X is built to do this--- the graphics model is communicating with the X-server by sending commands and getting events, not by dispatches. But the libraries on top of X don't take advantage of this, and follow Mac/Microsoft brain-damage and make the event-loop mandatory. I'll have to port the C with X11::protocol. – Ron Maimon Sep 30 '12 at 06:00
  • 2
    If your C HGR library does what you need, could you wrap it in Perlishness via Inline::C, and thus expose its API to Perl? – DavidO Sep 30 '12 at 06:48
  • @DavidO: Inline C worked without effort on the first try, the problem is solved, and if you post it as an answer, I will accept. – Ron Maimon Sep 30 '12 at 19:16
  • Related: http://stackoverflow.com/questions/1051333/combing-an-external-event-loop-with-qts – Ron Maimon Oct 01 '12 at 04:58
  • @Ron Maimon Glad it worked. Inline::C is great. Its autowrap feature is really convenient too. Anyway, I've posted a brief answer that I think conveys the information you needed. – DavidO Oct 01 '12 at 05:01
  • @DavidO:The problem is that I spoke too soon: so not like openid.anonym has given the right way to do this in Gtk, and I found the way in Qt on another question on this site (in C++, but it's the same issue) you use QApplication::ProcessEvents. Thanks for your suggestion, but I must accept the other fellow's answer, as it is really the best answer to the posted question (although your method is the one that I will actually use), thank you again, inline C is a fantastic tool. – Ron Maimon Oct 01 '12 at 05:06
  • Inline::C: *(although your method is the one that I will actually use)*: That's reward enough. I'm glad it worked. Sometime you might check out Inline::CPP too (shameless plug; I maintain it). – DavidO Oct 01 '12 at 06:28

4 Answers4

4

Pretty much every GUI toolkit supports building windows without entering the event loop. Here is tk example

#!/usr/bin/perl --
use strict;
use warnings;
use Tk;
my $mw = tkinit();
my $ca = $mw->Canvas(
    background => 'white'
)->pack(qw/-expand 1 -fill both /);
$mw->geometry( '500x600+10+10' );
for my $cc (
    { red   => \&red, green => \&der, blue => \&erd },
    { green => \&red, red   => \&der, blue => \&erd },
    { green => \&red, blue  => \&der, red  => \&erd },
  )
{
    $ca->delete('all');
    my %color = %$cc;

    for my $fill ( keys %color ) {
        my $red = $color{$fill};
        for my $t ( 10 .. 220 ) {
            my $d = 0.1;
            my ( $x, $y ) = $red->( $t, 0.1 );
            $_ += 250 for $x, $y;
            $ca->createOval(
                $x, $y,
                ( $t * 2 * $d ) + $x,
                ( $t * 2 * $d ) + $y,
                -fill => $fill,
            );
            $mw->update;
            select undef, undef, undef, 0.001;
        }
        $mw->update;
    }
}

Tk::DoOneEvent(0) while Tk::MainWindow->Count; ## MainLoop;
exit 0;

sub red {
    my ( $t, $d ) = @_;
    my $x = $t * sin( $t * $d );
    my $y = $t * cos( $t * $d );
    $x, $y;
}

sub der {
    my ( $t, $d ) = @_;
    my $x = $t * sin( $t * $d );
    my $y = $t * cos( $t * 2 * $d );
    $x, $y;
}

sub erd {
    my ( $t, $d ) = @_;
    my $x = $t * sin( $t * 2 * $d );
    my $y = $t * cos( $t * $d );
    $x, $y;
}

__END__

Both Gtk2 and Wx support the same thing, its either update/show/display ... and the windows get drawn, and when you want user interaction, you call MainLoop or run the app or handle it yourself, its just a while loop dispatching pending events

wxTheApp->Dispatch       while wxTheApp->Pending;
Gtk2->main_iteration     while Gtk2->events_pending;
so not liopa
  • 485
  • 2
  • 5
  • +1 and thanks a bunch--- doing Gtk2->main_iteration a couple of times worked for me! I will use my C library inline, as it is tiny, fast, and binary, and I am familiar with it, but at least now I know how to do it using standard tools. Do you know the analog of main_iteration in Qt? – Ron Maimon Oct 01 '12 at 04:54
  • The Qt analog is QApplication::ProcessEvents, according to another question on this site. I haven't tried it in Perl, but in the question linked in the comments, it worked for the fellow in C++. I will accept this answer--- it is correct and complete. – Ron Maimon Oct 01 '12 at 05:07
1

I recommend using the Perl module Inline::C. That module makes it easy to expose a C library to your Perl application. With some C libraries the 'AUTOWRAP' option makes it completely trivial. But even if you have to write some wrappers yourself, Inline::C really eases the XS burden.

DavidO
  • 13,812
  • 3
  • 38
  • 66
0

I'm not exactly sure what your end goal is, and this may not help, but perhaps you want to look at David Merten's App::Prima::REPL. David has been building a plotting library for Perl/PDL built on the Prima graphical toolkit. To take it to its logical extreme, he built a REPL around it, making a GUI REPL with plotting built-in. As you say, Prima relies on an event-loop to function, however, because you are inside a REPL you still can direct the REPL to plot new data. His goal is scientific use too!

Joel Berger
  • 20,180
  • 5
  • 49
  • 104
  • The end goal is to be able to write "HGR(600,400)" in Perl to get a 400 by 600 graphics window, and then to write "HCOLOR(65535)" and "HPLOT(40,50)" to choose a color and put a point in the window, all without any fork. This is to follow what's going on in a complicated simulation in real time, the simulation is doing the bulk of the computing, the graphics is called rarely and you only need keyboard events occasionally to change parameters. This is how all graphics programming worked before the mac screwed everything up. – Ron Maimon Sep 30 '12 at 16:13
  • @RonMaimon An event-loop-based GUI just requires that you set everything up, start the loop, and have a button or keystroke that starts whatever simulation you want to run. From there it seems to be exactly the same as far as I can tell. Is there something that the event-loop does that messes up your code? – Joel Berger Sep 30 '12 at 18:24
  • I know what an event-loop-based GUI is, I don't want it. I have a normal command-line program, compatible with normal pipes. I want to munch munch munch, and when I feel something interesting has happened in the simulation, to pop up a few windows and draw with primitive byte data, and query the window back for any key/mouse events, just to change parameters. I did this in XWindows, it is _vastly_ superior to buttons and menus for scientific work, and now it works in Perl thanks to Inline C. The event loop GUI philosophy makes programs into stand-alone products, not my cup of tea. – Ron Maimon Sep 30 '12 at 19:52
0

I think you somewhat missed the plot of things like GLib/GTK in that case. It's very-much possible to handle GTK without forking; in fact if you do things like fork you are likely doing it wrong there. What you describe should be easily possible using GTK.

LeoNerd
  • 8,344
  • 1
  • 29
  • 36
  • It is not possible in PerlGtk2 as far as I can see, if you post sample code in Perl that puts up a window, draws a circle, and does a busy loop while checking for a keystroke/mouseclick every once in a while, then exits when it gets the keystroke (without forking a Gtk2 main loop), I will accept this answer. But I wrote a working Gtk2 window-draw routine, then I omitted the event loop call, and it would not show the window, no matter what I did. The same for Qt. – Ron Maimon Sep 30 '12 at 19:18