QInputDialog in mouse event - c++

In example code:
class MyWidget : public QWidget
{
Q_OBJECT
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << event;
event->accept();
QInputDialog::getText(NULL, "", "");
}
};
When I click Right mouse button on widget Input dialog appear on screen. After I click any button on dialog it closed and mousePressEvent call again and again and show dialog. If I click Left mouse button or Ctrl+Left Mouse button on widget all work fine.
This bug apper only on Mac OS (under Windows work fine).
Please help me to avoid this bug.

Those static/synchronous dialog functions always seemed a bit dubious to me -- they are implemented by recursively re-invoking the Qt event loop routine from within the getText() call, and so it's easy to get "interesting" re-entrancy issues when you use them. (For example, if some event in your program were to delete the MyWidget object before the user had dismissed the QInputDialog, then after QInputDialog::getText() returned, the program would be executing from within the mousePressEvent() method of a deleted MyWidget object, which is a situation that is just begging to cause undefined behavior)
In any case, my recommended fix is to avoid the static/synchronous getText() call and use signals instead, something like this:
#include <QWidget>
#include <QInputDialog>
#include <QMouseEvent>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget() : dialog(NULL) {}
~MyWidget() {delete dialog;}
protected:
void mousePressEvent(QMouseEvent *event)
{
event->accept();
if (dialog)
{
dialog->raise();
dialog->setFocus();
}
else
{
dialog = new QInputDialog;
connect(dialog, SIGNAL(finished(int)), this, SLOT(dialogDismissed()));
dialog->show();
}
}
private slots:
void dialogDismissed()
{
if (dialog)
{
int result = dialog->result();
QString t = dialog->textValue();
printf("Dialog finished, result was %i, user entered text [%s]\n", result, t.toUtf8().constData());
dialog->deleteLater(); // can't just call delete because (dialog) is what is calling this function (via a signal)!
dialog = NULL;
}
}
private:
QInputDialog * dialog;
};

Related

How to combine a QLineEdit with a QPushButton having no space in between?

I need to make an element like this:
It's a combination of a line edit and a button with the same height and no space between them.
I have tried in Qt Designer but the height of them is not the same and there is always a small space between these 2 elements.
How can I solve this problem?
Maybe you could customize a QComboBox, something like this:
#include <QComboBox>
#include <QLineEdit>
class CustomBox : public QComboBox
{
Q_OBJECT
public:
CustomBox(QWidget * parent = nullptr) : QComboBox(parent)
{
setEditable(true);
QString ss =
"QComboBox::drop-down {border: none;}"
"QComboBox::down-arrow { image: url(/data/whatever.png); }"
"QComboBox::down-arrow:pressed { image: url(/data/whatever-pressed.png); }";
setStyleSheet(ss);
connect(lineEdit(), &QLineEdit::editingFinished, [this](){
QString t = lineEdit()->text();
clear();
lineEdit()->setText(t);
});
}
void setText(const QString & t) { lineEdit()->setText(t); }
QString text() const { return lineEdit()->text(); }
protected:
void showPopup() override
{
emit buttonClicked();
}
signals:
void buttonClicked();
};
The combo box is set as editable, so you can use an underlying QLineEdit, which is, more or less, under full control through the protected lineEdit member function. As you can see, I connected its editingFinished signal to a lambda to avoid adding items to the combo each time (this happens when the user press Enter in an editable combo box).
The styling is quite simple, given that you have a couple of icons for the button.
I exposed the line edit text getter/setter, and added a signal for the button click, which gets emitted from the showPopup protected method (called when the user press the button).
Just an idea.

How to recognize QMouseEvent inside child widgets?

