3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4 <link rel="stylesheet" type="text/css" href="style.css">
9 <span>CL-GTK2 — a Common Lisp binding for Gtk+</span>
11 <div class="navigation">
13 <li><a href="index.html">Overview</a></li>
14 <li><a href="compatibility.html">Compatibility</a></li>
15 <li><a href="installation.html">Installation</a></li>
16 <li><a href="examples.html">Examples</a></li>
17 <li><a href="tutorial.html">Tutorial</a></li>
18 <li><a href="manual.html">Manual</a></li>
22 <h1>CL-GTK2 Tutorial</h1>
23 <p>Dmitry Kalyanov <<a href="mailto:Kalyanov.Dmitry@gmail.com"><code>Kalyanov.Dmitry@gmail.com</code></a>></p>
26 CL-GTK2 is in alpha stage and is unstable and not feature-complete. It is being developed on x86-64 gentoo linux with SBCL. It should, in general, work with other lisp compilers and on other platforms. CL-GTK2 requires some features not present in the Common Lisp Standard, namely it requires CFFI support with callbacks and long-long support (most modern lisp implementations are supported, including clisp) and it requires CLOS MOP (MetaObject Protocol) which is also is present (or is being added to) most modern lisp implementations.
29 CL-GTK2 requires Gtk+ version 2.16 or later. CL-GTK2 was tested on SBCL-1.0.18 and SBCL-1.0.28
31 <p>If you have any difficulties installing or using CL-GTK2, contact me (the author of this tutorial and of CL-GTK2) via email Kalyanov.Dmitry@gmail.com or via jabber mo3r@jabber.ru.</p>
32 <p>First, install CL-GTK2 dependencies. CL-GTK2 has the following dependencies (CL-GTK2 was tested with specified versions; it would probably not work with earlier versions but should work with later versions):</p>
34 <li><a href="http://common-lisp.net/project/cffi/">CFFI</a> (version 0.10.4)</li>
35 <li><a href="http://www.cliki.net/trivial-garbage">Trivial-Garbage</a> (version 0.18)</li>
36 <li><a href="http://common-lisp.net/project/iterate/">Iterate</a> (version 1.4.3)</li>
37 <li><a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux-Threads</a> (version 0.6.0)</li>
38 <li><a href="http://common-lisp.net/project/closer/closer-mop.html">Closer-MOP</a> (version 0.55)</li>
40 <p>Currently, CL-GTK2 is only available in Git repository at <a href="http://repo.or.cz/w/cl-gtk2.git">http://repo.or.cz/w/cl-gtk2.git</a>. If you do not want or can not use Git, download the snapshot from <a href="http://repo.or.cz/w/cl-gtk2.git?a=snapshot;h=HEAD;sf=tgz">http://repo.or.cz/w/cl-gtk2.git?a=snapshot;h=HEAD;sf=tgz</a>.</p>
41 <p>Unpack the CL-GTK2 sources, and add them to <code>asdf:*central-registry*</code>:</p>
43 (push "/path/to/cl-gtk2/glib/" asdf:*central-registry*)
44 (push "/path/to/cl-gtk2/gdk/" asdf:*central-registry*)
45 (push "/path/to/cl-gtk2/gtk/" asdf:*central-registry*)
47 <p>or create symlinks:</p>
49 $ cd ~/.sbcl/systems # or other directory in asdf:*central-registry*
50 $ ln -s /path/to/cl-gtk2/glib/cl-gtk2-glib.asd .
51 $ ln -s /path/to/cl-gtk2/gdk/cl-gtk2-gdk.asd .
52 $ ln -s /path/to/cl-gtk2/gtk/cl-gtk2-gtk.asd .
55 <p>Now you should be able to load the CL-GTK2 system:</p>
56 <pre>(asdf:operate 'asdf:load-op :cl-gtk2-gtk)</pre>
57 <p>When the system is loaded, run <code>(gtk-demo:demo-text-editor)</code>. A text editor window should pop up.
58 <img src="lisp_ide.png"></p>
59 <p>This is a very simple text editor written in CL-GTK2. Apart from editing the text, it can evaluate expressions: select expression and press the "execute" button. Expression will be evaluated and its result will be put into text view.</p>
60 <p>Let's start from a simple example.</p>
61 <p>Start Slime, type the following code in the REPL:</p>
63 (asdf:operate 'asdf:load-op :cl-gtk2-gtk)
66 (let ((window (make-instance 'gtk:gtk-window :title "Hello, world!")))
67 (gtk:widget-show window)))
70 <p>The empty window with title "Hello, world!" should appear.</p>
71 <img src="hello_world.png">
72 <p>Let's analyze this example line-by-line.</p>
73 <p><code>(asdf:operate 'asdf:load-op :cl-gtk2-gtk)</code> loads the GTK system into Lisp.</p>
74 <p>CL-GTK2 runs Gtk+ main loop in background thread (because Lisp development is interactive in its nature; if main loop would block the REPL thread, you would have to restart the Lisp image too often). Because all access to Gtk+ should come from Gtk+ thread, we should run the code in that thread. Macro gtk:within-main-loop does exactly that: it schedules the code to be tun in the Gtk+ thread. You should use this macro whenever you want evaluate the code from the REPL or when you start you application.</p>
75 <p>Next, we create the window with make-instance and set its title property to "Hello, world!".</p>
76 <p>When the window is created, it is not yet shown on the screen. To show it, we call <code>(gtk:widget-show window)</code>.</p>
77 <p>After this code executes, you should get back to the REPL (rememer, Gtk+ runs in background thread) and the window should appear on the screen.</p>
78 <p>CL-GTK2 runs the Gtk main loop in background thread. This is done so you could have your application running and interacting with the Lisp system through the REPL.</p>
79 <p>To execute some code and ensure that Gtk+ main loop is started, WITH-MAIN-LOOP macro is used. It runs the body of code within the Gtk+ main loop. Because all calls to Gtk+ functions require locking, it is neccessary to run this code from th main loop. Because we are running the code in another thread, its dynamic bindings (including <code>*standard-output*</code>) will be lost. To be able to print at REPL, we save reference to the standard output stream in the closure.</p>
80 <p>Gtk+ objects are created with make-instance and are properly garbage-collected.</p>
81 <p>Object have properties, which are represented as slots in CL-GTK2. Some properties (slots) of objects are constructor-only properties and can only be set at object construction time. For example, "type" property of GtkWindow can only be set during its creation. To access properties, you may use slot-value function or slot accessor methods. For property Y declared on class X, method X-Y returns the value of the property. Properties are setfable (with exception of read-only and constructor-only properties).</p>
82 <p>Call to container-add puts button as a child widget into window, and widget-show shows all widgets of window.</p>
83 <p>In Gtk+, objects have "signals", to which handlers can be attached. When something happens that results in "emitting" the signal (e.g., button being clicked emits "clicked" signal), all handlers of this signal are called. Handler of GtkButton's "clicked" signal has only one argument - the button that was clicked. CL-GTK2 allows attaching any function (including closures) as signal handlers and ensures that closure is freed properly.</p>
86 <p>GObject is an object system that is at the core of Gtk+. GObject provides:</p>
89 <li>Classes. Classes define which properties, methods and signals are present in instances of this class and how are they implementation. Classes are grouped into single-inheritance hierarchy. Classes implement zero or more interfaces.</li>
90 <li>Interfaces. Interfaces, like classes, define which properties, methods and signals are present in instances of classes that implement this interface but does not specify their implementation.</li>
91 <li>Properties. Properties are attributes of objects that have type and can be read or set. Classes provide implementation of getter and setter procedures. Properties may be readable, writable. Some of properties may only be set at object construction time.</li>
92 <li>Signals. Signals and callbacks are used to connect event handling code with code that notifies about events.</li>
93 <li>Memory management. Objects can be created and destroyed. Memory is managed with reference counting.</li>
95 <h2>Defining GObject classes</h2>
96 <p><code>G-OBJECT-CLASS</code> metaclass is used to define classes corresponding to GObject classes. <code>G-OBJECT</code> is base class for all classes corresponding to GObject classes.</p>
97 <p><code>G-OBJECT-CLASS</code> uses <code>:G-TYPE-NAME</code> option (mandatory) and <code>:G-TYPE-INITIALIZER</code> option (optionally). <code>:G-TYPE-NAME</code> option specifies the GObject class type name that corresponds to the class. <code>:G-TYPE-INITIALIZER</code> specifies the function name that returns the GType of the class.</p>
98 <p>Classes of <code>G-OBJECT-CLASS</code> metaclass may have regular slots and GObject slots. Such slots correspond to GObject properties.
99 Slot with <code>:ALLOCATION</code> of <code>:GOBJECT-PROPERTY</code> is a GObject slot. Such slot should have <code>:G-PROPERTY-NAME</code> option that specifies the name of the property to which this slot corresponds. <code>:G-PROPERTY-TYPE</code> option may also be used to specify the type of the property. All other properties may be specified, including <code>:ACCESSOR</code> and <code>:INITARG</code>.
101 <p><code>G-OBJECT-CLASS</code> metaclass provides slot accessors for GObject slots that read and write corresponding properties of objects.</p>
102 <p>To define a GObject class, <code>defclass</code> form is used:</p>
103 <pre>(defclass widget (gtk-objetc atk-implementor-iface buildable)
104 ((name :allocation :gobject-property
105 :g-property-name "name"
106 :g-property-type "gchararray"
107 :accessor widget-name :initarg :name)
108 (parent :allocation :gobject-property
109 :g-property-name "parent"
110 :g-property-type "GtkContainer"
111 :accessor widget-parent
114 (:metaclass gobject-class)
115 (:g-type-name . "GtkWidget")
116 (:g-type-initializer . "gtk_widget_get_type"))</pre>
117 <h2>Using GObject classes</h2>
118 <p>Except defining, GObject classes are used as CLOS classes:</p>
120 <li>Created with <code>MAKE-INSTANCE</code>, and <code>:INITARGS<code> for slots may be specified.
121 <pre>(make-instance 'gtk:label :label "Button 1")</pre>
123 <li>Slots are accessed with <code>SLOT-VALUE</code> and accessors:
124 <pre>(slot-value l 'label)
125 (setf (slot-value l 'label) "Button 2")
127 (setf (label-label l) "Button 2")</pre>
131 <p>Signals are used to register functions that get called when specific events are happening in the program.</p>
132 <p><code>G-SIGNAL-CONNECT</code> function is used to connect handler to the signal.</p>
133 <p>Signal handler is a function that accepts the same arguments as the signal.</p>
135 <pre>(g-signal-connect button "clicked" (lambda (button) (format t "Button ~A was clicked~%" button))</pre>
136 <p><code>EMIT-SIGNAL</code> function emits the given signal on given object with given arguments. Example:</p>
137 <pre>(emit-signal button "clicked" button)</pre>
139 <p>Following symbols are provided in <code>GOBJECT</code> package as a public interface.</p>
141 <li>Class <code>G-OBJECT</code>
142 <p>Base class for all GObject classes. Metaclass <code>GOBJECT-CLASS</code> ensures that class inherits from <code>G-OBJECT</code>.</p>
144 <li>Function <code>(G-SIGNAL-CONNECT <em>object</em> <em>signal</em> <em>handler</em> &key <em>after</em>)</code>
145 <p>Connects the <em>handler</em> to the <em>signal</em> of the <em>object</em>.</p>
146 <p>If <em>after</em> is not <code>NIL</code>, then the handler is connected after other handlers.</p>
148 <li>Class <code>G-INITIALLY-UNOWNED</code>
149 <p>Class that corresponds to GInitiallyUnowned.</p>
151 <li>Function <code>(EMIT-SIGNAL <em>object</em> <em>signal-name</em> &rest <em>args</em>)</code>
152 <p>Emits the signal named <em>signal-name</em> on <em>object</em> with arguments <em>args</em>.</p>
154 <li>Metclass <code>G-OBJECT-CLASS</code>
155 <p>Metaclass for GObject classes.</p>
158 <li><code>G-TYPE-NAME</code> name of GType corresponding to the class</li>
159 <li><code>G-TYPE-INITIALIZER</code> name of C function returning the GType of the class</li>
166 <p>Gtk+ is a high-level, object-oriented toolkit for creating graphical user interfaces (GUIs).</p>
167 <p>Gtk+ is based on a set of other libraries:</p>
169 <li>GLib. Provides low-level functionality: interacting with OS, GObject object system.</li>
170 <li>Gdk. Provides abstraction around windowing system.</li>
171 <li>Pango. Library for Unicode text layout.</li>
172 <li>Cairo. Library for drawing.</li>
174 <p>Gtk+ operates on widgets that are grouped into widget hierarchy. Some widgets are containers and may contain other widgets inside them. Gtk+ assigns widgets' sizes and positions automatically. To achieve this, layouts are used (horizontal and vertical packing, table, size groups).</p>
176 <p>At the core of Gtk+ is the main loop. This loop waits for new events from the windowing system and dispatches them to widgets.</p>
177 <p>Gtk+ is thread-aware but not thread-safe. All operations with Gtk+ should run within Gdk locks or from main loop. Gtk+ allows code to be scheduled to be run in the main loop.</p>
178 <p>CL-GTK2 runs Gtk+ main loop in background thread, and all code interacting with Gtk+ should be run from within main loop. Macros <code>WITHIN-MAIN-LOOP</code> and <code>WITHIN-MAIN-LOOP-AND-WAIT</code> run the code in the Gtk+ main loop. <code>WITHIN-MAIN-LOOP</code> schedules the code to be run within main loop and returns immediately. <code>WITHIN-MAIN-LOOP-AND-WAIT</code> schedules the code to be run within main loop, waits for it to complete and returns the result of its evaluation.</p>
179 <p>Running Gtk+ main loop in background threads allows to run the REPL at the same as the GUI is running.</p>
180 <p>Example of REPL session:</p>
183 (within-main-loop (setf *window* (make-instance 'gtk-window)))
184 (within-main-loop (container-add *window* (make-instance 'label :label "Hello, <i>world</i>!" :use-markup t)))
185 (within-main-loop (widget-show *window*))
187 <p>Example of using inside non-interactive code:</p>
191 (let ((window (make-instance 'gtk-window :title "Hello, world!"
194 :default-height 300))
195 (widget-show window)))))
197 <p>To shutdown the main loop (to quit the application), <code>GTK-MAIN-QUIT</code> function is used which will cause the main loop to quit. From the main thread, you can use the <code>JOIN-MAIN-THREAD</code> function that will wait until the main loop quits.</p>
202 (let ((window (make-instance 'gtk-window)))
203 (g-signal-connect window "delete-event"
204 (lambda (window event)
205 (declare (ignore window event))
207 (widget-show window))))