I have a modeless dialog which I'm creating as below,
CPlotDlg * newd = new CPlotDlg ();
newd->Create(IDD_PLOT,this->GetParentOwner());
newd->SetParent(this->GetParentFrame()->GetParent());
newd->ShowWindow(SW_SHOW);
I want to close this dialog when a different window closes (not the parent). How can I achieve this?
Thanks.
Just, save CPlotDlg* to other window which will be used for closing CPlotDlg window.
If the closer window is SomeWhereDlg,
class SomeWhereDlg
{
public:
...
...
CPlotDlg* m_plotDlg;
};
void SomeWhereDlg::SetPlotDlg(CPlotDlg* plotDlg)
{
ASSERT(plotDlg);
if(plotDlg == nullptr) { return;}
m_plotDlg = plotDlg;
}
And then, when create CPlotDlg window, save the pointer.
CPlotDlg* newd = new CPlotDlg ();
//Save newd(CPlotDlg*) to somewhere
//i.e) specific window which will close this newd window
//SomeWhereDlg->SetPlotDlg(newd);
newd->Create(IDD_DIALOG1,this->GetParentOwner());
newd->SetParent(this);
newd->ShowWindow(SW_SHOW);
if a closing event occur, just call Close() or delete, etc via m_plotDlg.
To close the modeless dialog save the pointer as CodeDreamer shows, and call m_plotDlg->DestroyWindow()
Related
I am using wxEVT_SET_FOCUS for wxTextCtrl in wxWidgets. What I need to do is when user clicks textctrl I have to open a new window and remove focus from textctrl so that FocusHandler function will be executed only once. Is there any function to remove focus from wxTextCtrl ?
Using this connect event in constructor
//Connect Events
m_passwordText->Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(MyFrame::OnPasswordTextBoxSetFocus), NULL, this);
void MyFrame::OnPasswordTextBoxSetFocus(wxFocusEvent& event)
{
if(some condition is true)
//code to open a new window
event.Skip()
// is there any option to remove focus from password textCtrl so that once a new window opens
//I can remove focus from password and avoid executing this function again and again.
// If new window opens for the first time, I perform the operation and close it
// then also it opens again as control is still in password textbox.
//Is there any way to resolve this?
}
Basically I want to stop multiple executions of handler function once the new window is opened without disconnecting the wxeVT_SET_FOCUS from wxTextCtrl.
Each time the control gains or looses the focus a focus event is fired, and your handler comes into action.
There are some causes for this focus event. The most cumbersome is when the new window is created and deleted, because it may generate its own focus events, and your handler may deal with them all. There's a reentrance issue.
We deal with reentrance using a flag, which tell us if we are in a reentrance case.
void MyFrame::OnPasswordTextBoxSetFocus(wxFocusEvent& event)
{
static bool selfFired = false; //our flag
event.Skip(); //always allows default processing for focus-events
if ( event.GetEventType() == wxEVT_KILL_FOCUS )
{
//Nothing to do. Action takes place with wxEVT_SET_FOCUS
return;
}
//Deal with re-entrance
if ( !selFired )
{
if (some condition)
{
selfFired = true;
//Open a new window.
//Pass 'this' to its ctor (or similar way) so that new window
//is able to SetFocus() back to this control
newWin = open a window...
newWin->SetFocus(); //this may be avoided if the new window gains focus on its own
}
}
else
{
//restore the flag
selfFired = false;
}
}
I have a MFC project I am working on. In the main dialog box there is a button to open a child dialog box for some user input. This data is then set to variables in the parent dialog box when OK is clicked in the child box. This bit all works perfectly fine.
Right now the text boxes in the child box initialize to preset values in the OnInitDialog() of the child dialog box. I would like these values to initialize to whatever the variable they are connected to in the parent dialog box currently is.
So, for example I have a text box that sets in integer variable called sampleCount. In the child dialog box I have (just showing the code for this variable)
void ChildBox::DoDataExchange(CdataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_SAMPCOUNT, sampCnt);
}
BOOL FNameIn::OnInitDialog()
{
CDialog::OnInitDialog();
GetDlgItem(IDC_SAMPCOUNT)->SetWindowTextA("1");
return TRUE;
}
Then in the parent dialog box I have
void ParentDialog::OnInput()
{
ChildBox dlg;
if (dlg.DoModal() == IDOK)
{
sampleCount = dlg.sampCnt;
}
}
As I said, this code all works, but every time I open the ChildBox the sampleCount text box is set to 1. If I type in 20, then hit ok and set the sampleCount variable in ParentDialog to 20 I want it to show a 20 in the text box next time I open the child dialog box. The variable could also be set automatically by another function in ParentDialog, so the last value entered in child dialog isn't always correct, it needs to use whatever is currently the value of the variable sampleCount in ParentDialog.
I feel like this should be pretty straight forward but I can't quite figure it out, thanks for the help.
Modify the child dialog's constructor to accept a CString parameter and store that into the child's sampCnt variable. Then the MFC code will display it in the control because of the DDX_Text statement.
Remove the GetDlgItem(IDC_SAMPCOUNT)->SetWindowTextA("1"); line and set the value before calling dlg.DoModal(), like
ChildBox dlg;
dlg.sampCnt = sampleCount;
if (dlg.DoModal() == IDOK)
{ sampleCount = dlg.sampCnt;
}
I think you are creating local variable of ChildBox Dialog
so , even if you are assigning a value to its variable it will not work.
Rather create a pointer variable of ChildBox Dialog
void ParentDialog::OnInput()
{
ChildBox *dlg;
if (dlg->DoModal() == IDOK)
{
sampleCount = dlg->sampCnt;
}
dlg = NULL ;
}
I have followed this question to make a non-modal/modeless dialog:
How to display a non-modal CDialog?
I'm using MFC/C++ in VS2008. I'm more fluent with C# and .net than with MFC and C++.
I have a menu item in my form that launches the dialog. There can only be one instance of the dialog opened. The dialog displays fine. I can close it by clicking the X in the corner and it closes when I close the main form. The problem I am having is the dialog cannot be opened again after I click the X to close the dialog. I know it is because the pointer is never set back to NULL.
I have this in my form's header file:
CChildDialog *m_pDialog;
I have this part in my form's constructor:
m_pDialog = NULL;
When clicking on a menu item I have this code in the menu item's method (I modified it from the other SO answer because I only want one instance of the dialog opened):
if(m_pDialog == NULL)
{
// Invoking the Dialog
m_pDialog = new CChildDialog();
BOOL ret = m_pDialog->Create(IDD_CHILDDIALOG, this);
if (!ret) //Create failed.
{
AfxMessageBox(_T("Error creating Dialog"));
}
m_pDialog->ShowWindow(SW_SHOW);
}
Now I know I need to execute this part and set the pointer to NULL, but I don't know where to put this:
// Delete the dialog once done
delete m_pDialog;
m_pDialog = NULL;
Do I need to keep monitoring if the dialog has been disposed? Is there an event triggered to the parent form when the dialog is closed?
If you want to recycle the contents of the window after closing it with X, you have to handle the WM_CLOSE message in your dialog:
void CChildDialog::OnClose()
{
ShowWindow(SW_HIDE);
}
Then in the code that opens the window:
if(m_pDialog == NULL)
{
// Invoking the Dialog
m_pDialog = new CChildDialog();
BOOL ret = m_pDialog->Create(IDD_CHILDDIALOG, this);
if (!ret) //Create failed.
{
AfxMessageBox(_T("Error creating Dialog"));
}
}
m_pDialog->ShowWindow(SW_SHOW); //moved outside the if(m_pDialog == NULL)
Hope it can help
If you want to delete the modeless dialog, then just do so.
If you want to delete the dialog's object when the user closed the modeless dialog you might take a look at WM_PARENTNOTIFY. If a child window is destroyed and the child windows has not the extended window style WS_EX_NOPARENTNOTIFY set, then windows sends a WM_PARENTNOTIFY with wParam=WM_DESTROY to the parent window. You should implement a handler for that message in the parent window and check if it's the modeless dialog that is being destroyed.
I had the question drafted up and was ready to post it, but then I had an idea and ended up solving my own problem. So for anyone else who has an issue with detecting the closing of a modeless dialog, this is what I did:
void Form1::MenuItemMethod()
{
if(m_pDialog == NULL)
{
// Invoking the Dialog
m_pDialog = new CChildDialog();
BOOL ret = m_pDialog->Create(IDD_CHILDDIALOG, this);
if (!ret) //Create failed.
{
AfxMessageBox(_T("Error creating Dialog"));
}
m_pDialog->ShowWindow(SW_SHOW);
}
else
{
// cannot check if visible at the beginning of method because
// pointer could be NULL and will throw an exception
if(m_pDialog->IsWindowVisible())
{
return;
}
m_pDialog->DestroyWindow();
m_pDialog = NULL;
MenuItemMethod();
}
}
I just ended up checking if the modeless dialog is visible after clicking on the form's menu item again. If it is visible, don't do anything. If not, destroy the existing non-visible dialog, set the pointer to NULL, and recursively call the method again. Since the pointer is now NULL, it should recreate the dialog normally and then return to normal operation.
you have to delete the memory in PostNcDestroy like this
void CChildDialog ::PostNcDestroy()
{
CDialog::PostNcDestroy();
GetParent()->PostMessage(WM_WIN_CLOSED,0,0);
delete this;
}
and send a user defined message to the parent window that your window is closed. In the parent window add a message handler for WM_WIN_CLOSED like
LRESULT CMainDialog::OnMyMethod(WPARAM wParam, LPARAM lParam)
{
m_pDialog = NULL;
return 0;
}
I open QDialog window from QMainWindow. Now when I press the QDialog window
its not always closing in the first press - I need to press few times (3-4) to close it .
I have closeEvent slot that has simple event->accept(); inside it.
This is how I call the QDialog from the main window:
void MyManager::DialogContainerOpen(type t)
{
if(pMyDialogContainer == NULL)
{
pMyDialogContainer = new MyDialogContainer();
}
int returnVal = QDialog::Rejected;
if(!m_bContainer)
{
m_bContainer = true;
int returnVal = pMyDialogContainer->exec();
if(returnVal != QDialog::Accepted ) {
m_bContainer = false;
}
}
}
This is the first problem.
The second problem is how do i set the QDialog windows NOT to be allays on top? (I don’t want it to block the parent window.
UPDATE
well i found out that the function from the MainWindow that showing the contexMenu
and inside it has the connect single/slot is keeps to invoke so i just used the disconnect
i dont know if its the best sulotion but its working.
now i juat have the final problem .
here is the code i hope its ok
void MainWindowContainer::ShowContextMenu(const QPoint& pos) // this is a slot
{
QModelIndex modelIndx;
QPoint globalPos = ui.treeView_mainwindow->mapToGlobal(pos);
bool b1 = connect(OpenAction, SIGNAL(triggered()),m_SignalMapper, SLOT(map()) );
m_SignalMapper->setMapping(OpenAction,voidID);
bool b2 = connect(m_SignalMapper, SIGNAL(mapped(QString)), this, SLOT(OpenWin(QString)));
QAction* selectedItem = ContextMenu.exec(globalPos);
}
void MainWindowContainer::OpenWin(QString gid)
{
//disconnect(sender0, SIGNAL(overflow()),receiver1, SLOT(handleMathError()));
disconnect(m_SignalMapper, SIGNAL(mapped(QString)),this, SLOT(OpenWin(QString)));
disconnect(OpenAction,SIGNAL(triggered()),m_SignalMapper, SLOT(map()));
....
....
}
For your second question, the term you are looking for is modal vs modeless dialogs. The QDialog documentation tells exactly how you create non-modal dialogs:
Modeless dialogs are displayed using show(), which returns control to the caller immediately.
i.e. don't use exec() as that will make a modal dialog (which blocks the parent).
You should not connect the same signal/slot more than once unless you want the action run multiple times. All you need to do is to connect the QAction's signal to the slot once. This is usually done in the constructor (or a dedicated function called from the constructor) where you create the action.
I have a Qt main window that will pop up a dialog box that has an OK and Cancel button. This dialog has a simple spinner that asks a user for a number that should be returned to the main window when OK or Cancel is pressed (for cancel, it will just send back -1).
I thought about using code in a signal in mainWindow.cpp like so:
void mainWindow::slot_openNumberDlg(){
// Create a new dialog
numberDlg dlg( this );
// Show it and wait for Ok or Cancel
if( dlg.exec() == QDialog::Accepted ){
return;
}
}
But how would I return the value of the spinner in the dialog back to main window if it is destroyed when the button is pressed?
I also thought about an alternative solution where I would initialize the dialog in mainWindow's constructor, but it isn't shown until a signal activated a slot in mainWindow.cpp like so:
// Create as pointer in main.h:
numberDlg *dlg;
// Initialize pointer and setup listener in constructor (main.cpp):
mainWindow::mainWindow(){
dlg = new numberDlg();
QObject::connect( dlg, SIGNAL( sig_retVal(int) ), ui->widget, SLOT( slot_showNum(int) ) );
}
// Make a function to handle show/close:
void mainWindow::slot_numberOpenClose(bool trigger){
if(trigger){
dlg->show();
} else {
dlg->close();
}
}
The above code would be able to send the number from the dialog's spinner to mainWindow using a signal/slot, but it won't lock out mainWindow. I do not want the user to continue using mainWindow until the dialog has been accepted or rejected.
Any ideas on how I can lock the main window and send a number back to main on dialog close?
What you should do is create a function in your dialog class that returns the value you want, and use it like so from your main window:
void mainWindow::slot_openNumberDlg(){
// Create a new dialog
numberDlg dlg( this );
// Show it and wait for Ok or Cancel
if( dlg.exec() == QDialog::Accepted ){
m_theValue = dlg.myValue(); // <--
return;
}
} // dlg goes out of scope
dlg won't be destroyed until it goes out of scope where I placed the comment, so your method will work fine.
use dlg.setWindowModality(Qt::WindowModal); :
void mainWindow::slot_openNumberDlg(){
numberDlg dlg( this );
dlg.setWindowModality(Qt::WindowModal);//blocks input to its parent window
if( dlg.exec() == QDialog::Accepted )
{
dlg.setResult(true);//mask
return;
}
else if(dlg.exec() == QDialog::Rejected)
{
dlg.setResult(false);//mask
return;
}
}
if(dlg.getResult() == true)//value usable only when accepted
{
m_theValue = dlg.myValue();
}
If you want more accurate result, override the closeEvent() method of QWidget to handle the exit button of your dialog. (or you could set default value as false)
void numberDlg::closeEvent(QCloseEvent *event)
{
setResult(false);
close();
}