Network Programming with Perl

April 1st, 1999 by James Lee in

Using Perl to make network task is easy—here's how.

Perl has been called the glue that holds the Internet together because it is an extremely powerful text processing and CGI programming language. Although Perl was designed in the beginning to be a text manipulation language, it has evolved into a potent multi-purpose programming language. One area in which Perl has shown its power is that of network programming.

Perl makes network programming easy by providing built-in functions that can be used to create low-level client/server programs from scratch. Also, many modules are freely available that make programming common networking tasks simple and quick. These tasks include pinging remote machines, TELNET and FTP sessions. This article presents examples of each of these types of network programs.

Introduction

Client/server network programming requires a server running on one machine to serve one or more clients running on either the same machine or different machines. These different machines can be located anywhere on the network.

To create a server, simply perform the following steps using the built-in Perl function indicated:

  • Create a socket with socket.

  • Bind the socket to a port address with bind.

  • Listen to the socket at the port address with listen.

  • Accept client connections with accept.

Establishing a client is even easier:

  • Create a socket with socket.

  • Connect (the socket) to the remote machine with connect.

Several other required functions and variables are defined in the Socket.pm module. This module is probably already installed on your machine, but if not, it is available at the Comprehensive Perl Archive Network (CPAN), the official Perl source code repository (see Resources). To use this module in our programs, the following statement is required at the top of the program:

use Socket;

This statement will locate the file Socket.pm and import all of its exported functions and variables.

Viewing Module Documentation

All examples in this article use modules that are available at no cost from CPAN.

Perl modules are usually self-documenting. If the author of the module follows the generally accepted rules of creating a Perl module, they will add Plain Old Documentation (POD) to the module's .pm file. One way to view the POD for the Socket module (assuming Perl and Socket.pm were installed correctly) is to execute the following at the shell:

perldoc Socket

This command displays Socket.pm's POD converted to a man page. The output is a relatively thorough discussion of the functions and variables defined in this module.

Another way to view the documentation is to convert the POD to text using:

pod2text \
/usr/lib/perl5/i686-linux/5.00404/Socket.pm | more

The program pod2text is included in the Perl distribution, as are the programs pod2html, pod2man, pod2usage and pod2latex.

A Simple Server

Listing 1.

Our first programming example is a simple server running on one machine that can service only one client program at a time connecting from the same or a different machine. Recall that the steps for creating a server were to create a socket, bind it to a port, listen at the port and accept client connections.

Listing 1, server1.pl, is the source code for this simple server. First, it is generally a good idea to compile using Perl's strict rules:

use strict;

This requires all variables to be declared with the my function before they are used. Using my may be inconvenient, but it can catch many common syntactically correct yet logically incorrect programming bugs.

The variable $port is assigned the first command-line argument or port 7890 as the default. When choosing a port for your server, pick one that is unused on your machine. Note that the only way to ensure you select a port that does not have a predefined use is to look at the appropriate RFC (see Resources).

Next, the socket is created using the socket function. A socket is like a file handle—it can be read from, written to or both. The function setsockopt is called to ensure that the port will be immediately reusable.

The sockaddr_in function obtains a port on the server. The argument INADDR_ANY chooses one of the server's virtual IP addresses. You could instead decide to bind only one of the virtual IP addresses by replacing INADDR_ANY with

inet_aton("192.168.1.1")

or

gethostbyname("server.onsight.com")
The bind function binds the socket to the port, i.e., plugs the socket into that port. Then, the listen function causes the server to begin listening at the port. The second argument to the listen function is the maximum queue length or the maximum number of pending client connections. The value SOMAXCONN is the maximum queue length for the machine being used.

Once the server begins listening at the port, it can accept client connections using the accept function. When the client is accepted, a new socket is created named CLIENT which can be used like a file handle. Reading from the socket reads the client's output and printing to the socket sends data to the client.

To read from a file handle or socket in Perl, wrap it in angle brackets (<FH>). To write to it, use the print function:

print SOCKET;

The return value of the accept function is the Internet address of the client in a packed format. The function sockaddr_in takes that format and returns the client's port number and the client's numeric Internet address in a packed format. The packed numeric Internet address can be converted to a text string representing the numeric IP using inet_ntoa (numeric to ASCII). To convert the packed numeric address to a host name, the function gethostbyaddr is used.

Let's assume all of the servers referred to in this article are started on the machine named server.onsight.com. To start the server on this machine, execute:

[james@server networking]$ server1.pl
SERVER started on port 7890

The server is now listening at port 7890 on server.onsight.com, waiting for clients to connect.

A Simple Client

Listing 2.

Listing 2, client1.pl, shows a simple client. The first command-line argument to this program is the host name to which it should connect, which defaults to server.onsight.com. The second command-line argument is the port number which defaults to 7890.

The host name and the port number are used to generate the port address using inet_aton (ASCII to numeric) and sockaddr_in. A socket is then created using socket and the client connects the socket to the port address using connect.

The while loop then reads the data the server sends to the client until the end-of-file is reached, printing this input to STDOUT. Then the socket is closed.

Let's assume all of the clients are started on the the machine named client.avue.com, although they could be executed from any machine on the network. To execute the client, type:

[james@client networking]$ client1.pl server.onsight.com
Hello from the server: Tue Oct 27 09:48:40 1998

The following is the standard output from the server:

got a connection from: client.avue.com [192.168.1.2]
Perl Makes Life Easy

Creating sockets using the functions described above is good when you want to control how the socket is created, the protocol to be used, etc. But using the functions above is too hard; I prefer the easy way—IO::Socket.

The module IO::Socket provides an easy way to create sockets which can then be used like file handles. If you don't have it installed on your machine, it can be found on CPAN. To see this module's POD, type:

perldoc IO::Socket
A Simple Server Using IO::Socket

Listing 3.

Listing 3, serverIO.pl, is a simple server using IO::Socket. A new IO::Socket::INET object is created using the new method. Note that the arguments to the method include the host name, port number, protocol, queue length and an option indicating we want this port to be immediately reusable. The new method returns a socket that is assigned to $sock. This socket can be used like a file handle—we can either read the client output from it, or write to it by sending data to the client.

A client connection is accepted using the accept method. Note the accept method returns the client socket when evaluated in scalar context:

$new_sock = $sock->accept()

and returns the client's socket and the client's IP address when evaluated in list context:

($new_sock, $client_addr) = $sock->accept()
The client address is computed and printed the same as in Listing 1, server1.pl. Then the socket associated with that client is read until end-of-file. The data read is printed to STDOUT. This example illustrates that the server can read from a client using < > around the socket variable.
A Simple Client Using IO::Socket

Listing 4.

Listing 4, clientIO.pl, is a simple client using IO::Socket. This time, a new object is created that connects to a host at a port using the TCP protocol. Ten strings are then printed to that server, then the socket is closed.

If the server in Listing 3, serverIO.pl, is executed and then the client Listing 4, clientIO.pl, connects, the output would be:

[james@server networking]$ serverIO.pl
got a connection from: client.avue.com [192.168.1.2]
hello, world: 1
hello, world: 2
hello, world: 3
hello, world: 4
hello, world: 5
hello, world: 6
hello, world: 7
hello, world: 8
hello, world: 9
hello, world: 10
Bidirectional Communication

It is possible to create servers and clients that communicate with one another in both directions. For instance, the client may send information to the server, then the server may send information back to the client. Therefore, network programs can be written so that the server and client follow some predetermined protocol.

Listing 5.

Listing 5, server2way.pl, shows how a simple server can be created to read a command from a client, then print out an appropriate response to the client. The module Sys::Hostname provides a function named hostname that returns the host name of the server. To insure output is seen as we print, IO buffering is turned off for the STDOUT file handle using the autoflush function. Then a while loop is executed that accepts connections. When a client connects, the server reads a line from the client, chopping off the newline character. Then a switch statement is executed. (The switch is cleverly disguised as a foreach loop, which happens to be one of my favorite ways of writing a switch.) Depending on the input entered by the client, the server outputs an appropriate response. All lines from the client are read until end-of-file.

Listing 6.

Listing 6, client2way.pl, shows the companion client. A connection to the server is made, then the client prints a few commands to the server reads the response and prints the response to STDOUT.

The following is the output of the client code in Listing 6:

[james@client networking]$ client2way.pl
server.onsight.com
Hi
server.onsight.com
Tue Oct 27 15:36:19 1998
DEFAULT
A Forking Client

