Saturday, September 24, 2016

Adding simple gtk# gui to a console F# application

In this post I am quickly talking about how I added a simple gtk# interface to a program I've done some times ago as an exercise in F#.
(Animal Quiz kata:

The first version was command line based, and was developed with extensive use of unit tests and interaction tests (using Rhino framework).

That version is still incorporated in this new version (git: except that it is possible to run in a gui gtk# wideget adding the paremeter "gui" to the command line.

For instance, under mono, I will run it as
> mono fsharpAnimalQuizKata.exe gui

and the following window will appear:

The interaction with the user is still text based, as in the console based version: the user dialogues with the "engine" using the textbox and the label of the textbox, instead of the standard input/standard output.

Now the knowledge tree can be visualised, hidden, expanded by using the three buttons available.

The knowledge base starts from as single node with the animal "elephant" in it.

An example of interaction to make the engine add a new node with a "cat" is:

After this dialogue, the knowledge tree can be expanded and we can see the following:

Now the loop starts again, and in a new dialogue we can feed the engine to make it store a new node "ant", which is small and is an insect, so expanding the knowledge tree we can see the following:

Essentially the logic of using gtk# in F# is the same as using it in C#.
I've found that translating existing C# based tutorials of gtk# in F# requires:
- declaring as mutable any objects that will be reassigned.
- when event handling in C# has the form:

// adding

// adding handler
btn.Clicked += on_btn_clicked;

// declaration of the handler
void on_btn_clicked (object obj, EventArgs args) {
// method body

An equivalent in F# is 

// adding handler
do btn.Clicked.AddHandler(fun i e -> this.on_btn_clicked(i,e))

// declaration of the handler
member this.on_btn_clicked(i,e: EventArgs) =
// method body

Using Monodevelop/Xamarin I can create an empty F# gtk# template project that starts with an empty window. In my experience starting from that, and consulting any C# gtk#  tutorial to add buttons, menus etc... is good enough to play with gtk# gui.

(I'm sure they it should work also in .net and Windows, but I have not tried yet.)

To "wrap" a knowledge tree to a gtk# TreeStore view I used this function

let rec treeToTreeStore tree (storeTreeStore) iter  prefix =
    match tree with 
        | AnimalName name -> 
             do store.AppendValues(iter,[|prefix + name|]) |> ignore 
        | SubTree {Question=questYesBranch=yBranchNoBranch=nBranch } -> 
             let innerIter = store.AppendValues(iter,[|prefix + quest|])
             do treeToTreeStore yBranch store innerIter "YES: " 
             do treeToTreeStore nBranch store innerIter "NO:  "

Just as a reminder. A knowledge tree type is defined as follows:

type KnowledgeTree = AnimalName of string | SubTree of Tree
and Tree = {QuestionstringYesBranchKnowledgeTreeNoBranchKnowledgeTree}

which means that a knowledge tree is a leaf node constituted by a string (the name of the animal) or is a yes/no question and two sub trees (one leading to the animals for whom the answer is "yes" and another to the animals for whom the answer is "no").

I found reasonable to make extensive use of automated testing of the console based version by mocking the standard input and standard output, and avoiding adding tests related to the gui.

Mocking standard output and standard input requires only making those kind of abstractions

type OutStream =
    abstract Writestring -> unit

type InStream =
    abstract Inputunit -> string
that will be implemented in the real application as follows:

type ConsoleOutput() =
    interface OutStream with
       member this.Write(x) = printf "%s\n" x

type ConsoleInput() =
    interface InStream with
        member this.Input() = Console.ReadLine()

whereas mocks can be used to simulate input/output interaction (by simulating that the user write some input values, and adding expectations about values that the engine will write)

    let ``with a one leaf node knowledge tree, engine asks if it is the animal on that node, the user says yes, and so the engine answer with "yeah!"``() =
        // setup 
        let mockrepo = new MockRepository()
        let inStream = mockrepo.StrictMock<InStream>()

        let outStream = mockrepo.StrictMock<OutStream>()

        // setup expectations
        Expect.Call(outStream.Write("think about an animal")) |> ignore
        Expect.Call(inStream.Input()).Return("whatever") |> ignore

        Expect.Call(outStream.Write("is it a cat?")) |> ignore     
        Expect.Call(inStream.Input()).Return("yes") |> ignore

        Expect.Call(outStream.Write("yeah!")) |> ignore 
        Expect.Call(inStream.Input()).Return("anything") |> ignore

        // arrange
        let tree = AnimalName "cat"
        let initState = Welcome
        let numOfLoops = 3

        // act
        runUntilIndex outStream inStream tree initState numOfLoops |> ignore

        // verify expectations

I guess it would be more complicated to make a similar mechanism of mocking the gui as well, with less value, because the gui is just a thin layer upon the engine which is already extensively tested.

Special mention to Visual Studio Code and  Ionide as editor with some autocompletion features that are missing in Monodevelop/Xamarin.

Future work could be related to
-saving/loading trees, and
-editing tree nodes in the tree view.



Steve Gilham said...

While you can just translate the C# form of event handling, F# provides first-class event objects (like in the Rx framework) out of the box, which helps reduce the amount of boilerplate in UI coding. More idiomatic than

btn.Clicked.AddHandler(fun i e -> this.on_btn_clicked(i,e))

where most of the time, the arguments are never looked at, is

|> Event.add (fun e -> ...)

or even

|> Event.add (fun _ -> ...)

in the usual case where the event object is never inspected. Writing a closure here allows us to pull in the context that actually gets used, even if the closure body is just a call to another function using a list of closed-over arguments, like

let label1 = ...

|> Event.add (fun _ -> OnClick label1)

Tonino Lucca said...

That is more coincise. Thanks.