
Problem
In Qt, there is a wonderful thing - Q_PROPERTY, which allows you to add the necessary property to any QObject class. But in some cases it is inconvenient to use them.
For example, you have an application in which many different objects exist and (or can be created) that need to be customized (display a dialog with the properties of the selected object). An example of such applications can be almost any engineering program, vector graphics editor, Visual Studio after all.
')
So, there are many objects that the user can "customize." It is logical to have some kind of universal mechanism for this, because otherwise you have to implement your own customization dialog for each object. Q_PROPERTY for this is not quite suitable. Firstly, there is no standard Q_PROPERTY widget. Secondly, even if you use a third-party widget for this purpose, along with your properties, standard ones (at least objectName) will be displayed. Further, Q_PROPERTY does not support hierarchies (it is sometimes useful to group related properties); no description (text string explaining this property); There are no tools to customize the appearance of Q_PROPERTY in the GUI (to improve usability).
For a programmer, it is sometimes convenient to work with properties as with a separate object, independent of the owner object. They can then be transferred somewhere without transferring the entire owner object, or even transfer a separate subgroup of properties.
Decision
Trying to make convenient properties, I created a
QtnProperty project. Next, I will tell you how good this project is and how to use this library.
First of all, the library consists of several parts:
- QtnPropertyCore - library with non-GUI classes
- QtnPropertyWidget - widgets for editing properties, plus delegates for customizing the look and editing of various properties
- QtnPEG is an application (similar to the moc generator) that creates the C ++ property property class PropertySet from a * .pef file (this is a simpler way of describing properties, similar in syntax to QML)
- QtnPropertyTests - tests for the QtnPropertyCore library
- QtnPropertyDemo is a demo application where you can edit properties using QtnPropertyWidget , as well as from a script and setting the text as "SuperProperty.SubProperty = value" strings
QtnPropertyCore
First of all, the library defines two main classes,
QtnProperty and
QtnPropertySet . The first is the base for all types of properties (for example,
QtnPropertyBool or
QtnPropertyFloat ), and the second is a group of properties. Both
QtnProperty and
QtnPropertySet have a common parent,
QtnPropertyBase , which, in turn, is a descendant of QObject. In the
QtnPropertyBase class, everything that is common between
QtnProperty and
QtnPropertySet is defined :
- QString name - the name of the property or propertyset (same as objectName)
- QString description - a text description that can be displayed in the bottom panel of QtnPropertyWidget
- qint32 id - unique property ID among others in the group (used when saving in binary form)
- QtnPropertyState state - the state of the property (for example, QtnPropertyStateImmutable is a non-editable property or QtnPropertyStateInvisible is invisible)
- load / save - save / load via QDataStream
- fromStr / toStr - converting property value from / to QString
- fromVariant / toVariant - converting property value from / to QVariant
- propertyWillChange - a signal that is triggered before a property changes (when changing not only the value but also other attributes - name, description, id, state, etc.)
- propertyDidChange - a signal that triggers after a property change (symmetric propertyWillChange)
The QtnPropertySet class contains another list of child properties and methods for working with them. For each property type (for example, integer), there are two classes (
QtnPropertyInt and
QtnPropertyIntCallback ). The first stores the value in the class, and the second calls some function to get or save the value.
Here is a list of properties that is implemented directly in the
QtnPropertyCore module:
- QtnPropertyBool - represents a boolean value.
- QtnPropertyInt - represents a signed integer value.
- QtnPropertyUInt - represents an unsigned integer value.
- QtnPropertyFloat - represents a floating point value.
- QtnPropertyDouble - represents the real value of double precision.
- QtnPropertyEnum - represents the value from the enumeration.
- QtnPropertyEnumFlags - represents a combination of values from an enumeration.
- QtnPropertyQString - represents a string value.
- QtnPropertyQPoint - represents a QPoint value.
- QtnPropertyQSize - represents the value of QSize.
- QtnPropertyQRect - represents the value of QRect.
- QtnPropertyQFont - represents the value of a QFont.
- QtnPropertyQColor - represents the value of QColor.
All numerical properties (
QtnPropertyInt ,
QtnPropertyUInt ,
QtnPropertyFloat ,
QtnPropertyDouble ) have three additional methods:
- minValue is the minimum allowed value.
- maxValue is the maximum allowed value.
- stepValue - the step of changing the value.
QtnPropertyWidget
This module contains two widgets and property delegates.
The
QtnPropertyView class implements a tree widget for a set of properties.
The
QtnPropertyWidget class is composite and contains
QtnPropertyView and QLabel as the bottom panel.

