QDialog flash back after create - c++

I have a QListWidget on my QMainWindow, and I connect the itemDoubleClicked(QListWidgetItem*) signals to a slot like the following code:
connect(listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(popUpMyDialog(QListWidgetItem*)));
My popUpMyDialog(QListWidgetItem*) function is like:
QMyDialog *myDialog = new QMyDialog(this);
myDialog->show();
QMyDialog is a class which I inherent from QDialog, and no operation except ui->setupUi(this); was done.
But when I try to double click on the item of QListWidget, the myDialog flashed and disappear very soon.
So I had tried to write some code to judge if the myDialog is deleted like that:
QMyDialog *myDialog = new QMyDialog(this);
connect(myDialog, SIGNAL(destroyed(QObject*)), this, SLOT(handleQMyDialogClose(QObject*)));
myDialog->show();
and the slots function handleQMyDialogClose(QObject*) just do:
qDebug() << "myDialog is closed";
When I double click on the item of item of QListWidget, the console print myDialog is closed, which means the myDialog object is deleted, but I don't delete the pointer, so I feel confused.
I Googled it and tried to setModal attributes to myDialog , but it take no effect.
I tried to copy the same code to my Mac, the strange thing is that it runs perfect.
I tried to add a for loop in my popUpMyDialog(QListWidgetItem*) like that:
QMyDialog *myDialog = new QMyDialog(this);
connect(myDialog, SIGNAL(destroyed(QObject*)), this, SLOT(handleQMyDialogClose(QObject*)));
myDialog->show();
for(int i = 0; i < 100; ++i) {qDebug() << i;}
to block the thread, and find that the myDialog window work prefect, but if I comment the for loop code, it flash back again.
So, I want to know what error happened to my code, and how I can try to handle it.
My coding environment is:
Windows 10 and Mac OS X 10.10.4 Yosemite, the version of Qt is Qt5.5.0, and on my Windows , the Qt runs with mingw.

So, you have the following method:
void Foo::popUpMyDialog(QListWidgetItem*) {
QMyDialog *myDialog = new QMyDialog(this);
myDialog->show();
}
The only reason why the dialog would get prematurely destroyed is if the instance of Foo, that the dialog is a child of, got destructed.
To troubleshoot the issue, first try to create a parentless dialog:
void Foo::popUpMyDialog(QListWidgetItem*) {
QMyDialog *myDialog = new QMyDialog;
myDialog->show();
}
If that dialog remains visible, then you know that you gave it a wrong, short-lived parent. The solution would be to find another parent. To avoid leaking the dialogs, you can give the dialog a Qt::WA_DeleteOnClose attribute.

QDialog has an exec() function that "blocks" the execution (as you do with your loop), to use instead of show() so your dialog can stay visible. Isn't it what you are looking for?

Related

Qt - How to handle memory management for dialogs?