If you want to write a client that accepts commands from STDIN and sends them to the server, the easiest solution is to write a client that forks a child. (A solution can be written using select that does not fork, but it is more complicated.) The client's parent process will read the commands from the user through STDIN and print them to the server. The client's child process will then read from the server and print the responses to STDOUT.

Listing 7.

Listing 7, clientfork.pl, is an example of a client that forks.

To fork in Perl, call the cleverly named fork function. It returns undef if the fork fails. If it succeeds, it returns 0 to the child, non-zero (the child's pid) to the parent. In clientfork.pl, an if statement checks the value of $kid, the return value from the fork. If $kid is true (non-zero, the child's pid), parent executes reading from STDIN printing to the server. If $kid is false (zero), the child executes reading from the server printing to STDOUT.

The following is the example session executing the client code in Listing 7, clientfork.pl connecting to the code in Listing 5, server2way.pl:

[james@client networking]$ clientfork.pl
server.onsight.com
NAME
server.onsight.com
DATE
Tue Oct 27 15:42:58 1998
HELP
DEFAULT
HELLO
Hi

When the parent process is finished reading from STDIN, it executes the kill function to kill the child process. It is very important the parent reap its child so that the child does not outlive the parent and become a zombie.

A Forking Server

Listing 8.

Servers usually don't handle only one client at a time. One approach to a server that can handle more than one client is a server that forks a child process to handle each client connection. Listing 8, serverfork.pl, is an example of a forking server.