EDIT and Some Self Critisicm
I tried both given solutions, which both solved my problem, and thus I thank you both! I marked the transparent solution as accepted because I thought it is was the easiest implementation when I only had one child widget, but I wish to share some insight for other beginners:
I first used QLabel, which apperently has enabled Qt::WA_TransparentForMouseEvents by default and thus obviously worked, but I also wanted the text to be selectable, by using QPlainTextEdit instead. Laughably, this is not possible in any way, because if you try to select the text (by clicking) you will close the window! I ended up keeping the transparancy, and neglecting the select-text-feature.
I'm guessing my following question has been answered somewhere before, but after an hour of searching I now post the question myself. I'm grateful if someone can point me to an already answered question that solves my problem.
Anyhow, I'm creating a popup window, using C++ and Qt. I've created the following PopupDialog class which works well and fine for all its purposes. However, I've removed its frame (including the bar containing the close button and window title) to make it look minimalistic, and now I want it to close if the user presses/releases the mouse button anywhere inside the popup window (dialog).
The below code works, however in such a way that I have to click and release the mouse exactly at the QDialog-window itself. It will not close when i click if the mouse hovers over the child widget(s) inside the QDialog, e.g. a QPlainTextEdit, which is displaying text.
Hence, I'm in need of a solution for QDialog to recognize QMouseEvents inside its child widgets. Please, don't hesitate to ask if something is unclear. I have not included my mainwindow.h/.cpp files, or popupdialog.ui file since I believe it would be a little too much to post here, but the .ui extremely simple: Just the QDialog window holding a QBoxLayout, containing a single widget, a QPlainTextEdit. I may posts these on request if it helps.
// popupdialog.h
#ifndef POPUPDIALOG_H
#define POPUPDIALOG_H
#include <QDialog>
#include <QString>
namespace Ui {class PopupDialog;}
class PopupDialog : public QDialog
{
Q_OBJECT
public:
explicit PopupDialog(QWidget *parent = 0, QString msgTxt="");
~PopupDialog();
private:
Ui::PopupDialog *ui;
QString messageText;
void mouseReleaseEvent(QMouseEvent*);
};
#endif //POPUPDIALOG_H
...
// popupdialog.cpp
#include "popupdialog.h"
#include "ui_popupdialog.h"
PopupDialog::PopupDialog(QWidget *parent, QString msgTxt) :
QDialog(parent),
ui(new Ui::PopupDialog),
messageText(msgTxt)
{
ui->setupUi(this);
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
setModal(true);
ui->message_text_display->setText(messageText);
// The message_text_display is an instance of the class,
// "PlainTextEdit". Using "QLabel" partly solves my
// problem, but does not allow text selection.
}
PopupDialog::~PopupDialog()
{
delete ui;
}
void PopupDialog::mouseReleaseEvent(QMouseEvent *e)
{
this->close();
}
As you already noticed mouse events are handled from child widgets and propagated to parents if not accepted. You can read more about it here
To close your popup window when the click is done inside a child widget you can do two things. You could try looking into installEventFilter and set it up on each child widget to call close().
Another option would require you to have a kind of centralWidget (like the MainWindow usually has) - just to group all your child widgets. This way you could call setAttribute() on it to set Qt::WA_TransparentForMouseEvents property to simply skip handling mouse events on the widget and all of its children.
groupWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
According to Qt docs:
When enabled, this attribute disables the delivery of mouse events to
the widget and its children. Mouse events are delivered to other
widgets as if the widget and its children were not present in the
widget hierarchy; mouse clicks and other events effectively "pass
through" them. This attribute is disabled by default.
Which basically means the event would be passed up the chain to the first widget which can handle the event. In your case it would be the PopupDialog and the already overriden mouseReleaseEvent slot.
in header file
class PopupDialog : public QDialog
{
Q_OBJECT
public:
explicit PopupDialog(QWidget *parent = 0, QString msgTxt="");
~PopupDialog();
//////////////////////////////////
protected:
bool eventFilter(QObject *obj, QEvent *event);
//////////////////////////////////////
private:
Ui::PopupDialog *ui;
QString messageText;
void mouseReleaseEvent(QMouseEvent*);
};
in cpp
PopupDialog::PopupDialog(QWidget *parent, QString msgTxt) :
QDialog(parent),
ui(new Ui::PopupDialog),
messageText(msgTxt)
{
ui->setupUi(this);
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
setModal(true);
ui->message_text_display->setText(messageText);
// The message_text_display is an instance of the class,
// "PlainTextEdit". Using "QLabel" partly solves my
// problem, but does not allow text selection.
///////////////////////////////////////
foreach (QObject *child, children())
{
child->installEventFilter(this);
}
///////////////////////////////////////
}
///////////////////////////////////////
bool PopupDialog::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonRelease)
{
this->close();
}
}

Qt mouse events not working in QGraphicsScene

