QMessageBox result as a buttonrole - c++

I wanna get result of QMessagebox as a buttonrole. but result always 16384 or 65536
I dont wantto use standartresults, want to use only buttonrole kind of buttons. what am I doing wrong here ?
(Im very newbie in QT)
void MainWindow::on_pushButton_clicked()
{
QMessageBox msgBox;
QPushButton *a=msgBox.addButton("OK",QMessageBox::ActionRole);
QPushButton *b=msgBox.addButton("CANCEL",QMessageBox::RejectRole);
int result=msgBox.question(this,"Hola","My 1st Msg");
//result always return 16384 or 65536(integer) PROBLEM HERE
if(result==QMessageBox::RejectRole)
this->setWindowTitle("rejected");
else
this->setWindowTitle("accepted");
}

The question method is static. It doesn't use the message box you have above. The first 3 lines of your method essentially don't do anything.
Here's what your method really does:
void MainWindow::on_pushButton_clicked()
{
int result = QMessageBox::question(this,"Hola","My 1st Msg");
[...]
}
Alas, the QMessageBox has a long-standing bug: it ignores (!) the custom button roles when it comes to acceptance or rejection of the dialog. While the role is passed to the underlying QDialogButtonBox, it is not interpreted correctly when the button is clicked.
Although you can get the role back using QMessageBox::buttonRole, the QMessageBoxPrivate::_q_buttonClicked invokes QDialog::done with the index of the button.
Thus, the first button you add will cause the dialog to be rejected, the second one will cause it to be accepted, and further buttons will cause neither. The acceptance/rejection ignores the role completely and is only based on the index of the button, due to the order it was added in.
Thus you should not use the rejected/accepted signals, unless the first two buttons map directly to these roles in this order, and should use the buttonClicked signal and obtain the role of the button directly:
void MainWindow::on_pushButton_clicked()
{
auto box = new QMessageBox{this};
box->setAttribute(Qt::WA_DeleteOnClose);
box->addButton("OK", QMessageBox::ActionRole);
box->addButton("CANCEL", QMessageBox::RejectRole);
box->setIcon(QMessageBox::Question);
box->setWindowTitle("Hola");
box->setText("My 1st message.");
box->show();
connect(box, &QMessageBox::buttonClicked, [=](QAbstractButton *button){
switch (box->buttonRole(button)) {
case QMessageBox::AcceptRole: return setWindowTitle("accept-role");
case QMessageBox::ActionRole: return setWindowTitle("action-role");
case QMessageBox::RejectRole: return setWindowTitle("reject-role");
}
});
}
Alas, there's another problem: the dialog will be also rejected by closing it via the platform's window manager (the close button on the dialog's title bar). So you need to be able to use the rejected signal, but not when it's in error. It's best to factor this functionality out to a MessageBoxAdapter class that will only emit correct accepted and rejected signals:
// https://github.com/KubaO/stackoverflown/tree/master/questions/messagebox-roles-40753898
#include <QtWidgets>
class MessageBoxAdapter : public QObject {
Q_OBJECT
public:
MessageBoxAdapter(QObject *parent = nullptr) : QObject(parent) {
watch(parent);
}
void watch(QObject *obj) {
auto box = qobject_cast<QMessageBox*>(obj);
if (!box) return;
connect(box, &QMessageBox::rejected, [=]{
if (!box->clickedButton()) emit rejected();
});
connect(box, &QMessageBox::buttonClicked, [=](QAbstractButton *button){
auto role = box->buttonRole(button);
if (role == QMessageBox::AcceptRole) emit accepted();
else if (role == QMessageBox::RejectRole) emit rejected();
emit roleClicked(role);
});
}
Q_SIGNAL void accepted();
Q_SIGNAL void rejected();
Q_SIGNAL void roleClicked(QMessageBox::ButtonRole role);
};
And some user interface to try it out:
struct Ui : public QWidget {
QVBoxLayout layout{this};
QTextBrowser browser;
QPushButton button{"Open"};
MessageBoxAdapter adapter{this};
public:
Ui() {
layout.addWidget(&browser);
layout.addWidget(&button);
connect(&button, &QPushButton::clicked, this, &Ui::onClicked);
connect(&adapter, &MessageBoxAdapter::accepted, [=]{ browser.append("accepted"); });
connect(&adapter, &MessageBoxAdapter::rejected, [=]{ browser.append("rejected"); });
connect(&adapter, &MessageBoxAdapter::roleClicked, [=](QMessageBox::ButtonRole role){
browser.append(QStringLiteral("clicked role=%1").arg(role));
});
}
void onClicked() {
auto box = new QMessageBox{this};
adapter.watch(box);
box->setAttribute(Qt::WA_DeleteOnClose);
box->addButton("OK", QMessageBox::AcceptRole);
box->addButton("ACTION", QMessageBox::ActionRole);
box->addButton("CANCEL", QMessageBox::RejectRole);
box->setIcon(QMessageBox::Question);
box->setWindowTitle("Hola");
box->setText("My 1st message.");
box->show();
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Ui ui;
ui.show();
return app.exec();
}
#include "main.moc"

Related

Modal QProgressDialog updated from worker thread

I want to update a modal QProgressDialog from my worker thread. However, if I set the dialog to be modal, my application crashes (and the dialog did not show any progress). If I do not, everything goes fine (but the user can tinker around with the rest of the program, which may cause issues).
What am I doing wrong?
Minimum code sample follows:
filereader qfr;
QProgressDialog progress("Importing file.", "Cancel", 0, file_size);
connect(&qfr, &filereader::signalProgress, &progress, &QProgressDialog::setValue, Qt::QueuedConnection);
QThread worker_thread;
std::atomic<bool> success = false;
connect(&worker_thread, &QThread::started,
[&]() {
success = qfr.read_file(/* parameters */);
worker_thread.quit();});
worker_thread.start();
//progress.setWindowModality(Qt::WindowModal); // Works only fine when this line is commented !!
while (worker_thread.isRunning()) {
QApplication::processEvents();
QThread::sleep(0);
}
progress.close();
Your thread is pretty much pointless. It serves no real purpose. You could have as well just called QApplication::processEvents in your read_file method. But you shouldn't, calling processEvents is bad practice.
What you should do is remove that while loop, and make your progress dialog a member of your class. I don't really like how that lambda looks either. I would personally just use filereader::read_file as a slot.
Note that Qt::windowModal blocks input to the parent window. Your progress dialog has no parent. So you would either have to call progress->setModal(true), or progress.setWindowModality(Qt::ApplicationModal);. Or set a parent to it.
Here is a small example (it is not tailor made for your application, but it should point you in the right direction):
#include <QtWidgets>
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject *parent = nullptr) : QObject(parent){}
public slots:
void simulateLongProcess()
{
for(int i = 0; i < 101; i++)
{
emit progressChanged(i);
QThread::msleep(100);
}
emit finishedWorking(true);
}
signals:
void progressChanged(int progress);
void finishedWorking(bool result);
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
setLayout(new QHBoxLayout);
progress_dialog.setModal(true);
progress_dialog.setAutoReset(false);
progress_dialog.setCancelButton(nullptr);
QThread *thread = new QThread(this);
connect(thread, &QThread::started, &worker, &Worker::simulateLongProcess);
connect(&worker, &Worker::finishedWorking, thread, &QThread::quit);
connect(&worker, &Worker::progressChanged, &progress_dialog, &QProgressDialog::setValue);
connect(&worker, &Worker::finishedWorking, &progress_dialog, &QProgressDialog::close);
connect(&worker, &Worker::finishedWorking, this, &Widget::handleResult);
QPushButton * start_button = new QPushButton("START");
connect(start_button, &QPushButton::clicked, this, [=]
{
progress_dialog.show();
thread->start();
});
layout()->addWidget(start_button);
resize(400, 300);
}
public slots:
void handleResult(bool result)
{
// do something with the result
}
private:
QProgressDialog progress_dialog;
Worker worker;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"

