[SOLVED] Accessing a parent widget's variables



  • If I have a parent widget and a child widget, shouldn't I be able to access the parent's variables from within the child through the QWidget parent pointer in the child's constructor? So far, when I write an expression that includes parent->someVariable I get an error -- something like "the base is not a pointer."



  • I think it's parent() -> someMethod() note the () after parent. And as a general use case you won't be able to access the variables directly, as most of them are protected / private.



  • I confirm, you should be able to access the parent’s methods and variables from your widget (provided the fact that you pass it to your child's constructor)
    Can you check that you effectively passed a pointer to parent in your child constructor :
    @
    ChildWidget::ChildWidget(QWidget *parent)
    {
    parent->someVariable;
    }
    @
    and NOT:
    @
    ChildWidget::ChildWidget(QWidget parent)
    {
    parent->someVariable;
    }
    @
    Moreover, i agree Johan: in general, you don't access variables directly, but through accessors...
    Hope this will help you.

    [Edit: Wrapped code in @-tags; mlong]



  • Well, DO'H, your second "snippet" copies the parent as a local, so the -> syntax will not even work even if it wasn't a copy and with the dot syntax you will still access the copy not the actual parent, even if QWidget wasn't non-copyable to begin with. Also, please wrap your code in the appropriate code tags for better readability.

    Acceding the parent through the pointer, passed into the constructor is one way, and as Johan Solo already pointed out, the parent() method is another option, which returns a pointer to the parent, which makes it possible to access the parent outside of the object's constructor.



  • @sraboisson: I double checked and the constructor's parameter does have a * in the right place. The exact error messages are

    bq. invalid use of member (did you forget the '&' ?)

    and

    bq. base operand of '->' is not a pointer

    @Johan Solo and ddriver: I tried a version with parent()->someVariable and received the following error:

    bq. 'class QObject' has no member named 'someVariable'

    I should mention that someVariable is a public static member of the parent class.



  • Well, obviously you need a pointer to your parent object, not to QObject which is passed in the constructor or returned by parent(). It is your class that inherits QObject that has the variable, what you need is to cast that QObject pointer to your custom class type. Without this procedure, you are limited to the facilities of QObject. You can use either qobject_cast or dynamic_cast just to introduce some runtime type safety, if the cast fails it will return zero, whereas a plain cast will work in the blind and crash you app if something goes wrong.

    @YourParentClass *myParent= qobject_cast<YourParentClass *>(parent()); // or without () if you are in the constructor
    if (myParent)
    myParent->someVariable ...@

    ... or alternatively, if you don't need the pointer object you can simply do:

    @qobject_cast<YourParentClass *>(parent())->someVariable ... @

    Note with the second approach you will neither get autocomplete in the IDE, nor is there a way to check if the cast failed for some reason. Since the outcome of the cast is not known until runtime, if you call someVariable to a NULL pointer, you are likely to crash.



  • Hi planarian,

    In such case, you had better write your constructor as

    @
    Child::Child(YourParentClass * parent);
    @

    instead of

    @
    Child::Child(QObject * parent);
    @

    Otherwise , you will have to using qobject_cast or static_cast or dynamic_cast



  • That was actually my first idea, but I wasn't sure if it is 100% OK to pass a QObject derived class instead of QObject for parent. Just tested it, it seems to work fine. Not that it is a waste to learn how to use qobject_cast ;) Note, this is only possible in the constructor, outside of it the parent() method will still return a QObject pointer unless you overload the method, or easier - just cast the pointer as mentioned above.



  • bq. bq. Well, obviously you need a pointer to your parent object, not to QObject which is passed in the constructor

    Oh, of course! That "Qwidget * Parent" line is so common I never stopped to think about what it. Ok, so this leaves me with two questions:

    1. How does the parent() function differ from simply using (or casting) the parent pointer? As I pointed out earlier, I get very different errors for each of these.

    2. Whether I modify the constructor or use a cast, I have to refer to the parent class by name within the child class. Since the parent class already includes the child's header, how do I avoid the circularity of including the parent's header within the child? (I realize this is a C++ issue rather than Qt)



  • Use forward declaration - in the header declare your class, but don't #include its header:

    @class YourParentClass;@

    And that's all you will need in the header, forward declaration is a promise to the compiler that you will define YourParentClass later on. This is used to avoid circular dependencies and the associated object redefinition compiler errors. The actual header for your class you include in the cpp file instead.

    The parent() method by default returns a QObject pointer, which is the lowest compatible facility, the bottom level base class. Your class inherits QObject, either directly or through QWidget, so if you need to access your custom members, you have to do a cast of that QObject pointer to your class type. Parent() will always return a QObject pointer, unless you overload the method yourself. This is not the case with passing a QObject pointer or a pointer to a QObject derived class in the constructor as a parameter, thus in the constructor you can avoid using the parent() method and casting it to your class and instead use pointer to your class directly, it can still be a parent since it "contains" a QObject inside of it. One of the errors you got is you passed your QObject as a copy and not as a pointer, since the -> syntax only works with pointers. Another error you might get is when you try to pass a QObject or derived class as a copy, QObject is by design non-copyable, the constructor is explicit to make sure it does not end up being used as a copy constructor.


  • Moderators

    bq. How does the parent() function differ from simply using (or casting) the parent pointer?

    ddriver covered the basics, but I wanted to point out that the "parent" pointer that's in the constructor is arbitrarily named, and as such, may be adding to the confusion on what the details are. Just be aware that parent() is a method that is always named parent() and that the "parent" pointer could just as easily be named anything, like:
    @
    MyClass::MyClass(QObject *somethingElse) ...
    @



  • Ah yes, parent() is a method that exists in the body of the class, the pointer, passed into the constructor exists only in the body of the constructor, and parent is just the logical identifier/name for the object, you can use whatever name you want.

    The parent() method and the parent constructor local have nothing to do with each other. I didn't realize there was any confusion about it :)

    I might add:

    @MyClass::MyClass(AnyQObjectDerivedClass *somethingElse) {
    somethingElse->someVariable...@



  • bq. Use forward declaration – in the header declare your class, but don’t #include its header:

    Yes, I've tried a couple different forward declarations, but I can't get rid of the following errors:

    invalid use of incomplete type 'struct ParentClass'
    forward declaration of 'struct ParentClass'

    Incidentally, the child class now includes the line

    @tempVar = qobject_cast<YourParentClass *>(parent());@

    where tempVar is of type YourParentClass *.

    bq. parent is just the logical identifier/name for the object, you can use whatever name you want[...]I didn’t realize there was any confusion about it

    I'm afraid I'm now even more confused! :) If "Parent" is an arbitrary name, how would Qt decide what the parent of a given widget is if I passed more than one QObject pointer?


  • Moderators

    In your QObject-derived class, you pass a pointer to a parent object, then it's up to you to pass that pointer's value back to your superclass. This functionality is basic C++ Inheritance stuff and not unique to Qt, though it does apply heavily to Qt.

    @
    MyQObjectClass : public QObject
    {
    Q_OBJECT
    public:
    MyQObjectClass(QObject *theparent = 0);
    ...
    }

    // In the constructor, you call QObject's constructor and pass the parent value upstream.
    // This is how QObject knows what the parent is.
    MyQObjectClass::MyQObjectClass(QObject *theparent) : QObject(theparent)
    {
    ...
    // parent() now returns whatever value was passed to the QObject upstream, since
    // the parent() method originates back up the inheritance tree at the QObject level.
    }
    @



  • bq. invalid use of incomplete type ‘struct ParentClass’
    forward declaration of ‘struct ParentClass’

    You forget to include the header of "ParentClass" in the *.cpp files which using the class.

    By the way, Qt is a library of C++, make sure that you have mastered the basis of C++.



  • [quote author="planarian" date="1332957173"]

    I'm afraid I'm now even more confused! :) If "Parent" is an arbitrary name, how would Qt decide what the parent of a given widget is if I passed more than one QObject pointer?[/quote]

    Consider this, names a.k.a identifies MEAN NOTHING at runtime, those are just for the human who writes/reads the code. The compiler uses identifies to get the address of that object in memory, and knowing the type, the entire "topology" of the object in memory is known.

    Since you inherit QObject, with inheritance, in the constructor you initialize the base class object as well, as mlong's code snippet indicates. So no matter how many QObject pointers you pass in the constructor of your object, it is the one pointer you use to initialize QObject with that becomes the parent. The reason you might have missed this is Qt Creator automatically initializes QObject when you create a new class inheriting QObject through the new class wizard.

    This is not even Qt, this is general C++, as 1+1=2 suggested you should spend some time mastering C++, and Qt will suddenly make more sense :)



  • bq. it is the one pointer you use to initialize QObject with that becomes the parent

    That's the source of my confusion: I simply didn't realize that QObject can never take more than one pointer.

    bq. You forget to include the header of “ParentClass” in the *.cpp files which using the class.[...]make sure that you have mastered the basis of C++.

    As it currently stands, parent.h includes child.h, parent.cpp includes parent.h, child.h has a "class parent" forward declaration, and child.cpp includes child.h. So I don't believe your diagnosis is correct.

    My knowledge of C++ is not so remedial as I may have given the impression, but you are right to suppose that it isn't second nature to me, and your impatience is understandable. I appreciate everyone's time, and will try hard to ensure that any future posts are "on topic."



  • [quote author="planarian" date="1332968041"]
    That's the source of my confusion: I simply didn't realize that QObject can never take more than one pointer.

    As it currently stands, parent.h includes child.h, parent.cpp includes parent.h, child.h has a "class parent" forward declaration, and child.cpp includes child.h. So I don't believe your diagnosis is correct. [/quote]

    It is YOU (or eventually Creator) that constructs a QObject inside the QObject derived class, if inheritance is indirect the previous base class is responsble for it, and the parent pointer is passed down the hierarchy until it reaches the bottom base QObject class. Unlike with signals and slots, there is no magic here, no code is generated for you behind the scenes. QObject itself has only 3 constructors, and neither of them can take more than one parent parameter, and the parent parameter is not automatically passed into QObject, it is done in the constructor of the first class that inherits QObject by code, no matter how deep it is before your own class.

    Your forward declaration should work the way you have described your situation. Either that is not the case, or there is some other issue, you are best posting your code and the exact error message that goes with it, so we can see what you are trying to do and if necessary test it ourselves. For me accessing parent members from a child totally works.

    @// parent header
    #ifndef MYPARENT_H
    #define MYPARENT_H

    #include <QObject>

    class MyParent : public QObject
    {
    Q_OBJECT
    public:
    explicit MyParent(QObject *parent = 0);
    int someVariable;
    };
    #endif // MYPARENT_H

    // parent cpp
    #include "myclass.h"

    MyParent::MyParent(QObject *parent) :
    QObject(parent), someVariable(7) {} // some variable initialized to 7

    // child header
    #ifndef MYCHILD_H
    #define MYCHILD_H

    #include <QObject>
    class MyParent; //forward declaration

    class MyChild : public QObject
    {
    Q_OBJECT
    public:
    explicit MyChild(MyParent *parent = 0);
    };
    #endif // MYCHILD_H

    // child cpp
    // parent header included here in the child cpp
    #include "mychild.h"
    #include "myparent.h"

    MyChild::MyChild(MyParent *parent) :
    QObject(parent)
    {
    parent->someVariable = 666; // access parent member through pointer and change from 7 to 666
    }

    //main
    #include <QtCore/QCoreApplication>
    #include <QDebug>
    #include "myparent.h"
    #include "mychild.h"

    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);

     MyParent parent;
     qDebug() << parent.someVariable; // prints out 7
    
     MyChild child(&parent);
    
     qDebug() << parent.someVariable; // prints out 666
     qDebug() << dynamic_cast <MyParent *>(child.parent())->someVariable; // casting the QObject * to MyParent * using C++ dynamic cast, no checking, will crash if cast fails, prints out 666
     
     MyParent * temp = qobject_cast <MyParent *>(child.parent()); //create a local pointer to parent from casting child parent() method with Qt qobject cast
     if (temp) { //check if cast succeeded 
            temp->someVariable = 333;
            qDebug() << parent.someVariable; // prints out 333
     }
    
     return a.exec(&#41;;
    

    }@



  • bq. You forget to include the header of “ParentClass” in the *.cpp files which using the class.

    @1+1=2: I'm embarrassed to say that you were dead on after all. The one declaration I was missing was #include "parent.h" in child.cpp but due to my inexperience with forwarding declarations I didn't spot that I hadn't covered all the bases.

    @ddriver: Once again you've written a ton of code for my benefit, and although I would have taken a lot longer to figure out this problem without your example, I still feel badly about it. If I lived in Bulgaria, I'd bring a cake to your office, or something....

    bq. Unlike with signals and slots, there is no magic here, no code is generated for you behind the scenes.

    I see now that moc has spooked me: I've had an unconscious tendency to assume that anything in Qt I don't immediately recognize is a mysterious extension of the language. This thread has done a great deal to clarify my understanding of Qt's boundaries, which is actually more valuable to me than the solution to any particular problem.

    Thanks very much!



  • [quote author="planarian" date="1333043253"]
    @ddriver: Once again you've written a ton of code for my benefit, and although I would have taken a lot longer to figure out this problem without your example, I still feel badly about it. If I lived in Bulgaria, I'd bring a cake to your office, or something....
    [/quote]

    Well, I am new to programming myself, and that code I did for your sake as much as I did for myself, what a better way to improve than doing so helping others, besides that "ton" of code took only a few minutes, thanks to Qt Creator. So no need to feel bad, or of a cake for that matter, besides I don't eat sweet stuff, it is decremental in far too many ways :P

    Don't forget to mark the thread with [SOLVED] once it has ran its course.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.