scriptable widget system

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

scriptable widget system

István Csanády
Hi All!

I am developing a custom widget system. The widgets are written in C++, since the rendering requires very high performance. The widgets are loaded from xml files. A widget can contain several different UI elements, for example a slider, a textbox, a label, buttons, etc... Until now I used a simple but quite powerful solution to synchronize the UI values and the python object attributes. The concept is the following: C++ UI elements can have properties (see the code below). Properties can be bounded to a python object's attribute. If a property changes, it sets the python attribute, and after the setter ran, it gets the actual python value, and sets the property's value to it. (For better understanding: when a property changes because of some user input it rather suggests a value then set it. This is useful when for example you want a slider to be able to set only to odd values, or want to limit the availible values of a numeric text box. So this way you have full control of the "control UI elements" values.) From the other side: when you set an attribute of the python object the object checks if it is bounded to any C++ property, and if it is, then it sets the property to the value. Here are some code snippets:

This is the C++ property:

template <typename T> class Property : public PythonPropertyBinder {




        void bindToAttribute(PythonComponent* component,std::string name) {

            //Add a PyCFunction to the bindings of the python object. 



        void unbind() {

            //Remove the bindings.



        T& operator= (const Property<T>& p) {

            return (*this = p.value);



        T & operator = (const T &i) {

            if(!updating) {

                updating = true;

                if(component) {

                    component->setAttribute(i, pythonAttributeName.c_str());




                else {

                    value = i;


                updating = false;


                return value;

            }else {

                return value;




        operator T const & () const {

            return value;




        void addObserver(PythonPropertyBinderObserver* observer) {




        void removeObserver(PythonPropertyBinderObserver* observer) {




        Property(const T& v):Property() {

            value = v;



        Property():updating(false),component(nullptr) {

            //set up stuff...



        ~Property() {





        T value;

        std::string pythonAttributeName;


        //...not interesting


And for example a slider looks like the following:

class Slider : public View, public PythonPropertyBinderObserver {




        Property<double> value;

        Property<double> low;

        Property<double> high;



The python class:

class Component(object):

    def bind_to_attribute(self,attribute_name, cpp_property_setter):

        if not hasattr(self,attribute_name):


        if self.binder_functions.has_key(attribute_name):



            s = set()


            self.binder_functions[attribute_name] = s 


    def unbind_from_attribute(self,attribute_name, cpp_property_setter):

        if self.binder_functions.has_key(attribute_name):


    def __setattr__(self,attributename,value):



    def sync_attribute(self,attributename):

        if(hasattr(self,'binder_functions') and self.binder_functions.has_key(attributename)):

            s = self.binder_functions[attributename]

            for cpp_property_setter in s:



    And several other methods...


So this way I can simply bind a UI element's property to a python attribute, and it is guaranteed that they will be the same (until a python property is not accessed by its setter...) 

For example:



class Slider(Component):


    def get_value(self):

        return self.__value


    def set_value(self,value):

        if((value<=self.high and value>=self.low) or (value>=self.high and value<=self.low)):

            if self.integers_only:

                self.__value = round(value)


                self.__value = value



    def get_high(self):

        return self.__high    


    def set_high(self,high):

        self.__high = high


    def get_low(self):

        return self.__low


    def set_low(self,low):

        self.low = low



    value = property(get_value,set_value)

    low = property(get_low,set_low)

    high = property(get_high,set_high)

And then all I have to do from the C++ UI:

    Slider* slider = new Slider(Point(20, 30.), 195.);





So this way:

- the python objects work like models

- the values are synced

- the script writer is not able access to any other properties of the UI elements

- however one have total control of the values

- all the widgets can work without binded to any UI elements

- if I want to serialize the objects (and I want to), the PyCFunctions and other - runtime added - objects can be easily excluded

- properties can be easily bounded and unbouded

- I did not have to create any new types, such as MySyncedFloat, MySyncedString etc.

- the script writer doesn't even have to know about that a value will be bounded to a UI element

- the UI elements are not strongly connected to any python code

- more UI elements can be bounded to a single python attribute for example I can bind an attribute to a slider's max value and to a numeric text box at the same time

- this approach can be easily used with a graphical interface builder: one just have to drag and drop some UI elements to a widget, and tell the interface builder that a property of an element will be bounded to a python attribute. Then it can be exported to an XML and loaded runtime and bind the properties runtime. Cool.

 This approach worked very well. But... Few days ago our UI/UX designer created some new UI elements which I call "batched controllers". A batched controller is an array of same controllers for example sliders. The number of the sliders can be changed runtime for example when I click on a button a new slider is added. And this type of view can not be used with the previous approach, since a value is determined by two values: the ordinal of the slider and the value itself. 

So my question is: does anybody have any idea how to create something that works like I previously specified for these "batched controllers" (==properties with indices)


Cplusplus-sig mailing list
[hidden email]