diff --git a/tutorial/gtk_part1.wmml b/tutorial/gtk_part1.wmml new file mode 100644 index 0000000000000000000000000000000000000000..4c0c8620035a6a4430f672d401357907d8e876da --- /dev/null +++ b/tutorial/gtk_part1.wmml @@ -0,0 +1,316 @@ +<section title="The Pike GTK wrapper library" name=gtk> + +<section title="Introduction"> + +The GTK module provides an object oriented wrapper library for the <a +href="http://www.gtk.org/">GTK+ Widget library</a> + +<p> +From the GTK web-site: + +<p> +<i> +GTK (GIMP Toolkit) is a library for creating graphical user +interfaces. It is licensed using the LGPL license, so you can develop +open software, free software, or even commercial non-free software +using GTK without having to spend anything for licenses or royalties. + +<p> +It's called the GIMP toolkit because it was originally written for +developing the General Image Manipulation Program (GIMP), but GTK has +now been used in a large number of software projects, including the +GNU Network Object Model Environment (GNOME) project. GTK is built on +top of GDK (GIMP Drawing Kit) which is basically a wrapper around the +low-level functions for accessing the underlying windowing functions +(Xlib in the case of the X windows system). The primary authors of GTK +are: + +<ul> +<li>Peter Mattis <a href=mailto:petm@xcf.berkeley.edu>petm@xcf.berkeley.edu</a> +<li>Spencer Kimball <a href=mailto:spencer@xcf.berkeley.edu>spencer@xcf.berkeley.edu</a> +<li>Josh MacDonald <a href="mailto:jmacd@xcf.berkeley.edu">jmacd@xcf.berkeley.edu</a> +</ul> +<p> +</i> + +The Pike GTK module is written mainly by <a +href=http://per.hedbor.org>Per Hedbor</a> <a +href="mailto:per@idonex.se">per@idonex.se</a> + + +<p> + +Pike GTK contains more than simple wrappers for all GTK functions, +especially image handling capabilities has been added. + +<p> +</section> + +<section title="Tutorial"> +<section title="Getting started with Pike GTK"> + +First, you must have GTK 1.2 or newer installed when you compile pike, +otherwise the GTK module will not be enabled. + +<p> +If you are unsure whether or not the pike you are using has GTK, try +running 'pike -e GTK.setup_gtk()' from a shell, if you do not get an +error message, GTK is available. + +<p> + +The first program is a (very) short example program that creates a +window, and then does nothing else. + +<example language=pike> +#!/usr/local/bin/pike +void main(int argc, array argv) +{ + argv = GTK.setup_gtk( argv ); + GTK.Window( GTK.WindowToplevel )->show(); + GTK.main(); +} +</example> + +<p> + +All GTK programs must call GTK.setup_gtk() before any widgets are +created (you will get an error if you don't do this). + +<p> + +The array returned by GTK.setup_gtk() is the new argv array, all +arguments handled by GTK has been removed from the list of arguments. + +<p> + +<tt>GTK.Window( GTK.WindowToplevel )</tt> creates a new instance of a +window widget, and <tt>show()</tt> causes the window to be shown on the screen. + +<p> + +GTK.main() starts the GTK mainloop, and waits for events to +occur. Since our window does not have any signals connected to it, the +program will actually never exit, unless you press break in the shell. + +<p> + +Another way to start the GTK mainloop is to return -1 from main, this +latter approach has one rather big advantage and one small +disadvantage. + + +<p> The advantage is that you can use the normal pike backend +functions (such as nonblocking callback based I/O and timouts using +call_out) + +<p> The disadvantage is that it will use some CPU, since a polling +mechanism is used. The amount of CPU used is not even noticeable on +most machines (less than 0.001% CPU usage on my PII350 PC machine) +</section> + +<section title="Hello world"> + +Let's create a somewhat more advanced example, this time using custom +widgets and a few signals. + +<example language=pike> +class HelloWorldButton +{ + inherit GTK.Button; + void show_hi() + { + GTK.Alert( "Hi there" )->show(); + } + + void create() + { + ::create( "Hello World" ); + signal_connect( "clicked", show_hi ); + } +} + + +class MainWindow +{ + inherit GTK.Window; + + void create() + { + ::create( GTK.WindowToplevel ); + add( HelloWorldButton()->show() ); + signal_connect( "destroy", exit, 0 ); // Call exit(0) directly + } +} + +int main( int argc, array argv ) +{ + argv = GTK.setup_gtk( argv ); + MainWindow( )->show(); + return -1; +} +</example> + +This example would undoubtedly been shorter without the custom widgets +(the MainWindow and the HelloWorldButton one), but it is usually a +good idea to write pike GTK applications by overloading existing +widgets and add code in their constructor to add their contents. + +<p> + +The reason is that the MainWindows does not need to know how a +HelloWorldButton is constructed, and the main function does not need +to know how to build a MainWindow widget, it is thus very easy to +change the subwidgets later on. + +<p> +A more 'standard' approach would be: + +<example language=pike> +void show_hi() +{ + GTK.Alert( "Hi there" )->show(); +} + +int main( int argc, array argv ) +{ + GTK.Window window; + GTK.Button button; + + argv = GTK.setup_gtk( argv ); + window = GTK.Window( GTK.Windowtoplevel ); + button = GTK.Button( "Hello World" ); + + window->add( button->show() ); + + window->signal_connect( "destroy", exit, 0 ); + button->signal_connect( "clicked", show_hi ); + + window->show(); + return -1; +} +</example> +AS programs grow more complex, the object oriented approach is much +easier to maintain than the "standard" approach. +</section> + +<section name="Theory of Signals and Callbacks"> + +GTK is an event driven toolkit, which means it will sleep until an +event occurs and control is passed to the appropriate function. + +<p> +This passing of control is done using the idea of "signals". (Note +that these signals are not the same as the Unix system signals, and +are not implemented using them, although the terminology is almost +identical.) + +<p> + +When an event occurs, such as the press of a mouse button, the +appropriate signal will be "emitted" by the widget that was +pressed. This is how GTK does most of its useful work. There are +signals that all widgets inherit, such as "destroy", and there are +signals that are widget specific, such as "toggled" on a toggle +button. + +<p> +To make a button perform an action, we set up a signal handler to +catch these signals and call the appropriate function. This is done by +using the signal_connect method in the widget. + + +<tt> + int <b>signal_connect</b>( string <b>signal_name</b>, + function <b>callback_function</b> + mixed|void <b>callback_argument</b> ); +</tt> +The signal name is either a string such as "destroy", or one of the +predefined GTK.s_* constants (which are actually strings, +GTK.s_destroy is the string "destroy". + +<p> + +The specified function will be called when the signal is emitted, the +first argument will always be the one you specified as the (optional) +last argument to signal_connect, the second argument is always the +widget to which the signal was connected, all other arguments are +signal specific. +</section> + +<section title="Packing widgets"> + +When creating an application, you'll want to put more than one widget +inside a window. Our first helloworld example only used one widget so +we could simply use a <tt>add</tt> call to "pack" the widget into +the window. But when you want to put more than one widget into a +window, how do you control where that widget is positioned? This is +where packing comes in. + +<p> +Most packing is done by creating boxes. These are invisible widget +containers that we can pack our widgets into which come in two forms, +a horizontal box, and a vertical box. When packing widgets into a +horizontal box, the objects are inserted horizontally from left to +right or right to left depending on the call used. In a vertical box, +widgets are packed from top to bottom or vice versa. You may use any +combination of boxes inside or beside other boxes to create the +desired effect. + +<p> +To create a new horizontal box, we use a call to GTK.Hbox(), or create +a widget that inherits the GTK.Hbox widget, and for vertical boxes +Vbox is used instead. pack_start and pack_end member functions are +used to place objects inside of the boxes. + +<p> + The pack_start() function will start at the top +and work its way down in a vbox, and pack left to right in an +hbox. + +pack_end() will do the opposite, packing from bottom to +top in a vbox, and right to left in an hbox. + +<p> + +Using these functions allows us to right justify or left justify our +widgets and may be mixed in any way to achieve the desired effect. We +will use pack_start() in most of our examples. An object may be +another container or a widget. In fact, many widgets are actually +containers themselves, including the button, but we usually only use a +label inside a button. + +<p> +By using these calls, GTK knows where you want to place your widgets +so it can do automatic resizing and other nifty things. There are also +a number of options as to how your widgets should be packed. As you +can imagine, this method gives us a quite a bit of flexibility when +placing and creating widgets. +<p> + + + +Because of this flexibility, packing boxes in GTK can be confusing at +first. There are a lot of options, and it's not immediately obvious +how they all fit together. +</section> + +<section title="Notable differences between GTK and Pike GTK"> + +<section title="Image handling"> +</section> + +<section title="The .pgtkrc file"> + Pike GTK reads $HOME/.pgtkrc if it exists, and passes it through the + pike Pre-processor. This is mostly for compatibility with older Pike + GTK releases, it is reccommended that the normal .gtkrc file is used + instead, since this gives the same style to all GTK applications. +</section> + +</section> + +</section> + +<section title="Reference"> + <include file="gtk_reference.wmml"> +</section> \ No newline at end of file