How to handle keypress event for all child widgets, in Qt? - c++

I want to handle keypress event for all the child widgets, for which I am trying something like below:
Widget::Widget(QWidget *parent):QWidget(parent)
{
QGroupBox *gBox = new QGroupBox(this);
QPushButton *button1 = new QPushButton("1");
QPushButton *button2 = new QPushButton("2");
QVBoxLayout *vBox = new QVBoxLayout;
vBox->addWidget(button1);
vBox->addWidget(button2);
gBox->setLayout(vBox);
gBox->installEventFilter(this);
button1->installEventFilter(this);
button2->installEventFilter(this);
}
bool Widget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
if(obj == gBox)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_F1)
{
emit somesignal();
}
}
if(obj == button1)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_F1)
{
emit somesignal1();
}
}
if(obj == button2)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_F1)
{
emit somesignal2();
}
}
}
return QObject::eventFilter(obj, event);
}
But whwnever I press F1, only somesignal() is emitted. I want to emit somesignal1(), somesignal2() also, for button1 and button2.
Can somebody help me to achieve this?

You should implement a window-global QShortcut, and use qApp->focusWidget() to determine which widget you want help on. You should use the property framework to set the help URL for a widget:
const char kHelpUrl = "helpUrl";
void setHelpUrl(QWidget * w, const QUrl & url) {
w->setProperty(kHelpUrl, url);
}
QUrl getFocusedHelpUrl() {
auto w = qApp->focusWidget();
return w ? w->property(kHelpURL).value<QUrl>() : QUrl{};
}
void showHelp() {
auto url = getFocusedHelpUrl();
...
}
class MainWin : public QDialog {
...
QPushButton button1{"Hello"};
QPushButton button2{"GoodBye"};
public:
MainWin(QWidget * parent = nullptr) : QDialog{parent}
{
setHelpUrl(&button1, {"qthelp://button1"});
setHelpUrl(&button2, {"qthelp://button2"});
...
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
...
MainWindow mainWin;
QShortcut s{QKeySequence::HelpContents, &mainWin};
QObject::connect(&s, &QShortcut::activated, showHelp);
...
return app.exec();
}

Related

QWidget shown problems after QStackedLayout setCurrentIndex

I have a task which was to show a dialog beside the windows desk's edge. When the mouse move to the specific regions(some where in the windows desk), the dialog shows, while out it'll be hide.
I put some QWidget in the dialog with QStackedLayout used as layout,
and use QStackedLayout::setCurrentIndex to change the current widget before the dialog shown.
The problem is that the previous widget will stay a littel while when the dialog is shown, and then the new widget is shown. And what i want is to show the new widget immediately.
So why is that happen, and how can i fix it?
ConfigDialog::ConfigDialog(int role, QWidget *parent) :
QDialog(parent),
ui(new Ui::ConfigDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::SubWindow | Qt::WindowStaysOnTopHint);
// here is the code i commented out and the problem seems to be gone
// setAttribute(Qt::WA_TranslucentBackground);
buttonArea = new QRect();
canReShow = true;
init(role);
}
ConfigDialog::~ConfigDialog()
{
delete ui;
}
void ConfigDialog::setModel(int model)
{
int tmp = model;
QStackedLayout* lay = qobject_cast<QStackedLayout*>(layout());
if (lay->count() < tmp || tmp < 0)
{
tmp = 0;
}
lay->setCurrentIndex(tmp);
}
void ConfigDialog::init(int role)
{
QStackedLayout* stackLayout = new QStackedLayout(this);
stackLayout->setMargin(0);
stackLayout->setSpacing(0);
// 根据系统缩放比例,缩放窗口
qreal scale = ::GetDpiForSystem() / 96;
int iconWidth = 64 * scale;
int iconHeight = 64 * scale;
// system menu
QWidget* syscfg = new QWidget(this);
QHBoxLayout* lay = new QHBoxLayout(syscfg);
lay->setSpacing(0);
lay->setMargin(0);
lay->setAlignment(Qt::AlignCenter);
lay->addStretch();
QPushButton* btnSysConfig = new QPushButton(syscfg);
QIcon syscfgIcon;
syscfgIcon.addFile(":/icon/icon/console.png");
btnSysConfig->setIcon(syscfgIcon);
btnSysConfig->setIconSize(QSize(iconWidth, iconHeight));
btnSysConfig->setFlat(true);
connect(btnSysConfig, &QPushButton::clicked, [this]()
{
emit sysConfig();
accept();
});
lay->addWidget(btnSysConfig);
if (2 != role)
{
QPushButton* btnUserConfig = new QPushButton(syscfg);
QIcon usrcfgIcon;
usrcfgIcon.addFile(":/icon/icon/setting.png");
btnUserConfig->setIcon(usrcfgIcon);
btnUserConfig->setIconSize(QSize(iconWidth, iconHeight));
btnUserConfig->setFlat(true);
connect(btnUserConfig, &QPushButton::clicked, [this]()
{
emit userConfig();
accept();
});
lay->addWidget(btnUserConfig);
}
QPushButton* btnExitApp = new QPushButton(syscfg);
QIcon exitIcon;
exitIcon.addFile(":/icon/icon/exit.png");
btnExitApp->setIcon(exitIcon);
btnExitApp->setIconSize(QSize(iconWidth, iconHeight));
btnExitApp->setFlat(true);
connect(btnExitApp, &QPushButton::clicked, [this]()
{
emit exitSys();
// accept();
});
lay->addWidget(btnExitApp);
lay->addStretch();
stackLayout->addWidget(syscfg);
// end system menu
// app menu
QWidget* appcfg = new QWidget(this);
lay = new QHBoxLayout(appcfg);
lay->setSpacing(0);
lay->setMargin(0);
lay->addStretch();
QPushButton* btnResetWindow = new QPushButton(appcfg);
QIcon resetIcon;
resetIcon.addFile(":/icon/icon/reset.png");
btnResetWindow->setIcon(resetIcon);
btnResetWindow->setIconSize(QSize(iconWidth, iconHeight));
btnResetWindow->setFlat(true);
connect(btnResetWindow, &QPushButton::clicked, [this]()
{
emit resetWindow();
accept();
setModel();
});
lay->addWidget(btnResetWindow);
lay->addStretch();
stackLayout->addWidget(appcfg);
// end app menu
// web menu
QWidget* webcfg = new QWidget(this);
lay = new QHBoxLayout(webcfg);
lay->setSpacing(0);
lay->setMargin(0);
lay->addStretch();
// reset
btnResetWindow = new QPushButton(webcfg);
btnResetWindow->setIcon(resetIcon);
btnResetWindow->setIconSize(QSize(iconWidth, iconHeight));
btnResetWindow->setFlat(true);
connect(btnResetWindow, &QPushButton::clicked, [this]()
{
emit resetWindow();
accept();
setModel();
});
lay->addWidget(btnResetWindow);
// back
QPushButton* btnBackward = new QPushButton(webcfg);
QIcon backIcon;
backIcon.addFile(":/icon/icon/back.png");
btnBackward->setIcon(backIcon);
btnBackward->setIconSize(QSize(iconWidth, iconHeight));
btnBackward->setFlat(true);
connect(btnBackward, &QPushButton::clicked, [this]()
{
emit backWard();
accept();
});
lay->addWidget(btnBackward);
// forward
QPushButton* btnForward = new QPushButton(webcfg);
QIcon forwardIcon;
forwardIcon.addFile(":/icon/icon/front.png");
btnForward->setIcon(forwardIcon);
btnForward->setIconSize(QSize(iconWidth, iconHeight));
btnForward->setFlat(true);
connect(btnForward, &QPushButton::clicked, [this]()
{
emit forWard();
accept();
});
lay->addWidget(btnForward);
lay->addStretch();
stackLayout->addWidget(webcfg);
// end web menu
// show & hide test
showHideTest = new QTimer(this);
connect(showHideTest, &QTimer::timeout, [this]()
{
QScreen* screen = qApp->primaryScreen();
auto testArea = new QRect(0, 0, screen->size().width(), rect().height());
if (testArea->contains(cursor().pos()))
{
if (canReShow)
{
show();
}
}
else
{
canReShow = true;
hideTest->stop();
hoverTest->stop();
accept();
}
});
showHideTest->start(30);
// hover test
// 当处于非有效区域(对话框之外)5秒后,对话框隐藏
hoverTest = new QTimer(this);
connect(hoverTest, &QTimer::timeout, [this]()
{
qDebug() << buttonArea->left() << buttonArea->right() << cursor().pos();
if (buttonArea->contains(cursor().pos()))
{
canReShow = true;
hideTest->stop();
}
else
{
canReShow = false;
if (!hideTest->isActive())
{
hideTest->start(5000);
}
}
});
// hide when hover time out
hideTest = new QTimer(this);
connect(hideTest, &QTimer::timeout, [this]()
{
hideTest->stop();
hoverTest->stop();
accept();
});
}
bool ConfigDialog::event(QEvent *e)
{
if (e->type() == QEvent::Show)
{
QScreen* screen = qApp->primaryScreen();
QSize curSize = size();
QPoint point(screen->size().width() / 2 - curSize.width() / 2, 0);
move(point);
buttonArea->setTopLeft(point);
buttonArea->setSize(size());
// 启动悬停检测
hoverTest->start(30);
}
// if (e->type() == QEvent::Leave)
// {
// QScreen* screen = qApp->primaryScreen();
// auto testArea = new QRect(0, 0, screen->size().width(), rect().height());
// if (!testArea->contains(cursor().pos()))
// {
// hideTest->stop();
// hoverTest->stop();
// accept();
// }
// }
// qDebug() << "----------------------- config dialog event: " << e->type();
return QWidget::event(e);
}
void ConfigDialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(rect(), QColor(49, 52, 58, 255));
}

