The absolute beginners guide - Part VI - for gnu-smalltalk/gtk (RAD with gnu smalltalk, gtk and glade)

Tagged:  •    •    •    •    •    •    •  

After I had some issues with the planned subject of the next guide, I switched - for an example - to glade and would like to show, how you can do RAD with glade and gnu-smalltalk. It's really easy and fast, and you won't blow up your source-code with the complete gui-creation stuff. As an example I picked a password-generator, because I frequently have to set passwords for different user etc. in the company I work for.


Update warning:
|Currently, I use extensions to the GTK.GtkBuilder instance in this example. But they are not completely clarified for me, if they are in the right place. If that will be clear one day, i might update this thread accordingly.
Might be updated in the future...
|In the first, I thought building up an interface between gst and libglade, but then I saw, that GTK has such a functionality right build in. The functionality is covered by the GtkBuilder object. But in using glade in conjunction with GtkBuilder there a re some points, where you should have a look on.
  • glade could only since version 3.6 directly generate projects for GtkBuilder. Before this version you have to save your projects in libglade format and convert it afterwards with a conversion-script to GtkBuilder format.
  • GtkBuilder is (I think) supported since gtk-2.12 but in this version, without the possibility to cover the menu-creation part. This part was added in version 2.16.
  • glade itself would need at least gtk-2.14 to compile.

For this test with the menu-creation part, I set up a virtual-machine with ubuntu-9.04, where gtk and glade are contained in the right version. (my current lenny system does not provide the required versions for this test.)

Some glade and GtkBuilder issues:

So, after all the above is made clear, let's start with the example.

I decided to break the application into a model and the view. So we need a class, which provides the password generation method, which could than be called from the gui. Here it is, with the idea from Samuel Montgomery-Blinn and his blog-post:

Object subclass: Model [
    | chars charsLength |

    Model class >> new [
        ^super new init

    init [
        chars := '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
        charsLength := chars size

    createPassword: length [
        | returnValue |

        "This code is directly from:"

        returnValue := String new: length.
        1 to: length do: [:x|
            returnValue at: x put: (chars at: (Random between: 1 and: charsLength))

Then I created with glade the user interface and saved it as a GtkBuilder project. This gui has a menubar with an exit and about menuitem, 2 entry fields for selecting the length of the password and an output field for displaying the generated password, and a button, which starts the generation of the password.
(see the code of the GtkBuilder xml-file at the end of this post.)

Then I start to coding the gui-part.

Therefore I created a new Object with the name Pwgen and initialized the gui afterwards. For the initialization, I have to instantiate the GtkBuilder class, call the method addFromFile: with the name of the generated glade-project file and connect instance variables to all the fields where I want to have access to, while running the application. And, I have of course connect the signals where I'm interested in to methods in my app. The rest is already explained in one of the previous blog-posts.

Object subclass: Pwgen [
    | model window lengthField outputField |

    Pwgen class >> open [
        ^super new initialize show

    initialize [
        | builder error |

        model := Model new.

        (builder := GTK.GtkBuilder new)
            addFromFile: '' error: error.

        (window := builder getObjectAsWindow: 'window')
            connectSignal: 'delete' to: self selector: #delete:event:data:;
            connectSignal: 'destroy' to: self selector: #destroy:data:.

        (builder getObjectAsToolItem: 'menuExit') connectSignal: 'activate' to: self selector: #menuExit.
        (builder getObjectAsToolItem: 'menuAbout') connectSignal: 'activate' to: self selector: #menuAbout.

        lengthField := builder getObjectAsEntry: 'entryLength'.
        outputField := builder getObjectAsEntry: 'entryOutput'.

        (builder getObjectAsButton: 'button')
            connectSignal: 'clicked' to: self selector: #buttonClicked:data:.

    show [
        window showAll

    delete: object event: event data: data [
        window destroy

    destroy: object data: data [
        GTK.Gtk mainQuit

    menuExit [
        GTK.Gtk mainQuit

    menuAbout [
        (GTK.GtkAboutDialog new)
            setVersion: '0.1';
            setLicense: 'See the license under';
            setWebsite: '';
            setComments: 'a password generation utility, written in GNU-smalltalk!';
            setProgramName: '';
        run; destroy

    showError: message [
            new: window
            flags: GTK.Gtk gtkDialogDestroyWithParent
            type: GTK.Gtk gtkMessageError
            buttons: GTK.Gtk gtkButtonsClose
            message: message)
        setTitle: 'Pwgen Error!';
        run; destroy

    buttonClicked: object data: data [
        | length |
        length := lengthField getText.
        (length size = 0) ifTrue: [ self showError: 'You have to enter a password length' ]
            ifFalse: [ (length asInteger > 12) ifTrue: [ self showError: 'The password length should be less than 13' ]
                ifFalse: [ outputField setText: (model createPassword: (length asInteger)) ]


|Because currently, the GtkBuilder object only provides the getObject: method with a return-type of GObject, but I have to use different type of returns, I extended the GtkBuilder class with some methods like:
  • getObjectAsWindow:
  • getObjectAsToolItem:
  • getObjectAsEntry:
  • getObjectAsButton:

this you will the in the following source code!

As explained earlier:

Namespace current: GTK [
    GtkBuilder extend [

        getObjectAsWindow: name [
            <cCall: 'gtk_builder_get_object' returning: #{GtkWindow}
                args: #( #self #string )>

        getObjectAsToolItem: name [
            <cCall: 'gtk_builder_get_object' returning: #{GtkToolItem}
                args: #( #self #string )>

        getObjectAsEntry: name [
            <cCall: 'gtk_builder_get_object' returning: #{GtkEntry}
                args: #( #self #string )>

        getObjectAsButton: name [
            <cCall: 'gtk_builder_get_object' returning: #{GtkButton}
                args: #( #self #string )>

That's it.

And a huge saving, instead of writing the gui-code by yourself.

Of course, you have to load the package 'GTK' before running this script and call:

GTK.Gtk gstGtkInit.
Pwgen open.
GTK.Gtk main

but that's the same as in the examples from the last days.

Happy coding,

I think having multiple methods is not necessary. Just use #getObject:, it uses #{GTK.GObject} and the bindings will assign the correct class automatically. It would be a bug otherwise.


this one was/is very instructive to me.

I could replicate your example using Glade 2.12 and the conversion utility gtk-builder-convert. I've put your .st codes into separate files, made a loading script and could finally run it successfully.

I have two problems though. First, the menu options don't call their handlers, at least there is no visible respond and second, the error box doesn't work. It shows up with the correct title, but it has no contents.

Anyway, it's another great big step into scripting the Gnome desktop from Smalltalk and I'll definitely stay tuned. Upto now, I've done this kind of work using Tcl/Tk (a very flexible tool) and it's 'Gnocl' extention, but I'd like to ascend to Smalltalk for lots of reasons ;)

Thanks for the tutorials.

My pleasure! And even nicer if someone is reading them!


User login