A Basic GUI

<?php
    function doshutdown() {
        gtk::main_quit();
    }

    function btnClick($button) {
        print "Hello, console!\n";
    }

    $window =& new GtkWindow();
    $window->connect("destroy", "doshutdown");
    $button =& new GtkButton("Hello, GTK!");
    $button->connect("clicked", "btnClick");
    $window->add($button);
    $window->show_all();

    gtk::main();
?>

Our first program, which you should save as gtk1.php in your home directory, is simply the "Hello, World!" of PHP-GTK. Yes, I realise that it is twelve lines longer than a simple "print 'Hello, World!';" would have been; this is because working with graphical interfaces requires a great deal more work: you must create and assign properties to widgets, set up signal handlers, and more. However, do not be put off by the length of the script - I want to go through it line by line in order to show quite how easy it is.

Ignore the two functions near the beginning for the time being; I will come to them shortly. Beyond them lies $window =& new GtkWindow(), which is probably something quite new to you, depending on your experience with the language.

If you did not know already, "=&" means "assign to equals" rather than "copy to equals". When new GtkWindow() is called, PHP creates a new object of the class GtkWindow. If we just had $window = new GtkWindow() then PHP creates the GtkWindow, then copies it into the $window variable. So, behind the scenes, this process involves two GtkWindows, one that is created using "new" and one that is created using "=".

Using =& rather than = circumvents this problem; $window becomes a reference to the same GtkWindow created by the new operator, so the process involves only one GtkWindow being created. You're quite able to rewrite the line as $window = &new GtkWindow() if you prefer; it is all down to your coding style.

The class GtkWindow itself is a general-purpose application window, and it descends from the class GtkBin. I mention this specifically because descendants of the GtkBin container widget are only allowed to have one child widget inside them. For GtkWindow, this means that you are only allowed one widget (e.g. one button, one combo box) in your window, which may sound a little restrictive at first, but all will be revealed later on.

GtkBin itself descends from GtkContainer - it is a specific kind of container in that it allows only one child widget -- and this provides it with GtkContainer's automagic resizing of child widgets. This means that any widget created inside a GtkBin will take up all the free space inside the GtkBin, and will resize as the GtkBin is resized.

On the next line, we again use "&= new" to create a new widget: this time it is a GtkButton. When you create your button, you can pass in as a parameter the caption you wish to give the button; that is, what text you wish to appear upon it. In the above example, "Hello, GTK!" is passed for the caption of the button, and internally this is used to create a GtkLabel inside the button, to which the caption is assigned - this will become important if you later wish to change the caption of the button.

Again, the connect() function is called, this time we connect the "clicked" signal of our new GtkButton to the PHP function btnClick(). This works in the same way as our previous called to connect().

The add() method of our GtkWindow is native to all descendants of GtkContainer, and works as you would expect: the widget passed in as the first parameter is added to the container and placed where available. In the situation of GtkWindow, which, remember, is a descendant of GtkBin, this means that the widget being passed (our new GtkButton) will automatically take up all free space in the window.

The next line, $window->show_all(), translates to "Show this window, and all its child widgets". An alternate method here is show(), which would have displayed the GtkWindow but not the GtkButton.

Finally, we come to the call to gtk::main(). If this line looks a little odd to you, don't worry: it is a remnant of PHP's mantra of "If we want to do something complicated, at least make it look like C++ - that way at least some people will understand it". Technically speaking, gtk::main() is a call to the static member function "main" of class gtk. In normal circumstances, one creates an object of a class before calling a function of that class. However, sometimes it is not necessary or indeed it is counter-productive to use an object on some occasions, and so these functions are "static": always in the same place.

If this all seems complicated, do not worry about it - it really is quite complicated, and you need not understand any more about how it works other than that it is a function call you can use like any other.

What it does, though, is quite important, and I am sorry but it includes even more theory! Put simply, owing to the fact that GUIs are signal-based -- that is, they wait until told what to do -- your control over the program ceases once you have finished creating your GUI and performing any start-up tasks. Instead of running everything yourself, control gets passed onto GTK, which enters what is known as its message loop, which internally looks something like this:

while (1) {
    if user moves over widget {
        if widget has signal handler set {
            send signal to program that mouse is over widget
        } else {
            do nothing
        }
    }

    if user terminates program {
        send signal to program that it is being killed
        break out of while(1) loop
    }

    ...
}

Granted, that is quite a big simplification, however you should get the gist: GTK gets control of your GUI and does all the processing for it such as resizing buttons, dropping down combo boxes, highlighting buttons when the mouse is over, etc, and only passes control back to you by sending a signal which in turn calls your signal handler functions. While these functions are running, you are back in control, and you may run all the PHP code you like, including calling other functions. However control will eventually be passed back to GTK, again leaving you waiting for a signal to be passed.

So, calling gtk::main() instructs GTK that you are done setting up, and that you are ready for it to take control. Now, onto our two functions: doshutdown() and btnClick().

Doshutdown() was passed to our first call to connect() to tie it to the signal "destroy". The end result of this is that when the GtkWindow$window is being destroyed, that is, closed by the user, it will be sent the event "destroy" by GTK causing it to emit the signal "destroy", which in turn will call the doshutdown() function. doshutdown() has just one line inside it: a call to another static member function, gtk::main_quit().

Gtk::main_quit() only has any use when gtk::main() has been called, because it instructs GTK that you are ready to exit its message loop and resume control of the application. Generally speaking, this is the end of your application, as control is passed back to you after gtk::main_quit() has been called, PHP continues to execute any code that lies after your original gtk::main() call.

Our btnClick() function was tied to the "clicked" signal of our GtkButton, and so it will be called every time a user clicks "Hello, GTK!". The "clicked" signal is often sent part-way between two other signals, "pressed" and "released", which correspond to a mouse button being pressed on a button and a mouse button being released on a button. The "clicked" signal is also sent when the button is activated by way of the keyboard (pressing Enter, etc).

Inside btnClick(), we again only have one line, which calls some relatively simple PHP. However, it is important to realise that calling print from inside your PHP-GTK scripts allows you to write to the console as easily as if this were a standard PHP script.

A helpful bonus is that one signal can execute several functions simply by calling connect() multiple times with varying second parameters.

There you have it! Although it may seem like an awful lot of explanation is required for quite a short script, the large percentage has been the theory behind how the script works.

Go ahead and run your script with the following command line:

php -q gtk1.php

If your PHP CGI is named differently, you shall need to make the appropriate change. However, as long as you have installed PHP-GTK correctly, the end result is the same. -q, as you may know, is "quiet mode" for the PHP CGI, and it forces PHP not to emit its standard HTTP headers. You can exclude this parameter if you are using the PHP CLI SAPI.

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Multiple Windows >>

Previous chapter: GUI toolkits

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.