Qt - How to handle memory management for dialogs? - c++

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.

Related

Open a new Qt window with a slot

In my Qt Program, there is a menu bar where one of the menu options is Settings. When the user click on the Settings window, it should open a Settings window. The settings window gets opened with a openSettingsWindow() function. This is how I made the Settings menu in the main window:
QMenu settingsMenu("&Settings");
QAction *settings = toolsMenu.addAction("&Settings");
Window::connect(settings,&QAction::triggered,&mainWindow,[&mainWindow](){
openSettingsWindow();
});
menuBar.addMenu(&toolsMenu);
mainWindow is the main window and Window is the class used to create windows which inherits from QWidget. Its constructor takes two arguments: the title of the window and the icon of the window. This is the openSettingsWindow() function:
void openSettingsWindow(){
Window settingsWindow("Settings","icon.png");
settingsWindow.show();
}
The problem is that when I click ont he Settings option in the Settings menu, the Settings window opens as it should, but it closes directly after less than a second. What should I do to keep the Settings window opened?
The local variable settingsWindow gets destructed when your function openSettingsWindow goes out of scope, you need to keep the object valid as long as you want to show your settingsWindow.
one solution would be to allocate the Window object on the heap, and use the Qt::WA_DeleteOnClose to make Qt delete the Window object for you when it is closed, here is how your openSettingsWindow would look like:
void openSettingsWindow(){
Window* settingsWindow = new Window("Settings","icon.png");
settingsWindow->setAttribute(Qt::WA_DeleteOnClose);
settingsWindow->show();
}
You need to return a reference to that Window and keep it until you're no longer using it.
Window *openSettingsWindow() {
Window *settingsWindow = new Window("Settings, "icon.png");
settingsWindow.show();
return settingsWindow;
}
QMenu settingsMenu("&Settings");
QAction *settings = toolsMenu.addAction("&Settings");
Window *settingsWindow = null;
Window::connect(settings,&QAction::triggered,&mainWindow,[&mainWindow, &settingsWindow](){
settingsWindow = openSettingsWindow();
});
menuBar.addMenu(&toolsMenu);
You might want to find a better way of storing the settingsWindow pointer in the main function if you're going to have many possible open windows but this will work.
Remember to call delete() on that pointer when you're done with the settings window (likely on the window close event)

How to create dialog without blocking main form?

For now I can do:
void MainWindow::on_actionPATH_triggered() {
std::unique_ptr<QDialog> win(new QDialog());
win->exec();
}
Should I use async / run in separate threadto avoid blocking main window or is there way to subscribe to close even and delete / free object there?
You can use just show()
void MainWindow::on_actionPATH_triggered() {
QDialog* win = new QDialog();
//needed connect
win->setAttribute(Qt::WA_DeleteOnClose);//we don't want memory leak
win->show();
}
and use
win->setModal(false);//but it is default option, you don't need to change it
From doc:
By default, this property is false and show() pops up the dialog as
modeless. Setting his property to true is equivalent to setting
QWidget::windowModality to Qt::ApplicationModal. exec() ignores the
value of this property and always pops up the dialog as modal.
Qt::WA_DeleteOnClose will delete your dialog, when user close it.
You can also set parent to dialog:
QDialog* win = new QDialog(this);
In this case win will be delete with your mainWindow.
Info about Qt parent child relationship
And you don't need here separate thread.

wxWidgets - modeless wxDialog

I am creating my own subclass of wxDialog and it works well when used as a modeless dialog like so:
AddDialog newAddDialog = new AddDialog(this, wxID_ANY, _T("Dialog Title"), wxDefaultPosition, wxDefaultSize, 0);
if (newAddDialog.ShowModal() == wxID_OK)
{
//do something
}
When using ShowModal(), the flow of the program stops until OK or Cancel is pressed. I need the dialog to show up, but not stop the flow of the GUI, so I tried this:
AddDialog newAddDialog = new AddDialog(this, wxID_ANY, _T("Dialog Title"), wxDefaultPosition, wxDefaultSize, 0);
if (newAddDialog.Showl() == wxID_OK)
{
//do something
}
When using Show(), the dialog briefly shows up and then disappears. I thought it might be a scope issue, so I used a pointer for newAddDialog. Then, the dialog shows up, when when I click OK or Cancel, the dialog closes, but the if statement code does not execute even if OK is clicked.
Does anyone know how to proceed? Thanks.
Further clarification:
I have virtual void function in my Dialog subclass that I overide in another class. I can't seem to get the event working when I overide, however, if I have a void in the actual Dialog subclass, I get the event call. This seems to be an overide problem, but I don't know what the problem would be. This is not the main GUI that I'm calling in the OnInit() call - could that be a problem?
ShowModal blocks execution of your program and returns the outcome (like wxID_OK). On the other hand, Show just shows the dialog and returns immediately, so you can't check the outcome (what the user pressed for buttons) from it's return value. Instead you have to communicate the outcome of that dialog-box by sending an event from withing the dialog or something like that.
AddDialog newAddDialog = new AddDialog(...
Does this compile? Really?
The 'new' operator returns a pointer to void, so the code you have posted looks very odd indeed.
The usual way of doing this is:
AddDialog * newAddDialog = (AddDialog *) new AddDialog( ...
Or
AddDialog newAddDialog( ...

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.

How to display a non-modal CDialog?

Can someone tell me how I could create a Non Modal Dialog in MFC's Visual c++ 6.0 and show it?
I wrote this code:
CDialog dialog;
if (dialog.init(initialization values...))
dialog.DoModal();
But it blocks my application from showing the dialog. I dont know if there exists any method or other way to do it.
Thanks
/* CChildDialog class is inherited from CDialog */
CChildDialog *m_pDialog = NULL;
// Invoking the Dialog
m_pDialog = new CChildDialog();
if (m_pDialog != NULL)
{
BOOL ret = m_pDialog->Create(IDD_CHILDDIALOG, this);
if (!ret) //Create failed.
{
AfxMessageBox(_T("Error creating Dialog"));
}
m_pDialog->ShowWindow(SW_SHOW);
}
// Delete the dialog once done
delete m_pDialog;
Use CDialog::Create and then use CDialog::ShowWindow. You now have a modeless dialog box.
You can call CDialog::Create and CWnd::ShowWindow like the others have suggested.
Also, keep in mind your dialog will be destroyed right after its creation if it is stored in a local variable.
In this case I find it most convenient to let it self-delete itself to handle the cleanup.
Often it's considered bad form to make "implicit" memory freeing from within a class, and not by what it created it, but I usually make exceptions for modeless dialog boxes.
That is;
Calling code:
#include "MyDialog.h"
void CMyApp::OpenDialog()
{
CMyDialog* pDlg = new CMyDialog(this);
if (pDlg->Create(IDD_MYDIALOG, this))
pDlg->ShowWindow(SW_SHOWNORMAL);
else
delete pDlg;
}
Dialog code:
void CMapBasicDlg::OnDestroy()
{
CDialog::OnDestroy();
delete this; // Shown as non-modal, we'll clean up ourselves
}
You need to call CDialog::Create instead. You will need to call DestroyWindow when you are finished with the dialog. You might also need to pass dialog messages onto the object but I can't remember if MFC handles this for you or not.
DoModal is blocking. You have to create your dialog on the heap or make it a member of your class (this is important), call Create then call ShowWindow.