After reading up on the interesting parent-child system of QObject I am wondering how common it is for Qt developers to use this in place of a more traditional container. Assuming memory contiguity is not a requirement, it seems this offers some interesting features.
For example, you could have a QObject and give it children of different types, and then find all children easily based on their types, giving QObject a dynamic heterogenous container-like feature, as opposed to the required homogenous collection of a traditional container.
And QObject naturally manages the memory of its children, which is convenient as well.
Is this a common use of this feature?
QObject::findChildren could be much slower than storing your objects in a normal container like QList because:
It iterates over all children each time. It even searches recursively (but this can be disabled).
It performs runtime type check.
It constructs new QList each time. This can be slow and expensive it there are many objects in result.
All the above it unnecessary if you just use QList<Type*> my_objects. Also in this case:
You can name your collection. QList<QPushButton*> panic_buttons is clearer than findChildren<QPushButton*>().
You can have several collections of objects of the same type.
If you want to make a heterogenous container, you can use QHash<any_type_identifier, QObject*>. It will be faster.
Maybe, findChildren approach may be simplier sometimes. But if you have many objects or a complicated class, you'd better use normal containers. You can still use QObject's memory management with them without any problems.
As #PavelStrakhov states, using QObject::findChildren could be slower. However, one method I use is to combine storing objects in QList as well as having the QObject parent hierarchy. It's based on doing something like this: -
class BaseObject : public QObject
{
Q_OBJECT
public:
static BaseObject* FindObject(unsigned int id); // find object by id
private:
unsigned int m_id;
static unsigned int s_nextId; // next id for a new BaseObject
static QList<QBaseObject*> s_objectsList; // list of all BaseObject-type instances
};
All objects now inherit BaseObject instead of QObject. When a new class is created, the constructor of the BaseObject will set the item's id, increment s_nextId and finally, the object is added to s_objectsList. Finding objects is now a simple matter of searching the static object list.
This may not suit the design of the application that you're developing, but it certainly helped me, especially when using the QGraphicsView / QGraphicsScene system. In that situation, the BaseObject is derived from QGraphicsObject.
Of-course, if you're using a lot of standard widgets, you're less likely to want to create new classes for them all, but it's an option that can suit some designs.
Related
A simple question regarding the new signal/slot syntax in Qt5:
Are there still benefits for a Q_OBJECT-derived class to have public slots: sections declared?
Note: With the new syntax you're able to connect a signal to any public function of a class or directly implement a C++11 lambda (which can also call some member functions itself).
Qt's new signal/slot syntax
While the answers by vahancho and TheDarkKnight are valid: slots is not required for connections, but it makes the intent clearer and allows introspection. I think I should list some use cases where you do need slots.
First please note that you can use slots, Q_SLOTS, Q_SLOT or Q_INVOKABLE to make a function known to the meta object (introspection) system. Q_INVOKABLE has the advantage that it can be used on constructors.
And here are the use cases in no particular order:
Make your code works with Qt 4. Even if Qt 4 is not maintained I think some big company are still using it and it is fairly easy to make a library works with Qt 5 and Qt 4.
Make the function available in QML (and Qt Quick)
Make the function available in javascript (Qt Script, Qt WebEngine, etc.)
Make the function callable with QMetaObject::invokeMethod(). An overload that accepts functors will be available in Qt 5.10.
Make use of QMetaObject::connectSlotsByName(). Note that this function should not be used as it can be affected by object name collisions, but it is still the way the Qt widget designer connects the slots it creates.
Make your class instantiatable with QMetaObject::newInstance().
And every other use case that requires run-time introspection
you're able to connect a signal to any public function of a class or directly implement a C++11 lambda
Whilst this was made available in Qt 5, which allows for compile-time verification of the slot, as opposed to when using the SIGNAL and SLOT macros, it is no longer a requirement to declare a function as a slot to connect to it.
However, for clarity I still do, as it makes the intention of a class clearer for usage, when others come to using the class.
For example:
class Foo : public QObject
{
public:
Foo();
public slots:
void AddData();
private:
void CalculateStuff();
};
Just by looking at the class, we can assume that the function AddData is designed to be called via a signal; perhaps it executes on a separate thread.
public slots: etc. declarations still needed for moc introspection if you are going to use the "old" connection style. With the new syntax this declarations do not make any sense, because, as you also noticed, "slots" are called directly by function pointers. "Slots" may even be a non class member functions as well.
However, you still need to declare your signals under signals: section of your class declaration.
They're still needed for Qml, so that you can connect to C++ slots. However, if you want to call a C++ QObject member function, you can just declare it as Q_INVOKABLE. You don't need to make it a slot. Although using slots: might be more readable compared to using Q_INVOKABLE. Up to you.
They're also needed if you want Designer to see them. Designer has a "signal/slot" editor, and it will not list functions that are not in the slots: section. However, Designer still uses the old string-based syntax for signals and slots, so I wouldn't recommend using its signal/slot editor.
Since Qt 5.6 we can finally write code like this:
ListView {
id: list
model: MyModel
delegate: TextInput {
text: display
onEditingFinished: {
model.edit = displayText
}
}
i.e. model.edit will call MyModel's setData() with Qt::EditRole and display value from the TextInput. Great, was headache for a long time.
However even if using QAbstractItemModel is the recommended practice for more complex C++ based models I still have the feeling that all of it is meant only for read only models, i.e. that a qml view can read the number of rows, columns etc but it was never meant as a way for adding or removing rows (for clean implementation of QAbstractItemModel::setData the row must be already present).
It feels really dirty to reimplement all the insert/remove functions with Q_INVOKABLE and qml's ListModel is far too simple for anything serious.
What would you recommend for a qml based widget which should add/remove rows, edit items and yet have a C++ model?
Reimplementing the insertRows() and removeRows() for your new subclass of QAbstractListModel or QAbstractItemModel is not dirty, it's normal!
Notice that insertRows() and removeRows() are marked virtual in the base class, indicating just that.
The base class does not know how to manipulate your data structure, as it could be a QList, or it could be something much more complicated like a SQL database or a 3rd party library.
In your class definition you can either mark the methods as Q_INVOKABLE or as public slots. Note also that several functions in Qt models classes - both virtual and non - are already marked as invokable, see e.g. here.
I recommend you to override insertRows() and removeRows() methods which are Q_INVOKABLE and takes index as integer and invokes actual insertRows() and removeRows() methods.
So you can use both widget and qml UI approaches.
I think it is not dirty because you just make that ready for different usage and also override is normal.
Is it possible to use QPointer with QHash?
QPointer<QHash<QString, QPointer<QStringList>> > pHash;
QPointer can only be used with QObject subclasses. Thus it cannot be used with QHash or QStringList, as both aren't QObject's. If the code above compiles for you, that's probably because you don't use pHash yet? Even initializing such a QPointer, e.g.
QPointer<QHash<QString, QString> > foo( new QHash<QString, QString>() );
gives errors like the following one (gcc):
error: cannot convert ‘QHash<QString, QString>*’ to ‘QObject*’ in initialization
If you really need (smart) pointers to containers, try QSharedPointer, which doesn't require the contained object to be of any specific type.
Usually one creates containers on the stack though, creating them on the heap is unidiomatic and unnecessary in almost all cases. Qt's containers are implicitly shared, thus copying them is cheap.
I have a QListWidget of calendars. Each QListWidgetItem is logically associated with an instance of Calendar, which is a class that belongs to the Model side of the application.
Can I store this association in the form of a pointer using QListWidgetItem::setData? When I attempt to do this, I get the following error:
error: 'QVariant::QVariant(void*)' is private
There is another constructor for void*: QVariant::QVariant(int typeOrUserType, const void * copy) where you should pass an unique integer to represent the pointer type.
But as stated by the documentation, you could declare your pointer type with Q_DECLARE_METATYPE(Calendar*) and use QVariant::fromValue<Calendar*>(...) and QVariant::value<Calendar*>() to store and retrieve the value.
Or instead, because you are using a QListWidget instead of a regular model, you can just subclass QListWidgetItem, and add a Calendar* member variable with the required accessors, to avoid the overhead of using QVariant.
I would suggest looking at this solution as well, which I think is quite elegant:
(there are minor syntax errors, but you will spot them quickly or the compiler will issue an error)
https://web.archive.org/web/20171025163314/http://blog.bigpixel.ro/2010/04/storing-pointer-in-qvariant/
I have a feeling this isn't possible with the current API, but I have to ask. Is it possible to query a particular QObject's signal or slot name (from the metaObject) and retrieve all the QObjects and their slot or signals names that are connected to it?
I'm doing this because, in effect, I have a large number of layouts that contain an identical arrangement of widgets, for each layout there an object and each of the layout's widgets control the various properties of it. I want to keep one layout, and connect it's widgets' signal/slots to all the other objects in the same pattern, but in order to do this I need to 'record' all the signal-slot data.
Is it possible?
There is an interesting file in Qt - %Qtdir%/src/corelib/kernel/qobject_p.h, it contains class QObjectPrivate, used by Qt internally.
Use
static QObjectPrivate *get(QObject *o) function to get QObjectPrivate member for your widgets, and try to call its interesting members like
QObjectList receiverList(const char *signal) const; or QObjectList senderList() const;. File is totally undocumented, but it seems to contain exactly what you need...