close button on qDialog only closing on second click - qt

I'm trying to generate a dialog that contains an ad-on tool that is separate from my main program, it its triggered from an action within the menus.
I've got the following code:
void MainWindow::on_actionCalibration_Tool_triggered()
{
QGridLayout *grid = new QGridLayout;
NewDialog.setLayout(grid);
NewDialog.setMinimumHeight(500);
NewDialog.setMinimumWidth(800);
QLabel *label = new QLabel;
QFont sansFont("MS Shell Dlg 2",22, QFont::Bold);
label->setText("Test");
label->setFont(sansFont);
QPushButton *okbutton = new QPushButton;
QPushButton *closebutton = new QPushButton;
okbutton->setText("Ok");
closebutton->setText("Close");
QTimer *timer = new QTimer;
connect(okbutton,SIGNAL(clicked()),this,SLOT(on_ScanpB_clicked()));
connect(closebutton,SIGNAL(clicked()),this,SLOT(CloseDialog()));
grid->addWidget(label);
grid->addWidget(okbutton);
grid->addWidget(closebutton);
NewDialog.exec();
NewDialog.show();
}
void MainWindow::CloseDialog()
{
NewDialog.close();
}
With NewDialog being defined in main window.h as a QDialog.
My issue is when I click the close button, the dialog will close for a split second then reopen, after I click the close button for a second time it closes for good.
Is there any better implementation or way around this?
Thanks

You should not call QDialog::show and QDialog::exec. Instead, pick one to call.
Use exec if you want to block user interaction with the dialog's parent while the dialog is open. The user will not be play with anything else in the application until they dismiss the dialog. This is called a modal.
Use show if you want to allow the user to work with the dialog and the rest of the application at the same time.
Usually you'd choose exec. It is easier to work with. In your case, you displayed the dialog twice by calling both functions.

Related

In QDialogButtonBox, where does it disconnect on-destroy connection of buttons when dialog closed?

I want to build something like QMessageBox but with my own style and layout. So I am tracing qt's source code and learn from it.
QMessageBox is composed with some label widgets as content and QDialogButtonBox as buttons.
int ret = QMessageBox::warning(nullptr, QString("My Application"),
QString("Dialog content"),
QMessageBox::Save | QMessageBox::Cancel,
QMessageBox::Save)
The code snippet above build a simple QMessageBox with two buttons save and cancel.
In source code, qt will add button with two connection clicked and destroyed in qdialogbuttonbox.cpp
void QDialogButtonBoxPrivate::addButton(QAbstractButton *button,
QDialogButtonBox::ButtonRole role,
bool doLayout)
{
Q_Q(QDialogButtonBox);
QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_handleButtonClicked()));
QObject::connect(button, SIGNAL(destroyed()), q, SLOT(_q_handleButtonDestroyed()));
buttonLists[role].append(button);
if (doLayout)
layoutButtons();
}
When clicking Cancel buttons in a QMessageBox, it will run:
q->done(execReturnCode(button)); // does not trigger closeEvent
in qmessgebox.cpp and return from event loop and destroy the QMessageBox instance.
In my understanding, all buttons will also be destroyed when QMessageBox is destroyed, so it should trigger _q_handleButtonDestroyed handler and cause some weird behavior.
I set some breakpoints in IDE and run. It doesn't step into _q_handleButtonDestroyed on clicking buttons. It also doesn't step into all disconnection in line #663 and #726 of qdialogbuttonbox.cpp
I want to know where does it disconnect the _q_handleButtonDestroyed connection when clicking button? Or is there anything I got wrong?
Thanks in advance.

QAction doesn't show QMenu

