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 { public: 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()); component->getAttribute(value,pythonAttributeName.c_str ()); PyErr_Print(); } else { value = i; } updating = false; notifyObservers(); return value; }else { return value; } } operator T const & () const { return value; } void addObserver(PythonPropertyBinderObserver* observer) { observers.insert(observer); } void removeObserver(PythonPropertyBinderObserver* observer) { observers.erase(observer); } Property(const T& v):Property() { value = v; } Property():updating(false),component(nullptr) { //set up stuff... } ~Property() { unbind(); } private: T value; std::string pythonAttributeName; //...not interesting }; And for example a slider looks like the following: class Slider : public View, public PythonPropertyBinderObserver { public: //... 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): return if self.binder_functions.has_key(attribute_name): self.binder_functions[attribute_name].add(cpp_property_setter) else: s = set() s.add(cpp_property_setter) self.binder_functions[attribute_name] = s def unbind_from_attribute(self,attribute_name, cpp_property_setter): if self.binder_functions.has_key(attribute_name): self.binder_functions[attribute_name].remove(cpp_property_setter ) def __setattr__(self,attributename,value): super(Component,self).__setattr__(attributename,value) self.sync_attribute(attributename) def sync_attribute(self,attributename): if(hasattr(self,'binder_functions') andself.binder_functions.has_key(attributename)): s = self.binder_functions[attributename] for cpp_property_setter in s: cpp_property_setter() ''' 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 andvalue<=self.low)): if self.integers_only: self.__value = round(value) else: self.__value = value self.run() 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.); slider->value.bindToAttribute(controller->getModel(),"value"); slider->low.bindToAttribute(controller->getModel(),"low"); slider->high.bindToAttribute(controller->getModel(),"high"); 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) István
_______________________________________________ Cplusplus-sig mailing list Cplusplus-sig@python.org http://mail.python.org/mailman/listinfo/cplusplus-sig