I am running into the following issue:
Users presses "Ctrl+N" which goes into function MainWindow::newAction()
In MainWindow::newAction(), create a QDialog dlg(centralWidget()) and call dlg.exec()
While dlg is open, users pressed "Ctrl+N" again
The result is that dlg never gets deleted (it will only get deleted once centralWidget() gets deleted).
The call stack is something like this:
MainWindow::newAction ()
...
MainWindow::newAction()
I am wondering how to handle this case. I want all of the local dialog variables from the first call to newAction() to be deleted by the time we go into the function newAction() again.
You also can try something like this:
void MainWindow::newAction() {
const auto dialog = new MyDialog(centralWidget());
// When user will close the dialog it will clear itself from memory
connect(dialog, &QDialog::finished, [dialog]() {
dialog->deleteLater();
});
dialog->exec();
}
However, a good move would be to stop user from summoning more QDialogs than a single one, given that this one is a modal dialog(might be a good idea to keep this dialog pointer as a class member and check is it on screen already before calling exec() on it.
If i understood the question right, you want one dialog to be opened and want to delete it before a new dialog request comes in?
If that's the case you can do following:
In MainWindow.h declare QDialog *dlg = nullptr
In your MainWindow.cpp newAction() function you can do following:
void newAction()
{
if(dlg != nullptr)
{
dlg->close();
dlg->deleteLater();
//or
//dlg->destroy(); // this will immediately free memory
}
dlg = new QDialog(centralWidget());
...
//dlg->exec(); // This will automatically make QDialog modal.
dlg->show(); // This will not make a QDialog modal.
}
I hope this will help. Remember QDialogs when displayed with exec() they automatically behave as Modal window. show() will make it non-modal.

How to release memory?

I have Created a simple QT application for my university assignment. What i have done is pop up a new QManinWindow from a Above QMainWindow. When i click a button from the main ui it will pop up a new QMainWindow object (Note Pad)
Note pad is also a QMainWindow object
My Problem is when I'm creating the object it takes some memory from the ram but when I'm closing it (pop up window) memory is not releasing. When each time I'm pressing a button memory is allocated but application does not relese the memory when im closing it. Please check the main screen of the app.
i just want to know how to release that memory. I have tried so many things but nothing worked well.
I have set the closeEvent public on NotePad class and I listen the close event from main object when its get triggered i have deleted the poped up object. But it cause ad unexpected stop.
void MainWindow::on_notePadBtn_clicked()
{
NotePad *notePad = new NotePad(this);
notePad->raise();
notePad->activateWindow();
notePad->show();
}
NotePad::NotePad(QWidget *parent) :QMainWindow(parent),ui(new Ui::NotePad) {
ui->setupUi(this);
this->setWindowTitle("Note Pad");
}
You don't really need to override closeEvent, Qt has Qt::WA_DeleteOnClose attribute, that does exactly what you want, you can use it like this:
//...
NotePad *notePad = new NotePad(this);
notePad->setAttribute(Qt::WA_DeleteOnClose);
notePad->raise();
notePad->activateWindow();
notePad->show();
//...
I'm not familiar with Qt.
But to my understanding if you use the operator new
you must use delete (in a scope where you have access to the pointer created with new).
Object *foo = new Object();
// Do stuff with foo...
delete foo;
// DO NOT use foo from now on.
Hope that helps a bit, maybe. Like I said I'm not familiar with Qt
so if you have doubts about how some features are implemented you should look at the their docs.
(cf: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf §3.7.4p63)

QLineEdit editingFinished signal twice when changing focus?

I've found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler. In my case I am a bit stuck as I am getting the editFinished signal twice even when my slot handler is doing nothing.
For a test, I have an array of QLineEdit which use a signalMapper to connect the editingFinished() signals to a single slot. The signalMapper passes the array index so I can see where the signal came from.
eg:
testenter::testenter(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::testenter)
{
// setup the UI according to the .h file
ui->setupUi(this);
signalMapper = new QSignalMapper(this);
// init the labels and edit boxes
for (int i = 0; i < 10; i++)
{
pm_label[i] = new QLabel(ui->scrollArea);
QString text = QString("Number %1").arg(i);
pm_label[i]->setText(text);
pm_label[i]->setGeometry(10,20+i*30, 50, 20);
pm_label[i]->show();
pm_editBox[i] = new QLineEdit(ui->scrollArea);
pm_editBox[i]->setGeometry(80,20+i*30, 50, 20);
pm_editBox[i]->show();
signalMapper->setMapping(pm_editBox[i], int(i));
connect(pm_editBox[i], SIGNAL(editingFinished()), signalMapper, SLOT(map()));
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(data_entry(int)));
}
void testenter::data_entry(int entry)
{
//dummy
}
When run in the debugger, if I enter data into one box then either hit return or select another box with the mouse (ie change focus) , then it calls data_entry twice, the first time with index of the box that is losing focus and the 2nd time with the box which gets the focus.
So my question: Am I missing something? Is this expected behaviour or a bug?
If a bug, anyone know a way round it as I wanted to use this signal to do custom validation on data when it is entered (by either return, tab or mouse click to change focus).
First off, no this isn't expected behavior, i.e. selecting a QLineEdit should not cause it's editingFinished signal to be emitted.
There are a couple of possible things that may cause this problem:
You've accidentally connected a signal twice to a slot
The slot map() is causing the newly selected box to lose focus
In the same vain, if you're debugging and using a break point to detect when the slots are getting called you may be causing the QLineEdit to lose focus when the active application changes from your QWidget to your debugger, again causing the signal to be sent again.
If you're having problems because of a doubly connected slot, which doesn't seem to be the case because you're specifically getting a signal from two different QLineEdits, you can make sure that this isn't happening by specifying the connection type, the connect method actually has an additional optional argument at the end which allows you to change the type from a DefaultConnection to a UniqueConnection.
That being said, data validation is something that Qt has an established mechanism for, and I suggest that you use it if possible, look into extending the QValidator abstract base class Ref Doc. You then tell each of your QLineEdit's to use the same validator.
I have run into the same issue. It really does emit the signal twice, which is a known bug: https://bugreports.qt.io/browse/QTBUG-40 which however has not been addressed for a very long time.
Finally I found that the best solution in my case is to change the signal from editingFinished to returnPressed. As a side effect this behaves much more predictably from the user perspective. See also here: http://www.qtforum.org/article/33631/qlineedit-the-signal-editingfinished-is-emitted-twice.html?s=35f85b5f8ea45c828c73b2619f5750ba9c686190#post109943
The OP "found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler." Well, that is my situation also, and here is where I ended up. So, at the risk of going off topic...
In my situation, when my slot receives the editingFinished signal sent from the QLineEdit, I launch a modal QMessageBox to ask the user something. The appearance of that message box is what triggers the QLineEdit to send the second, undesirable editingFinished signal.
A post in the bug report (https://bugreports.qt.io/browse/QTBUG-40) mentioned by #V.K. offers a workaround which helped me. The following is my implementation of the workaround. I let Qt magic mojo automatically connect the QLineEdit signal to my MainWindow slot.
void MainWindow::on_textbox_editingFinished( void )
{
QLineEdit * pTextbox = qobject_cast<QLineEdit *>( QObject::sender() );
if ( !pTextbox->isModified() )
{
// Ignore undesirable signals.
return;
}
pTextbox->setModified( false );
// Do something with the text.
doSomething( pTextbox->text() );
}
void MainWindow::doSomething( QString const & text )
{
QMessageBox box( this );
box.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
box.setText( "Are you sure you want to change that text value?" );
if ( box.exec() == QMessageBox::Yes )
{
// Store the text.
m_text = text;
}
}

Is it necessary to delete dialog window pointer in Qt?

I use this code:
MyDialog *md = new MyDialog();
md -> show();
to open a dialog window in Qt. Will md be deleted automatically when the dialog window is closed or do I need to run delete md when the window is finished?
In your little piece of code you need to delete it, because it doesn't have a parent, if you set a parent, the parent will delete it's children and you only need to delete the "main-window" (the window that doesn't have a parent).
Also for QWidget derived classes you can use the: Qt::WA_DeleteOnClose flag and then the memory will be deallocated when the widget closes, see the documentation here
Then code will become:
MyDialog *md = new MyDialog();
md->setAttribute(Qt::WA_DeleteOnClose);
md->show();
Yes. Unless you pass this while this is a QWidget or any other QWidget:
MyDialog *md = new MyDialog(this);
md->show();
you need to:
delete md;
at some point in order to release its memory. Also you need to make sure in this case that the object tree is well linked. What you can also do is call setAttribute(Qt::WA_DeleteOnClose); on md so that when you close the dialog, its memory will also be released as Zlatomir said. However if you need md to live after it was closed setAttribute(Qt::WA_DeleteOnClose); is not an option. This is also dangerous and could lead to access violation/segmentation fault if you are not careful.

How to properly clean-up a QWidget / manage a set of windows?

Let's say I have 2 windows in my application, and two classes responsible for them:
class MainWindow: public QMainWindow and class SomeDialog: public QWidget.
In my Main Window I have a button. When it is clicked, I need to display the second window. I do it this way:
SomeDialog * dlg = new SomeDialog();
dlg.show();
Now, the user does something in the window, and closes it. At this point I want to get some data from that window, and then, I suppose, I will have to delete dlg. But how do I catch the event of that window being closed?
Or is there another way not to have a memory leak? Maybe It would be better to create an instance of each window on startup, and then just Show()/Hide() them?
How do I manage such a case?
It is advised to use show() / exec() and hide() instead of dynamically creating the dialog every time you want to show it. Also use QDialog instead of QWidget.
In the constructor of your main window create it and hide it
MainWindow::MainWindow()
{
// myDialog is class member. No need to delete it in the destructor
// since Qt will handle its deletion when its parent (MainWindow)
// gets destroyed.
myDialog = new SomeDialog(this);
myDialog->hide();
// connect the accepted signal with a slot that will update values in main window
// when the user presses the Ok button of the dialog
connect (myDialog, SIGNAL(accepted()), this, SLOT(myDialogAccepted()));
// remaining constructor code
}
In the slot connected to the buttons's clicked() event simply show it, and if necessary pass some data to the dialog
void myClickedSlot()
{
myDialog->setData(data);
myDialog->show();
}
void myDialogAccepted()
{
// Get values from the dialog when it closes
}
Subclass from QWidget and reimplement
virtual void QWidget::closeEvent ( QCloseEvent * event )
http://doc.qt.io/qt-4.8/qwidget.html#closeEvent
Also it looks like the widget you want to show is a dialog. So consider using QDialog or it's subclasses. QDialog has useful signals you can connect to:
void accepted ()
void finished ( int result )
void rejected ()
I think you are looking for the Qt::WA_DeleteOnClose window flag: http://doc.qt.io/archives/qt-4.7/qt.html#WidgetAttribute-enum
QDialog *dialog = new QDialog(parent);
dialog->setAttribute(Qt::WA_DeleteOnClose)
// set content, do whatever...
dialog->open();
// safely forget about it, it will be destroyed either when parent is gone or when the user closes it.