I'm creating my UI from Qt Designer and it generares this code:
toolBar = new QToolBar(MainWindow);
QIcon icon;
icon.addFile(QStringLiteral(":/main"), QSize(), QIcon::Normal, QIcon::Off);
MainWindow->addToolBar(Qt::TopToolBarArea, toolBar);
actionConvert = new QAction(MainWindow);
actionConvert->setObjectName(QStringLiteral("actionConvert"));
actionConvert->setIcon(icon);
toolBar->addAction(actionConvert);
Now, back in my frame code:
QMenu *menuAdd = new QMenu (this);
menuAdd->addAction (tr("&Files..."));
menuAdd->addAction (tr("&Directory..."));
ui->actionConvert->setMenu (menuAdd);
When I run the application I can see the qaction in the toolbar even the arrow pointing down, which indicates that there is a menu, but when I click it, the menu doesn't appear...any ideas?
There does not seem to be anything wrong with your example code.
It's possible that the reason you aren't seeing the menu is that you need to press and hold the button for a few seconds in order for the menu to appear. A single click will just execute the button's normal action.
See: QToolButton::ToolButtonPopupMode.
You should add menu with menuBar() method as in my case:
void MainWindow::ueInitMenu()
{
this->ueSetCodeRegisterPlacesAction(new QAction(tr("Places"),
this));
this->ueCodeRegisterPlacesAction()->setShortcut(tr("Ctrl+P"));
this->ueCodeRegisterPlacesAction()->setStatusTip(tr("Shows places code register"));
connect(this->ueCodeRegisterPlacesAction(),
SIGNAL(triggered()),
this,
SLOT(ueSlotShowPlacesView()));
this->ueSetCodeRegisterMenu(this->menuBar()->addMenu(tr("Code register")));
this->ueCodeRegisterMenu()->addAction(this->ueCodeRegisterPlacesAction());
} // ueInitMenu
especialy the line:
this->ueSetCodeRegisterMenu(this->menuBar()->addMenu(tr("Code register")));
so in your case:
this->menuBar()->addMenu(tr("System menu");
and then add actions. Also take a look at Menus Example.

How to programmatically close QMenu

I have pretty specific situation. I want to place a QAction into QToolbar and reach following behaviour:
Checkable QAction with icon.
Classic arrow on the right side which is used for showing menu
By pressing this arrow my QDialog should appears on screen instead of QMenu-like one
Now I'm a bit confused with implementing all this things together.
For now I've created QAction added it to toolbar and also created an empty QMenubecause I didn't get the idea of how to add the "dropdown" arrow another way.
So, I also connected my slot to QMenu aboutToShow() signal and now, I can create my dialog and exec() it just before QMenu shows. But here's the main problem appears: after I did everything with my dialog an click OK button QMenu trying to appear, but as it is empty it shows nothing and further actions become available only after I left-click somwhere to "close" this menu.
Is there any way to force QMenu not to show or can inherit from QMenu and reimplemnt its behaviour (I've tried to do such trick with exec() show() popup() methods of QMenu after subclassing from it, but none of them are being called when menu appears on the screen) ?
Here's the solution, which worked for me.
class QCustomMenu : public QMenu
{
Q_OBJECT
public:
QCustomMenu(QObject *parent = 0):QMenu(parent){};
};
In code:
QAction* myActionWithMenu = new QAction ( "ActionText", toolbar);
QCustomMenu* myMenu = new QCustomMenu(toolbar);
connect(myMenu, SIGNAL(aboutToShow()), this, SLOT(execMyMenu()));
execMyMenu() implementation:
void execMyMenu(){
m_activeMenu = (QCustomMenu*)sender(); // m_activeMenu -- private member of your head class, needs to point to active custom menu
QMyDialog* dlg = new QMyDialog();
// setup your dialog with needed information
dlg->exec();
// handle return information
m_myTimer = startTimer(10); // m_myTimer -- private member of your head(MainWindow or smth like that) class
}
Now we have to handle timerEvent and close our menu:
void MyHeadClass::timerEvent(QTimerEvent *event)
{
// Check if it is our "empty"-menu timer
if ( event->timerId()==m_myTimer )
{
m_activeMenu->close(); // closing empty menu
killTimer(m_myTimer); // deactivating timer
m_myTimer = 0; // seting timer identifier to zero
m_activeMenu = NULL; // as well as active menu pointer to NULL
}
}
It works great on every platform and does what I wanted.
Hope, this would help someone. I've spent week trying to find this solution.

Qt Menu with QLinEdit action

I want to have a line edit field in a popup menu I've got. I'm basically letting the user pick from one of several common sizes for something, but I want them to be able to enter a custom size as the last entry in the menu.
So I've got something like this (snipped from larger code, new_menu is the menu of interest):
QWidget *widget = new QWidget(new_menu);
QHBoxLayout *layout = new QHBoxLayout;
QLineEdit* le = new QLineEdit;
le->setPlaceholderText("Custom");
le->setFixedWidth(100);
ayout->addWidget(le);
widget->setLayout(layout);
QWidgetAction* wa = new QWidgetAction(new_menu);
wa->setActionGroup(group);
wa->setDefaultWidget(widget);
new_menu->addAction(wa);
connect(le, SIGNAL(returnPressed()), this, SLOT(leslot()));
Which works great, the LineEdit shows up nice and centered in the menu, it's got the placeholder text, I can click it and edit, everything. However, when I hit enter on the textBox, it emits the returnPressed signal and the menu emits a triggered signal with one of the other actions on the list, so at best I'm changing my configuration twice and at worst things break.
Additionally, when I click off the edge of the LineEdit (still in the menu though, but not in the editable area), the menu emits a triggered signal with the QWidgetAction associated with it, which isn't what I want.
So two questions:
1) Can I get the return to work the way I want. It's fine if the menu closes when it's hit, but it can't emit another action too.
2) Can I get it to not emit an action at all when the lineEdit is clicked?
Here's what I ended up doing for anyone that follows. I subclassed QLineEdit thusly:
class EnterLineEdit : public QLineEdit {
Q_OBJECT
public:
void keyPressEvent(QKeyEvent *evt) {
if (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) {
emit returnPressed();
} else {
QLineEdit::keyPressEvent(evt);
}
}
};
This lets me manually emit the returnPressed signal when enter/return is hit and not pass it up the widget hierarchy, so the menu never sees it when enter is hit over the lineedit. I connected the returnPressed signal to the hide() slot of the menu so that the menu will still close, but without triggering an action.

