Making two QT dialogs close each others - c++

I've seen this question : Qt/C++ - Closing two widgets when one is closed
However I do not have this MainWindow <-> Widget relationship.
class Ui_DialogResults
{
public:
QDialog *_Dialog;
void setupUi(QDialog *Dialog)
{
_Dialog = Dialog;
}
};
class Ui_DialogSearch
{
public:
QDialog *_Dialog;
void setupUi(QDialog *Dialog)
{
_Dialog = Dialog;
}
};
namespace Ui {
class Ui_Search : public Ui_DialogSearch {};
class Ui_Results : public Ui_DialogResults {};
}
class Search : public QDialog{
public:
Search(){
ui.setup(this);
}
void closeEvent(QCloseEvent *event)
{
//saves scan settings here
pResults->_Dialog->close();
}
Ui::Ui_Search ui;
Ui::Ui_Results *pResults;
}
class Results : public QDialog{
public:
Results(){
ui.setup(this);
}
void closeEvent(QCloseEvent *event)
{
//saves scan settings here
pSearch->_Dialog->close();
}
Ui::Ui_Search *pSearch;
Ui::Ui_Results ui;
}
When a Search dialog is open, a result dialog is also open and vice versa.
The Thread opening both dialogs also fills the pointer in the Search instance towards Result and vice versa.
Each class has overriden closeEvent()so that they close each others as such.
I would have expected cross recursive consequences and crash but it apparently works perfectly.
Qt doc states
The QCloseEvent class contains parameters that describe a close event.
Close events are sent to widgets that the user wants to close, usually by choosing "Close" from the window menu, or by clicking the X title bar button. They are also sent when you call QWidget::close() to close a widget programmatically.
Is this UB?

Since you already have a mutual reference between the search and results dialog, you can easily avoid any potential incidents by setting pointers to null and doing some basic checks.
The dialog that initiates the closing sets its own pointer in the other dialog to null, copies the pointer to the other to a temporary value, sets the other pointer to null and closes the other dialog through the temp pointer. If the pointer to the other dialog is already null, that would mean the other dialog has initiated the closing, so do nothing except call the base class implementation in order to close the dialog.

Is it undefined Behavior? No. Undocumented? Possibly. (Did not find any explicit statement that this is supposed to work). Unintended? Probably not. Qt is pretty mature, although not always documented to the last detail. I bet they only allow only one close event per widget.

Related

Calling function each time qt windows gain focus

I have a mainwindow which in there is a Qtableview by clicking on insert record I go to other modal windows to add record when I add record and close the second windows I come back to main windows but the qtableview doesn't show the new record that is added. The record is in database.
I already make this somehow work with :
void MainWindow::showEvent( QShowEvent* event ) {
QWidget::showEvent( event );
updTbl();
}
But it only works when windows get minimized.
QMainWindow has also two event handler from QWidget
void QWidget::focusInEvent(QFocusEvent *event)
void QWidget::focusOutEvent(QFocusEvent *event)
If you use QtCreator, go to your mainwindow.h and search the line "class MainWindow : public QMainWindow". Right click on QMainWindow -> Refactoring -> Insert virtual function. That's an easy way to find which virtual functions exist and that can be overloaded, you can select focusInEvent and focusOutEvent from there.
Handling activate/deactivate events as follows will give you the desired behaviour
// overloading event(QEvent*) method of QMainWindow
bool MainWindow::event(QEvent* e)
{
switch (e->type())
{
case QEvent::WindowActivate:
// gained focus
//Update Table
break;
case QEvent::WindowDeactivate:
// lost focus
break;
};
return QMainWindow::event(e);
}
Ref: https://gist.github.com/01walid/2276009

No double / Only one QMessageBox at same time

