views:

73

answers:

1

I wrote a small program to test accessing a widget parent's slot. Basically, it has two classes:

Widget:

namespace Ui
{
    class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
    QLabel *newlabel;
    QString foo;

public slots:
    void changeLabel();

private:
    Ui::Widget *ui;
};

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    customWidget *cwidget = new customWidget();
    newlabel = new QLabel("text");
    foo = "hello world";
    this->ui->formLayout->addWidget(newlabel);
    this->ui->formLayout->addWidget(cwidget);

    connect(this->ui->pushButton,SIGNAL(clicked()),cwidget,SLOT(callParentSlot()));
    connect(this->ui->pb,SIGNAL(clicked()),this,SLOT(changeLabel()));
}

void Widget::changeLabel(){
    newlabel->setText(this->foo);
}

and customWidget:

class customWidget : public QWidget
{
    Q_OBJECT
public:
    customWidget();
    QPushButton *customPB;

public slots:
    void callParentSlot();
};

customWidget::customWidget()
{
    customPB = new QPushButton("customPB");
    QHBoxLayout *hboxl = new QHBoxLayout();
    hboxl->addWidget(customPB);
    this->setLayout(hboxl);
    connect(this->customPB,SIGNAL(clicked()),this,SLOT(callParentSlot()));
}

void customWidget::callParentSlot(){
    ((Widget*)this->parentWidget())->changeLabel();
}

in the main function, I simply created an instance of Widget, and called show() on it. This Widget instance has a label, a QString, an instance of customWidget class, and two buttons (inside the ui class, pushButton and pb). One of the buttons calls a slot in its own class called changeLabel(), that, as the name suggests, changes the label to whatever is set in the QString contained in it. I made that just to check that changeLabel() worked. This button is working fine. The other button calls a slot in the customWidget instance, named callParentSlot(), that in turn tries to call the changeLabel() slot in its parent. Since in this case I know that its parent is in fact an instance of Widget, I cast the return value of parentWidget() to Widget*. This button crashes the program. I made a button within customWidget to try to call customWidget's parent slot as well, but it also crashes the program. I followed what was on this question. What am I missing?

+1  A: 

You never set the parent widget for your customWidget instance. So, this->parentWidget() is likely returning a NULL pointer. Make the following changes:

customWidget *cwidget = new customWidget(this);
...
customWidget(QWidget *parent);
...
customWidget::customWidget(QWidget *parent) : QWidget(parent)

I would also advise using a dynamic_cast and checking the return value. This went prevent your crash in both the case where parent is NULL and where parent is not of the correct class.

void customWidget::callParentSlot()
{
    Widget *w = dynamic_cast<Widget *> (this->parentWidget());
    if (0 != w)
        w->changeLabel();
    /* else handle the error */
}

Another approach would be to call the parent slot through the signals and slots interface. Connect the new customWidget signal to the Widget slot in the Widget constructor. Then you can call the slot from customWidget as follows.

emit callParentSignal();
Judge Maygarden
It still crashes, even after explicitly setting its parent. Shouldn't cwidget's parent be automatically set when addWidget(cwidget) is called?
David McDavidson
I know that it's not a good idea, but I'm stuck with doing this or having to rewrite a huge chunk of code from an app I'm working with
David McDavidson
I'm not sure if addWidget reparents it. Even so, are you sure it uses the correct widget as a parent?
Judge Maygarden
What do you mean by 'uses the correct widget'?
David McDavidson
I mean, that function could possibly set a different QWidget as the parent. Why don't you take a look at the source code for Ui::Widget::addWidget?
Judge Maygarden
BTW, have you tried explicitly setting the parent to see if it fixes the crash bug?
Judge Maygarden
I tried, it still crashes. Now that you said it, maybe when I call addWidget(cwidget), it resets the formLayout as the parent, instead of Widget, which is the parent of the formLayout.
David McDavidson
Yes, that was it. I changed the ((Widget*)this->parentWidget())->changeLabel() to ((Widget*)this->parentWidget()->parentWidget())->changeLabel() and it worked. It's so goddamn ugly.
David McDavidson
A very explicit solution would be to pass a Widget reference to customWidget and hold it as a member variable instead of using the Qt parent/child tree.
Judge Maygarden
I guess I still don't really know how to use the signals-slots system. Seems like the proper way to do what I want. Thanks
David McDavidson
The signal thing worked, thanks!
David McDavidson