March 16, 2001, 10:21 AM —
Let's start with an easy example, the kind you'll want to do on your own: create a C source file, test1.c:
#include
#include
main()
{
struct timeval timevalue;
struct timezone timezone;
gettimeofday(&timevalue, &timezone);
printf("The current time is %s",
ctime((time_t *) &timevalue.tv_sec));
}
and make an executable program from it. Under Unix, you'd generally do that with a command-line invocation:
cc -o test1 test1.c
Now create the Perl/Tk source file, test1.pl:
use Tk;
$mw = MainWindow->new;
$label = $mw->Label();
$label->pack;
$mw->Button(-text => "Show the time",
-command => sub{
$label->configure(-text => `./test1`)
})->pack;
MainLoop;
There! If you execute:
perl test1.pl
you'll see a small GUI panel with a button. Pushing the button launches the C-coded program, retrieves output, and displays it. The problem's solved, for both Unix and Windows.
There's more to the story
For many programmers, though, that is only the beginning. Let's look at the example in more detail.
To run the example, we launch a Perl program, which itself controls the legacy test1 as a separate process. With all the examples in this article, it doesn't matter that test1 is compiled from C source; it could be a Fortran, C++, Java, or even another Perl program. All that matters in that architecture is that it can be launched as a command-line application, which puts its results to standard output.
Most modern languages available for Unix and Win* have at least one way of invoking an external process and retrieving its result in that way. The gluing languages we discuss in this column are particularly rich in those facilities. Most accessible to beginners are the backtick operator or function used in test1.pl above, which is also available in much the same form in Ruby, Tcl, Bourne shell, and several other languages.
A simple control panel of that sort is a great way to wrap a legacy application with complex command-line arguments or inputs. A high-level scripting language validates all the flags, ensures that they're properly consistent, then invokes the legacy application correctly. That is particularly valuable because it means that the legacy application need not change. There's no need to relink it, port it, or do anything else that might introduce delays or errors. Scripting languages just translate between a more modern GUI appearance and the older command-line interface.
Both the legacy application and the scripting wrapper can be in nearly any language. The final element of this solution's universality is the choice of GUI toolkit. Most languages bind to at least a couple of GUI toolkits, and deciding between them is independent of process invocation -- it's a free choice. The architecture works the same whether you're using Perl with Tk to run a C application or Python with Qt to control a Pascal process.
Predictable sequence
Questioners are happy to see for themselves that test1.pl and its equivalents in other languages are easy to use. They usually come back the next day, though, complaining, "The GUI locks up until the subprocess returns! I can't have that; I need for it to stay responsive, so my users can do other things. In fact, it ought to show a progress bar while the subprocess is running, and..."
Most scripting languages have a good answer to that, too, and we'll look at an example in a moment. First, though, let's clarify a couple of new concepts.
When we want the GUI wrapper to be alive at the same time the legacy application is grinding through to a result, we're asking for concurrency or multitasking. Modern operating systems know about concurrency; in fact, many support more than one programming construct for concurrency.
Java and Microsoft development kits emphasize threading for multitasked operations.