Show a QWidget after pressing a button

I want to change a QWidget in a QMainWindow dynamically. Therefore, the old widget (if it exists) will be deleted, a new one will be constructed and added to the main window.
The widget (_visualization) is a QMainWindow itself that contains a menu bar and a widget that shows an OSG scene graph.
If I donĀ“t call show() on the new widget, I will only see the menu bar, but not the scene graph.
My goal is to call show(), when the user clicks on a button. The button is connected with the outer QMainWindow (MyQMainWindow).
Unfortunately, when I call show() on _visualization in the connected method, the scene graph will not be shown. In contrast to that, the scene graph will be shown, if I call it in the constructor method (loadVisualization(...)).
MyQMainWindow::MyQMainWindow(QWidget *parent ) :
QMainWindow(parent) {
...
loadVisualization(...);
connect(_ui->nextButton, SIGNAL(clicked()), this, SLOT(showNext()));
...
}
void MyQMainWindow::loadVisualization(QString filePath) {
if (_visualization) {
_heightWidgetLayout->removeWidget(_visualization);
delete _visualization;
}
_visualization= new HeightVisualization(0, filePath);
_visualization->setParent(_mainWindowWidget);
_heightWidgetLayout->addWidget(_visualization);
//_visualization->show(); // will work here
}
void MyQMainWindow::showNext() {
_visualization->show(); // does not work here!
}
I know that QT will call setVisible(...) on the widget. This method first tests some states in QT (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)). If I call show() in showNext(), these tests lead to a return without any change.
Is it a problem with connectors and slots? Is there a possibility to show the widget, when the user clicked on a button?
What I have learned is that it is easy to use stackedWidget.
You can then programmatically change it with this
currentPageIndex = (currentPageIndex + 1) % 3;
ui->stackedWidget->setCurrentIndex(0);
The 3 can be the total pages you have in your stack widget. You can either use currentPageIndex, or you can just create some constants with the page ids like I have done with the 0.

Resources