In my application, I have the following situation:
In a verify-method for inputs of a QTabWidget, I'm coming to determine an error for a sub-widget's input.
In cause of that I set this sub-widget as the active widget
In showEvent of this widget I'm firing an QTimer::singleShot to a method onShowEventFinished() in which I'm going to show a QMessageBox
After activating the the new sub-widget, I want to show at first the error message from my verify (step 1).
So then the message from verify shows up, but then in cause of my singleShot in step 3 the other one show up over it. I want to show the second one at the earliest, when the first one is closed.
(It's important that the first message shows up at the earliest when the regarding sub widget ist showing.)
Example code:
class MySubwidget : public QWidget
{
// omitted (ctor, etc.)
protected:
void showEvent( QShowEvent* e )
{
QShowEvent( e );
QTimer::singleShot( 200, this, SLOT(onShowEventFinished()) );
};
private slots:
void onShowEventFinished()
{
bool showEntryHint = false;
// omitted (some stuff to determine to show an entry hint or not)
if( showEntryHint )
{
QMessageBox t_MessageBox( this );
// omitted (set up the message box
t_MessageBox.exec();
}
};
};
I've tried different ways to solve this with QMutex, QWaitCondition or QSemaphore in a derived class from QMessageBox but this doesn't work because the execution of the message box is in the same thread.
This means, when the first message box is started with QMessageBox::exec() than QApplication::processEvents (called by QMessageBox) caused the invokement of my slot and called QMessageBox::exec() twice (for the second hint, while the first exec() is alive until the first message box is closed).
My currently workaround is now to have a subclass where I'm calling QApplication::processEvents() as long as another instance of MyMessageBox is shown:
class MyMessageBox : public QMessageBox
{
public:
// omitted (ctor, etc.)
int exec()
{
while( MessageBoxShowingCount > 0 )
QApplication::processEvents();
return QMessageBox::exec();
};
protected:
void showEvent( QShowEvent* e )
{
MessageBoxShowingCount++;
QMessageBox::showEvent(e);
};
void hideEvent( QHideEvent* e )
{
QMessageBox::hideEvent(e);
MessageBoxShowingCount--;
};
static int MessageBoxShowingCount = 0;
};
(For this solution I have replaced all my QMessageBox-instances by an MyMessageBox-instance.)
What you seem to need is an application-wide message manager. The QMessageBox then becomes that class's implementation detail and is not used directly anymore. When you want to show a message, you'd then use your MessageManager class.

The order of arrangement of windows

Sorry for my English. I need to implement the order of the QWidget. I have a stack of objects QWidget. And I need to place the window in the order they appear in a stack.
For example:
Stack: window1 -> window2 -> window3
From this example window2 always closes window3, but it, in turn, closes window1.
Is there any such functionality in Qt? I am not limited to the QWidget. Maybe somehow you can specify the order \ windows priority?
The QStackedWidget class provides a stack of widgets where only one widget is visible at a time. I need to get all the widgets to be seen and they cover each other in order of priority.
Maybe there are some flags. Qt::WindowStaysOnTopHint it does not suit me, so the widget \ windows a lot and one should cover everything. And all in order of priority.
You can close the child window explicitly in the close event of a window, something like:
class WindowWithChildWindow : public QWidget
{
Q_OBJECT
public:
WindowWithChildWindow(QWidget *child)
: m_child(child)
{ }
....
protected:
void closeEvent(QCloseEvent *e) override
{
if (!m_child || m_child->close())
e->accept(); // close this window
else // child ignored the close request in its close event
e->ignore(); // do not close this window
}
....
};

Open only one instance of QDialog with show() , and also does the Object is deleted if i close the QDialog

in Qt im opening QDialog windows each time i click some item
im doing it with new , i like to be sure im opening only one instance of QDialog for each item
im clicking :
void foo::treeWidget_itemClicked(QTreeWidgetItem *item,nt column)
.....
QString groupID = item->data(0, Qt::UserRole).toString();
QString groupName = item->text(0);
GroupDialogContainer* pGroupDialogContainer = new GroupDialogContainer(groupID, groupName, this);
pGroupDialogContainer->show();
}
class GroupDialogContainer : public QDialog
{
Q_OBJECT
public:
GroupDialogContainer(QString GroupId,QString GroupName,QWidget *parent=0);
GroupDialogContainer(QWidget *parent=0);
virtual ~GroupDialogContainer();
Ui::GroupDialog ui;
public slots:
void closeEvent(QCloseEvent *event);
};
do i need to keep some kind of hash or vector of GroupDialogContainer ?
also my second question is :
does each time im closing the QDialog window with close () the object pGroupDialogContainer
that was responsible to open it is destroyer ed ? or do i need to delete it when im detecting that the QDIalog has closed?
Yes, you should probably keep some kind of list of your dialogs to keep track of which ones are already open. If your GroupID is your unique ID then you could do something like this:
QMap DialogMap;
void foo::treeWidget_itemClicked(QTreeWidgetItem *item,nt column)
{
.....
QString groupID = item->data(0, Qt::UserRole).toString();
if (! DialogMap.contains(groupID))
{
// Popup the dialog and add it to map
...
DialogMap.insert(groupID, pGroupDialogContainer);
}
}
Now, for the other part. The most important thing is that you need to remove the item from the map when the dialog closes. You could either delete the dialog then, or my suggestion would be to let the dialog delete itself when it closes - as follows:
// set automatic deletion of object on close
setAttribute(Qt::WA_DeleteOnClose);
But as I said, you'll still need to remove the dialog from the Map, or else you'll have a bad pointer in there, and your code will still think the dialog is open.
So you'll need some kind of signal from the dialog to indicate that it is closing. There is the finished(int result) signal, that is called when you trigger a result:
This signal is emitted when the
dialog's result code has been set,
either by the user or by calling
done(), accept(), or reject().
But, you can always create your own signal in your dialog, and emit it when the closeEvent is called in your dialog.
Then in the code that handles the map...
connect( pGroupDialogContainer, SIGNAL(WindowClosed()), this, SLOT(vCleanUpTheMap()));
...
void vCleanUpTheMap()
{
GroupDialogContainer *pDialog = dynamic_cast<GroupDialogContainer *>(sender());
if (pDialog)
{
// Just to keep things clean disconnect from the dialog.
disconnect(pDialog);
// I am assuming that you can get the key groupID from the dialog
// Cause it's a lot easier to remove from a map with the key
DialogMap.remove(pDialog->GetGroupID());
}
}
And that's it.