To change the appearance or behavior of a particular property, delegates were introduced. For example, for
QtnPropertyBool, there are two delegates:
QtnPropertyDelegateBoolCheck and
QtnPropertyDelegateBoolCombobox . The first displays a checkbox, and the second displays a combobox with two custom values (for true and false values). You can create your delegates and set them as default, then all properties of this type will be displayed using the new delegate. It is sometimes easier and more convenient to create a new delegate type than a new property type. For example, the property for storing the file path is implemented through a special
QtnPropertyDelegateQStringFile delegate for
QtnPropertyQString .
How to use
For example, consider a small set of properties for a text editor.
This is what we want to show the user.

But what a programmer needs to do to get such a property set:
QtnPropertySet* textProperties = new QtnPropertySet(owner); QtnPropertyBool* enableWrapping = new QtnPropertyBool(textProperties); enableWrapping->setName(tr("enableWrapping")); enableWrapping->setDescription(tr("Enable/disable text wrapping")); enableWrapping->setValue(true); QtnPropertyQColor* textColor = new QtnPropertyQColor(textProperties); textColor->setName(tr("textColor")); textColor->setDescription(tr("Foreground text color")); textColor->setValue(QColor(0, 0, 0)); QtnPropertySet* Tabulation = new QtnPropertySet(textProperties) Tabulation->setName(tr("Tabulation")); Tabulation->setDescription(tr("Tabulation settings")); QtnPropertyBool* replaceWithSpaces = new QtnPropertyBool(Tabulation); replaceWithSpaces->setName(tr("replaceWithSpaces"); replaceWithSpaces->setDescription(tr("Automatically replace tabs with spaces")); replaceWithSpaces->setValue(false); QtnPropertyUInt* tabSize = new QtnPropertyUInt(Tabulation); tabSize->setName(tr("tabSize")); tabSize->setDescription(tr("Number of spaces to be placed.")); tabSize->setState(QtnPropertyStateImmutable); tabSize->setValue(4); tabSize->setMinValue(1); tabSize->setMaxValue(10);
To make life easier for programmers (or even to allow non-programmers to describe properties), a small
QtnPEG code generator was
developed .
Here is the description of our example in the Text.pef file:
property_set Text { Bool enableWrapping { description = "Enable/disable text wrapping"; value = true; } QColor textColor { description = "Foreground text color"; value = QColor(0, 0, 0); } property_set Tabs Tabulation { description = "Tabulation settings"; Bool replaceWithSpaces { description = "Automatically replace tabs with spaces"; value = false;
Simple enough and succinctly.
QtnPEG creates two files: Text.peg.h and Text.peg.cpp, where there are two classes.
QtnPropertySetText and
QtnPropertySetTabs are the classes derived from
QtnPropertySet .
In the generated classes, you can see the following
sub-properties declaration, for example for
QtnPropertySetText :
Thus, a programmer in C ++ code can work with this class as with a field structure. For example:
void doSomething(const QtnPropertySetText& textParams, QString& text) { if (textParams.Tabulation.replaceWithSpaces) { QString spaces(QChar::Space, textParams.Tabulation.tabSize); text.replace(QChar::Tabulation, spaces); } }
Total
I hope I could give some insight into my project.
I would be glad if it is useful to someone, especially where there are a lot of settings and developing a GUI for everything and everything is expensive. If, in your opinion, for the full use of
QtnProperty some functionality is missing, please comment.