Qt Auto-UI Testing stopping because of messagebox. How to simulate enter on messagebox?

My task is to write an automated UI test for a software being developed. It happens so, that there are radiobuttons triggering a messagebox, which stops my automated test until I manually confirm it (pressing ENTER). The issue is that I do not know how I can call that newly summoned messagebox and then have it confirmed by QTest::keyClick(<object>, QtKey::Key_Enter); and have my automated test continue the run.
I am using QWidgets, QApplication, Q_OBJECT and QtTest.
I will provide a code similar to what I am working with:
void testui1(){
Form1* myform = new Form1();
myform->show();
QTest::mouseClick(myform->radioButton01, Qt::LeftButton, Qt::NoModifier, QPoint(), 100);
// Message box pops up and stops the operation until confirmed
QTest::mouseClick(myform->radioButton02, Qt::LeftButton, Qt::NoModifier, QPoint(), 100);
// again
...
}
How exactly can I script to confirm the message box automatically? The message box is only an [OK] type, so I don't need it to return whether I have pressed Yes or No. A QTest::keyClick(<target object>, Qt::Key_Enter) method needs to know to which object it should press enter to. I tried including myform into the object and it did not work. Googling I did not find the answer. I found the following result as not functioning for what I am looking for
QWidgetList allToplevelWidgets = QApplication::topLevelWidgets();
foreach (QWidget *w, allToplevelWidgets) {
if (w->inherits("QMessageBox")) {
QMessageBox *mb = qobject_cast<QMessageBox *>(w);
QTest::keyClick(mb, Qt::Key_Enter);
}
}
The problem is that once you've "clicked" on your radio button, which results in QMessageBox::exec being called, your code stops running until the user clicks on of the buttons.
You can simulate the user clicking a button by starting a timer before you click on the radio button.
In the callback function for the timer you can use QApplication::activeModalWidget() to obtain a pointer to the message box, and then just call close on it.
QTimer::singleShot(0, [=]()
{
QWidget* widget = QApplication::activeModalWidget();
if (widget)
widget->close();
});
If you want to press a specific key on the message box, you can use QCoreApplication::postEvent to post a key event to the message box
QTimer::singleShot(0, [=]()
{
int key = Qt::Key_Enter; // or whatever key you want
QWidget* widget = QApplication::activeModalWidget();
if (widget)
{
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
QCoreApplication::postEvent(widget, event);
}
});
So in terms of how this ties into the sample code you gave:
void testui1()
{
Form1* myform = new Form1();
myform->show();
QTimer::singleShot(0, [=]()
{
QWidget* widget = QApplication::activeModalWidget();
if (widget)
widget->close();
else
QFAIL("no modal widget");
});
QTest::mouseClick(myform->radioButton01, Qt::LeftButton, Qt::NoModifier, QPoint(), 100);
}
A sample app putting all the above together:
#include <QApplication>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QPushButton>
#include <QTimer>
#include <QMessageBox>
#include <iostream>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QMainWindow window;
QWidget widget;
window.setCentralWidget(&widget);
QHBoxLayout layout(&widget);
QPushButton btn("go");
layout.addWidget(&btn);
QObject::connect(&btn, &QPushButton::clicked, [&]()
{
QTimer::singleShot(0, [=]()
{
QWidget* widget = QApplication::activeModalWidget();
if (widget)
{
widget->close();
}
else
{
std::cout << "no active modal\n";
}
});
QMessageBox(
QMessageBox::Icon::Warning,
"Are you sure?",
"Are you sure?",
QMessageBox::Yes | QMessageBox::No,
&window).exec();
});
window.show();
return app.exec();
}
Clicking on Go will look like nothing is happening, as the QMessageBox is closed immediately.
To prove that the message box is shown and then closed you can increase the time in the call to QTimer::singleShot to a value such as 1000. Now it will show the message box for 1 second, and then it will be closed by the timer.