How to add a QMouseEvent to an EventFilter?

I added an EventFilter to a QCompleter because I had the same problem as this person.
Now I want to add a left mouse button event if an item is clicked from the PopupCompletion.
I tried but I have no idea if I'm on the right track because im new to Qt.
eventfilter.h
class EventFilter : public QObject
{
Q_OBJECT
public:
EventFilter(QLineEdit* lineEdit, QObject* parent = NULL)
:QObject(parent)
,mLineEdit(lineEdit)
{ }
virtual ~EventFilter()
{ }
bool eventFilter(QObject* watched, QEvent* event);
private:
QLineEdit* mLineEdit;
};
eventfilter.cpp
#include "eventfilter.h"
bool EventFilter::eventFilter(QObject* watched, QEvent* event)
{
QAbstractItemView* view = qobject_cast<QAbstractItemView*>(watched);
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Return ||
keyEvent->key() == Qt::Key_Enter ||
keyEvent->key() == Qt::Key_Tab)
{
mLineEdit->clear();
mLineEdit->setFocus();
view->hide();
return true;
}
}
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::LeftButton)
{
mLineEdit->clear();
mLineEdit->setFocus();
view->hide();
return true;
}
}
return false;
}
mainwindow.h
#include "eventfilter.h"
class QCompleter;
class QLabel;
class QLineEdit;
class QWidget;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
private:
void createMenu();
QAbstractItemModel *modelFromFile(const QString& fileName);
QCompleter *completer;
QLabel *contentsLabel;
QLineEdit *lineEdit;
QWidget *widgetBox;
};
mainwindow.cpp
#include "mainwindow.h"
completer = new QCompleter(this);
completer->setModel(modelFromFile(":/resources/wordlist.txt"));
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
lineEdit->setCompleter(completer);
EventFilter *filter = new EventFilter(lineEdit);
completer->popup()->installEventFilter(filter);

