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);
Related
I am new in qt I want to create a button when I right click
There is my code:
void MainWindow::right_clicked(QMouseEvent *event)
{
if(event->button() == Qt::RightButton)
{
QPushButton *item = new QPushButton();
item->setIcon(QIcon(":/images/7928748-removebg-preview(1).ico"));
item->setIconSize(QSize(32, 32));
item->setGeometry(QRect(QPoint(event->x(), event->y()), QSize(32, 32)));
}
}
But nothing appears
To capture any mouse event in a QWidget you must override the mousePressEvent method.
class MainWindow : public QMainWindow
{
Q_OBJECT
protected:
void mousePressEvent(QMouseEvent *event);
};
And in the mainwindow.cpp, implement it as follows:
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::RightButton) {
// make mainwindow parent of this button by passing "this" pointer
QPushButton *item = new QPushButton(QIcon(":/images/close-button-icon"), "", this);
// set button position to the location of mouse click
item->setGeometry(QRect(QPoint(event->x()-16, event->y()-16), QSize(32, 32)));
item->show();
}
}
If you don't save the pointer to QPushButton, then you will not be able to use it afterwards.
I have 5 buttons in my window qt app. I would like to set a label text, which is a description for each process on hovering above the button. How can I do that?
We can use eventFilter for this.
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == (QObject*)ui->pushButton) {
if (event->type() == QEvent::Enter)
{
ui->label_3->setText("Your text");
}
else if (event->type() == QEvent::Leave){
//Clear text when mouse leaves
ui->label_3->setText("");
}
return QWidget::eventFilter(obj, event);
}
else return QWidget::eventFilter(obj, event);
}
QEvent::HoverEnter and QEvent::HoverLeave can do this for you. Set an event filter (https://doc.qt.io/qt-5/eventsandfilters.html) on your QPushButton and you are good to go.
Assuming you are using QPushButton in your UI, you can reimplement the following methods :
virtual void enterEvent(QEvent *e)
{
setText("mouse in");
}
virtual void leaveEvent(QEvent *e)
{
setText("mouse out");
}
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);
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();
}
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);