Using QMDIArea with Qt 4.4.

I'm using the QMdiArea in Qt 4.4.
If a new project is created, I add a number of sub windows to a QMdiArea. I'd like to disallow the user to close a sub window during runtime. The sub windows should only be closed if the whole application is closed or if a new project is created.
How can I do this?
You need to define your own subWindow. create a subclass of QMdiSubWindow and override the closeEvent(QCloseEvent *closeEvent). you can control it by argument. for example:
void ChildWindow::closeEvent(QCloseEvent *closeEvent)
{
if(/*condition C*/)
closeEvent->accept();
else
closeEvent->ignore(); // you can do something else, like
// writing a string in status bar ...
}
then subclass the QMdiArea and override QMdiArea::closeAllSubWindows () like this:
class MainWindowArea : public QMdiArea
{
Q_OBJECT
public:
explicit MainWindowArea(QWidget *parent = 0);
signals:
void closeAllSubWindows();
public slots:
};
// Implementation:
MainWindowArea::closeAllSubWindows()
{
// set close condition (new project is creating, C = true)
foreach(QMdiSubWindow* sub,this->subWindowList())
{
(qobject_cast<ChildWindow*>(sub))->close();
}
}
you may also need to override close slot of your mdi area.
You'd do this the same as for a top-level window: process and ignore the QCloseEvent it sent. QMdiArea::closeActiveSubWindow/QMdiArea::closeAllSubWindows just call QWidget::close, which sends a closeEvent and confirms that it was accepted before proceeding.
You can process this event by subclassing QMdiSubWindow and reimplementing QWidget::closeEvent, or by using an event filter to intercept it..