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_OBJECT
s. - Qt-specific modifications to the relevant classes are not an option.
- Declaring the relevant classes via
Q_DECLARE_METATYPE
is okay. - In particular, I need to represent vector and matrix types (and possibly more later).
The browser you refer to depends on the
QObject
property 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 aQTreeView
and provides an adapter model that exposes theQObject
property 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
QMetaObject
describing that class. By adding theQ_GADGET
macro to a class deriving from the subject class, and describing the properties usingQ_PROPERTY
macro, you can leveragemoc
and the gadget system to access your unmodified types' properties.The only reason you'd do that is to require minimal changes to the
ObjectPropertyBrowser
system. You don't want theObjectDynamicPropertyBrowser
, since it works on dynamic properties, and your objects don't have any. They have static properties, given throughQ_PROPERTY
macros and code generated by moc.So, you'll proceed as you would for implementing your own type support for
QVariant
and 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
QVariant
can carry the simple types. Add theQ_DECLARE_METATYPE
macro 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
QTableView
and 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_GADGET
macro (notQ_OBJECT
since they are not QObjects). Such class should not have any members of its own. Its only methods should be optional property accessors. TheQ_GADGET
macro adds the static (class) memberstaticMetaObject
.The underlying type can be
static_cast
to 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
QMetaProperty
system directly, without casting! You'd use the wrapper'sstaticMetaObject
for its static metaobject, but theQMetaProperty
readOnGadget
andwriteOnGadget
will take the pointers to the base class directly.Thirdly, since
ObjectPropertyBrowser
most 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::readOnGadget
andQMetaProperty::writeOnGadget
instead ofQMetaProperty::read
andQMetaProperty::write
. See the serialization answer for comparison between the two.