Qt QWidget hide animation

I have a sub-class of QWidget that is a popup widget. I would like to add some animation when it shows and disappears. So I re-implemented showEvent(QShowEvent * event) and hideEvent and added some QPropertyAnimation in the functions. The showEvent works just fine for me but the hideEvent doesn't. Because
Hide events are sent to widgets immediately after they have been hidden.
Any idea about how to do it?
Update:
I don't think it's the right reason. When I use Nejat's solution. The show part works. But when I click outside the widget. It disappears immediately.
You should override QWidget::closeEvent() so when trying to close immediatly it will be ignored AND we start our animation and after finishing (QPropertyAnimation::finished()) we close the widget as normal.
Here is a demo to demonstrate:
class AnimatedWidget : public QWidget {
Q_OBJECT
Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha)
public:
AnimatedWidget(QWidget* parent = nullptr) :QWidget{ parent }, opacityAnimation{ new QPropertyAnimation{this, "alpha",this} } {
setWindowFlags(windowFlags() | Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::Tool);
auto pal = palette();
pal.setColor(QPalette::Background, Qt::cyan);
setAutoFillBackground(true);
setPalette(pal);
setFixedSize(200, 200);
}
qreal alpha() const {
return windowOpacity();
}
void setAlpha(qreal level) {
setWindowOpacity(level);
update();
}
protected:
void closeEvent(QCloseEvent* e) override {
if (opacityAnimation->currentValue().toReal() == 1.0) { // Ignore event + start animation
e->ignore();
startHide();
QObject::connect(opacityAnimation, SIGNAL(finished()), this, SLOT(onAnimationCallBack()), Qt::UniqueConnection);
} else {
e->accept();
if (!isHidden())
hide();
QWidget::close(); // necessary actions
}
}
public Q_SLOTS:
void show() {
startShow();
QWidget::show(); // necessary actions
}
private Q_SLOTS:
void onAnimationCallBack() {
if (opacityAnimation->currentValue().toReal() == 0.0) { // we're finished so let's really close the widget
QCloseEvent ev;
QApplication::sendEvent(this, &ev);
qApp->sendEvent(this, &ev);
}
}
void startHide() {
opacityAnimation->setStartValue(1.0);
opacityAnimation->setEndValue(0.0);
opacityAnimation->setDuration(1500);
opacityAnimation->start();
}
void startShow() {
opacityAnimation->setStartValue(0.0);
opacityAnimation->setEndValue(1.0);
opacityAnimation->setDuration(1500);
opacityAnimation->start();
}
private:
QPropertyAnimation* opacityAnimation = nullptr;
};
class Base : public QWidget {
public:
Base(QWidget* parent = nullptr) :QWidget{ parent }, widget{ new AnimatedWidget{} } {
}
private:
AnimatedWidget* widget;
protected:
void mouseReleaseEvent(QMouseEvent* e) override {
if (widget->isHidden())
widget->show();
else
widget->close();
QWidget::mouseReleaseEvent(e);
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Base base;
base.show();
return app.exec();
}
You can override the eventFilter in your widget and check for QEvent::Show and QEvent::Close events.
bool MyWidget::eventFilter(QObject * obj, QEvent * event)
{
if(obj == this && event->type() == QEvent::Show)
{
//about to show
}
else if(obj == this && event->type() == QEvent::Close)
{
//about to close
}
return false;
}
You should also install the event filter in the constructor by:
this->installEventFilter(this);

How to make non-modal QDialog not cancellable by pressing Esc key?

Can I prevent non-modal QDialog from closing when the user hits Esc, other than by overriding reject() or using event filter / event method?
If not, is it possible with a modal QDialog?
If you don't want to use eventFilter, you can use QShortcut to create a keyboard shortcut for Escape and call the dialog's show slot. This can be done like:
MyDialog::MyDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::MyDialog)
{
ui->setupUi(this);
QShortcut * shortcut = new QShortcut(QKeySequence(Qt::Key_Escape),this,SLOT(show()));
shortcut->setAutoRepeat(false);
}
You can override eventFilter in your dialog and manage it on your own when Escape is pressed :
MyDialog::MyDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::MyDialog)
{
ui->setupUi(this);
this->installEventFilter(this);
}
bool MyDialog::eventFilter(QObject *obj, QEvent * event)
{
if((MyDialog *)obj == this && event->type()==QEvent::KeyPress && ((QKeyEvent*)event)->key() == Qt::Key_Escape )
{
return true;
}
return false;
}
Create a QObject-based class to receive the events: -
class MyEvents : public QObject
{
Q_OBJECT
public:
MyEvents(QObject* parent);
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_Escape)
return true;
else
return QObject::eventFilter(obj, event);
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
};
Then install the object as an event filter for the dialog
MyEvents* myEvents = new MyEvents;
QDialog myDialog(parent);
myDialog->installEventFilter(myEvents);

Qt: the signal handler is not called when the signal is emitted

I am doing something like this:
MyClass::MyClass(QWidget *parent) : QLabel(parent)
{
this->MyMenu = new QMenu();
QAction* act_del = new QAction(MyMenu);
act_delete->setText("MyAction");
MyMenu->addAction(act_del);
QObject::connect(act_del,SIGNAL(triggered()),this,SLOT(MySlot()));
}
void MyClass::MySlot()
{
//Not called
}
Any suggestion on when the SIGNAL is triggered the SLOT is not called. Here is where the menu is displayed:
void MyClass::contextMenuEvent(QContextMenuEvent *ev)
{
QPoint globalPos = this->mapToGlobal(ev->pos());
QAction* selectedItem = MyMenu->exec(globalPos);
if (selectedItem)
{
}
else
{
// nothing was chosen
}
}
Any suggestions on why the SLOT is not called?
MyClass needs to contain the Q_OBJECT macro for the signal-slot connections to work.