QT clear widget contents - c++

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.

Related

Would this QChangesOpacityEffect usage cause a potential memory leak? (but only way to force repaint...)

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.

setSelectionModel in QListWidget stops emission of itemSelectionChanged signal

In my QListWidget subclass, when I replace its QItemSelectionModel using setSelectionModel, the list widget stops emitting its itemSelectionChanged signal. In the real code, I'm doing this to install a subclass of QItemSelectionModel, but the problem appears even if I use just a new QItemSelectionModel.
MyListWidget::MyListWidget(QWidget* parent)
: QListWidget(parent)
{
QItemSelectionModel* oldSelectionModel = selectionModel();
QItemSelectionModel* newSelectionModel = new QItemSelectionModel(model(), oldSelectionModel->parent());
setSelectionModel(newSelectionModel);
oldSelectionModel->deleteLater();
}
In the debugger, I can see QItemSelectionModel emit its selectionChanged signal, but QListWidget never emits its corresponding itemSelectionChanged signal.
If I comment out the body of this constructor, things work fine. As before, I can see QItemSelectionModel emit its selectionChanged signal, but now that triggers QListWidget to emit itemSelectionChanged.
It looks as if my call to setSelectionModel causes the connection between QItemSelectionModel::selectionChanged and QListWidget::itemSelectionChanged to break.
By the way, I checked to see if I'm passing the wrong arguments to my new QItemSelectionModel. After I create it, model() and parent() of newSelectionModel are identical to those of oldSelectionModel. I also tried commenting out the call to deleteLater, but that didn't help.
Any help much appreciated.
As per the Qt documentation of QAbstractItemView, setSelectionModel replaces the current selection model and it won't work if setModel() is called after this.
Note that, if you call setModel() after this function, the given
selectionModel will be replaced by one created by the view.
Also, from QListWidget documentation,
QListWidget is a convenience class that provides a list view similar
to the one supplied by QListView, but with a classic item-based
interface for adding and removing items. QListWidget uses an internal
model to manage each QListWidgetItem in the list.
it looks like whenever you add items, the model gets updated / refreshed invalidating your custom selection model (just my thought).
Also, you don't need to delete the old selection model as per Qt docs
Note: It is up to the application to delete the old selection model if
it is no longer needed; i.e., if it is not being used by other views.
This will happen automatically when its parent object is deleted.
However, if it does not have a parent, or if the parent is a
long-lived object, it may be preferable to call its deleteLater()
function to explicitly delete it.
if you want that level of customisation, QListView might be a better option.
QListWidget
QAbstractItemView
Just wondering, have you tried setting custom selection model after adding all the items to the list widget?
This turned out to be a Qt bug. See my two posts on the Qt Forums and the bug report:
http://forum.qt.io/topic/63676/qlistwidget-how-to-prevent-unselect-all/2
http://forum.qt.io/topic/63674/qlistwidget-setselectionmodel-stops-emission-of-itemselectionchanged-signal
https://bugreports.qt.io/browse/QTBUG-50891

Reusing a QMenu within multiple Qmenu

Hi all,
I have some code generating a dynamically filled QMenu depending on some data (I will call it thisMenu). The QMenu thisMenu is created, taking some "main" QWidget as parent and is added to a QMenuBar within this QWidget (menuBar.addMenu(&thisMenu). Latter on, I want the user to be able of accessing thisMenu from a context menu (the user right click on some portion of the QWidget, which pops a QMenu (called contextMenu) with some actions, and the previous QMenu as a sub-menu).
If I reuse the QMenu that I first created with contextMenu.addMenu(&thisMenu) I find out that, even if contextMenu pops at the right global position, thisMenu is always translated to some other position and appearing sometimes above, sometimes under contextMenu.
I can test that this is linked to the parenting chain : thisMenu is not a child of contextMenu, if I create it a child of contextMenu, everything is fine. Is there a way of cleanly handling this case without recreating a QMenu similar to thisMenu, or changing the parent of thisMenu; i.e. reusing thisMenu in both QMenuBar and in some context menu/QMenu? In other what is the proper way of handling parenting chain for QMenu and sharing QMenu?
Thank you,
In other what is the proper way of handling parenting chain for QMenu and sharing QMenu?
You cannot share a QMenu across multiple places -- each QMenu can only exist in one place at a time. You should create separate QMenus: One for your menu bar and one for your context menu.
A simple way is to put your menu-generating code in a for-loop, to create multiple identical menus.
May I ask why you want to reuse your QMenu?
I can test that this is linked to the parenting chain : thisMenu is not a child of contextMenu
Yes, that is described in the documentation. When you add one QMenu to another, the parent doesn't change: http://qt-project.org/doc/qt-5/QMenu.html#addMenu
if I create it a child of contextMenu, everything is fine.
The position of a widget is always painted in a position relative to its parent. (Remember: A QMenu is a QWidget)
Following JKSH's answer, I decided to use a function to duplicate QMenu, without duplicating the QAction in it (they are not inheriting QWidget), hence conserving all established connections :
void duplicateMenu(QMenu* dst, QMenu& origin)
{
QMenu* sub = dst->addMenu(origin.title());
QList<QAction*> actions=origin.actions();
for(QList<QAction*>::iterator it=actions.begin(); it!=actions.end(); it++)
{
QMenu* itMenu = (*it)->menu();
if(itMenu!=NULL)
duplicateMenu(sub, *itMenu);
else
sub->addAction(*it);
}
}

How to change/swap the layout of a widget?

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.

Qt -how to know whether content in child widgets has been changed?

In QMainWindow I have 2 QSplitters. In that splitters I have QTextEdit, QLineEdits, QTableWinget, Ragio buttons and so on... I want to know if somthing has been chaged after pressing File->New menu button. Is there any general method for doing this?
Somwhere I have read that it is recomended to use isWindowModified() function of QMainWindow, but seems it doesn't work.
setWindowModified() does not propagate the windowModified flag to the parents. This bug is described here: https://bugreports.qt.io/browse/QTBUG-20150. I have just tried it and indeed it did not work.
The isWindowModified() could be useful here since according to http://doc.trolltech.com/4.6/qwidget.html#windowModified-prop it propagates up to the parent.
However, I think you would need to set this yourself. For example, if you clicked the new button which leads to some text being inserted into a QTextEdit, you still need to call QTextEdit's setWindowModified() function - which will then propagate up to your QMainWindow - and you can just check QMainWindow afterwards. (However, you wouldn't know which children were modified)
Maybe you should have a look at QWidget::changeEvent.