Qt is it possible to define a function pointer in place of a signal?

I have a lot of signals which all have the same parameters but perform different functions.
The connect and disconnect code for all the signals will be the same, as is the slot handler that the signals connect to.
Instead of writing this code over and over. I would like to use a function pointer or something similar to assign to the signal, then have a common code block which performs the connection or disconnection.
The following code is just to illustrate what I am describing, it isn't valid and will not compile.
void (*pfnSignal)(quint8, QString);
switch( eSigID ) {
case SIGNAL_A:
pfnSignal = signalA;
break;
case SIGNAL_B:
pfnSignal = signalB;
break;
default:
pfnSignal = NULL;
}
if ( pfnSignal != NULL ) {
QObject::connect(pobjRef, pfnSignal, this, SLOT(handler(quint8, QString)));
}
In Qt5, this can be done easily, as it allows connecting using a new pointer to member function syntax.
// Using decltype to avoid figuring out the ugly pointer-to-member-function syntax.
// Assumes all signals have the same arguments.
decltype<&ThatClass::someSignal> pfnSignal = nullptr;
switch( eSigID ) {
case SIGNAL_A:
pfnSignal = &ThatClass::signalA;
break;
case SIGNAL_B:
pfnSignal = &ThatClass::signalB;
break;
}
if (pfnSignal) {
connect(pobjRef, pfnSignal, this, &ThisClass::handler);
}
But actually, this is even possible with Qt4, as the SIGNAL macro is of type const char*.
const char *pfnSignal = nullptr;
switch( eSigID ) {
case SIGNAL_A:
pfnSignal = SIGNAL(signalA(quint8, QString));
break;
case SIGNAL_B:
pfnSignal = SIGNAL(signalB(quint8, QString));
break;
}
if (pfnSignal) {
QObject::connect(pobjRef, pfnSignal, this, SLOT(handler(quint8, QString)));
}
C++11 allows you to write very concise Qt code.
Leverage range-based for loops to iterate over pointers. Those can be pointers to widgets, pointers to methods, etc:
for (auto signal : {&Class::signal1, &Class:signal2})
QObject::connect(sender, signal, receiver, slot);
Leverage lambda expressions to capture constant argument values:
auto const cMySlot = [&](void (Sender::*signal)(int)){
QObject::connect(sender, signal, receiver, slot);
Then:
for (auto signal : {&Class::signal1, &Class:signal2}) cMySlot(signal);
Full example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/signals-simpler-43631464
#include <QtWidgets>
#include <initializer_list>
class Receiver : public QLabel {
Q_OBJECT
public:
Receiver(QWidget * parent = {}) : QLabel{parent} {}
Q_SLOT void intSlot(int val) {
setText(QStringLiteral("int = %1").arg(val));
}
};
class Sender : public QWidget {
Q_OBJECT
QFormLayout m_layout{this};
QPushButton btn1{"Send 1"}, btn2{"Send 5"}, btn3{"Send 10"};
public:
Sender(QWidget * parent = {}) : QWidget{parent} {
m_layout.setMargin(1);
for (auto w : {&btn1, &btn2, &btn3}) m_layout.addWidget(w);
auto const clicked = &QPushButton::clicked;
connect(&btn1, clicked, this, [this]{ emit signal1(1); });
connect(&btn2, clicked, this, [this]{ emit signal2(5); });
connect(&btn3, clicked, this, [this]{ emit signal3(10); });
}
Q_SIGNAL void signal1(int);
Q_SIGNAL void signal2(int);
Q_SIGNAL void signal3(int);
};
using Widgets = std::initializer_list<QWidget*>;
int main(int argc, char **argv)
{
QApplication app{argc, argv};
QWidget win;
QVBoxLayout layout{&win};
Sender sender;
Receiver receiver;
for (auto w : Widgets{&sender, &receiver}) layout.addWidget(w);
// Factor out connection
auto const cIntSlot = [&](void (Sender::*signal)(int)){
QObject::connect(&sender, signal, &receiver, &Receiver::intSlot);
};
// Factor out connection on a list
for (auto signal : {&Sender::signal1, &Sender::signal2, &Sender::signal3})
cIntSlot(signal);
win.show();
return app.exec();
}
#include "main.moc"
Actually, Thomas McGuire was faster than me. (Damn.) Though, I want to add this answer because:
It provides a complete sample.
It uses functors instead of object/member function pointers for signal handlers.
Thus, it may be and add-on to the answer of Thomas McGuire.
Before Qt 5 the signal was described by a char* which should be very simple to handle. Therefore, I assume your question is concerning the new API since Qt 5.
This should work as well if you use the correct method pointer type. I did this for QPushButton and QCheckBox for demonstration because both are derived from QAbstractButton which in turn has two signals with equal signature. IMHO equal signature of signals is mandatory for your solution.
#include <QtWidgets>
enum SigType { None, Click, Toggle };
template <typename FUNCTOR>
void installSignalHandler(
QAbstractButton *pQBtn,
SigType sigType,
FUNCTOR sigSlot)
{
void (QAbstractButton::*pSignal)(bool) = nullptr;
switch (sigType) {
case Click: pSignal = &QAbstractButton::clicked; break;
case Toggle: pSignal = &QAbstractButton::toggled; break;
}
if (pSignal) QObject::connect(pQBtn, pSignal, sigSlot);
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
// main application
QApplication app(argc, argv);
// setup GUI
QWidget qWin;
QVBoxLayout qVBox(&qWin);
QPushButton qBtn1("Button 1 -> Click");
qVBox.addWidget(&qBtn1);
QPushButton qBtn2("Button 2 -> Toggle");
qVBox.addWidget(&qBtn2);
QPushButton qBtn3("Button 3 -> None");
qVBox.addWidget(&qBtn3);
QCheckBox qTgl1("Toggle 1 -> Click");
qVBox.addWidget(&qTgl1);
QCheckBox qTgl2("Toggle 2 -> Toggle");
qVBox.addWidget(&qTgl2);
QCheckBox qTgl3("Toggle 3 -> None");
qVBox.addWidget(&qTgl3);
qWin.show();
// install signal handlers
installSignalHandler(&qBtn1, Click,
[](bool) { qDebug() << "Button 1 received clicked."; });
installSignalHandler(&qBtn2, Toggle,
[](bool) { qDebug() << "Button 2 received toggled."; });
installSignalHandler(&qBtn3, None, // will be actually never called
[](bool) { qDebug() << "Button 3 received none."; });
installSignalHandler(&qTgl1, Click,
[](bool) { qDebug() << "CheckBox 1 received clicked."; });
installSignalHandler(&qTgl2, Toggle,
[](bool) { qDebug() << "CheckBox 2 received toggled."; });
installSignalHandler(&qTgl2, None, // will be actually never called
[](bool) { qDebug() << "CheckBox 3 received none."; });
// run-time loop
return app.exec();
}
Compiled and tested with VisualStudio & Qt 5.6 on Windows 10 (64 bit):

My Qt eventFilter() doesn't stop events as it should

Something is fundamentally wrong with my eventFilter, as it lets every single event through, while I want to stop everything. I've read lots of documentation on QEvent, eventFilter() and so on, but obviously I'm missing something big. Essentially, I'm trying to create my own modal-functionality for my popup-window class based on QDialog. I want to implement my own since the built-in setModal(true) includes a lot of features, e.g. playing QApplication::Beep(), that I want to exclude. Basically, I want to discard all events going to the QWidget (window) that created my popup. What I have so far is,
// 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 *window=0, QString messageText="");
~PopupDialog();
private:
Ui::PopupDialog *ui;
QString messageText;
QWidget window; // the window that caused/created the popup
void mouseReleaseEvent(QMouseEvent*); // popup closes when clicked on
bool eventFilter(QObject *, QEvent*);
};
...
// popupdialog.cpp
#include "popupdialog.h"
#include "ui_popupdialog.h"
PopupDialog::PopupDialog(QWidget *window, QString messageText) :
QDialog(NULL), // parentless
ui(new Ui::PopupDialog),
messageText(messageText),
window(window)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true); // Prevents memory leak
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
ui->message_text_display->setText(messageText);
window->installEventFilter(this);
//this->installEventFilter(window); // tried this also, just to be sure ..
}
PopupDialog::~PopupDialog()
{
window->removeEventFilter(this);
delete ui;
}
// popup closes when clicked on
void PopupDialog::mouseReleaseEvent(QMouseEvent *e)
{
close();
}
Here's the problem, the filter doesn't work. Note that if I write a std::cout
inside the if(...), I see that it does trigger whenever events are sent to window, it just doesn't stop them.
bool PopupDialog::eventFilter(QObject *obj, QEvent *e)
{
if( obj == window )
return true; //should discard the signal (?)
else
return false; // I tried setting this to 'true' also without success
}
When the user interacts with the main program, a PopupDialog can be created like this:
PopupDialog *popup_msg = new PopupDialog(ptr_to_source_window, "some text message");
popup_msg->show();
// I understand that naming the source 'window' might be a little confusing.
// I apologise for that. The source can in fact be any 'QWidget'.
Everything else works as expected. Only the event filter fails. I want the filter to remove events sent to the window that created the popup; like mouse clicking and key pressing, until the popup is closed. I'm expecting to be extremely embarrassed when someone points out a trivial fix in my code.
You have to ignore all events that arrive in the widget tree of the window. Therefore, you need to install the eventFilter application-wide and check, if the object you are filtering on is a descendant of window. In other words: Replace
window->installEventFilter(this);
by
QCoreApplication::instance()->installEventFilter(this);
and implement the event filter function this way:
bool PopupDialog::eventFilter(QObject *obj, QEvent *e)
{
if ( !dynamic_cast<QInputEvent*>( event ) )
return false;
while ( obj != NULL )
{
if( obj == window )
return true;
obj = obj->parent();
}
return false;
}
I tried it, tested it and it worked for me.
Note: Using event filters in Qt is a bit messy in my experience, since it is not quite transparent what is happening. Expect bugs to pop up from time to time. You may consider disabling the main window instead, if you and your clients don't have a problem with the grayed-out main window as a consequence.
After the massive amount of responses, feedback, suggestions and time ivested in extensive research I've finally found what I believe to be the optimal, and safest solution. I wish to express my sincere gratidtude to everyone for their aid to what Kuba Ober describes as "(...) not as simple of a problem as you think".
We want to filter out all certain events from a widget, including its children. This is difficult, because events may be caught in the childrens default eventfilters and responded to, before they are caught and filtered by the the parent's custom filter for which the programmer implements. The following code solves this problem by installing the filter on all children upon their creation. This example assumes the use of Qt Creator UI-forms and is based on the following blog post: How to install eventfilters for all children.
// The widget class (based on QMainWindow, but could be anything) for
// which you want to install the event filter on, includings its children
class WidgetClassToBeFiltered : public QMainWindow
{
Q_OBJECT
public:
explicit WidgetClassToBeFiltered(QWidget *parent = 0);
~WidgetClassToBeFiltered();
private:
bool eventFilter(QObject*, QEvent*);
Ui::WidgetClassToBeFiltered *ui;
};
...
WidgetClassToBeFiltered::WidgetClassToBeFiltered(QWidget *parent) :
QMainWindow(parent), // Base Class constructor
ui(new Ui::WidgetClassToBeFiltered)
{
installEventFilter(this); // install filter BEFORE setupUI.
ui->setupUi(this);
}
...
bool WidgetClassToBeFiltered::eventFilter(QObject *obj, QEvent* e)
{
if( e->type() == QEvent::ChildAdded ) // install eventfilter on children
{
QChildEvent *ce = static_cast<QChildEvent*>(e);
ce->child()->installEventFilter(this);
}
else if( e->type() == QEvent::ChildRemoved ) // remove eventfilter from children
{
QChildEvent *ce = static_cast<QChildEvent*>(e);
ce->child()->removeEventFilter(this);
}
else if( (e->type() == QEvent::MouseButtonRelease) ) // e.g. filter out Mouse Buttons Relases
{
// do whatever ..
return true; // filter these events out
}
return QWidget::eventFilter( obj, e ); // apply default filter
}
Note that this works, because the eventfilter installs itself on added children! Hence, it should also work without the use of UI-forms.
Refer this code to filter out specific event:-
class MainWindow : public QMainWindow
{
public:
MainWindow();
protected:
bool eventFilter(QObject *obj, QEvent *ev);
private:
QTextEdit *textEdit;
};
MainWindow::MainWindow()
{
textEdit = new QTextEdit;
setCentralWidget(textEdit);
textEdit->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == textEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
qDebug() << "Ate key press" << keyEvent->key();
return true;
} else {
return false;
}
} else {
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
}
If you want to set more specific event filter on multiple widgets you can refer following code:
class KeyPressEater : public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
qDebug("Ate key press %d", keyEvent->key());
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
KeyPressEater *keyPressEater = new KeyPressEater(this);
QPushButton *pushButton = new QPushButton(this);
pushButton->installEventFilter(keyPressEater);

