Qt ignore click when dismissing popup on Windows - c++

I have an issue with Qt where the behavior on Windows is different than Mac or Linux. I discovered the issue on PySide2 but was able to reproduce it in a minimal C++ application as well (see below).
When I dismiss a Popup widget by clicking outside the click is ignored by the rest of the application. This is the desired behavior and is the way it works on Linux and Mac. However in Windows this click is registered by the widget that was clicked which in my application leads to unwanted user input on the underlying widgets.
Is there a way to will prevent the dismissal click from being passed on in Windows? I am fine with having platform dependent code for this issue.
The behavior can be reproduced with this example. When the popup is open and testButton is clicked the onTestButton method will be executed.
#include "mainwindow.h"
#include <QPushButton>
#include <QHBoxLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
this->setFixedHeight(600);
this->setFixedWidth(800);
QWidget* w = new QWidget();
QDialog* popUp = new QDialog();
popUp->setFixedHeight(200);
popUp->setFixedWidth(200);
popUp->setWindowFlag(Qt::Popup | Qt::FramelessWindowHint);
popUp->setModal(true);
popUp->setVisible(false);
QHBoxLayout *layout = new QHBoxLayout(this);
w->setLayout(layout);
QPushButton* openButton = new QPushButton("open popup");
// same behavior whether using QDialog::show or QDialog::exec
connect(openButton, &QPushButton::clicked, popUp, &QDialog::show);
QPushButton* testButton = new QPushButton("catch mouse");
connect(testButton, &QPushButton::clicked, this, &MainWindow::onTestButton);
layout->addWidget(openButton);
layout->addWidget(testButton);
this->layout()->addWidget(w);
}
void MainWindow::onTestButton()
{
qDebug() << "caught mouse";
}

QWidget is just a widget, if you want it to "hold" focus, then use QDialog instead.
Also it changes the "logic" if u use it as QDialog.show or QDialog.exec.
Can you try with QDialog?

QDialog::setModal(bool) should set the dialog to modal, thus prohibiting clicking into the mainwindow.
Your windowflag Qt::Popup blocked modality. If you want to use that approach and create Qwidget as a modal dialog (Qt::Dialog), it should be launched from another window, or have a parent and used with the QWidget::windowModality property.
Tested:
auto w = new QWidget;
QDialog* popUp = new QDialog;
popUp->setFixedHeight(200);
popUp->setFixedWidth(200);
popUp->setWindowFlags( Qt::FramelessWindowHint);
popUp->setModal(true);
QHBoxLayout *layout = new QHBoxLayout(this);
w->setLayout(layout);

Related

Non-modal QWidget dialog that stays on top of the window

I want a dialog which stays on top of my main window and not other windows. I derived a class and added some flags. If I call the dialog now with show() the dialog appears and is staying on top as long as I don't press a button or whatever. Then the dialog goes to background again.
Dial::Dial(QWidget *parent) : QWidget(parent)
{
this->setWindowFlags(Qt::Tool | Qt::Dialog);
// ...
Consequently, I looked into the docu and found this:
Indicates that the widget is a tool window. A tool window is often a
small window with a smaller than usual title bar and decoration,
typically used for collections of tool buttons. If there is a parent,
the tool window will always be kept on top of it.
Happily, I added this line into my singleton creating the dialog.
d->mainWindow = new Foo();
d->dial->setParent(d->mainWindow);
Now the dialog is just embedded into my central widget (QOpenGlWidget) and is not a dialog anymore. Somehow, I seem to lack understanding what the docu is telling me? How can I get the dialog stay on top of my application and what does the docu mean?
I'm not able to reproduce your problem. The following code will generate a QWidget that will allways stay on top of the QMainWindow:
#include "QApplication"
#include "QMainWindow"
#include "QLineEdit"
int main(int argc, char * argv[])
{
QApplication a(argc, argv);
QMainWindow w;
w.show ();
QWidget *pLineEdit = new QWidget(&w);
pLineEdit->setWindowFlags(Qt::Tool | Qt::Dialog);
pLineEdit->show ();
a.exec ();
}
Tested with Qt 5.9.
Not sure if you've already solved this by now but you can try the WindowStaysOnTopHint flag when you construct the dialog:
Qt::WindowFlags flags = this->windowFlags();
flags |= Qt::WindowStaysOnTopHint;
this->setWindowFlags(flags);
Then use show() instead of exec() to make it non-modal:
dlg->show();
You need to set the modality (documentation) of the widget, like this:
QWidget *dialog = new QWidget(window, Qt::Dialog);
dialog->setWindowModality(Qt::ApplicationModal);
dialog->show();
However, I'd recommend to use the pre-configured QDialog class, which handles all that stuff for you:
QDialog *dialog = new QDialog(window);
dialog->exec();
Use QDialog instead of QWidget, and pass the parent widget in its constructor function.
QDialog* pDlg = new QDialog(this);
pDlg->show();

Qt: Add Widget to Menu

I created a menu in my QMainWindow derived class, and added a QWidget to it via QWidgetAction. Now my app does not close and stays in infinite loop when I try to close it. Any hints on why this happens?
Here's the code:
MainWindow* parent = ...;
auto menu = parent->menuBar()->addMenu("Menu");
auto action = new QWidgetAction(menu);
auto widget = new QLabel("Lol");
action->setDefaultWidget(widget);
menu->addAction(action);
This happens under OSX Sierra, Qt 5.7

Qt C++ Creating toolbar

I am learning Qt and trying some examples in the book "Foundations of Qt Development".
In the book, there is a section teaching Single Document Interface with an example creating a simple app like a notepad.
However I am having problem with toolbar creating.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setWindowTitle(QString("%1[*] - %2").arg("unnamed").arg("SDI"));
connect(ui->docWidget->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setWindowModified(bool)));
createActions();
createMenu();
createToolbars();
statusBar()->showMessage("Done");
}
It is the constructor of the main window.
void MainWindow::createToolbars()
{
QToolBar* toolbar;
toolbar = addToolBar(tr("File"));
toolbar->addAction(anyaction);
}
This is how the book create the toolbar.
However, when I try to run the program, there are two toolbars created.
One is the toolbar created by the code and called "File"
Another is a blank toolbar created by the ui designer ie. *ui.toolbar.
In order to get rid of two toolbars, I tried using only the *ui.toolbar.
It's working. The code is shown below.
void MainWindow::createToolbars()
{
ui->toolBar->addAction(anyaction);
}
But I tried to create the toolbar by code only, ie. not adding a toolbar in the ui designer.
So I write this:
void MainWindow::createToolbars()
{
QToolBar* FileBar = this->addToolBar(tr("File"));
FileBar->addAction(anyaction);
}
However, there is a compile error.
The compiler use this function:
void QMainWindow::addToolBar(QT::ToolBarArea area, QToolBar * toolbar)
instead of what I want:
QToolBar * QMainWindow::addToolBar(const QString & title)
http://doc.qt.io/qt-5/qmainwindow.html#addToolBar-3
What is my mistake here?
When you removed QToolBar from MainWindow QtCreator automatically removed import of QToolBar class.
Just add this to the top of mainwindow.h:
#include <QToolBar>
And it is better to define QToolBar* FileBar in private section of MainWindow in mainwindow.h. Then you will be able to access it from any method of MainWindow class.
void MainWindow::createToolbars()
{
FileBar = this->addToolBar(tr("File"));
FileBar->addAction(anyaction);
}
When you see such message:
must point to class/struct/union/generic type
First of all try to include headers for necessary class.

