Like the title,why the "q_ptr" pointer is assigned to "this" pointer of QObject? in source code.
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
>>Q_D(QObject);
>>d_ptr->q_ptr = this;/*question*/
.......
Then,when use Q_Q() macro in source code like blow:
Q_Q(QWidget)
It will return the q pointer handled by the function q_fun():
QWidget*q_func() {return static_cast<QWidget*>(q_ptr);}
As all we know,static_castis not safe when cast from parent to child.
I am very frustrated about /*question*/ ,can any guy tell me the secret?Thanks!
d_ptr->q_ptr = this;/*question*/
This is where the private implementation object (PIMPL idiom) is told about the object it is working for/with (the non-private QObject). Here's a good link for info about Qt and d pointers (d_ptr).
Q_Q macro returns the pointer to the QObject, so you can emit signals from it (among other things). As for the static_cast bit, that is safe because the macro is defined differently for each class created by the Q_DECLARE_PRIVATE and Q_DECLARE_PUBLIC macros: the result being, static_cast is always casting to the correct type. Again, I recommend reading the link.
Related
Let's say I have a C++ object with a member object that I expose to QML:
class X : public QObject
{
Q_OBJECT
};
class Y : public QObject
{
Q_OBJECT
Q_PROPERTY(X* x READ getX CONSTANT)
public:
X* getX(void) { return &x; }
X x;
};
Most of the time this works, but sometimes it will cause crashes. The call stack is quite lengthy but goes like this:
QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData> >::data
qGetPtrHelper<QScopedPointer<QObjectData>>
QObject::d_func
QObjectPrivate::get
QQmlNotifierEndpoint::disconnect
QQmlNotifierEndpoint::~QQmlNotifierEndpoint
QQmlJavaScriptExpressionGuard::~QQmlJavaScriptExpressionGuard
QRecyclePool<QQmlJavaScriptExpressionGuard, 1024>::Delete
QQmlJavaScriptExpressionGuard::Delete
QQmlJavaScriptExpression::GuardCapture::captureProperty
QQmlEnginePrivate::captureProperty
QV4::QObjectWrapper::getProperty
etc.
If instead I set X to be a pointer:
class Y : public QObject
{
Q_OBJECT
Q_PROPERTY(X* x READ getX CONSTANT)
public:
Y()
{ x = new X; }
X* getX(void) { return x; }
X* x;
};
The crashes go away.
Is this a known restriction of Q_PROPERTY, that if you return a pointer to a QObject, that object is assumed to be a pointer (not a member) and things like deleteLater() might be called?
This crash most likely happens because QML takes ownership of the object c your function returns, and want to delete it at some point late.
In your first example, since x is not allocated from the free store, trying to delete it will crash.
In the second example, the QML engine still tries to delete it, but it does so without problem.
The question to ask is why does the QML engine takes ownership of the object ?
The documentation states in Data Type Conversion Between QML and C++ | Data Ownership :
When data is transferred from C++ to QML, the ownership of the data always remains with C++. The exception to this rule is when a QObject is returned from an explicit C++ method call: in this case, the QML engine assumes ownership of the object, unless the ownership of the object has explicitly been set to remain with C++ by invoking QQmlEngine::setObjectOwnership() with QQmlEngine::CppOwnership specified.
Additionally, the QML engine respects the normal QObject parent ownership semantics of Qt C++ objects, and will not ever take ownership of a QObject instance which already has a parent.
So, in order to remedy to your problem, you could either affect a QObject parent to your x object, or explicitely declaring it having QmlEngine::CppOwnership.
I am attempting to determine which classes are subclassed from several different types. For example, if E1 extends A, E2 extends A, and E3 extends B, I want to find all classes that are subclassed from A and return a list of the types E1 and E2. These types are registered with the QMetaSystem.
My first attempt was to iterate over the user declared types, instantiate it, and get the superClass name:
int type = QMetaType::User;
while( QMetaType::isRegistered(type) ) {
QObject *o = (QObject*)QMetaType::construct(type);
QString parent = o->metaObject()->superClass()->className();
}
This seemed like a bad idea when I wrote it and didn't expect it to work. Unsurprisingly, it segfaults when attempting to get the metaObject.
Is it possible to get the information I need from the QMetaType or is there another way of getting this information?
Update
The problem seems to be in trying to iterate over all the user types. Before I register my types, some Qt type seem to be getting registered before mine. Specifically, the two registered types are QPaintBufferCacheEntry (typeId = 256) and QPaintBufferCacheEntryV2 (typeId = 257). I can initialize the object just fine, but it crashes when I attempt to get the meta object, so I'm thinking the cast is illegal. I'm not quite sure where these are being registered because my code base is not doing it explicitly.
I took this to mean that it just wasn't safe to iterate over and construct each of the user types.
Your idea is correct. This should give you the name of the parent class:
QObject *o = (QObject*)QMetaType::construct(type);
QString parent = o->metaObject()->superClass()->className();
For it to work, your classes must have been declared with Q_DECLARE_METATYPE(Type) and registered with qRegisterMetaType(). This is the case as QMetaType::isRegistered(type) is true.
Your user defined classes must also inherits from QObject (directly or not) and have the Q_OBJECT macro in their definition. This could explain your segfault.
superClass() can also return 0 if there is no parent class, but here all your classes should at least inherit QObject.
There is also QObject::inherits(const char * className) to know if an object is of a class inheriting, even indirectly, from className.
Problem
I am making a project using Q_OBJECT and Q_PROPERTY to access some objects from scripts. I have two problems:
making classes that use forward declarations scriptable
returning a property as pointer
Explanations
1. Why forward declaration?
The class B gets the forward declaration to A, because A needs the complete B type in the header due to the templates. B needs only an incomplete type (A*) in the header, thus the forward declaration is valid.
2. Why returning a pointer?
We cannot return a copy, as we need access to the actual object in the script. We cannot return a reference, as Qt does not allow slots to return references - their type would be ignored, they would only return void*.
Code
Complete code download on pastebin or as ZIP archive or as ZIP archive of minimal example is available, for testing / playing: I needed to split up the files for the forward declaration and for MOC. I added a Makefile to test it. Make deps: g++, moc, Qt.
Important parts
class A; // forward declaration necessary, see explanation above
class B : public QObject {
Q_OBJECT
Q_PROPERTY(A a READ GetA) // <-- ERROR HERE
// ...
public slots:
A* GetA() {
return mA;
}
private:
A* mA;
// ...
}
The error line in the script:
print(bObj.GetA().GetName());
Compile error
This error disappears when I comment out the Q_PROPERTY above.
tmp/B.moc.hpp:95:51: error: invalid use of incomplete type ‘struct A’
tmp/../B.hpp:10:7: error: forward declaration of ‘struct A’
Script exception
When leaving out the Q_PROPERTY and calling the GetA() method as a slot from the script, I get the following exception:
Line 7: "TypeError: cannot call GetA(): unknown return type `A*'
(register the type with qScriptRegisterMetaType())"
When registering A* with qRegisterMetaType<A*>("A*"); this changes to:
Line 7: "TypeError: Result of expression 'bObj.GetA().GetName'
[undefined] is not a function."
That shows that GetA() does not return the A object, or somehow it returns a pointer, but the script cannot dereference it. GetA() then actually returns a QVariant(A*), can this be used somehow?
Questions:
Can I somehow make a Q_PROPERTY from an incomplete type, or how could I avoid the forward declaration?
Can I return a reference in a slot (maybe some tricks, e.g. a class that wraps the pointer and "overrides" the script operator., if something similar exists) or
Can I somehow dereference a QVariant(A*) to A in QtScript?
Your property type is A, not A*, that's why you get very reasonable error.
You should use QScriptValue. Look this code. It works Ok:
class A; // forward declaration necessary, see explanation above
class B : public QObject
{
Q_OBJECT
// Using QScriptValue, made from A instead of A to allow script work correctly with an object
Q_PROPERTY(QScriptValue a READ GetA)
public slots:
QScriptValue GetA() {
//making QScriptValue from A. Type conversion in C style only due to limitation of incomplete type
//In real app it's beter to put defenition of this slot after A's defenition
return static_cast<QScriptEngine*>(parent())->newQObject((QObject*)mA);
}
private:
A* mA;
// ...
public:
//I decided my object will be a child of scriptEngine, but you can take a pointer to it in some other way
B(QScriptEngine * parent);
};
class A: public QObject
{
Q_OBJECT
public slots:
QString GetName() const {return "a name";}
public:
A(QScriptEngine*parent):QObject(parent){}
};
B::B(QScriptEngine *parent):QObject(parent), mA(new A(parent)){}
I have derived QGraphicsItem and QGraphicsScene classes. I want the items to be able to call scene() and get a derviedGraphicsItem * instead of a QGraphicsItem *, so I reimplemented QGraphicsScene::itemAt to return a derived pointer.
DerivedItem* DerivedScene::itemAt( const QPointF &position, const QTransform &dt ) const
{
return qobject_cast< DerivedItem * >(
QGraphicsScene::itemAt(position, dt) );
}
I get the following error (Qt 4.6, GCC 4.4.3 on Ubuntut 10.4)
scene.cpp: In member function ‘DerivedItem* DerivedScene::itemAt(qreal, qreal, const QTransform&) const’:
scene.cpp:28: error: no matching function for call to ‘qobject_cast(QGraphicsItem*)’
I then noticed QGraphicsItem doesn't inherit QObject, so I made my derived QGraphicsItem class have multiple inheritance from QObject and QGraphicsItem, and after adding the Q_OBJECT macro and rebuilding the project I get the same error.
Am I going about this the wrong way? I know it's supposed to be bad design to try to cast a parent class as a child, but in this case it seems like what I want, since my derived item class has new functionality and its objects need a way to call that new functionality on items around themselves, and asking the items scene object with itemAt() seems like the best way - but I need itemAt() to return a pointer of the right type. I can get around this by having the derived items cast the QGraphicsItem * returned by QGraphicsScene::itemAt() using dynamic_cast, but I don't really understand why that works and not qobject_cast, or the benefits or disadvantages to using dynamic_cast vs. qobject_cast.
EDIT:
forgot to mention that I also reimplemented QGraphicsItem::scene() in my derived class to return a DerivedScene *, as
DerivedScene* DerivedItem::scene() const
{
return qobject_cast< DerivedScene * >( QGraphicsItem::scene() );
}
but this doesn't appear to be causing a compilation error...
There is no point in inheriting from QObject just for casting. The advantage of qobject_cast
over dynamic cast is summed up pretty much in the qobject_cast documentation:
The qobject_cast() function behaves similarly to the standard C++ dynamic_cast(), with the advantages that it doesn't require RTTI support and it works across dynamic library boundaries.
It's nice to have and useful if you have QObjects, but not worth to inherit from QObject if it is all you want from QObject.
Also, for QGraphicsIems there is qgraphicsitem_cast, which should do exactly what you want :)
You have to pass a QObject pointer to qobject_cast() and QGraphicsScene::itemAt returns a QGraphicsItem pointer. Since, as you mentioned, QGraphicsItem does not derive from QObject, the complier gave you that error.
I want to execute a slot in different class. Is it possible
UI_CDSK Obj;
connect(Obj.penDrive,SIGNAL(clicked()),this,SLOT( Obj.caller()));
This code is in different class and from this class i want to execute slot of different class(UI_CDSK )
Here penDrive and caller belongs to function UI_CDSK class and the mentioned code is in other class
It's a little difficult without knowing the internals of UI_CDSK, but the correct syntax should be:
connect( Obj.penDrive, SIGNAL(clicked()), Obj, SLOT(caller()) );
So long as caller() is a public slot in UI_CDSK.
The connect method takes a pointer as receiver object, so if Obj isn't a pointer to a UI_CDSK object :
connect(Obj.pendrive, SIGNAL(clicked()), &Obj, SLOT(caller()));
Also this is probably already done (otherwise your compiler would have complained), but to use Qt signal slots mechanism, your UI_CDSK class must inherit from QObject.
It looks like the slot doesn't need to be public.