Qt Browser stack when dropping link to QMainWindow

I have simple drag and drop functions implemented in QmainWindow the reference taken from here and here
all i want to do is to accept valid url and open QDialog when the url dropped .
but when the url drooped and the QDialog poped up the browser is stocked in the background until i close the QDialog . this is wrong the browser should be free all the time.
here is my code :
void MainWindow::dragMoveEvent(QDragMoveEvent *event)
{
if (event->mimeData()->hasFormat("text/html"))
{
event->acceptProposedAction();
}
else
{
event->ignore();
}
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
// accept just text/uri-list mime format
if (event->mimeData()->hasFormat("text/html"))
{
event->acceptProposedAction();
}
else
{
event->ignore();
}
}
void MainWindow::dragLeaveEvent(QDragLeaveEvent *event)
{
event->accept();
}
void MainWindow::dropEvent(QDropEvent *event)
{
QList<QUrl> urlList;
QString fName;
QStringList pathList;
QFileInfo info;
QString suffix;
if (event->mimeData()->hasFormat("text/html"))
{
urlList = event->mimeData()->urls(); // returns list of QUrls
// if just text was dropped, urlList is empty (size == 0)
if ( urlList.size() > 0) // if at least one QUrl is present in list
{
QString url = urlList.at(0).toString();
event->acceptProposedAction();
openDialog(url); // THIS IS THE FUNCTION THAT I OPEN THE QDIALOG window
}
}
//event->acceptProposedAction();
}
void MainWindow::openDialog(QString& slink)
{
QHash<QString,QVariant> DataMap;
QString link = slink;
DataMap.insert("m_webpage",link);
PublishToDialog* pPublishToDialog = new PublishToDialog(this);
pPublishToDialog->Init(DataMap);
if(pPublishToDialog->exec() != QDialog::Accepted)
{
}
}
when i remove the call to the QDialog , so every thing is working fine .
and the browser doesn't stuck. i even tryed as suggested using signal/slot put again
when i start the QDialog when drop invoked the browser stucked!
What exactly is PublishToDialog? I would assume that it is a custom dialog implementation of yours that inherits QDialog. And given this line:
pPublishToDialog->exec() != QDialog::Accepted
This opens the dialog as a modal dialog. A modal dialog is blocking and will block the execution of the current thread until some action is performed on the dialog. Instead of using a modal dialog, you should use a non-modal dialog. Since I am still not sure if PublishToDialog inherits QDialog or what else, I am just going to assume it is. Here is what you could do:
PublishToDialog* pPublishToDialog = new PublishToDialog(this);
// Make it a non-modal dialog
pPublishDialog->setModal(false);
// Connect it to a slot to handle whenever the user performs some action on it
QObject::connect(pPublishDialog, SIGNAL(finished()), this, SLOT(handleDialogAction());
pPublishDialog.show();
You will have to implement handleDialogAction in your code. At the same time, you may want to make pPublishDialog a class-member as you will need it to access QDialog::reuslt in handleDialogAction.