Create QAction with shortcut, without inserting in menu

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <cassert>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QAction* back = new QAction(this);
back->setVisible(true);
back->setShortcut(QKeySequence("Ctrl+M"));
bool cres = connect(back, SIGNAL(triggered(bool)), this, SLOT(mySlot()));
assert(cres);
}
In this code I tried to catch Ctrl+M key event. I don't want to put the action in menu. connect returns true but mySlot is never called. When action is inserted in menu, shortcut works well. What I have done wrong?
QAction is dormant until you insert it somewhere. As vahancho has suggested, use QShortcut. You need to instantiate the shortcut for each top-level widget (window) where you want it to be active. Thus if you have 5 top-level windows, you'll need 5 shortcuts, each having one of windows as its parent.
There is no way to use QShortcut as a global shortcut without the gui. QShortcut is only active when its associated widget has focus. The widget could be a top-level window.
System-global shortcuts are the subject of this question.

Set QLineEdit focus in Qt

I am having a qt question. I want the QLineEdit widget to have the focus at application startup. Take the following code for example:
#include <QtGui/QApplication>
#include <QtGui/QHBoxLayout>
#include <QtGui/QPushButton>
#include <QtGui/QLineEdit>
#include <QtGui/QFont>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget *window = new QWidget();
window->setWindowIcon(QIcon("qtest16.ico"));
window->setWindowTitle("QtTest");
QHBoxLayout *layout = new QHBoxLayout(window);
// Add some widgets.
QLineEdit *line = new QLineEdit();
QPushButton *hello = new QPushButton(window);
hello->setText("Select all");
hello->resize(150, 25);
hello->setFont(QFont("Droid Sans Mono", 12, QFont::Normal));
// Add the widgets to the layout.
layout->addWidget(line);
layout->addWidget(hello);
line->setFocus();
QObject::connect(hello, SIGNAL(clicked()), line, SLOT(selectAll()));
QObject::connect(line, SIGNAL(returnPressed()), line, SLOT(selectAll()));
window->show();
return app.exec();
}
Why does line->setFocus() sets the focus on the line widget #app startup only if it is placed after laying out the widgets and if used before it's not working?
Keyboard focus is related to widget tab order, and the default tab order is based on the order in which widgets are constructed. Therefore, creating more widgets changes the keyboard focus. That is why you must make the QWidget::setFocus call last.
I would consider using a sub-class of QWidget for your main window that overrides the showEvent virtual function and then sets keyboard focus to the lineEdit. This will have the effect of always giving the lineEdit focus when the window is shown.
Another trick that might work is by using the singleshot timer:
QTimer::singleShot(0, line, SLOT(setFocus()));
Effectively, this invokes the setFocus() slot of the QLineEdit instance right after the event system is "free" to do so, i.e. sometime after the widget is completely constructed.
Perhaps this is an update as the last answer was in 2012 and the OP last edited the question in 2014. They way I got this to work was to change the policy and then set the focus.
line->setFocusPolicy(Qt::StrongFocus);
line->setFocus();
In Qt setFocus() is a slot, you can try other overloaded method which takes a Qt::FocusReason parameter like the line shown below:
line->setFocus(Qt::OtherFocusReason);
You can read about focus reason options in the following link:
http://doc.trolltech.com/4.4/qt.html#FocusReason-enum