PDA

View Full Version : Qt unable to add widget dynamically in clicked slot



Cupidvogel
25th February 2015, 17:50
Here is my situation. I have a QFrame, inside which I have a QPushButton. When I click on the button, it invokes a slot, where I eventually find out which button was clicked, from that its parent widget (which here is the QFrame), and then add some other widget to it. This is what I have so far:



QFrame *base = new QFrame(parent);
base->setGeometry(50,50,160,30);
base->setStyleSheet("background: #ffffff; border-radius: 5px;");
base->setCursor(Qt::PointingHandCursor);

QPushButton *button = new QPushButton("Some text",base);
button->setGeometry(0,0,160,30);
button->setCheckable(false);
button->setStyleSheet("background: transparent; border-radius: 5px; padding-left: 9px; text-align: left; ");
connect(button,SIGNAL(clicked()),this,SLOT(myslot( )));

void MyClass::myslot()
{
QObject* pObject = sender();
QPushButton *button = qobject_cast<QPushButton* >(pObject);
QFrame *container = qobject_cast<QFrame* >(button->parentWidget());
int x = container->rect().height();

qDebug() << "Height : " << x << endl; //prints correct value
qDebug() << "Text: " << button->text() << endl; // prints correctly as well
button->setVisible(false); //this works

QFrame *newWidget = new QFrame(container);
newWidget->setGeometry(0,0,160,150);
newWidget->setStyleSheet("background: #ffffff; border-radius: 5px;");
newWidget->setCursor(Qt::PointingHandCursor); //doesn't work
container->stackUnder(newWidget);
}


As you see, I ma getting the parent container QFrame and the button all right, as evident from getting the correct values for them. But the child widget which I am now adding doesn't show up at all. What am I doing wrong, and how do I fix this?

wysota
25th February 2015, 18:14
Where would it "show up"? Do container and newWidget have the same parent? If not, stackUnder() will not do anything.

Cupidvogel
25th February 2015, 18:18
Umm, no, newWidget is supposed to belong to container. The sender is the button, from which I get the parent (named 'container' in slot, 'base ' where it was defined). So newWidget should ideally be appended to the 'base' widget. Essentially the button and the newWidget both share the same parent - base, which we get in the slot as container.

wysota
25th February 2015, 19:17
Umm, no, newWidget is supposed to belong to container.
So "container" and not "this" (as you had in your original code) should be its parent.


The sender is the button, from which I get the parent (named 'container'
Why don't you just store container as a pointer in your class? You are risking that if whenever you (or someone else) choose to change the widget hierarchy, your code will break.


So newWidget should ideally be appended to the 'base' widget.
So "base" or "container"? :)

Cupidvogel
25th February 2015, 19:21
'Base' and 'container' are same, right? First I name it as base while constructing, then in the slot, when I obtain it by referring to the sender's parent, I name it container. So the hierarchy is Container > Button. I want to append newWidget to base (renamed to container in slot), so that will make newWidget and button siblings, and children of base (container).

I can't store container as a pointer. There will be lots of elements of that type in my application.

wysota
25th February 2015, 19:36
'Base' and 'container' are same, right? First I name it as base while constructing, then in the slot, when I obtain it by referring to the sender's parent, I name it container. So the hierarchy is Container > Button. I want to append newWidget to base (renamed to container in slot), so that will make newWidget and button siblings, and children of base (container).
Ok.

1. Container should have a layout unless you want to position all the items manually
2. Create the widget you want to add without a parent
3. Add the widget to the layout mentioned in (1). QWidget::layout() returns a widget's layout which you can downcast to the proper type if you need.


I can't store container as a pointer. There will be lots of elements of that type in my application.
Sure you can. You should have proper encapsulation anyway instead of reaching with your right hand to your left ear under your left leg.

Cupidvogel
25th February 2015, 19:38
I will try your solution. But why isn't mine working?

wysota
25th February 2015, 20:10
One obvious thing is the stackUnder() call which doesn't make sense. But it shouldn't be breaking things. Or are you simply missing a show() call to the new widget? You have it there somewhere, don't you? :)

Cupidvogel
25th February 2015, 20:12
Yes, I doubted that stackUnder might be causing problems (though like you said should not break things), so I commented it out. Same problem still. I didn't use show anywhere. Why do I need to show the widget explicitly if I already assigned a parent while constructing it?

wysota
25th February 2015, 20:23
I didn't use show anywhere.
And that's why you don't see the widget :)


Why do I need to show the widget explicitly if I already assigned a parent while constructing it?
Because visibility is a three state mechanism. The docs on QWidget::setVisible() should explain that. If not then you can come back here and ask.

Cupidvogel
25th February 2015, 20:32
Hmmmph. Works. My bad. On a kinda unrelated note, imagine that the new widget has to exactly cover the original widget (what we defined as base). Now in the slot, we get the button as sender, from it we get the parent widget through parentWidget(), from there we call ->geometry().topLeft().x() to get the x coordinate of the base frame. And width, height and y in the same way. Then I use these values to set the geometry of the new widget so that it has the same dimensions and position as the base widget and thus covers it up.

Question is, will it ever fail? Can the topLeft().x() value be different in the slot compared to what I originally set , due to some layout nuances?

wysota
25th February 2015, 20:44
Now in the slot, we get the button as sender, from it we get the parent widget through parentWidget(), from there we call ->geometry().topLeft().x() to get the x coordinate of the base frame.
You got me completely lost. For a widget only its direct parent matters.


Question is, will it ever fail? Can the topLeft().x() value be different in the slot compared to what I originally set , due to some layout nuances?
You should (almost) never be setting geometry manually. That's what you have layouts for - give your widget a vertical or horizontal layout, set its margins to 0 and add a child widget to the layout. Done and always works.

Cupidvogel
25th February 2015, 20:45
Good idea. Will do. :)