I'm writing a program that requires the user to be very flexible in manipulating data on a given object. I figured I would use a property browser of some kind; Qtilities' ObjectDynamicPropertyBrowser caught my eye.
However, I need to be able to add my own data types. The documentation is not clear on how to do so.
How can I allow my own data types to be represented in Qtilities' property browser widgets?
Also, more about my needs:
- The data types are not part of Qt, nor are they even
Q_OBJECTs. - Qt-specific modifications to the relevant classes are not an option.
- Declaring the relevant classes via
Q_DECLARE_METATYPEis okay. - In particular, I need to represent vector and matrix types (and possibly more later).
The browser you refer to depends on the
QObjectproperty system. So, unless your classes are QObjects, it won't work - but don't despair, Qt 5.5 to the rescue (read on). The browser seems to use aQTreeViewand provides an adapter model that exposes theQObjectproperty system. So, it leverages Qt's type and delegate system.In Qt 5.5, there is a general purpose property system, known as gadgets, that can be used on any class, as long as there is a
QMetaObjectdescribing that class. By adding theQ_GADGETmacro to a class deriving from the subject class, and describing the properties usingQ_PROPERTYmacro, you can leveragemocand the gadget system to access your unmodified types' properties.The only reason you'd do that is to require minimal changes to the
ObjectPropertyBrowsersystem. You don't want theObjectDynamicPropertyBrowser, since it works on dynamic properties, and your objects don't have any. They have static properties, given throughQ_PROPERTYmacros and code generated by moc.So, you'll proceed as you would for implementing your own type support for
QVariantand views in general. You also need Qt 5.5, since you need gadget support for it to work. A solution for Qt 5.4 and below requires a different approach and might be less cumbersome to implement in another way.See this answer for a reference on using gadget property system for object serialization, it's fundamentally what the property browser would do, sans the serialization proper of course.
There are three steps. First, you need to address simple custom types that don't have a structure, but represent a single value (such as a date, or time, or geographic position, etc.), or a collection of simple values (such as a matrix).
Ensure that
QVariantcan carry the simple types. Add theQ_DECLARE_METATYPEmacro right after the type's definition in an interface (header file).Implement delegates for the types. For types with table structure, such as a matrix, you can leverage
QTableViewand provide an adaptor model that exposes the type's contents as a table model.Secondly, you get to your complex types that have internal structure:
Create a wrapper class that derives from the complex type, declares all properties using
Q_PROPERTY, and has theQ_GADGETmacro (notQ_OBJECTsince they are not QObjects). Such class should not have any members of its own. Its only methods should be optional property accessors. TheQ_GADGETmacro adds the static (class) memberstaticMetaObject.The underlying type can be
static_castto the wrapper class if needed, but that's normally not necessary.At this point, any class that you wrote a wrapper for is accessible to the
QMetaPropertysystem directly, without casting! You'd use the wrapper'sstaticMetaObjectfor its static metaobject, but theQMetaPropertyreadOnGadgetandwriteOnGadgetwill take the pointers to the base class directly.Thirdly, since
ObjectPropertyBrowsermost likely doesn't implement the support for gadgets in Qt 5.5, as that's quite new, you'll have to modify it to provide such support. The changes will be minimal and have to do with usingQMetaProperty::readOnGadgetandQMetaProperty::writeOnGadgetinstead ofQMetaProperty::readandQMetaProperty::write. See the serialization answer for comparison between the two.