One way for the parent process to reap its children is to define a subroutine and assign a reference to that subroutine to $SIG{CHLD}. (The hash %SIG is Perl's way of handling signals.) In this example, a subroutine named REAP is defined and a reference to this subroutine is assigned to $SIG{CHLD}. When the parent receives the CHLD (child terminated) signal, the REAP subroutine will be invoked.

Within the while loop that accepts all the client connections, the server forks. If the fork returns true, the parent is running and it executes the next statement which immediately transfers control to the continue block, performs the housecleaning step of closing the child socket and waits for the next client to connect. If the fork returns undef, then the fork failed, so the server dies. If the fork returns neither true nor undef, then the child is running, so the parent socket is closed and the child reads from the client and processes the client. When the child is finished processing the client, the child exits and is reaped by the parent.

Thread Programming in Perl5.005

Perl version 5.005 supports thread programming. This means a threaded networking program can be created to be either a server or a client.

Listings 9, 10, and 11 are three different versions of a client that logs into several web servers and determines the type of server being used (Apache, Netscape, etc).

Listing 9.

Listing 9, getservertype1.pl, shows a non-forking, non-threaded client. First, an array of hosts is created and initialized to a few web sites. The subroutine doit is defined to receive the web server name as an argument, open a client connection to that server at port 80 (the HTTP port), send the HTTP request and read each line of response. When the line starting Server: is read, it will extract the server name and store it in $1. Then the host name and web server name are printed. This subroutine is called for each host in the array of hosts.

Here is the output of getservertype1.pl:

processing www.ssc.com...
www.ssc.com: Stronghold/2.2 Apache/1.2.5 PHP/FI-2.0b12
processing www.linuxjournal.com...
www.linuxjournal.com: Stronghold/2.2 Apache/1.2.5 PHP/FI-2.0b12
processing www.perl.com...
www.perl.com: Apache/1.2.6 mod_perl/1.11
processing www.perl.org...
www.perl.org: Apache/1.2.5
processing www.nytimes.com...
www.nytimes.com: Netscape-Enterprise/2.01
processing www.onsight.com...
www.onsight.com: Netscape-Communications/1.12
processing www.avue.com...
www.avue.com: Netscape-Communications/1.12

Note that the hosts are processed in the same order as stored in @hosts.

Listing 10.

Listing 10, getservertype2.pl, is a forking version of getservertype1.pl. The forking occurs within the foreach loop. The fork is executed and if it returns true, the parent then executes the next statement to the next host name. If the fork returns undef, then the program dies. Otherwise, the child calls the doit function passing in the host, then exits. After the parent completes its work in the while loop, it waits for all child processes to finish, then exits.

Here is the output of getservertype2.pl:

processing www.ssc.com...
processing www.linuxjournal.com...
processing www.perl.com...
processing www.perl.org...
processing www.nytimes.com...
processing www.onsight.com...
processing www.avue.com...
www.onsight.com: Netscape-Communications/1.12
www.nytimes.com: Netscape-Enterprise/2.01
www.avue.com: Netscape-Communications/1.12
www.linuxjournal.com: Stronghold/2.2 Apache/1.2.5 PHP/FI-2.0b12
www.perl.com: Apache/1.2.6 mod_perl/1.11
www.ssc.com: Stronghold/2.2 Apache/1.2.5 PHP/FI-2.0b12
www.perl.org: Apache/1.2.5
Parent exiting...

Note that the hosts are not printed in the order stored in @hosts. They are printed in the order processed, the slower hosts taking longer than the faster ones.

Listing 11.

Listing 11, getservertype3.pl, is a threaded version. In the loop through the host names, a new Thread object is created. When creating the Thread, the new method is passed a reference to a subroutine that the thread will execute, as well as the arguments passed into that subroutine. The thread then executes its subroutine and when the subroutine returns, the thread is destroyed. Here is the output of getservertype3.pl:

processing www.ssc.com...
processing www.linuxjournal.com...
processing www.perl.com...
processing www.perl.org...
processing www.nytimes.com...
processing www.onsight.com...
processing www.avue.com...
www.nytimes.com: Netscape-Enterprise/2.01
www.onsight.com: Netscape-Communications/1.12
www.avue.com: Netscape-Communications/1.12
www.linuxjournal.com: Stronghold/2.2 Apache/1.2.5 PHP/FI-2.0b12
www.perl.com: Apache/1.2.6 mod_perl/1.11
www.ssc.com: Stronghold/2.2 Apache/1.2.5 PHP/FI-2.0b12
www.perl.org: Apache/1.2.5
Net::Ping Module

Listing 12.

The Net::Ping module makes pinging hosts easy. Listing 12, ping.pl, is a program similar to a program on my server that pings my ISP to keep my connection alive. First, a new Net::Ping object is created. The protocol chosen is tcp (the choices are tcp, udp and icmp; the default is udp). The second argument is the timeout (two seconds). Then an infinite loop is executed, pinging the desired host. The ping() method returns true if the host responds, false otherwise, and an appropriate message is printed. Then the program sleeps ten seconds and pings again.

An example output of Listing 12, ping.pl, is:

Success: Wed Nov  4 14:47:58 1998
Success: Wed Nov 4 14:48:08 1998
Success: Wed Nov 4 14:48:18 1998
Success: Wed Nov 4 14:48:28 1998
Success: Wed Nov 4 14:48:38 1998
Success: Wed Nov 4 14:48:48 1998
Net::Telnet Module

Listing 13.

The Net::Telnet module makes automating TELNET sessions easy. Listing 13, telnet.pl, is an example of connecting to a machine, sending a few system commands and printing the result.

First, a server and a user name are used. The user name defaults to the user running the script by assigning to $user the value $ENV{USER}. (The hash %ENV contains all of the environment variables the script inherits from the shell.)

Next, the password is requested, then read in. Note that turning off the stty echoing is done through a system call. It can also be done using the Term::ReadKey module.

Then, a Net::Telnet object is created. To log in to the server using this object, the login method is called. Several system commands are executed using the cmd method which returns the STDOUT of the system command which is then printed. Note that part of that output is the system prompt, which is printed along with the output of the command.

Also note that the code $tn->cmd('/usr/bin/who') is evaluated in list context and stored in @who, which is an array that contains all the lines of ouptut of that command, one line of output per array element.

After all of the system commands are executed, the TELNET session is closed.

Here is an example output of Listing 13, telnet.pl:

Enter password:
Hostname: server.onsight.com
[james@server james]
Here's who:
james tty1 Oct 24 21:07
james ttyp1 Oct 27 20:59 (:0.0)
james ttyp2 Oct 24 21:11 (:0.0)
james ttyp6 Oct 28 07:16 (:0.0)
james ttyp8 Oct 28 19:02 (:0.0)
[james@server james]
What is your command: date
Thu Oct 29 14:39:57 EST 1998
[james@server james]
Net::FTP Module

Listing 14.

The Net::FTP module makes automating FTP sessions easy. Listing 14, ftp.pl, is an example of connecting and getting a file.

A Net::FTP object is created, the login is called to log in to the machine, the cwd changes the working directory and the get method gets the file. Then the session is terminated with quit.

There are methods to do many common FTP operations: put, binary, rename, delete, etc. To see a list of all the available methods, type:

perldoc Net::FTP

Here is an example output of Listing 14, ftp.pl:

[james@k2 networking]$ ftp.pl server.onsight.com
Enter your password:
Before
----------------------------------------
/bin/ls: *.gz: No such file or directory
----------------------------------------
After
----------------------------------------
perl5.005_51.tar.gz
----------------------------------------
Archive a Web Site

Using both Net::Telnet and Net::FTP, a very simple script can be created that can archive a directory structure on a remote machine.

Listing 15.

Listing 15, taritup.pl, is a Perl program that is similar to a program I use that logs in to my ISP and archives my web site.

The steps this program follows are:

  • Start a session on the remote machine with TELNET.

  • Go to the web page directory using cd.

  • Archive the directory using tar.

  • Start an FTP session to the remote machine.

  • Change to the directory containing the tar file.

  • Get the tar file.

  • Quit the FTP session.

  • Back in the TELNET session, delete the tar file on the remote machine.

  • Close the TELNET session.

This program outputs text to let the user know how the script is progressing.

Summary

Perl is a powerful, easy-to-use programming language. That power and ease of use includes network programming due to many built-in functions and modules. Best of all, it's free.

Resources

James Lee is the president and founder of Onsight (http://www.onsight.com/). When he is not teaching Perl classes or writing Perl code on his Linux machine, he likes to spend time with his kids, root for the Northwestern Wildcats (it was a long season), and daydream about his next climbing trip. He also likes to receive e-mail at james@onsight.com.

Getting Started with

Written by Steve Lidie
by Steve Lidie, April 1999

A favorite Perl module of mine is the Tk widget toolkit. With this collection of widgets, you can write sophisticated graphical applications, or just jazz-up your old command line program. Oh, you don't know what a widget is? How about a gadget, or a control? Actually, they're all just names that refer to a graphical element, such as this button:

Image Figure 1

You don't often see a naked button - usually it's embedded in a window, as part of a complete program. When you push the button it reacts and does something. The point here is to realize that widgets are just discrete entities that you arrange to make a whole graphical application. Tk has lots of other widgets, like labels, menus, dialogs, scrollbars, listboxes, and so on, as well as geometry managers to arrange them as you desire. Of course, there's more to a graphical application than just presenting a pretty window - it must interact with a life form (humanoid only, currently). As we'll see, things known as events help mediate the application-human conversation.

Tk was developed by John Ousterhout, originally for use with his scripting language, Tcl. That's where I first discovered it, and I found I could write my GUIs considerably faster that ever before. The Tk programs were also one tenth the size, had fewer bugs, and were more robust than an Xlib version.

I was hooked on Tk, but I faced a dilemma, because I was also hooked on Perl! I sometimes found myself writing a program in both Tcl/Tk (for the GUI) and Perl (for the systems stuff) - a sad state of affairs, indeed. Then Nick Ing-Simmons arrived on the scene, and, after a lot of work, molded Tk version 4 into a Perl extension. But Nick didn't stop there, he expanded Perl/Tk by adding new widgets, incorporating Tix and user contributed widgets, and making the package object oriented. Throughout the years Perl/Tk has been updated, tweaked and tuned, and now corresponds to Tcl/Tk version 8.0. The Perl/Tk version I base this article on is Perl 5.005_02 and Tk800.013, both available on the CPAN (for Unix), or http://www.activestate.com/(for Win32).

CPAN stands for Comprehensive Perl Archive Network, 100 or so machines spread around the globe, each a repository of everything that's Perl. I presume you have Perl installed and running somewhere, and, if it's not at least version 5.005_02, you'll update it. I do not presume you have Perl/Tk, so in a nutshell, here's how you install it.

Assuming you're on Unix, use a web browser and go to http://www.perl.com/ and click on the "Nearest CPAN Site" link. Then link to "authors", "Nick Ing-Simmons". Grab the latest tar file, presumably "Tk800.013.tar.gz" and save it somewhere. Like all well behaved Perl modules, the following steps should suffice for installation:


gunzip Tk800.013.tar.gz
tar -xvf Tk800.013.tar
cd Tk800.013
perl Makefile.PL
make
make test
make install

If you do not have the proper permissions to install Tk, you'll have to fetch and install Perl in your own file space before you can install Tk.

For Win32 users, install the latest Perl from the ActiveState site mentioned above. Then, at a DOS prompt, run PPM (the Perl Package Manager program), and install Tk:


ppm
PPM>install Tk
PPM>quit

To verify that Tk was installed properly, run the widget program from a Unix or DOS prompt. This program demonstrates most of the Perl/Tk widgets, and lets you view the code, modify it, and rerun demonstrations with your changes. Writing a GUI is fundamentally different than the classical line mode program - you use a technique called event driven programming. Essentially, it's your job to write subroutines, or callbacks, that are automatically called when certain events occur. Interesting events might be a button press, a file becoming readable, the expiration of a timer, or typing a keyboard character, and it's the subroutine's job to handle the event appropriately. To illustrate the concepts, we'll examine the typical "Hello World" program - actually, I'll show two versions. Here's the first, which produced Figure 2:

Image Figure 2


1 #!/usr/bin/perl -w
2 #
3 # hiworld1 - "Hello World" with a Label and explicit event binding.
4
5 use Tk;
6 use strict;
7
8 my $mw = MainWindow->new;
9
10 my $label = $mw->Label(-text => 'Hi World');
11 $label->pack;
12 $label->bind('' => sub {$mw->destroy});
13
14 MainLoop;

Note lines 1 and 6, where we specify run with warning enabled (-w) and strict programming style (use strict). I highly recommend you too write all your Perl/Tk program this way - it will find subtle (and sometimes stupid!) errors.

Line 3 is a comment, telling us that the program displays a Label widget. Labels (usually) draw simple text strings, in a particular font, size and color. Labels are also the simplest Tk widget - about the only other thing you can do with them is create explicit event bindings. More on this shortly.

Line 5 imports all the Tk widgets, definitions, variables and subroutines.

Line 8 is required by all Perl/Tk programs - it opens the program's first (main) window, the container within which (most) all your other widgets are arranged. This widget arrangement, or layout, is performed by a geometry manager. The geometry manager determines the actual location and size of a widget in the main window, relative to all other widgets. A widget never appears in the main window until it has been managed by a geometry manager.

Line 8 also demonstrates the OO (object oriented) nature of Perl/Tk. Here the scalar variable $mw is initialized with an object reference to a new instance of a MainWindow. We'll learn some basic OO concepts as we go along, but you needn't be an OO guru to use Perl/Tk, as long as you're not building new Perl/Tk widgets -and we aren't.

Line 10 creates our first widget, an instance of a Label, which displays the string "Hi World". (There may be many widgets of class Label in a program, and each may have a different text string, font, font size and color, but any particular one is known as an instance.) This line demonstrates typical Perl/Tk widget creation syntax: an object ($mw) has inherent operations called methods - Label() is one, a constructor method, because is constructs a new instance of a label widget. The methods are really subroutines that may have parameters, specified as keyword/value pairs. All keywords, like -text, start with a dash. Note that we use the => operator as the argument list separator, not to be confused with the -> operator used to invoke a method on an object.

Line 11 tells the pack() geometry manager to manage the label in the main window, which then displays it (Figure 2).

Line 12 is how we exit the program. It defines an anonymous subroutine that sends a "destroy" message to the object $mw, the main window. destroy() is a method which frees all memory associated with a widget and removes it from the display. When $mw is destroyed, everything contained in it is destroyed as well, and the program ends.

Line 12 uses the bind() method to associate an event with a subroutine. Subroutines of this type are termed callbacks, because the code is called back when the event in question (here, pressing Button-1 on the mouse) occurs.

Line 14 kick-starts the program by entering the Tk event loop. The event loop dispatches events to callbacks (yours and Tk's), and runs continuously until the main window goes away. Forget this statement and your Perl/Tk program thinks a while, then exits without displaying anything!

In summary, here are the characteristics of most Perl/Tk programs:

* A use Tk statement which imports the base Tk definitions.
* A MainWindow that contains other application widgets.
* Widget creation commands to populate the main window.
* Binding and callback creation commands.
* Geometry management commands to arrange and display widgets.
* A MainLoop() command to begin program execution.



Now let's look at a better version of the previous "Hello World" program; this new program generated Figure 3 . Compare Figures 2 and 3 and note the visual difference. There area several other subtle differences, as we'll see shortly.

Image Figure 3


1 #!/usr/bin/perl -w
2 #
3 # hiworld - "Hello World" with a Button and implicit class bindings.
4
5 use Tk;
6 use strict;
7
8 my $mw = MainWindow->new;
9
10 my $button = $mw->Button(-text => 'Hi World', -command => \&exit);
11 $button->pack;
12
13 MainLoop;

Line 3 reminds us that the program displays a Button widget rather than a Label, and that the button has "implicit class bindings". In this case, the button has a builtin binding for a event, so we don't have to create it manually as we did for the label. This binding applies to every instance of class Button - it's one feature that differentiates labels from buttons.

A button also has a three dimensional appearance due of its "raised" relief. If you'd like to make a label look like a button, just change its -relief option using the ubiquitous configure() method:


$label->configure(-relief => 'raised');

But adding a relief and a button binding still doesn't turn a label into a button because there are even more class bindings inherited by a button instance. Move the cursor over a button and watch what happens - it becomes highlighted. This is because the button has an binding, which invokes a builtin Tk callback that lightens the button. Similarly, the button has a binding to restore the widget's appearance when the cursor leaves. And finally, there are bindings to change the button's relief to "sunken" when selected, and then back to "raised".

Line 10 creates the button, and now we know some of things that go on behind to scene to make our programming life easier. The -command argument automatically links the code reference \&exit to a event.

It's time for something more complex - how about an application skeleton? By that I mean a main window with a menubar across the top, and File and Help menubuttons, which you can use as a basis for your real applications. Along the way we'll visit the Dialog widget, icons, and see some window manager commands. To make things really easy I've included a tiny Perl program named tkskel that will generate our mini application.

To use tksel, type something like "tkskel frog", and a complete Perl/Tk program named frog is created. Here's the resulting code:


1 #!/usr/bin/perl -w
2 #
3 # frog, Sun Apr 11 18:01:41 1999
4
5 use 5.005;
6 use Tk 8.0;
7 use Tk::widgets qw/Dialog/;
8 use subs qw/build_menubar fini init/;
9 use vars qw/$MW $VERSION/;
10 use strict;
11
12 $MW = MainWindow->new;
13 init;
14 MainLoop;
15
16 sub build_menubar {
17
18 # Create the menubar and File and Quit menubuttons. Note
19 # that the cascade's menu widget is automatically created.
20
21 my $menubar = $MW->Menu;
22 $MW->configure(-menu => $menubar);
23 my $file = $menubar->cascade(-label => '~File');
24 my $help = $menubar->cascade(-label => '~Help', -tearoff => 0);
25
26 # Create the menuitems for each menu. First, the File menu item.
27
28 $file->command(-label => "Quit", -command => \&fini);
29
30 # Finally, the Help menuitems.
31
32 $help->command(-label => 'Version');
33 $help->separator;
34 $help->command(-label => 'About');
35
36 my $ver_dialog = $MW->Dialog(-title => 'frog Version',
37 -text => "frog\n\nVersion $VERSION",
38 -buttons => ['OK'],
39 -bitmap => 'info');
40 my $about_dialog = $MW->Dialog(-title => 'About frog',
41 -text => 'About frog',
42 -buttons => ['OK']);
43 my $menu = $help->cget('-menu');
44 $menu->entryconfigure('Version', -command => [$ver_dialog => 'Show']);
45 $menu->entryconfigure('About', -command => [$about_dialog => 'Show']);
46
47 $menubar; # return the menubar
48
49 } # end build_menubar
50
51 sub fini {
52
53 exit;
54
55 } # end fini
56
57 sub init {
58
59 $VERSION = '1.0';
60
61 $MW->title("frog $VERSION");
62 $MW->iconname('frog');
63 my $xpm = $MW->Photo(-file => "skull.xpm", -format => 'xpm');
64 $MW->Icon(-image => $xpm); # full color icon picture
65
66 my $menubar = build_menubar;
67 my $frame = $MW->Frame(qw/-width 300 -height 50 -background cyan/)->pack;
68
69 } # end init

With the exception of line 7, I suspect lines 1 through 14 are self-explanatory. Line 7 is how we import Tk widgets that are not "core" widgets. Here are the 15 standard widgets automatically imported when we say "use Tk": Button, Canvas, Checkbutton, Entry, Frame, Label, Listbox, Menu, Menubutton, Message, Radiobutton, Scale, Scrollbar, Text, and Toplevel.

Lines 16 through 49 build the menubar, but I'll defer most of that discussion for another time. For now, it remains a task for you, gentle reader, to figure out!

Line 61 is a window manager command - it defines the text shown at the top of the window's title bar.

Some window managers support iconization, and line 62 simply gives our icon a name. Likewise, line 64 changes the default appearance of the icon to a colored image of our own design. To do that we first need to create a Tk image object, which is what line 63 does. The image is a small XPM file of, fittingly, a skull, grabbed from http://www.skellramics.com/ and used with their permission. Here's what frog looks like iconified:

Image Figure 4

Lines 66 and 67 complete the creation of the GUI elements. Line 66 creates the menubar and related components and saves its widget reference in $menubar, just in case we need to (re)configure it later. Line 67 creates a colored frame and packs it after the menubar. I don't cover geometry management here, suffice it to say that by default the packer arranges widgets top down in the main window, like this:

Image Figure 5

Notice that the Help menubutton has been pressed, a menu posted, and the Version menu item highlighted. Pressing Version pops up this modal dialog:

Image Figure 6

Notice that the dialog is independent of our main window - it's built from a special widget called a toplevel, which is similar to a main window. And like a main window, you can pack other widgets inside a toplevel. When we press OK the dialog is withdrawn from the display - but it's not destroyed - dialogs are persistent objects and must be explicitly destroyed.

Obviously, there's a lot more we can talk about, but enough's enough. To help straighten out the tangle I've no doubt made of your mind, take a look at these important Perl/Tk resources (in no particular order):

The man pages! For instance: perldoc Tk::Dialog

My list of important Perl/Tk links: http://www.Lehigh.EDU/~sol0/ptk/ptk.html

Perl and the Tk Extension

Using X from Perl

Steve Lidie

Well, hello! This is the first of several articles on the Tk toolkit, a marvelous object oriented Perl extension that provides a comprehensive widget collection for all kinds of spiffy graphical applications. Tk was developed by John K. Ousterhout and adapted and extended for Perl by Nick Ing-Simmons.

The Tk extension for Perl is referred to as Perl/Tk, and runs under the X Window System found on most Unix computers. X uses a client/server model, where clients (such as the one you are about to see) communicate with a server that manages the computer's display, keyboard, and mouse. For every display there is a window manager that provides a consistent "look and feel", at least at a high level, for all client's sharing the machine's display. There are many different window managers, but they all provide similar facilities, such as iconifying, moving, resizing windows, and framing them in decorative borders. You'll see window manager commands in later columns.

This article contains a gentle introduction to the fundamentals of Perl/Tk, after which it develops, step by step, a real application. As is typical of my writing style I often generate more questions than I answer, so you will want to keep this URL handy:

http://w4.lns.cornell.edu/~pvhp/ptk/ptkFAQ.html This is the location of the Perl/Tk FAQ, the repository of Almost Everything Ever Written About Perl/Tk, thoughtfully maintained by Peter Prymmer.

The current version of Perl/Tk is Tk800.012 and builds successfully against Perl 5.004_04 or 5.005. To obtain the latest Tk distribution link to a CPAN site near you by visting http://www.perl.com/

Perl/Tk programs are written using the object oriented syntax $object->method, where $object refers to a Tk widget, perhaps a Buttom or Menu, and method names an action to be performed. We'll learn more about objects and such in the next column, but now, without further ado, here is your prototypical "hello world" program written in Perl/Tk, swiped from the Tk distribution:

#!/usr/bin/perl -w
#
# Simple Tk script to create a button that prints "Hello, world". Click on
# the button to terminate the program.
#
# The first line below imports the Tk objects into the application, the
# second line creates the main window, the third through fifth lines create
# the button and define the code to execute when the button is pressed, the
# sixth line asks the packer to shrink-wrap the application's main window
# around the button, and the seventh line starts the event loop.

use Tk;
$MW = MainWindow->new;
$hello = $MW->Button(
-text => 'Hello, world',
-command => sub {print STDOUT "Hello, world\n"; exit;} );
$hello->pack;
MainLoop;

When the program is executed, this window appears:

Image



The program may or may not be self-explanatory! The main window, $MW, is the program's first toplevel window - the primary "container" for most, if not all, descendant widgets, which form a hierarchy (each widget always has a parent, and might have children as well).

This particular toplevel widget has a single child object belonging to the Button class. All widgets are objects derived from a base class, inheriting its characteristics. You might have several instances of button objects that look quite different, but share the distinguishing characteristics of class Button: they display a text label or bitmap, and "do something" when selected. When the button in the example is pressed the anonymous subroutine is executed which prints "Hello, world" and exits. The subroutine is called because it is bound to the button click. Almost all widget classes have default button and keypress bindings established by Perl/Tk, and you can add, delete or modify bindings on a class or per-widget basis as you see fit.

The statement:

$hello = $MW->Button( ... ); is a widget creation command: an object of class Button is constructed and configured with the specified options, which becomes a descendant of widget $MW, the main window. The variable $hello is initialized with an object reference to the newly created button widget. In Perl an object reference is just an ordinary reference that points to "something" that has been "blessed" (using the Perl bless() function) into a certain class. The "something" is a hash or a list, and the act of blessing an object ties it to a particular class. Perl/Tk widget objects are hashes, as shown in this debug run: Dandy:/home/bug/perl/tpj1/perl -de 0

Loading DB routines from perl5db.pl version 1.02
Emacs support available.

Enter h or `h h' for help.

main::(-e:1): 0

DB<1> use Tk
DB<2> $ref = {}
DB<3> $MW = MainWindow->new
DB<4> $oref = $MW->Button
DB<5> p $ref
HASH(0x200f78c8)
DB<6> p $oref
Tk::Button=HASH(0x2021c780)

The variable $ref is a plain reference to an anonymous hash, whereas $oref is an object reference to a hash of class Tk::Button. But from now on, I'll refer to variables like $hello and $oref simply as objects or widgets. (If you're not familiar with the Perl debugger, the idiom perl -de 0 starts an interactive instance of perl where you can debug, or simply enter Perl commands - a great prototyping environment.)

The statement:

$hello->pack; is a method invocation command: the Tk geometry manager known as the packer is invoked to assign a size and position to the $hello object, and then to "map" it. A widget must be mapped (or realized) before it becomes visible on the display. By default widgets are always packed inside their parent, and if you don't specify otherwise the packer aligns them in a column, from top to bottom.

Perl/Tk programs are event driven, meaning that you do not write a "main loop" in the standard sense, but rather delegate that job to Tk. Instead, you write small code sections, referred to as callbacks, a fancy name for a subroutine, to process those events and which Tk invokes as required. There are many Tk events that need to be processed in a timely fashion: timers, file input and output, and motion and button events generated by your mouse. You activate the Tk event loop with a MainLoop() statement, which should be the last line called by your program.

In summary, most Perl/Tk applications share these common features:

  • A use Tk statement at the beginning of the program which imports the base Tk definitions.
  • A primary MainWindow as the root of the widget hierarchy.
  • A series of widget creation commands.
  • Optional binding and callback creation and registration commands. (More about these soon.)
  • A series of geometry commands to pack widgets in a pleasing and user friendly manner.
  • A MainLoop() command to begin program execution. (Actually, there are times when you must control event processing yourself; we'll see an example of this in a later column.)

Tk provides 15 standard widgets; Perl/Tk provides additional composite widgets like ColorEditor, Dial, FileSelect, LabEntry and Table. Composite widgets, also called megawidgets, are complex objects built from these standard widgets.

  • Button widgets execute a callback when invoked. They're derived from the Label widget.
  • Canvas widgets provide a drawing surface for text and graphics.
  • Checkbutton widgets select one or more items from a list. They're derived from the Label widget.
  • Entry widgets allow users to enter and edit a single text string.
  • Frame widgets are primarily used as containers to group other widgets; for instance, during packing. Frames might be arranged inside an application's main window, with other widgets inside them. Frames are also used as spacers and to add colored borders.
  • Label widgets display a text or image label. Button, Checkbutton and Radiobutton widgets are derived from the Label widget.
  • Listbox widgets display a list of strings and allow users to select one, a range, or a scattered set of the strings.
  • Menu widgets are special widgets which work in conjunction with Menubuttons. Invoking a Menubutton displays its associated menu. There are various kinds of menu items, such as buttons, checkbuttons, radiobuttons, separators and cascades.
  • Menubutton widgets display a label (just like Buttons) but when selected display a Menu.
  • Message widgets are similar to Labels except they display multiline strings.
  • Radiobutton widgets select a single item from a list. They're derived from the Label widget.
  • Scale widgets consist of a slider which allows users to specify a value by moving the slider.
  • Scrollbar widgets control the view of other widgets, like Canvas, Entry, Listbox, and Text. Users can scroll the widget by dragging the slider.
  • Text widgets display lines of editable text. Characters in a text widget can be colored, given specific fonts, spacing, margins and more.
  • Toplevel widgets are essentially secondary MainWindows. They resemble Frames in that they act as container widgets, except they aren't internal widgets.

The Perl/Tk application that I am going to develop is called Plot Program, or plop for short, featuring Button, Canvas, Dialog, Frame, Label, LabEntry, Menu, Menubutton, Scrollbar and Text widgets. Plop plots a list of mathematical functions of the form $y = f($x), where $x iterates from the graph's X-minimum to X-maximum. Each function is evaluated in turn for a particular value of $x, a Y value is computed and then a point is painted on the canvas. (Plop emphasizes the canvas widget because I've noticed that new Tk users, after watching around 2000 lines of canvas documentation roll by, tend to place "exploring the canvas widget" at the end of their to-do list! I think you'll find that feature-rich does not mean difficult to use.)

Remembering that a canvas widget can be thought of as an artist's canvas for free-hand drawing of graphics and text, we'll treat it as a classical Cartesian coordinate system. A key difference is that the canvas origin, coordinate position (0,0), is defined to be the top-left corner of the canvas window, and that canvas X coordinates increase when moving right (as you'd expect) and Y coordinates increase when moving down (as you wouldn't). Also, canvas coordinates can't have negative values. For these reasons, we'll use an equation to transform between canvas and Cartesian coordinates.

Here's the very first version of the program:

#!/usr/bin/perl -w
use strict;
use Tk;
my($o, $s) = (250, 20);
my($pi, $x, $y) = (3.1415926, 0);
my $mw = MainWindow->new;
my $c = $mw->Canvas(-width => 500, -height => 500);
$c->pack;
$c->createLine(50, 250, 450, 250);
$c->createText(10, 250, -fill => 'blue', -text => 'X');
$c->createLine(250, 50, 250, 450);
$c->createText(250, 10, -fill => 'blue', -text => 'Y');

for ($x = -(3*$pi); $x <= +(3*$pi); $x += 0.1) {
$y = sin($x);
$c->createText( $x*$s+$o, $y*$s+$o, -fill => 'red', -text => '.');
$y = cos($x);
$c->createText( $x*$s+$o, $y*$s+$o, -fill => 'green', -text => '.');
}

MainLoop;

Granted, this is really ugly code, lacking in style, but it's a "proof of concept". As you'll see, I'll whip this code into proper shape pronto! Before I explain it, here's what it looks like when executed:

    Image
Some global variables are initialized, the main window, $MW, and a canvas widget, $c, are created and the canvas is realized. The next four statements create two canvas line items (for the graph axes) and two text items (for the axes labels). Other canvas item types are arc, bitmap, image, oval, polygon, rectangle and window.

The statements:

$c->createLine(50, 250, 450, 250);
$c->createText(10, 250, -fill => 'blue', -text => 'X');
draw and annotate the X axis. The canvas method createType creates a canvas item of type Type: here I'm creating one line item and one text item. Since the canvas is 500x500 pixels I deliberately arranged for canvas coordinate position (250,250) to coincide with the Cartesian origin (0,0). I also wanted to have 50-pixel wide top/bottom and left/right margins. Given these contraints the X axis line starts at (50,250) and extends horizontally to (450,250), with a blue letter "X" painted in the left margin at (10,250). Similarly, the Y axis is stroked vertically from top to bottom and labeled with a blue "Y". Now all that remains is to actually graph some functions.

The for statement varies from -3P to + 3P radians, and even old biology-types like myself know that sine() and cosine() return a value in the range [-1,1]. Such tiny values aren't especially useful unless you're looking for a graph one pixel high, so a transform is required:

$y = sin($x);
$c->createText($x*$s+$o, $y*$s+$o, -fill => 'red', -text => '.');
We want to scale our $y values, which is what the expression $y*$s+$o does: the Y value is enlarged 20 times and translated to the canvas origin. Then a red dot is deposited on the canvas. (There's actually a bug is the transform equation; can you spot it? Hint, try graphing the exp() function.)

So much for the ugly plop prototype; with a lot of work I can turn this code into a first-rate Perl/Tk application. For starters I want to eliminate every single hardcoded value and use variables instead. Then I'll add these features:

  • A menu across the top. Like all respectable applications, it'll have File and Help menubuttons.
  • Atitle for the graph.
  • Adjustable minimum and maximum X and Y values.
  • An editable list of functions.
  • The option to read in functions from a file. Heck, let's just do it: eval {require "plop.fnc";}. Store your private functions in the file plop.fnc and they'll be available for plotting. For instance, plop.fnc might contains these lines if you wanted to graph the hyperbolic arctangent:

    • sub atanh {
      return undef if ($_[0] < -1 or $_[0] > 1);
      .5 * log((1 + $_[0]) / (1-$_[0]));
      }

Below you'll see a sample run of the new plop. The complete program is available at TPJ web site:

Image


The main window is divided into three major regions, a top frame with menubuttons (containing the File and Help menus), the canvas in the middle (including the title and boundary values), and a bottom area containing a series of other widgets (including a scrollable text widget with the list of functions). The Perl code has been modularized and looks something like this:

my $MW = MainWindow->new;
initialize_dialogs;
initialize_menus;
initialize_canvas;
initialize_functions;
Subroutine initialize_dialogs() creates dialog widgets that are not part of the main window proper, but popup at certain times, wait for the user to intervene, and then go away. Typically they persist for the lifetime of the application, thus they are created once during program initialization and are then hidden by withdrawing them until it's time to "Show" them; Show() is a dialog method that deiconifies the widget, waits for the user to select a dialog button and then returns the label of the selected button to the program. Here is how plop's "About" dialog widget is created: $DIALOG_ABOUT = $MW->Dialog(
-title => 'About',
-text => "plot_program $VERSION\n\n 95/12/04",
-bitmap => 'info',
-buttons => ['Dismiss']);
Like all widget creation commands, $MW->Dialog() returns a reference to an object. The buttons attribute is a list of strings that specifiy the dialog's button labels. In this case, there's only one button, "Dismiss", which withdraws the dialog after you've read the really informative "About" information!

To create the plop menus, initialize_menus() reuses some old code that generates menubuttons from a data structure, mainly because I'm lazy and menus always take time to get just right. My next column goes into details on menus, cascades, etcetera, but for now examine this code:

$MBF = $MW->Frame(-relief => 'raised', -borderwidth => 1);
$MBF->pack(-fill => 'x');
make_menubutton($MBF, 'File', 0, 'left', [ ['Quit', \&exit, 0]]);
make_menubutton($MBF, 'Help', 0, 'right', [
['About', [$DIALOG_ABOUT => 'Show'], 0],
['', undef, 0],
['Usage', [$DIALOG_USAGE => 'Show'], 0]]);
The first statement creates the container frame to hold the menubuttons, with a relief of raised and a borderwidth of one. The relief attribute specifies the widget's 3D look, but you need a non-zero borderwidth to see it. Notice that the frame is packed with its fill attribute set to "x", which makes the packer geometry manager expand the frame in the X direction to fill all available space. Otherwise, the File and Help menubuttons would be mapped side-by-side and centered in the frame. Creating the menubuttons and their corresponding menu items entails calls to make_menubutton(), with these 5 parameters:
  • The parent widget.
  • The menubutton label.
  • The shortcut character index. All our menubuttons have a shortcut character index of zero. For example, the 0th (first) character of "File" is 'f', which means that users can type Alt-f to activate the File menu.
  • The side of the menu frame to pack the menubutton.
  • A list of lists describing the menu items. Each inner list has three components, a label, a callback that is executed when the menu item is invoked and a shortcut underline character. Null labels are treated as separators - do-nothing menu items that appear as lines.

Callbacks come in various flavors, and we'll see more of them in later columns, but in plop's case there are just two: an explicit reference to a subroutine (also called a code reference), and a reference to an array. An example of the first form is the Quit menu item, which calls exit(). The Help menu items use the second form, where the first array element is an object (widget reference) and the second is the name of the method to invoke. Thus, when the user selects "About" the about dialog widget appears. Note that widgets used in callbacks must exist before they are referred too - that's precisely why the dialog widgets were created first.

Subroutine initialize_canvas() generates the middle area of plop's main window, but is slightly different than the prototype version because it has a title line, embedded widgets with editable X and Y values, and axes moved to the borders of the area to reduce visual clutter.

$CANV = $MW->Canvas(
-width => $MAX_PXL + $MARGIN * 2,
-height => $MAX_PXL,
-relief => 'sunken');
$CANV->pack;
$CANV->CanvasBind('' => \&display_coordinates);
The above code creates the canvas but uses global "constants" rather than hardcoded values: $MAX_PXL is obviously the size of the canvas, in pixels. Here is our first callback registration command, which binds the subroutine display_coordinates() to mouse button 1. $CANV->createText( 325, 25,
-text => 'Plot Continuous Functions Of The Form y=f($x)',
-fill => 'blue');
Nothing new there, eh? But wait, something new follows, the canvas item type called a window! # Create the line to represent the X axis and label it. Then label the
# minimum and maximum X values and draw tick marks to indicate where they
# fall. The axis limits are LabEntry widgets embedded in Canvas windows.

$CANV->createLine(
$MIN_PXL + $MARGIN, $MAX_PXL - $MARGIN,
$MAX_PXL - $MARGIN, $MAX_PXL - $MARGIN);

$CANV->createWindow(
$MIN_PXL + $MARGIN, $MAX_PXL - $label_offset,
-window => $MW->LabEntry(
-textvariable => \$X_MIN,
-label => 'X Minimum'));

$CANV->createLine(
$MIN_PXL + $MARGIN, $MAX_PXL - $MARGIN - $tick_length,
$MIN_PXL + $MARGIN, $MAX_PXL - $MARGIN + $tick_length);

$CANV->createWindow(
$MAX_PXL - $MARGIN, $MAX_PXL - $label_offset,
-window => $MW->LabEntry(
-textvariable => \$X_MAX,
-label => 'X Maximum'));

$CANV->createLine(
$MAX_PXL - $MARGIN, $MAX_PXL - $MARGIN - $tick_length,
$MAX_PXL - $MARGIN, $MAX_PXL - $MARGIN + $tick_length);

The first canvas line item is simply the horizontal X axis, and the two remaining lines are the tick marks at each end. The two window items are containers where other objects can be stuffed, in this case two composite LabEntry widgets, which, as you might guess, combine the features of label and entry widgets. Their textvariable attributes are references to scalars $X_MIN and $X_MAX; when the program changes the variable's value it's reflected on the display, and when the user edits a LabEntry the associated textvariable is updated. The Y axis is handled in a similar manner.

Subroutine initialize_functions() creates plop's remaining widgets, which are, in top-to-bottom packing order, a spacer frame, a label providing rudimentary instructions, a text widget with an attached scrollbar, and finally another container frame to hold a button or so.

$MW->Frame(-height => 20)->pack;
$MW->Label(
-text => 'Enter your functions here',
-foreground => 'blue')->pack;

# Create a Frame with a scrollable Text widget that displays the function
# list, and a Button to initiate plot activities.

my $functions_frame = $MW->Frame;
$functions_frame->pack;
$TEXT = $functions_frame->Scrolled(qw/Text -height 6 -scrollbars e/ );
$TEXT->pack;
update_functions;

my $buttons_frame = $MW->Frame;
$buttons_frame->pack(-padx => 10, -pady => 5, -expand => 1, -fill => 'x');
my @pack_attributes = qw/-side left -fill x -expand 1/;
$buttons_frame->Button(
-text => 'Plot',
-command => \&plot_functions)->pack(@pack_attributes);

Ho hum, a 20 pixel high frame (so much for the ban on hardcoded constants!) to occupy space and some instructional text inked in blue. (But did you know that anywhere you can give a dimension as an integer pixel value you can also suffix the characters i, c, m or p, to indicate inches, centimeters, millimeters or points?) Then there's the text widget with a scrollbar anchored "east", and lastly a large "Plot" button. Notice the convenience method Scrolled() for attaching scrollbars to the text widget. The text widget contains the function list, which is particularly appropriate since each line can be tagged and assigned a different color. The function points are then plotted in that color.

The graphical interface in now complete, and when the user invokes the "Plot" button, the callback plot_functions() is executed. Before actually plotting the function list plop tidies up the text window and ensures that each function is assigned its proper color. Plop provides for up to nine simultaneous functions before the colors cycle. Here's the code:

$TEXT->delete('0.0', 'end');
my $i = 0;
foreach (@FUNCTIONS) {
$TEXT->insert('end', "$_\n", [$i]);
$TEXT->tagConfigure($i,
-foreground => $COLORS[$i % $NUM_COLORS],
-font => '9x15');
$i++;
}
$TEXT->yview('end');
First, everything is deleted, from line zero, character zero, to the end of the text widget. Then each function from the @FUNCTIONS array is inserted and assigned a tag, which just happens to be its ordinal in the text widget. A tag is simply an identifying string used for reference in other widget commands. In this case, the tagged text items are configured with their unique foreground color and assigned a fixed space font.

Now that the text widget is in synch with the function list, let's plot some functions:

$CANV->delete('plot');
$canv_x = $MIN_PXL + $MARGIN; # X minimun
$DX = $X_MAX - $X_MIN; # update delta X
$DY = $Y_MAX - $Y_MIN; # update delta Y

ALL_X_VALUES:
for ($x = $X_MIN; $x <= $X_MAX; $x += ($X_MAX - $X_MIN) / $ALEN) {

ALL_FUNCTIONS:
foreach (0 .. $#FUNCTIONS) {
$y = eval $FUNCTIONS[$_];
$canv_y = (($Y_MAX - $y) / $DY) * $ALEN + $MARGIN;
$CANV->createText($canv_x, $canv_y,
-fill => $COLORS[$_ % $NUM_COLORS],
-tags => ['plot'],
-text => '.') if $canv_y > $MIN_PXL + $MARGIN and $canv_y < $MAX_PXL - $MARGIN;
} # forend ALL_FUNCTIONS
$canv_x++; # next X pixel

} # forend ALL_X_VALUES

After all this we're back to where we started, except that the code has been made more general and the transform equation has been fixed. $X_MIN and $X_MAX are dynamically assigned because they are part of the LabEntry widgets, and the X increment is dynamically calculated based on those values and the axis length. Y points painted on the canvas are automatically assigned their proper colors. And each point is tagged with the string "plot", so all current graphs can be easily deleted the next time the "Plot" button is pushed; that's what the $CANV->delete('plot') statement is for.

But there is one stone left unturned: the button binding established during canvas creation. Since we already know how to convert a Cartesian coordinate to a canvas coordinate, I thought it would be fun to do the opposite: click anywhere on the canvas to display a Cartesian coordinate. The following code demonstrates how to handle an X event structure, in this case a button press:

sub display_coordinates {
my($canvas) = @_;
my $e = $canvas->XEvent;
my($canv_x, $canv_y) = ($e->x, $e->y);
my($x, $y);
$x = $X_MIN + $DX * (($canv_x - $MARGIN) / $ALEN);
$y = $Y_MAX - $DY * (($canv_y - $MARGIN) / $ALEN);
print "\n Canvas x = $canv_x, Canvas y = $canv_y.\n";
print "Plot x = $x, Plot y = $y.\n";
} # end display_coordinates
When a binding callback is executed the subroutine is implicitly passed a reference to its widget - here, the canvas. Using XEvent(), the variable $e is now assigned a reference to the event structure. Two of $e's methods, x() and y(), return the relative canvas coordinate position of the mouse when button 1 was pressed. Once the coordinates are known it's a simple matter of using the existing transform equation, solving for X and Y, and printing the results.

That's it for this time, if you have questions about this article, or ideas for upcoming ones, feel free to contact me. Next time we'll look more into objects, build a composite widget and examine menus in more detail.


Steve Lidie is a Systems Programmer at Lehigh University.
Comments
Help ...
Written by pearl on 2006-06-16 08:37:47
Why this code is not working ?

#!/usr/bin/perl -w
use Tk;
my $mw = MainWindow->new();
$mw->title("QRec 1.0");
$mw->geometry("725x541+140+108");

my $menubar = $mw->Menu;
my $file = $menubar->cascade( -label => '~File');

# ---- add a "Quit" command to the file menu.
$file->command( -label => '~Quit',
-command => sub{ exit; },
);
$mw->configure( -menu => $menubar );

my $textarea = $mw -> Frame();
my $txt = $textarea -> Text(-width=>100,-height=>10) -> pack(-side => 'bottom');
my $srl_y = $textarea -> Scrollbar(-orient=>'v',-command=>[yview => $txt]);
my $srl_x = $textarea -> Scrollbar(-orient=>'h',-command=>[xview => $txt]);
$txt -> configure(-yscrollcommand=>['set', $srl_y],-xscrollcommand=>['set',$srl_x]);
MainLoop;


But i removed the $textarea and directly placed the textarea ($txt) on the Main window ,is working fine ie ;
This code is working
#!/usr/bin/perl -w
use Tk;
my $mw = MainWindow->new();
$mw->title("QRec 1.0");
$mw->geometry("725x541+140+108");

my $menubar = $mw->Menu;
my $file = $menubar->cascade( -label => '~File');

# ---- add a "Quit" command to the file menu.
$file->command( -label => '~Quit',
-command => sub{ exit; },
);
$mw->configure( -menu => $menubar );

#my $textarea = $mw -> Frame();
my $txt = $mw -> Text(-width=>100,-height=>10) -> pack(-side => 'bottom');
#my $srl_y = $textarea -> Scrollbar(-orient=>'v',-command=>[yview => $txt]);
#my $srl_x = $textarea -> Scrollbar(-orient=>'h',-command=>[xview => $txt]);
#$txt -> configure(-yscrollcommand=>['set', $srl_y],-xscrollcommand=>['set',$srl_x]);
MainLoop;

Please explain me ,why it is not working and i want to place tree on the left side of the window ,just help me for that ,im new to perl/tk

download tutorial perl

Perl Beginner

What does this book cover?
  • Installing Perl on Windows and UNIX
  • Making use of online Perl resources like CPAN
  • First principles in programming and the Perl syntax
  • Working with files and databases
  • Writing web pages in Perl
  • Using Perl as an object-oriented language

Book overview

Perl is a well-established programming language that has been developed through the time and effort of countless free software programmers into an immensely powerful tool that can be used on pratically every operating system in the world. Perl started out as the "Swiss army knife" of computer languages and was used primarily by system administrators, but over time it's grown into an immensely robust language used by web-developers and programmers worldwide. It's also a great way to learn programming techniques and develop your own style of coding.

"There's more than one way to do it" is the motto of Perl, but this book aims to take you through them all. We'll take you from installation, through the core language elements - regular expressions, references, modules and the like - and onto basic applied techniques. You'll see how to access and work with databases in Perl, write CGI scripts to generate web pages and beyond. Coverage is bang up to date, using Perl 5.6 in all our examples and there's a handy reference section in the back.

Who is this book for?

This book is for those new to programming who want to learn with Perl. You will find it easier if you already have some basic programming experience, but the material covered can be mastered by anyone with just a text editor, some common sense and a computer. If you want to learn how to program with Perl and then find out far you can go with it, this is the book for you. An excellent companion on any programming course.

Wrox (now Apress) provides on-line discussion for the above title and surrounding technologies in their forums.

by Simon Cozens

PDF Setting Mikrotik hostspot authentikasi

Tutorial kali ini saya ingin membahas bagaimana mudahnya membangun router+hotspot authentikasi
menggunakan Mikrotik OS.
silakan download di pdf download

setting hotspot MIKROTIK

Setting Hotspot pada Mikrotik Router OS sangat mudah dikonfigurasi. Sistem autentikasi hotspot biasa digunakan ketika kita akan menyediakan akses internet pada areal publik, seperti : Hotel, café, Kampus, airport, taman, mall dll. Teknologi akses internet ini biasanya menggunakan jaringan wireless atau wired. Biasanya menyediakan akses internet gratis dengan menggunakan hotspot atau bisa juga menggunakan Voucher untuk autentikasinya. Ketika membuka halaman web maka router akan mengecek apakah user sudah di autentikasi atau belum. Jika belum melakukan autentikasi, maka user akan di arahkan pada hotspot login page yang mengharuskan mengisi username dan password. Jika informasi login yang dimasukkan sudah benar, maka router akan memasukkan user tersebut kedalam sistem hotspot dan client sudah bisa mengakses halaman web. Selain itu akan muncul popup windows berisi status ip address, byte rate dan time live. Penggunaan akses internet hotspot dapat dihitung berdasarkan waktu (time-based) dan data yang di download/upload (volume-based). Selain itu dapat juga dilakukan melimit bandwidth berdasarkan data rate, total data upload/download atau bisa juga di limit berdasarkan lama pemakaian.

Cara mudah setting hotspot pada mikrotik adalah ada 2 (dua) pilihan selain menggunakan teks mode kita juga bisa menggunakan setting wizard dengan menggunakan Winbox Router OS, Langkah-langkat berikut merupakan konfigurasi dasar hotspot mikrotik sebagai Gateway Server. Pertama install Mikrotik Router OS pada PC atau pasang DOM atau kalau menggunakan Rouer Board langsung aja Login = ‘admin’ sedangkan untuk pasword anda kosongin untuk defaultnya.

Masuk ke IP ==> Hotspot ==> Setup

hotspot-mikrotik

Kemudian tentukan IP lokal hospot yang akan ada gunakan, misal 192.168.10.1 dan Tentukan IP DHCP ke clientnya yang akan anda gunakan, dalam contoh ini adalah 192.168.10.2-192.168.10.255

hotspot1

hotspot3

Untuk SMTP Server sebaiknya anda kosongin saja, Kemudian DNS servernya anda isikan sesuaikan dengan Provider anda, dalam contoh ini adalah DNS1=202.47.78.1 DNS2=202.47.78.9

hotspot4

hotspot5

DNS lokal hotspot anda NEXT saja kemudian pada Hotspot user anda dalam contoh berikut diisi admin password admin123

hotspot6

hotspot7

Hotspot Server Profile digunakan untuk mensetting server yang akan sering digunakan untuk semua user seperti metode autentikasi dan Limitasi data rate. Ada 6 jenis autentikasi Hotspot mikrotik yang berbeda dalam profile setting, jenis autentikas tersebut adalah : HTTP PAP, HTTP CHAP, HTTPS, HTTP cookie, MAC address, Trial

new-hotspot-mikrotik

hotspot-profile

Metode autentikasi yang akan digunakan, biasanya cukup menggunakan metode HTTP CHAP

autentikasi

Data rate limitation digunakan sebagai default setting untuk user yang belum di setting bandwidth limit pemakaiannya. Dimana RX adalah Client upload dan TX adalah Client download. Misal setting default data rate di 64k/128k (upload/download)

bandwidth-limit

Hotspot user profile digunakan untuk menyimpan data user yang akan dibuatkan rule profilenya. Dimana didalamnya bisa dilakukan setting firewall filter chain untuk traffic yang keluar/masuk, juga bisa untuk mensetting limitasi data rate dan selain itu dapat juga dilakukan paket marking untuk setiap user yang masuk kedalam profile tersebut secara otomatis.
hotspot-user-profile

Hotspot user yaitu nama-nama user yang akan diautentikasi pada sistem hotspot. Beberapa hal yang dapat dilakukan dalam konfigurasi hotspot user yaitu : username dan password, Membatasi user berdasarkan waktu dan paket data yang akan digunakan, hanya ip address tertentu dari ip address dhcp yang ditawarkan atau hanya mengizinkan user untuk koneksi ke sistem hotspot dari MAC Address tertentu saja.

hotspot-user

IP Bindings digunakan untuk mengizinkan ip tertentu untuk membypass autentikasi hotpot, ini sangat berguna sekali ketika kita ingin menjalankan layanan server, atau IP telephony dibawah system hotspot. Misal, PC atau Notebook anda untuk dapat membypass hotspot system, dengan demikian anda dapat melakukan browsing tanpa autentikasi

ip-binding

Belajar Perl part III

Decision / selection / percabangan

Percabangan di perl terdapat dua jenis yaitu if dan unless. Pada if blok perintah akan dijalankan jika syarat atau kondisi di dalam if terpenuhi. Bentuk if di perl mirip seperti di bahasa c.

  • if tunggal :
if(ekspresi_kondisi)
{
Perintah;
}
  • if - else :
if(ekspresi_kondisi)
{
Perintah1;
}
else
{
Perintah 2;
}
  • if – elsif – else :
if(ekspresi_kondisi1)
{
Perintah1;
}
elsif(ekspresi_kondisi2)
{
Perintah2;
}
else
{
Perintah3;
}

Contoh dalam program :

#!/usr/bin/perl
# contoh penggunaan percabangan if
# nama program : percabangan_if.pl
print "apakah anda senang membuat program ?\n";
print "jawaban anda (y/t): ";
$jawab=;
chomp($jawab);
if($jawab eq "y") # if tunggal
{
print "anda memang programmer sejati\n";
}
print "\n"; # spasi

print "siapa nama anda?\n";
print "nama anda: ";
$nama=;
chomp($nama);
if($nama eq "tony") # if-else
{
print "selamat datang tony !!!\n";
}
else
{
print "maaf, anda tidak dikenal\n";
}
print "\n"; 
print "berapa umur anda?\n";
print "umur anda: "
$umur=;
chomp($umur);
if($umur <= 13) # if-elsif-else
{
print "anda masih anak anak \n";
}
elsif($umur <= 20)
{
print "anda masih remaja \n";
}
else
{
print "anda sudah dewasa \n";
}

    Sedangkan untuk unless kebalikan dari if yaitu perintah akan dijalankan jika kondisi tidak terpenuhi.

  • unless tunggal
unless(ekspresi_kondisi)
{
Perintah;
}
  • unless - else
    unless(ekspresi_kondisi)
    {
    Perintah1;
    }
    else 
    {
    Perintah2;
    }

Contoh dalam program :

#!/usr/bin/perl
# contoh percabangan unless
# nama program : percabangan_unless.pl
$spp="belum lunas";

unless($spp eq "lunas") # unless tunggal
{
print "anda belum bayar spp\n";
}
print "\n"; # spasi
print "berapa jumlah sks yang sudah anda ambil ? \n";
print "sks anda : ";
$sks=;
chomp($sks);
unless($sks > 100) # unless - else
{
print "anda belum boleh mengambil kerja praktek\n";
}
else
{
print "anda sudah bisa mengambil kerja praktek\n";
}





Looping / perulangan

Untuk mengulang suatu blok perintah digunakan perulangan. Perulangan di perl antara lain statemen for, foreach, while, do - while, until dan do - until. Untuk lebih jelasnya kita akan bahas satu per satu :

Perulangan for

For di perl sama seperti for di bahasa C/C++ maupun bahasa C like lainya. Bentuk unumnya adalah sebagai berikut :

for(keadaan awal ; syarat keadaan akhir ; increment/decrement)
{
daftar statement;
}

Perulangan foreach

Anda masih ingat dengan array? Bila tadi untuk statement for, pengulangan nya ditentukan berdasarkan inkrementasi, maka untuk statement foreach, pengulangannya ditentukan oleh jumlah anggota array. Anda masih ingat bukan bahwa jumlah anggota array dapat dihitung dengan menggunakan @array. Inilah yang digunakan sebagai pengendali bila anda membuat pengulangan dengan statement foreach

foreach $varname(array)
{
daftar statement;
}

Perulangan while

Berbeda dengan statement for dan foreach, statement while digunakan bila kita ingin mengendalikan proses pengulangan bukan berdasarkan jumlah pengulangannya , melainkan berdasarkan suatu syarat. Selama syarat tersebut masih terpenuhi, pengulangan akan terus berlangsung. Tetapi bila syarat tidak lagi terpenuhi, perulangan akan berhenti.

while(ekspresi)
{
daftar statement;
}

Perulangan do - while

Statement do - while tidak jauh berbeda dengan statement while. Prinsip kerjanya sama, tetapi strukturnya adalah sebagai berikut :

do {
daftar statement;
} while ekspresi;

Perulangan until

Statement until juga menggunakan syarat tertentu sebagai pengendali pengulangan. Berbeda dengan statement while, pada statement until pengulangan justru dilakukan selama syarat tidak dipenuhi. Begitu syarat dipenuhi, pengulangan akan berakhir. Strukturnya sebagai berikut :

until(ekspresi)
{
daftar Statement;
}

Perulangan do - until

Statement do - until tidak berbeda fungsinya dengan statement until, oleh karena itu kita tidak akan membahasnya lebih jauh. Strukturnya sendiri adalah sebagai berikut :

do {
daftar statement;
} until ekspresi;