Interpreting QCloseEvent differently - c++

I have a desktop windows application programmed in C++ with Qt. The application has several top - level windows which occasionally need to be closed and recreated programmatically. Also, when the user of the program clicks on the close button (the one next to the minimize and maximize buttons) the whole program is supposed to exit.
The problem I have is that in both cases the top level windows receive a closeEvent() call with a QCloseEvent object. I'd like to quit the program when I see that happen (because the user might have clicked the close button), but it's also possible that the window is closing because I'm deleting it programmatically to recreate it.
Is there a way of distinguishing between these two cases in QMainWindow::closeEvent()?

Is there a way of distinguishing between these two cases in QMainWindow::closeEvent()?
Yes: the close event triggered by the user clicking on the window's close button will be a spontaneous event, the one triggered by you calling window->close() will not. See the documentation of QEvent::spontaneous() for more information.

When you need to close your window programmatically, use deleteLater() instead of close. The window will be closed and deleted. To reopen the window you will need to create another window object.
You can also use hide() method. The window will be hidden but not destroyed. It can be shown again using show().
In both described cases the close event does not happen and closeEvent() isn't called. So when closeEvent is called, you know that the user has pressed the close button.

Related

MFC: Best place to prevent a window from re-appearing from a Restore operation

When my application is minimized, and the application programmatically closes a child window, the state of the child window between my framework and MFC goes out of sync because MFC will not send a WM_SHOWWINDOW message when the application is minimized. I noticed that Qt had the same problem: https://codereview.qt-project.org/#/c/93410/
Things that I have tried:
Override OnShowWindow() -- if the states are out of sync, then I alter the BOOL parameter before passing it to the CDialog::OnShowWindow. But doing so does nothing. It is as if the BOOL parameter given to the override is read-only.
Handle WM_SHOWWINDOW in PreTranslateMessage -- this does not work because WM_SHOWWINDOW does not appear here.
I know I can check SW_PARENTOPENING to know when to look for out-of-sync problems and handle it, but I just don't know where is the best place to do it.
My current solution is to override OnShowWindow, check for SW_PARENTOPENING, then post a SW_HIDE. It works, but it feels like a waste because I should be able to prevent it from restoring entirely rather than defer it.
Summary:
Basically, I am just programmatically closing a window, say from a timer call, or user's remote command, or whatever, while the main application is minimized. The dialog will be temporarily hidden when minimized (the MFC framework will automatically call ShowWindow(SW_HIDE) but with an internal flag to re-open when the app is restored). If my program sends ShowWindow(SW_HIDE) now, this call will not be registered, and the window will be re-opened by MFC when the app is maximized. To my user, he/she has closed the window remotely and does not expect the window to re-appear, so I need to re-call my ignored ShowWindow(SW_HIDE) somehow when restoring the main app.

C++ Modal dialog box continuing adding texts

I have dialog box and in it, it has OK and Cancel buttons then it also has a ListBox to display text in two columns. I would like to continue adding text into the ListBox after the dialog box is shown. How can I do that? Because after I call DoModal() to show the dialog box, the code does not continue to execute. Or should I create two threads (one is display dialog box while another thread continues adding text to dialog box)?
Make your dialog 'pull' the data it needs, maybe polling the data source with window messages every second, or every 100ms or so. Or, if you go the 'two threads' route (the better but more complicated option), have your data source post a window message to your dialog when there is new data, and then have the dialog fetch the data it needs. The reason for this is that it's much easier to use the existing CDialog infrastructure to get a window that behaves like an actual dialog, compared to building a modal window that acts like a dialog but isn't really.
If you do go the two threads route, your division of labor should be: one thread that does all the UI work (including showing the dialog), and one that 'generates' data and lets the UI know when there is new data. So the worker thread should not do anything related to the UI, nor call any methods on the dialog directly - you can't access windows from several threads. The only cross-thread window communication should happen through window message (i.e., use ::SendMessage()). So certainly don't do something like myDialog->m_theList.AddString("blah") from another thread, or something like it.
Showing a dialog box modally halts further execution until you close the box. Instead of showing it modal, show it normal but make it always on top so you can continue executing the code after the call to DoModal(). Alternatively, populate the box with all the info it will need before you call DoModal().
If you decide to take the "two threads" approach you will discover that the controls on MFC dialogs should not be updated or accessed from a thread other than the one that created the dialog. Even if you have pointers to these controls available in another thread it is not thread-safe to access them. This rule applies whether or not the dialog is modal.
Instead, your second thread would need to PostMessage or SendMessage to the dialog window, so the updates occur on the thread that created the dialog (most likely the main UI thread of the application).

