Gtkmm - Hiding a window without closing the application - c++

How do i hide a toplevel window without closing it?
NdmWindow is a class inherited from Gtk::Window.
I have added the instance of NdmWindow to Gtk::Application.
Now, how do i hide it instead of closing it?
Thanks in advance..

According to this, the behavior is that when a window is hidden, it is removed from Gtk::Application, and so if it is the last window, the app quits.
If you want your application to keep running when no windows are showing, the solution is to call hold() on your Gtk::Application object to increase the reference count of the application. Presumably you do this before setting up any windows, and call the corresponding release() as part of your quitting logic.

window->hide();
Don't forget to look at classes that Gtk::Window inherits from.
EDIT
I don't know, but my suggestion is to try hold(), and then release() after you show the next window.

Here's a solution that works and doesn't segfault.
Save a reference to Gtk::Application, say send it as a param to your main Window class and call this var app, you'll need it later, it's needed because in some cases get_application() will return null.
In the delete event handler of your window call
app->hold();
hide();
return true;
Don't call app->release() when you want to make your window show up again since it will make your application exit completely. Just call set_visible() on your window object and it will show up.

Related

Capturing the window handle of a newly created modal dialog

I'm using and improving on an open source MFC work-alike library called FFC. Sometimes the library associates the wrong window handle to a dialog object, which means the C++ object can't be found later when the correct handle is looked up. In particular, this is happening when the application opens its root window, which is a dialog that it opens with a call to DoModal.
In its DoModal function, the FFC library uses a... "surprising" way to attach the handle to the dialog object. It stashes the "this" pointer in a global variable and hooks a function to be called on all window messages before calling the DialogBox function. This hook function it registered in term assumes the handle from the first message it receives is the handle for the window in the global variable, and attaches that handle to it.
Sometimes, this works. Often - and I don't know if it's because of intrusive things done by the McAfee scanner on my work computer, or because my program starts from a console window, or something else - many unrelated messages will be captured before a message actually meant for the modal dialog comes through.
At first I thought it was because FFC wasn't making sure the message it looks for is "WM_CREATE". I added this check, but it didn't fix the problem. Turns out one or more of the spurious messages are also WM_CREATE messages! Before it gets the one for the real dialog, the first WM_CREATE it receives is a handle for a window with blank window text and rectangle 0,0-0,0.
So is this really the correct or canonical way to get the handle for a modal dialog? It seems unreliable. (Note that because the dialog is modal, you can't use the return value from CreateWindowEx because the DialogBox function doesn't return until the modal dialog is closed.) Is this really how MFC does it? Is there a better way? Could I associate some data with the dialog or look for data that should be associated with it to make sure I have the right window handle? (For instance checking the template parameter passed to the dialog box call, if I can get that back from the handle somehow.)
I am sure this is published in books, but MFC sets a windows hook (WH_CBT) and then looks for the HCBT_CREATEWND code in the hook to marry the C++ object to the HWND.

Can't return focus to the main window after showing dialog

I create dialog window in MFC using CWnd::DoModal function.
The window is instantiated in CWinApp::OnIdle()
CPatientFile pf;
pf.DoModal();
When the DoModal function returns the focus is not returned to the main window.
I tried to set focus manually
AfxGetMainWnd()->SetFocus();
Also tried to set the main window as foreground or active.
Generally I have touch screen, so when I close the Dialog I need to press the button on the main window to get it work.
So what is the right way to do it?
Just check the constructor of CPatientFile. It may be accepting the parent window CWnd *. Pass the main window as the parent.
Like
CPatientFile pf(this); //if this code is in main window class itelf
or
CPatientFile pf(AfxGetMainWnd());
Updated:
If you have instantiated the dialog from the main window, then the focus will automatically go back to the window when the dialog is closed. I am suspecting that you have instantiated the dialog from the app class itself (CWinApp) after creating the main window. If this is the case then the main window may not get the focus & you must create the dialog from within the main window. Or if you are creating the dialog in a separate thread.
As a work around you can use AfxGetMainWnd()->SetForegroundWindow() or AfxGetMainWnd()->SetActiveWindow(). But first I would try to find the cause of the issue & try to write better code (like suggested in above paragraph).
If you have copy-pasted, the statement is itself incorrect:
CPatientFile pf();
The next line shouldn't compile at all. Why? Because it is declaring a function named pf which returns CPatientFile.
If that is correct, may I ask if there is any multithreading involved. Is another thread creating a window?
How do you check if parent windows has (not) got the focus?

