source: project/wiki/iup-tutor @ 25603

Last change on this file since 25603 was 25603, checked in by juergen, 9 years ago

iup-tutor: keyword->string removed

File size: 18.4 KB
Line 
1[[toc:]]
2
3== Tutorial on the Iup Graphical User Interface toolkit
4
5Iup is a small and easy to use GUI toolkit, originally written in pure
6C, but accompanied by an optional resource language LED, to make life
7easier. It has later been ported to Lua and other scripting languages,
8and nowadays it is considered Lua's native GUI-toolkit. Thanks to
9[[/users/thomas-chust|Thomas Chust]] Chicken and Racket ports exist as well.
10
11Unfortunately, the documentation is somewhat scattered in the internet:
12
13[[http://www.chust.org/fossils/iup/doc/trunk/api/main.wiki|the official API docs]].
14
15[[http://www.tecgraf.puc-rio.br/iup/|IUP "portable user interface" GUI library]].
16
17Hence you need to consult the original C documentation very often. So it
18seems appropriate to start with an example (Hello World, you guess it)
19showing how a C program is translated to Scheme. This way you'll become
20comfortable writing Scheme programs while using the original C
21documentation. But before that let me recapitulate some design
22principles of Iup.
23
24=== Iup's design principles
25
26Iup programs consist of dialogs, which communicate with each other. A
27dialog can contain only one widget, usually a container, which in turn
28can contain other widgets, containers included. For widgets to be
29visible, they must transparently be mapped to native widgets of the
30system, which are gtk-widgets in Linux, in general. This mapping
31happens only, when a dialog is shown. Only dialogs need to be destroyed
32at the end of a program.
33
34Like other GUI-toolkits, interaction with the user happens within a main
35loop. But what makes Iup stand out of the crowd are two principles:
36
37First: No widget is ever positioned with explicit coordinates.
38Everything is done logically by nesting containers and positioning
39widgets like fill. This facilitates matters considerably.
40
41Second: All widgets and dialogs are controlled by attributes, including
42callbacks. This makes the library small.
43
44In C attribute names as well as attribute values are strings, in Scheme
45the former are keywords, the latter might be symbols or numbers as well. All
46attributes can be get and set. Getting is done (in Scheme) by the attribute
47function, setting by the attribute-set! procedure or a generalized set!,
48i.e. either by
49
50(attribute-set! widget name: val) or (set! (attribute widget name:) val)
51
52whichever you prefer. The same applies to callbacks, which are get and
53set by callback and callback-set! respectively.
54
55In Scheme's Iup callbacks are usually functions of one argument, self
56most of the time, which return a symbol, for example 'close, to close a
57dialog, or 'default, to keep it open. In C, on the other hand, the
58return values are #define'd integers.
59
60=== Hello World
61
62Let's start with the most trivial version, in C and in Chicken:
63
64==== hello0.c
65
66<enscript highlight="C">
67
68#include <iup.h>
69#include <stdlib.h>
70
71int main (int argc, char *argv[]) {
72  IupOpen(&argc, &argv);
73  IupShow(IupDialog(IupLabel ("Hello, world!")));
74  IupMainLoop();
75  IupClose();
76  return 0;
77}
78
79</enscript>
80
81==== hello0.scm
82
83<enscript highlight="C">
84
85(use iup)
86(show (dialog (label "Hello, world!")))
87(main-loop)
88(exit 0)
89
90</enscript>
91
92Note, that initializing and closing iup disappears in Chicken, since it
93is done, when inporting iup. Note also, that the Chicken names are much
94friendlier than the C ones: Chicken's module system makes Iup prefixes
95superfluous, they can be added, if needed, with appropriate import
96clauses.
97
98Now a version which shows, how to use attributes and callbacks, again in
99C, and in two Chicken versions. The first is almost a literal
100translation of the C version, the second a condensed version, where all
101attributes are set in the creation process.
102
103==== hello1.c
104
105<enscript highlight="C">
106
107#include <iup.h>
108#include <stdlib.h>
109
110int exit_cb (void) {
111  return IUP_CLOSE;
112}
113
114int main (int argc, char *argv[]) {
115
116  // declare widgets
117  Ihandle *btn, *lbl, *vb, *dlg;
118
119  // initialize iup
120  IupOpen(&argc, &argv);
121 
122  // create widgets and set their attributes
123  btn=IupButton("&Ok", "");
124  IupSetCallback(btn,"ACTION", (Icallback) exit_cb);
125  IupSetAttribute(btn, "EXPAND", "Yes");
126  IupSetAttribute(btn, "TIP", "Exit button");
127 
128  lbl=IupLabel("Hello,world!");
129
130  vb=IupVbox(lbl, btn);
131  IupSetAttribute(vb, "GAP", "10");
132  IupSetAttribute(vb, "MARGIN", "10x10");
133  IupSetAttribute(vb, "ALIGNMENT", "ACENTER");
134
135  dlg=IupDialog(vb);
136  IupSetAttribute(dlg, "TITLE", "Hello");
137
138  // Map widgets and show dialog
139  IupShow(dlg);
140
141  // Wait for user interaction
142  IupMainLoop();
143
144  // Clean up
145  IupDestroy(dlg);
146  IupClose();
147  return EXIT_SUCCESS;
148}
149
150</enscript>
151
152==== hello11.scm
153
154Here is a translation to Scheme ...
155
156<enscript highlight="Scheme">
157
158(use iup)
159
160(define (cb-exit self) 'close)
161
162(define btn (button))
163(set! (callback btn action:) cb-exit)
164(set! (attribute btn title:) '&Ok)
165(set! (attribute btn expand:) #t)
166(set! (attribute btn tip:) "Close button")
167
168(define lbl (label "Hello World!"))
169
170(define vb (vbox lbl btn))
171(attribute-set! vb gap: 10)
172(attribute-set! vb margin: '15x15)
173(attribute-set! vb alignment: 'ACENTER)
174
175(define dlg (dialog vb))
176(attribute-set! dlg title: 'Hello)
177
178(show dlg)
179(main-loop)
180(destroy! dlg)
181(exit 0)
182
183</enscript>
184
185Note, how the upper-case C-names of attributes change to lower-case
186Chicken-keywords (by the way, Chicken-keywords can be either written with
187a trailing colon or a leading hash-colon, but I prefere the former,
188which looks nicer).
189
190Note also, that attribute values can be numbers or symbols as well.
191And last, but not least, note the tips: predicate, which defines
192tooltips, and the ampersand in the button's title, which defines a
193shortcut, Alt+underlined-character, to execute the callback.
194
195==== hello12.scm
196
197... and here a condensed version.
198
199<enscript highlight="Scheme">
200
201(use iup)
202
203(define dlg
204  (dialog
205    (vbox
206      (label "Hello, World!")
207      (button title: '&Ok
208              expand: #t
209              tip: "Close button"
210              action: (lambda (self) 'close))
211      gap: 10
212      alignment: 'ACENTER
213      margin: '15x15)
214    title: 'IUP))
215
216(show dlg)
217(main-loop)
218(destroy! dlg)
219(exit 0)
220
221</enscript>
222
223Note, how smoothly the indentation reflects the logical representation
224of the dialog.
225
226=== The LED resource language
227
228The condensed version above can be almost literally transformed into a
229LED resource file, which can be either loaded into a C program or a
230Scheme program. Those resources are interpreted at runtime, so that even
231in a compiled program the resource can be changed afterwards, provided
232the identifiers remain consistent.
233
234The form of a LED-declaration looks as follows
235
236<enscript highlight="C">
237
238widget=widget-type[attribute-name=attribute-value, ...](arg, ...)
239
240</enscript>
241
242where the attributes' names and values are written without enclosing
243quotes, but interpreted as strings.
244
245Here is an example and its use in Chicken.
246
247==== hello.led
248
249btn = button[TIP = "Close window", EXPAND = Yes]("&Ok", 0)
250
251dlg = dialog[TITLE = Hello](
252            vbox[GAP = 10, MARGIN = 15x15, ALIGNMENT = ACENTER](
253                label("Hello world!"),
254                btn))
255
256==== hello13.scm
257
258<enscript highlight="Scheme">
259
260(use iup)
261
262(load/led "hello.led")
263
264(callback-set! (handle-ref "btn")
265               action: (lambda (self) 'close))
266
267(define dlg (handle-ref "dlg"))
268
269(show dlg)
270(main-loop)
271(destroy! dlg)
272(exit 0)
273
274</enscript>
275
276Note, that the LED-import is done via load/let and the identification
277of LED-names with Chicken-variables via handle-ref.
278
279Note also, that btn must be coded separately and given a name, because
280we need to set the callback, which can not be a string.
281
282You can compile hello13.scm with csc and change hello.led afterwards.
283Try it out and play with some attributes, for example, remove the
284EXPAND attribute from btn and set it for label, ...
285
286=== Porting some examples from the Iup Distribution
287
288Now we'll show, how some predefined widgets work. For that we'll port
289some of the C-examples.
290
291==== fill.scm
292
293The following dialog shows, how different positions of the fill widget
294change the dialog's appearance.
295
296<enscript highlight="Scheme">
297
298(use iup)
299
300;;; Create frame with left aligned button
301(define frame-left
302  (frame
303    (hbox
304      (button title: '&Left
305              tip: "Left button"
306              action: (lambda (self)
307                        (print "Stay in with Left button")
308                        'default))
309      (fill))))
310(set! (attribute frame-left title:) "Left aligned")
311
312;;; Create frame with centered button
313(define frame-center
314  (frame
315    (hbox
316      (fill)
317      (button title: '&Center
318              tip: "Central button"
319              action: (lambda (self)
320                        (print "Way out with Central button")
321                        'close))
322      (fill))))
323(attribute-set! frame-center title: "Centered")
324
325;;; Create frame with right aligned button
326(define frame-right
327  (frame
328    (hbox
329      (fill)
330      (button title: '&Right
331              tip: "Right button"
332              action: (lambda (self)
333                        (print "Way out with Right button")
334                        'close)))
335    title: "Right aligned"))
336
337; Note, that callbacks should return a symbol.
338; Only the symbol 'close of the action: callbacks closes the dialog!
339
340;;; Create dialog with these three frames
341(define dlg
342  (dialog
343    (vbox
344      frame-left
345      frame-center
346      frame-right)))
347(set! (attribute dlg size:) 120)
348(set! (attribute dlg title:) 'Fill)
349
350(show dlg)
351(main-loop)
352(destroy! dlg)
353(exit 0)
354
355</enscript>
356
357==== hbox.scm
358
359This dialog shows, how horizontal boxes within vertical ones work.
360
361<enscript highlight="Scheme">
362
363(use iup)
364
365(define dlg
366  (dialog
367    (vbox
368      (frame
369        (hbox (fill)
370              (button "1" "" size: "30x30")
371              (button "2" "" size: "30x40")
372              (button "3" "" size: "30x50")
373              (fill)
374              alignment: "ATOP"
375              gap: 10
376              size: 200)
377        title: "alignment: ATOP gap: 10 size: 200"
378        gap: 10
379        size: 200)
380      (frame
381        (hbox (fill)
382              (button "1" "" size: "30x30")
383              (button "2" "" size: "30x40")
384              (button "3" "" size: "30x50")
385              (fill)
386              alignment: "ACENTER"
387              gap: 20)
388        title: "alignment: ACENTER gap: 20"
389        gap: 20)
390      (frame
391        (hbox (fill)
392              (button "1" "" size: "30x30")
393              (button "2" "" size: "30x40")
394              (button "3" "" size: "30x50")
395              (fill)
396              alignment: "ABOTTOM"
397              size: 150)
398        title: "alignment: ABOTTOM size: 150"
399        size: 150))
400    title: "Hbox"))
401
402(show dlg x: 'center y: 'center)
403(main-loop)
404(destroy! dlg)
405
406</enscript>
407
408==== menu.scm
409
410Now a dialog with a menu and submenus, one of whose items call a
411predefined message-dialog.
412
413<enscript highlight="Scheme">
414
415(use iup iup-dialogs)
416
417(define item-open (menu-item "&Open"))
418
419(define item-save (menu-item "&Save"))
420
421(define item-undo (menu-item "&Undo" active: "NO"))
422
423(define item-exit (menu-item "E&xit"))
424(set! (callback item-exit action:)
425      (lambda (self) 'close))
426
427(define file-menu
428  (menu item-open
429        item-save
430        (menu-separator)
431        item-undo
432        item-exit))
433
434(define item-about (menu-item "&About"))
435(define (about-cb self)
436  (show (message-dialog value: "Information goes here ...") modal?: #t)
437  'default)
438(callback-set! item-about action: about-cb)
439
440(define help-menu
441  (menu item-about))
442
443(define mnu
444  (menu (menu-item "&File" file-menu)
445        (menu-item "&Help" help-menu)))
446;(set! (handle-name mnu) "mymenu")
447
448(define dlg (dialog (canvas "")))
449(set! (attribute dlg menu:) mnu);"mymenu")
450(set! (attribute dlg title:) "Menu")
451
452(show dlg)
453(main-loop)
454(exit 0)
455
456</enscript>
457
458Note, that the underlined characters behave differently in menus and
459submenus. In the former they execute their callbacks with the Alt
460prefix, in the latter without.
461
462Note also, that predefined dialogs can only be shown with the modal?:
463attribute set.
464
465==== filedlg.scm
466
467Now we use two predefined dialogs, file-dialag and message-dialog. You
468should note, that they are mapped to gtk-dialogs!
469
470<enscript highlight="Scheme">
471
472(use iup)
473
474(define (popup dlg . args)
475  (apply show dlg #:modal #t args))
476
477(define dlg (file-dialog title: "File save"
478                         dialogtype: 'SAVE
479                         filter: "*.led"
480                         filterinfo: "Iup resource file"))
481
482(popup dlg x: 'center y: 'center)
483
484(let ((status (attribute dlg status:)))
485  (cond
486    ((string=? status "1")
487     (popup
488       (message-dialog value: (string-append  "New File: "
489                                              (attribute dlg value:)))))
490    ((string=? status "0")
491     (popup
492       (message-dialog value: (string-append  "File already exists: "
493                                              (attribute dlg value:)))))
494    ((string=? status "-1")
495     (popup
496       (message-dialog value: "File-dialog: Operation Canceled")))))
497
498(destroy! dlg)
499(exit 0)
500
501</enscript>
502
503Note, that the status: attribute of the file-dialog is a string!
504
505==== multiline.scm
506
507Now, we'll show how widgets within a dialog can communicate whith each
508other. The trick is, that Iup can reference widgets by name, or, to be
509more precise, by string name. You have seen this in the LED example
510above: In LED a widget is named by a string and constructed by the LED
511interpreter. To use it in Scheme, you reference the widget proper
512from its name via handle-ref. This can be done without LED as well. But
513then you must give the widget a string name via handle-name-set! or the
514generalized version (set! (handle-name widget) name).
515
516The following dialog contains two textboxes, one of them multiline, a
517dropdown listbox and some buttons, allowing to get or set the multiline
518predicates. Please take note, how handle-ref and handle-name is used.
519
520<enscript highlight="Scheme">
521
522(use iup)
523
524(define (message title value)
525  (show (message-dialog title: title value: value) #:modal? #t))
526
527(define (set-attribute keyword)
528  (let (
529    (msg (sprintf "Attribute ~A set with value ~A"
530                  keyword (attribute line value:)))
531    (multi (handle-ref "multi"))
532    (line (handle-ref "line"))
533    )
534    (attribute-set! multi keyword (attribute line value:))
535    (message "Set attribute" msg)
536    'default))
537
538(define (get-attribute keyword)
539  (let (
540    (msg (sprintf "Attribute ~A get with value ~A"
541                  keyword (attribute multi keyword)))
542    (multi (handle-ref "multi"))
543    (line (handle-ref "line"))
544    )
545    (attribute-set! line value: (attribute multi keyword))
546    (message "Get attribute" msg)
547    'default))
548
549(define btn-append
550  (button title: '&Append
551          action: (lambda (self)
552                    (let ((lb (handle-ref "lb")))
553                      (if (eqv? (string->number (attribute lb value:)) 1)
554                        (set-attribute append:)
555                        (get-attribute append:))
556                      'default))))
557
558(define btn-insert
559  (button title: '&Insert
560          action: (lambda (self)
561                    (let ((lb (handle-ref "lb")))
562                      (if (eqv? (string->number (attribute lb value:)) 1)
563                        (set-attribute insert:)
564                        (get-attribute insert:))
565                      'default))))
566
567(define btn-border
568  (button title: '&Border
569          action: (lambda (self)
570                    (let ((lb (handle-ref "lb")))
571                      (if (eqv? (string->number (attribute lb value:)) 1)
572                        (set-attribute border:)
573                        (get-attribute border:))
574                      'default))))
575
576(define btn-caret
577  (button title: '&Caret
578          action: (lambda (self)
579                    (let ((lb (handle-ref "lb")))
580                      (if (eqv? (string->number (attribute lb value:)) 1)
581                        (set-attribute caret:)
582                        (get-attribute caret:))
583                      'default))))
584
585(define btn-readonly
586  (button title: '&Readonly
587          action: (lambda (self)
588                    (let ((lb (handle-ref "lb")))
589                      (if (eqv? (string->number (attribute lb value:)) 1)
590                        (set-attribute readonly:)
591                        (get-attribute readonly:))
592                      'default))))
593
594(define btn-selection
595  (button title: '&Selection
596          action: (lambda (self)
597                    (let ((lb (handle-ref "lb")))
598                      (if (eqv? (string->number (attribute lb value:)) 1)
599                        (set-attribute selection:)
600                        (get-attribute selection:))
601                      'default))))
602
603(define btn-selectedtextbox
604  (button title: 'Selected&textbox
605          action: (lambda (self)
606                    (let ((lb (handle-ref "lb")))
607                      (if (eqv? (string->number (attribute lb value:)) 1)
608                        (set-attribute selectedbox:)
609                        (get-attribute selectedbox:))
610                      'default))))
611
612(define btn-nc
613  (button title: '&Nc
614          action: (lambda (self)
615                    (let ((lb (handle-ref "lb")))
616                      (if (eqv? (string->number (attribute lb value:)) 1)
617                        (set-attribute nc:)
618                        (get-attribute nc:))
619                      'default))))
620
621(define btn-value
622  (button title: '&Value
623          action: (lambda (self)
624                    (let ((lb (handle-ref "lb")))
625                      (if (not (attribute lb value:))
626                        (set! (attribute lb value:) 1))
627                      (if (eqv? (string->number (attribute lb value:)) 1)
628                        (set-attribute value:)
629                        (get-attribute value:))
630                      'default))))
631
632(define lb (listbox #:1 'set #:2 'get value: 1 dropdown: #t))
633(define multi (textbox expand: #t multiline: #t))
634(define line (textbox expand: 'horizontal))
635
636
637(set! (handle-name lb) "lb")
638(set! (handle-name multi) "multi")
639(set! (handle-name line) "line")
640
641(define dlg
642  (dialog (vbox multi
643                (hbox lb
644                      line)
645                (hbox btn-append btn-insert btn-border btn-caret
646                      btn-readonly btn-selection)
647                (hbox btn-selectedtextbox btn-nc btn-value))
648          title: "Multiline example"
649          size: 'HALFxQUARTER))
650
651(show dlg x: 'center y: 'center)
652(main-loop)
653(destroy! dlg)
654(exit 0)
655
656</enscript>
657
658There is one caveat in the listbox: The lines must be denoted with
659prefixed keyword notation, 1: will not work, for example!
660
661=== Concluding remark
662
663All these examples show, that Iup is really easy to use ...
664
665== Author
666
667[[/users/juergen-lorenz|Juergen Lorenz]]
668
669== Initial version
670
671Nov 25, 2011
672
673== Last updated
674
675Nov 29, 2011
Note: See TracBrowser for help on using the repository browser.