📜 ⬆️ ⬇️

Properties framework for Qt



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

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 :
  1. QString name - the name of the property or propertyset (same as objectName)
  2. QString description - a text description that can be displayed in the bottom panel of QtnPropertyWidget
  3. qint32 id - unique property ID among others in the group (used when saving in binary form)
  4. QtnPropertyState state - the state of the property (for example, QtnPropertyStateImmutable is a non-editable property or QtnPropertyStateInvisible is invisible)
  5. load / save - save / load via QDataStream
  6. fromStr / toStr - converting property value from / to QString
  7. fromVariant / toVariant - converting property value from / to QVariant
  8. 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.)
  9. 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:
  1. QtnPropertyBool - represents a boolean value.
  2. QtnPropertyInt - represents a signed integer value.
  3. QtnPropertyUInt - represents an unsigned integer value.
  4. QtnPropertyFloat - represents a floating point value.
  5. QtnPropertyDouble - represents the real value of double precision.
  6. QtnPropertyEnum - represents the value from the enumeration.
  7. QtnPropertyEnumFlags - represents a combination of values ​​from an enumeration.
  8. QtnPropertyQString - represents a string value.
  9. QtnPropertyQPoint - represents a QPoint value.
  10. QtnPropertyQSize - represents the value of QSize.
  11. QtnPropertyQRect - represents the value of QRect.
  12. QtnPropertyQFont - represents the value of a QFont.
  13. QtnPropertyQColor - represents the value of QColor.

All numerical properties ( QtnPropertyInt , QtnPropertyUInt , QtnPropertyFloat , QtnPropertyDouble ) have three additional methods:
  1. minValue is the minimum allowed value.
  2. maxValue is the maximum allowed value.
  3. 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); //    propertyDidChange auto lambda = [tabSize, replaceWithSpaces](const QtnPropertyBase* changedProperty, const QtnPropertyBase* firedProperty, QtnPropertyChangeReason reason) { //    if (reason & QtnPropertyChangeReasonValue) { if (*replaceWithSpaces) //  tabSize  tabSize->removeState(QtnPropertyStateImmutable); else //  tabSize   tabSize->addState(QtnPropertyStateImmutable); }; QObject::connect(replaceWithSpaces, &QtnProperty::propertyDidChange, lambda); //       replaceWithSpaces ... 

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; //    delegate ComboBox { //     true labelTrue = "On"; //     false labelFalse = "Off"; } //      replaceWithSpaces.propertyDidChange slot propertyDidChange { tabSize.switchState(QtnPropertyStateImmutable, !replaceWithSpaces); } } UInt tabSize { description = "Number of spaces to be placed."; state = QtnPropertyStateImmutable; value = 4; } } } 

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 :
  // start children declarations QtnPropertyBool& enableWrapping; QtnPropertyQColor& textColor; QtnPropertySetTabs& Tabulation; // end children declarations 

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.

Source: https://habr.com/ru/post/217203/


All Articles