Bitmap in Buttons disappear

I have a CFormView SDI which calls and opens up a CDialog.
In the CDialog I have a button which has a bitmap displayed.
All works well, until I close the CDialog.
When I open up the same CDialog (using create function), the button appears, and the button's functionality is there, however the bitmap disappears.
Can someone please tell me why the bitmap on the button disappears on subsequent calls to CDialog?
Thank you in advance.
Here is the code:
In the CFormView I have a button that creates the CDialog:
void CTest4View::OnButton1()
{
m_dialog_calculator.Create(IDD_DIALOG1, GetDesktopWindow());
m_dialog_calculator.ShowWindow(SW_SHOW);
}
In the CDialog I have the bitmap put on the button in the InitDialog:
BOOL CCalculator::OnInitDialog()
{
CDialog::OnInitDialog();
if(!bitmapNew.LoadBitmap(IDB_BITMAP_NEW)){
MessageBox("problem in loadbitmap");
}
if(!m_button.SetBitmap(bitmapNew)){
MessageBox("problem in SetBitmap");
}
bitmapOpen.LoadBitmap(IDB_BITMAP_OPEN);
m_buttonOpen.SetBitmap(bitmapOpen);
//==========================
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
Upon further investigation, the problem seems to be in: m_button.SetBitmap(bitmapNew) since this returns FALSE. Can someone please help me?
Quick fix:
void CTest4View::OnButton1()
{
// only create the dialog once
if (m_dialog_calculator.m_hWnd==NULL)
m_dialog_calculator.Create(IDD_DIALOG1, GetDesktopWindow());
m_dialog_calculator.ShowWindow(SW_SHOW);
}
Additional information 1:
The information that OP provided in his question/code is quite little, so I actually have to recreate a similar test project to guess what is wrong with the missing bitmap. What I found is that the CDialog and CBitmap is being created multiple times when the button is pressed, this causes subsequent creation api call to fail, other than the first creation call. The result is some unexpected behavior as you can see now.
The program is supposed to generate some assertion errors when run in debug mode due to the creation failure but I guess the OP compiled it in release mode so all warnings are being suppressed.
The problem occurred because the calculator dialog is created as a modeless dialog as compared to a normal DoModal way of activation. The usual way of doing such modeless dialog is to create the dialog only once, by monitoring the m_hWnd member variable. When the user want the dialog to be dismissed, it is simply being hidden instead of being destroyed. This will avoid all the multiple creation problems altogether.
I guess presently, the calculator dialog is assumed to be closed and destroy by clicking the "X" button on the top right of dialog, well, actually it is only hidden but not actually being destroyed by the default handling of CDialog. The correct way to dismiss the modeless calculator dialog is thus to overide the OnClose event to hide it using ShowWindow(SW_HIDE). To activate it again, use ShowWindow(SW_SHOWNORMAL).

How to return from exec method programmatically in static singleton class

I am developing Qt application (Qt version 4.7.3) on SBC6000x board.
I have a MessageBox class derived from QDialog. I have made this class singleton.
Whenever a messagebox is to be show I am using .exec method to show it.
There are few places where I need to show messageboxes one after another.
So, to show new messagebox, I have to close previous one and show new one.
e.g. When Messagebox is open and at same time I receive an error from background I have to close the messagebox which is currently shown and show the one with error.
To closes previous dialog I have exposed CloseDlg method from messagebox class and trying to close it.
Inside this CloseDlg I am emitting finished signal.
void CMsgBox::CloseDlg()
{
if (NULL != CMsgBox::m_msgBox)
{
if(CMsgBox::m_msgBox->isVisible())
{
emit CMsgBox::m_msgBox->finished(0);
//QApplication::processEvents();
}
}
}
and calling it as
CMsgBox::CloseDlg();
My show method is :-
int CMsgBox::showMsgBox(Icon icon, const QString &textMsg, const QString &okBtnText)
{
if (CMsgBox::m_msgBox == NULL)
{
CMsgBox::m_msgBox = new CMsgBox();
}
CMsgBox::m_msgBox->setText(textMsg);
CMsgBox::m_msgBox->setIcon(icon);
CMsgBox::m_msgBox->setOkBtnText(okBtnText);
CMsgBox::m_msgBox->exec();
return CMsgBox::m_msgBox->m_btnPressed; //return, unblock the call
}
Again when I call showMsgBox,it is showing me following warning.
QDialog::exec: Recursive call detected
Problem is, it doesn’t return from previous exec call (unless we return, as commented above //).
I tried same with close(), accept(), reject() methods instead of finished() event but nothing worked.
What is the way to return from previous exe call and achieve above scenario? Any help is welcome.
What you have here looks like a race condition. A modal QDialog runs its own event loop, so your application behaves like a multithreaded application and you need to take care of concurrency and race conditions.
When you receive a second in your main event loop, you call CMsgBox::CloseDlg() and CMsgBox::showMsgBox() in quick succession. However, CloseDlg() tells the dialog's event loop to return, but CloseDlg() actually returns before the dialog's event loop is done cleaning up, and showMsgBox() attempts to call exec() on a dialog which hasn't finished exiting yet.
What you need to do is, when you call CMsgBox::CloseDlg(), connect to the finished(int) signal, and only when you receive the finished(int) can you safely exec() the dialog again.
NOTE: When connecting to the finished(int) signal, make sure to use a Qt::QueuedConnection instead of a Qt::DirectConnection which is the default.
So, you need modeless dialog box. As explained in their documentation :
Modeless dialogs are displayed using show(), which returns control to the caller immediately.
Therefore, instead of showing the box with exec(), show it with show().
Alternative to show() method suggested in another answer is, use QDialog::open(). It will return, but will still give you modal dialog, so the rest of the GUI will be disabled until you close it.

Reopen modal dialog in MFC

I need to open a dialog box instantiated from the same class twice. When I try this
CdelmeDlg dlg;
dlg.DoModal();
dlg.DoModal();
The second call opens the dialog only for a split second, then it is closed. My bet was there is a leftover message in the message queue, so I added this in between the calls
MSG msgCur;
while (::PeekMessage(&msgCur, NULL, NULL, NULL, PM_REMOVE))
;
This solves the problem, but it feels like a wrong kind of thing to do. Is there a way to process the leftover message properly?
Don't call EndDialog( IDOK );
To handle the ok or cancel buttons being pressed just inherit OnOk or OnCancel ... Otherwise EndDialog is going to be called twice and you'll get the problem you are getting!
I actually think that YeenFei has a good point here.
It's been a while since i've played with MFC (thank goodness), but from memory, a timer, may or may not be called from the UI thread depending on which one you use. If the timer is being raised on the main UI thread, then a modal dialog will likely halt the main thread until it is dismissed, after which it would be called by the next timer. If the timer is raised on a separate thread, then your dialog is not blocking the main UI thread as it is being shown on a separate thread.
It's does seem more conceivable as YeenFei has pointed out that you want to re-show your dialog each time the timer is raised, hiding it when the user clicks on the button to dismiss it. That way, if the time is raised again, all it does is re-show the dialog whether it is currently open or not.
There is a great post here (www.eggheadcafe.com) about Timers and concurrency that you may find interesting, and may make things clearer than what I managed to accomplish.
Why can't you code it like this:
CdelmeDlg dlg;
dlg.DoModal();
CdelmeDlg dlg1;
dlg1.DoModal();
If you want your application run in background without UI, why not juz temporary hide it? a simple function this->ShowWindow(SW_HIDE) will do the job for you.
i think you should revise your design decision as it seem illogical for an application to behave like what you wanted it to be.
I solved the problem by hiding the dialog instead of closing it and launching another thread that first sleeps and then unhides the dialog.
May be your code has line:
m_pMainWnd = &dlg;
If so, than application after first call of DoModal() will finished, all other call of DoModal() will return -1. From MSDN:
The Microsoft Foundation Class Library will automatically terminate your thread when the window referred to by m_pMainWnd is closed. If this thread is the primary thread for an application, the application will also be terminated.