I am working on an application in which I need to change the layout of my current tab when a radio button is pressed.
I am using the following code to swap the layout:
changed_layout = new QVBoxLayout;
label1 = new QLabel(string);
delete main_layout;
changed_layout->addWidget(label1);
setLayout(changed_layout);
But when I click the button the software exits (crashes).
You shouldn't try to directly delete QObjects with delete, call ojbect->deleteLater() and set your pointer to null. The Qt framework will safely delete the object after any slots have been exited and control has returned to the event loop.
In your code, change the line delete main_layout; to:
main_layout->deleteLater();
main_layout = NULL;
Update:
The comment from Steve S is correct. To quote Qt docs for setLayout()..
If there already is a layout manager
installed on this widget, QWidget
won't let you install another. You
must first delete the existing layout
manager (returned by layout()) before
you can call setLayout() with the new
layout.layout.
Since setLayout() is a special case of reparenting widgets, we must delete the old layout first and then set the new layout. We would have to be very sure of the signal/slot call stack we are in to call delete().
An alternative would be to setup the new layout as much as possible and then connect a slot to the old layouts destroyed() signal and then call deleteLater() on it. In the slot for the destroyed() signal you should be able to call setLayout() with the new layout.
If this sounds complicated, it's because it is. Changing and destroying layouts at runtime is unusual. If I found that I had to do something like this, I would probably create something like a gridLayout at the top of the hierarcy with the different layouts I would need set inside it and then move the child widgets from layout to layout without having to create and destroy them at runtime.
Related
I know that usually parents are set by being given in the constructor argument, for example if you wanted to have a push button within a frame you could do:
QFrame frame = new QFrame();
QPushButton* button = new QPushButton(frame);
But I already have the push button that I want to add as a child to a frame (it was received as an argument in the call to the function I'm working on).
Is there any way to add an already existing widget as a child to another widget? Or add a new child to an existing or nonexistent parent?
(Disclaimer: Answer blatantly stolen from G.M.'s comment (with permission - see above ;))
Simply use QWidget's setParent(QWidget *parent) method.
Note: The new parent takes ownership of the QWidget (i.e. deallocating it when the parent is destroyed etc.) and you will need to call show() on the widget again to make it visible, as changing parents turns QWidgets invisible.
So, I am working with some QWidgets inside a QMdiArea, and i want to play with the opacity og a graphicview inside the QWidgets with a dial, using the QGraphicsOpacityEffect.
This is the slot that receives the dial signal to set the new opacity:
void MainWindow::changeWindow1Transparency(int dialValue)
{
QGraphicsOpacityEffect* op = new QGraphicsOpacityEffect(ui->graphicsView); //Potential memory leak here
op->setOpacity(qreal(dialValue)/255);
ui->graphicsView->setGraphicsEffect(op);
ui->graphicsView->repaint();
}
This is the only way i've managed to make the opacity change immediately when turning the dial. But I fear that this might cause a memory leak because of the new constantly creating new effects.
I have tried to set this QGraphicOpacityEffect *op as an attribute of the class. But then, when I turn the dial, the opacity doesn't change immediately but only when I move the window around the QMdiArea. The same happens when calling the QGraphicsView->GraphicsEffects().. Any ideas on why this is happening? how could I prevent the memory leak and at the same time force that the opacity changes immediately with the dial?
Thanks!
There shouldn't be a memory leak as long as you pass a parent object to your QGraphicsOpacityEffect or set a widget with the effect.
The way you've set it up ui->graphicsView->setGraphicsEffect(op), According to QWidget::setGraphicsEffect:
Sets effect as the widget's effect. If there already is an effect installed on this widget, QWidget will delete the existing effect before installing the new effect.
You've got yourself a guarantee that the intermediate objects will be deleted. With regards to the final GraphicsEffect, as your ui->graphicsView widget is destroyed, so does the GraphicsOpacityEffect (see Qt Object Trees and Ownership).
As for forcing the opacity changes to your dial, try adding repaint(); to your slot. This will repaint your entire widget. (And after that, also try parentWidget()->repaint() as the parent sometimes needs a little nudging.)
As Jeremy Friesner mentions in the comments and as you've tried before, it may be more efficient to set QGraphicOpacityEffect *op as a member of the class, calling op->setOpacity(x) in your slot without having to create a new effect each time the slot is triggered. Keep in mind the repaint semantics above.
One of our teacher asked us to create a Qt application without any UI file for the main window (a QMainWindow). Usually I always create one, leave it empty and let the uic deal with it.
I know that if a parental relation is defined between a widget (child) and its parent, then there is no need to delete the widget (deleted when the parent is deleted). So, when the UI is deleted, all the children are destroyed.
If we do not use an UI file (not generated), do we have to manually delete all the widget added to the GUI?
A little sample:
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent){
layout = new QHBoxLayout(this);
aButton = new QButton(this);
layout->addWidget(aButton);
...
}
MainWindow::~MainWindow(){
delete ui; // No need to delete more if parental relation.
// However, what do we do if no ui has been generated?
// Do we have to delete aButton?
}
The value of parent is 0. It is the main entry of the application.
Thanks
Please refer to this article
QWidget, the fundamental class of the Qt Widgets module, extends the parent-child relationship. A child normally also becomes a child widget, i.e. it is displayed in its parent's coordinate system and is graphically clipped by its parent's boundaries. For example, when the application deletes a message box after it has been closed, the message box's buttons and label are also deleted, just as we'd want, because the buttons and label are children of the message box.
So, there is no difference do you use ui or not. When you delete window, all its children will be deleted too.
I have QWidget renameWidget which is referred to as ui->renameWidget. Within this, I have a QVBoxLayout *renamebox and within this I have several labels and textedits.
What I need to happen is that when I hit a button to submit these textedits, I need everything within the QWidget to be deleted. This will give the effect of the box being emptied or cleared.
I've tried to just delete the vboxlayout and I've also tried this:
qDeleteAll(ui->renameWidget->findChildren<QVBoxLayout *>());
Nothing has worked, any ideas?
try
qDeleteAll(ui->renamebox->findChildren<QLabel *>());
qDeleteAll(ui->renamebox->findChildren<QTextEdit *>());
Although it's typically better to call deleteLater on most QObject based classes because it allows the objects to be cleaned up in the next pass through the event loop, not in the middle of an event being processed
qDeleteAll(ui->renamebox->children()); would delete all children.
Do I need to free Qt toolbars and actions?
I created them this way
QToolBar *tb = new QToolBar(this);
tb->setWindowTitle(tr("Edit Actions"));
addToolBar(tb);
QAction *a;
a = actionUndo = new QAction(...ecc..);
are these deallocated automatically or do I need to free them up?
In short, yes they are deallocated automatically as part of the Qt framework as it appears you are properly passing in the parent (i.e. this in your case). Also, in the case of the QToolBar, calling addToolBar will cause it to be 're-parented' if it didn't already have a correct parent.