Skip to main content Skip to navigation

Lab 4 Exercises

Following on from Ant's lecture on WPF, we will look at how DOSTE can be used in a similar fashion. Hopefully this will demonstrate the difference between our tools and industry's current attempts at moving towards exploiting dependency in ways we have been exploring for 20+ years. The exercises today involve writing a C++ program that is loaded into DOSTE and can then be connected together with buttons and sliders to provide an interface. Don't worry if you do not know C++, you can just copy the code from the exerises.


Due to recent changes to DOSTE some of the GUI prototypes such as the trackbar/slider do not function correctly. You could try and fix this for us :-)


Making a GUI

One thing DOSTE is fairly good at, as is WPF, is creating a Graphical User Interface (GUI) and connecting this with some underlying program. The whole interface can be constructed from prototypes for things like windows and buttons so to begin we will construct a simple interface that has a window, a button and a slider.

Run our tool in the usual way:

~empublic/teaching/cs405/run lab4

Now make a window and place it on the screen:

@screen mywindow = (new union (@prototypes window) union (@prototypes dragable)
  x = 10
  y = 10
  width = 250
  height = 400
  title = "My Test Window"
);

For a complete list of available prototypes you can %list the @prototypes object. It may also be instructive to take a look at the scripts behind these prototypes to see how they work. The scripts can be found in ~empublic/teaching/cs405/wgd. Notice how union was used twice above to combine two prototypes, one for the basic window and another to make it draggable with the mouse. Next we create a slider and place it inside that window:

@screen mywindow children slider = (new union (@prototypes trackbar)
  caption = "Scale:"
  x = 10
  y = 30
);

So, just as in WPF, we can now put a simple definition in to link the width of the window to the slider we just created. The main difference is that we can put a formula in here instead of just a direct link:

@screen mywindow width is { .children slider value * 200 + 250 };

We can also make a label which displays the value of the slider:

@screen mywindow children value = (new union (@prototypes label)
  x = 10
  y = 70
  caption is { @screen mywindow children slider value }
);

Now let's make a button which is always aligned with the right hand edge of the window:

@screen mywindow children button = (new union (@prototypes button)
  x is { @screen mywindow width - (.width) - 20 }
  y = 100
  width = 150
  caption = "Click Me"
);



A DOSTE Module

DOSTE works as a collection of small modules, written in C++ currently, that provide some functionality such as drawing to the screen or processing keyboard input. Custom modules are easy to create so the next exercise is to make a very simple module, compile it and get DOSTE to use it.

Type the following into a file called 'mymodule.cpp' inside a directory called 'cs405' within your home directory.

#include <doste/agent.h>
#include <doste/dvm/dvm.h>

extern "C" void initialise(const doste::dvm::OID &base) {
    std::cout << "MyModule has loaded!\n";
}

extern "C" void finalise() {

}

Now compile this program using a script we have provided called ~empublic/teaching/cs405/lab4/build. Execute it in a shell window that is in the same directory as your 'mymodule.cpp' file.

This should have produced a 'mymodule.so' file.

Enter the following into the input window to actually load the module. Change the filename to your home directory.

this modules 8 = (new type=Module
    file = (new type=LocalFile
        filename = "/dcs/09/cs****/cs405/mymodule.so"
    )
    base = (this)
    update = false
);

Check that you see "MyModule has loaded" being printed out when the program loads.


Agents and Events

An agent in C++ is some class which can have properties (like WPF dependency properties) which come from the DOSTE graph and it can also have event handlers for when those properties change. Below is an example of a simple C++ class that has a single DOSTE property called 'myvalue'. When you make an instance of this class you give it an object (or node) id so that it knows which node in the graph it corresponds to. 'myvalue' will then be an edge in the DOSTE graph. Enter the following class declaration just below the #include lines in the 'mymodule.cpp' file.

class MyClass : public doste::Agent {

    public:
    MyClass(const OID &obj) : Agent(obj) {
        registerEvents();
    }
    ~MyClass() {}

    PROPERTY(float, myvalue);
};

In the 'initialise' method you can now make this object and give it the root object (same as @root):

new MyClass(doste::dvm::root);

This alone is not very exciting. You can now access the DOSTE graph edge by using myvalue() or myvalue(5.0). Let's add an event handler to do something when myvalue changes. Insert the following code after the PROPERTY line in the class:

BEGIN_EVENTS(Agent);
EVT(myvalue);
END_EVENTS;

And then the following just below the class declaration but above the initialise method:

OnEvent(MyClass, myvalue) {
    std::cout << "My value changed to: " << myvalue() << "\n";
}

IMPLEMENT_EVENTS(MyClass, Agent);

If you were to compile this now using the build script, then reload tkeden and type in the module loading script again, you would now see "My value changed to:" get printed. Test this properly by changing myvalue in DOSTE:

@root myvalue = 6.3;

Now try linking '@root myvalue' to the slider value. You will need to re-run the scripts above to make the window and slider again:

@root myvalue is { @screen mywindow children slider value }


Extras

Trying linking button clicks with an event in C++.

Also note that you can access and change the DOSTE graph from C++ as follows:

doste::dvm::root["widgets"]["root"]["children"]["mywindow"]["width"] = 400;