I am using Qt 5.7 (the latest version). I can't get the mouse events to work in QGraphicsScene, but they work in window outside of my scene. I have followed this question.
So I have overwritten QWidget::mouseMoveEvent() in my main widget's subclass like this:
// header:
class MyWidget {
...
void mouseMoveEvent( QMouseEvent * event );
};
// source:
MyWidget::MyWidget() {
setMouseTracking();
}
void MyWidget::mouseMoveEvent( QMouseEvent * event ) {
}
It doesn't work for: mouseMoveEvent, mouseGrabber, mousePressEvent, mouseReleaseEvent, or mouseDoubleClickEvent. But somehow it only works for mousePressEvent.
Could this be a bug in Qt?
SOURCE CODE:
In objectloader.cpp
ObjectLoader::ObjectLoader(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ObjectLoader)
{
ui->setupUi(this);
scene=new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
ui->graphicsView->setMouseTracking(true);
setMouseTracking(true);
}
Thats were i set mouse tracking twice
In objectloader.h
Then i define that method in objectloader.h
class ObjectLoader : public QMainWindow
{
Q_OBJECT
public:
explicit ObjectLoader(QWidget *parent = 0);
~ObjectLoader();
private slots:
void mouseMoveEvent(QMouseEvent *event);
protected:
private:
};
#endif // OBJECTLOADER_H
And implementation of that method in objectloader.cpp
void ObjectLoader::mouseMoveEvent(QMouseEvent *event){
qDebug()<<"Mouse moved";
}
When a mouse event is generated by Qt it is generally passed initially to the QWidget that was under the mouse pointer when the event was generated. If that QWidget accepts the event then no further processing will take place. If the event isn't accepted then Qt may propogate the event to that QWidget's parent and so on.
In your particular case the mouse move events you are interested in are being sent to the QGraphicsView/QGraphicsScene conponents where they are being accepted and, hence, no further processing takes place. In a case like that you generally need to install an event filter to intercept and process the events of interest.
Mouse move events will occur only when a mouse button is pressed down, unless mouse tracking has been enabled with QWidget::setMouseTracking().
So, I think you should check whether mouseTracking is really enabled or not, by using `bool hasMouseTracking() const'.

Keep QDialog open when parent is minimized?

I have a QMainWindow opening a QDialog (not modal). When I minimize the main window, the dialog is closed as well. Can I somehow keep it open? (the other way round as in Keep QMainWindow minimized when QDialogs show() ).
One thing I have tried is to ignore the event, but to my surprise I never see such a state. Actually I only see ActivationChange (0n99) there.
void CMyDialog::changeEvent(QEvent *evt)
{
QEvent::Type t = evt->type();
if (t == QEvent::WindowStateChange)
{
evt->ignore();
hide();
}
else
{
QDialog::changeEvent(evt);
}
}
Question in Qt center dealing with a similar topic:
http://www.qtcentre.org/threads/24765-Intercept-minimize-window-event
Here I create it as member:
QScopedPointer<MyDialog> m_navigator{new MyDialog(this)}; // this here is the main application window
It is displayed by a public slot:
void MyDialog::toogleNavigator()
{
this->setVisible(!this->isVisible());
}
and is a QDialog derived class:
class MyDialog : public QDialog { ...
---- Edit 2 ------
First Wouter has mentioned it , then Alexander. It is like you guys say, if I pass no parent (as in Alexander`s minimal example), the dialog stays open - with parent it is minimized along with its parent. My apologizes Wouter.
However, in my case it does not work like that. So I did not turn Wouter`s comment without checking or in bad intention. Now it is my duty to find out why. I suspect some utility classes to alter the dialog. I will report back here when I have found the root cause.
Ok, it is the windows flags. If the dialog is a tool window, it is always minimized, as normal window it depends on the parent.
Try to create MyDialog without this(MainApplication) like parent
and may be play with a second parameter of the constructor.
new MyDialog(0/*, ?*/);
Addition It is working code
MainWindow.cpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QScopedPointer<Dialog> dialog;
};
MainWindow.hpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
dialog(new Dialog(0))
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
dialog->setVisible(!dialog->isVisible());
}

Qt : catched mouseMoseEvent dont interact with QWebView html page element

I catch the mouseMoveEvent of my QWebView for restarting a timer of a screensaver. The problem is that now the mouseMoveEvent isnt distribued to the HTML elements so its impossible for example to move a sliding bar on the page.
I use connect to bind mouseMoveEvent to the restart slot :
QObject::connect(_view, SIGNAL(mouseMoveEvent(QMouseEvent*)), _mediaPlayer, SLOT(stop()));
WebView class :
class WebView : public QWebView
{
Q_OBJECT
public:
WebView(QString menu) : _menuDesc(menu) {};
WebView(){};
void setMenuDesc(QString menu) {_menuDesc = menu;};
QString getMenuDesc() {return _menuDesc;};
void setCurrPage(QString page) {_currPage = page;};
QString getCurrPage() {return _currPage;};
void setCurrCategory(QString page) {_currPage = page;};
QString getCurrCategory() {return _currPage;};
void mouseMoveEvent(QMouseEvent *)
{
emit mouseMoved();
};
signals :
void mouseMoved();
private:
QString _menuDesc = 0;
QString _currPage;
QString _currCategory = 0;
};
Is there a solution to still catch the signal and pass it to the HTML page?
mouseMoveEvent is not a signal but an event handler. You can reimplement this event handler and let it emit a signal you can connect to if you need that.
Like this:
MyWebView::mouseMoveEvent(QMouseEvent * e) {
emit mouseMoved(); // this would be signal you could connect to.
}
Seems you misunderstand event handler and signal usages.
mouseMoveEvent is a member method of QWidget, is not a signal so you cannot connect to it. You can override it in you subclass and emit your own signal.
And if a QWidget's mouse tracking is switched off, mouse move events only occur if a mouse button is pressed while the mouse is being moved. Maybe you need to call setMouseTracking too.