Setting focus to the parent window in the OnShowWindow of the child window doesn't work

Generally I have some button that opens child window and the second press on this button should close it. I use a touch screen.
The problem is when I try to press the button for closing the child window, it is not pressed the first time, so I need another click.
In order to fix this I am trying to return the focus to the parent window after the child window is opened.
I register the OnShowWindow message and call SetFocus on the parent window:
void CFlashGuidanceSteps::OnShowWindow(BOOL bShow, UINT nStatus)
{
CDialog::OnShowWindow(bShow, nStatus);
GetParent()->SetFocus();
}
While the function is called (I can see it in debugger), the focus is not returned to the parent window.
However, it works with the OnSetFocus event:
void CFlashGuidanceSteps::OnSetFocus(CWnd* pOldWnd)
{
CDialog::OnSetFocus(pOldWnd);
GetParent()->SetFocus();
}
Why is the focus is not retained with the OnShowWindow event?
The explanation
The usual rule in MFC is that OnXxx functions are called in response to similarly named window messages, e.g. WM_Xxx. So OnShowWindow would be called in response to WM_SHOWWINDOW.
And according to the documentation, WM_SHOWWINDOW is
Sent to a window when the window is about to be hidden or shown.
That means it is sent before the window is actually shown. So when you set the focus to the parent window inside of the OnShowWindow function, nothing actually happens because the parent window already has the focus. Then, after OnShowWindow finishes running, the child window is displayed and demands the focus. It is as if you never made any attempt to change the focus.
By contrast, OnSetFocus, which corresponds to WM_SETFOCUS, is not called until after the window has gained the focus. So when you reassign the focus here, it works because the child window doesn't steal the focus back.
The better idea
That explains the behavior you are seeing, and as you know, things work fine when you adjust the focus in OnSetFocus. But this really isn't the best way of solving the problem.
Manually changing the focus when a window gets and/or loses the focus is approaching the problem the wrong way around, and generally error-prone. You'll get all kinds of focus-related bugs that are difficult to debug. You are better off looking for ways to prevent the focus from changing when you don't want it to.
It sounds to me like you want to prevent the child window from getting the focus when it is created. And there is a way to do precisely that.
When you display the child window, presumably by calling the ShowWindow function or its moral equivalent in MFC, you should pass the SW_SHOWNA flag. That causes the window to be shown without activating it (i.e., granting it the focus).

A blocking but non-modal QDialog?

I have a stack of images on which I want to perform some operations. After processing each image, my program should pop up a dialog to prompt the user if they want to proceed with the next image or to abort. Before that, they should have an opportunity to do some manual changes either on the images or on the parameters. Anyway, they must have access to the windows of the applications, while the execution of the method that called the dialog should be blocked until the dialog is closed.
I tried to solve this with a QMessageBox, but if I open it via exec(), it blocks the entire application, and if I use show(), the execution of the program goes on without waiting for user's reaction.
Is there a convenient way to block the calling method or function with a dialog but permit the user to interact with other windows?
Thanks in advance for any hint.
You should split your method that you want to block into two parts. In the end of first part you need to show your dialog without blocking and connect "Next" button (for example) of the dialog to the slot that must contains second part of your old method. This slot will be executed only when user presses the button.
It's the right way to do it in Qt. You need posibly to change your code logic to implement this.
Also, do you really need the second dialog? You can place "Next" button to your main widget. You can also create another modal dialog that will contain some settings and "Next" button.

MFC dialog close issues

Good day!
I have an MFC dialog with progress.
Dialog automatically closes after reaching 100% using PostMessageW(WM_CLOSE).
The problem is, when, during progress, I'm moving dialog over the screen, dialog is not closing and WM_CLOSE message is ignored. Any suggestions? Thanks.
For a modal dialog you shouldn't really need to use a WM_CLOSE message.
Normally you'd use the OK or Cancel button events to close it, call the EndDialog method from functional code or just return when your processing is complete (assuming that its the process run as soon as the dialog is initialised). You can set your return value at the same time e.g. EndDialog(2);.
Either way the dialog will close once th current message handler returns, so there could well be a delay, in closure but it shouldn't be much.
Is the activity behind progress bar done in a separate thread? It look like to be the case otherwise when you drag the dialog the progress bar would have froze until you release the dialog than it would have resumed. This means you might have to look into inter thread communication, how the message is being posted to the dialog HWND.
It might have to do with the dialog being in freeze (no activity) state while you are dragging it which seems to be normal windows behavior. If that is the case you could use signals/CEvent to tell the dialog to close down.