How to change Icon image of QAction when press QToolButton

☆樱花仙子☆ 提交于 2021-02-16 20:21:56

问题


  1. I want to remove border of QToolButton when mouse hover.

  2. I want to change icon image of QAction when press QToolButton.

i changed stylesheet for QToolButton but it is not working, please help


  1. I want to remove border of QToolButton when mouse hover. I changed stylesheet for QToolButton

QToolButton:hover{ border: none; }
QToolButton:pressed{ border: none; }

but it display border bottom and button move to left like image


回答1:


For a “Warm-up”, I started with a QPushButton. This class provides the signals pressed() and released() which can be used to change the icon resp.

testQPushButtonDownUp.cc:

#include <QtWidgets>

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // build UI
  QIcon qIconBtn("dialog-info.svg");
  QIcon qIconBtnDown("dialog-error.svg");
  QPushButton qBtn(qIconBtn, QString::fromUtf8("Click Me."));
  qBtn.show();
  // install signal handlers
  QObject::connect(&qBtn, &QPushButton::pressed,
    [&qBtn, &qIconBtnDown]() { qBtn.setIcon(qIconBtnDown); });
  QObject::connect(&qBtn, &QPushButton::released,
    [&qBtn, &qIconBtn]() { qBtn.setIcon(qIconBtn); });
  // runtime loop
  return app.exec();
}

testQPushButtonDownUp.pro:

SOURCES = testQPushButtonDownUp.cc

QT += widgets

Compiled and tested in cygwin64 on Windows 10:

$ qmake-qt5 testQPushButtonDownUp.pro

$ make && ./testQPushButtonDownUp
Qt Version: 5.9.4

This was easy. Applying the same to QAction is a bit more complicated – QAction provides only one signal triggered(). I didn't check whether it's emitted for pressed() or released() – one of the both needed signals is surely missing.

To solve this, I used QAction::associatedWidgets() which

Returns a list of widgets this action has been added to.

This list is scanned for every occurrence of QToolButton which (is derived from QAbstractButton as well as QPushButton and) provides the same signals.

testQToolButtonDownUp.cc:

#include <QtWidgets>

void connectAction(QAction &qCmd, const QIcon &qIconDown, const QIcon &qIconUp)
{
  QList<QWidget*> pQWidgets = qCmd.associatedWidgets();
  for (QWidget *pQWidget : pQWidgets) {
    QToolButton *pQBtn = dynamic_cast<QToolButton*>(pQWidget);
    if (!pQBtn) continue;
    QObject::connect(pQBtn, &QToolButton::pressed,
      [pQBtn, qIconDown]() { pQBtn->setIcon(qIconDown); });
    QObject::connect(pQBtn, &QToolButton::released,
      [pQBtn, qIconUp]() { pQBtn->setIcon(qIconUp); });
  }
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // build UI
  QToolBar qToolbar;
  QIcon qIconBtn("dialog-info.svg");
  QIcon qIconBtnDown("dialog-error.svg");
  QAction qCmd(qIconBtn, QString::fromUtf8("Click Me."));
  qToolbar.addAction(&qCmd);
  qToolbar.show();
  // install signal handlers
  connectAction(qCmd, qIconBtnDown, qIconBtn);
  // runtime loop
  return app.exec();
}

testQToolButtonDownUp.pro:

SOURCES = testQToolButtonDownUp.cc

QT += widgets

Compiled and tested again in cygwin64 on Windows 10:

$ qmake-qt5 testQToolButtonDownUp.pro

$ make && ./testQToolButtonDownUp
Qt Version: 5.9.4

This works but is a bit maintenance-unfriendly – the connectAction() function has to be called after the QAction has been added to all widgets. (Double-calling it for the same instance of QAction might need additional effort to prevent duplicated signal handlers for the same instance of QToolButton.)

It would be nice to connect new QToolButtons automatically as soon as the associated widgets of such a resp. QAction have been changed. I scrolled through the doc. up and down to find something appropriate – without luck. I tried whether the QAction::changed() signal might provide the required behavior (although the doc. gave less hope) but it didn't work.

Finally, I decided to turn it around – i.e. detecting when a QAction is added to a QToolButton. However, in my code I add the QAction to a QToolBar and the resp. QToolButton appears automatically. Thus, I made an event filter, installed to qApp. This event filter will receive any event and, hence, is good to detect any QAction added to any QWidget. All I've to do additionally, is to filter these events for QActions and QToolButtons where the down-up icons are required for.

testQActionDownUp.cc:

#include <set>
#include <QtWidgets>

class Action: public QAction {
  public:
    class FilterSingleton: public QObject {
      private:
        std::set<Action*> _pActions;
      public:
        FilterSingleton(): QObject()
        {
          qApp->installEventFilter(this);
        }
        ~FilterSingleton() { qApp->removeEventFilter(this); }
        FilterSingleton(const FilterSingleton&) = delete;
        FilterSingleton& operator=(const FilterSingleton&) = delete;

        void addAction(Action *pAction)
        {
          _pActions.insert(pAction);
        }
        bool removeAction(Action *pAction)
        {
          _pActions.erase(pAction);
          return _pActions.empty();
        }

      protected:
        virtual bool eventFilter(QObject *pQObj, QEvent *pQEvent) override;
    };

  private:
    static FilterSingleton *_pFilterSingleton;

  private:
    QIcon _qIcon, _qIconDown;

  public:
    Action(
      const QIcon &qIcon, const QIcon &qIconDown, const QString &text,
      QObject *pQParent = nullptr);  
    ~Action();
    Action(const Action&) = delete;
    Action& operator=(const Action&) = delete;

  private:
    void addToolButton(QToolButton *pQBtn)
    {
      QObject::connect(pQBtn, &QToolButton::pressed,
        [pQBtn, this]() { pQBtn->setIcon(_qIconDown); });
      QObject::connect(pQBtn, &QToolButton::released,
        [pQBtn, this]() { pQBtn->setIcon(_qIcon); });
    }
};

bool Action::FilterSingleton::eventFilter(QObject *pQObj, QEvent *pQEvent)
{
  if (QToolButton *pQBtn = dynamic_cast<QToolButton*>(pQObj)) {
    if (pQEvent->type() == QEvent::ActionAdded) {
      qDebug() << "Action::eventFilter(QEvent::ActionAdded)";
      QAction *pQAction = ((QActionEvent*)pQEvent)->action();
      if (Action *pAction = dynamic_cast<Action*>(pQAction)) {
        pAction->addToolButton(pQBtn);
      }
    }     
  }
  return QObject::eventFilter(pQObj, pQEvent);
}

Action::FilterSingleton *Action::_pFilterSingleton;

Action::Action(
  const QIcon &qIcon, const QIcon &qIconDown, const QString &text,
  QObject *pQParent):
  QAction(qIcon, text, pQParent),
  _qIcon(qIcon), _qIconDown(qIconDown)
{
  if (!_pFilterSingleton) _pFilterSingleton = new FilterSingleton();
  _pFilterSingleton->addAction(this);
}

Action::~Action()
{
  if (_pFilterSingleton->removeAction(this)) {
    delete _pFilterSingleton;
    _pFilterSingleton = nullptr;
  }
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // build UI
  QMainWindow qWin;
  QToolBar qToolbar;
  QIcon qIconBtn("dialog-info.svg");
  QIcon qIconBtnDown("dialog-error.svg");
  Action qCmd(qIconBtn, qIconBtnDown, QString::fromUtf8("Click Me."));
  qToolbar.addAction(&qCmd);
  qWin.addToolBar(&qToolbar);
  QToolBar qToolbar2;
  qWin.setCentralWidget(&qToolbar2);
  qWin.show();
  QTimer qTimer;
  qTimer.setInterval(5000); // 5000 ms = 5s
  qTimer.start();
  // install signal handlers
  int i = 0;
  QObject::connect(&qTimer, &QTimer::timeout,
    [&i, &qToolbar2, &qCmd]() {
      if (++i & 1) qToolbar2.addAction(&qCmd);
      else qToolbar2.removeAction(&qCmd);
    });
  // runtime loop
  return app.exec();
}

There is a class Action (derived from QAction) to bundle everything necessary together. The class Action uses internally a singleton (of class Action::FilterSingleton), so that one event filter is shared between all instances of Action.

A QTimer is used to add/remove the sample Action qCmd periodically to QToolBar qToolbar2 to test/demostrate whether the auto-management works properly.

testQActionDownUp.pro:

SOURCES = testQActionDownUp.cc

QT += widgets

Compiled and tested again in cygwin64 on Windows 10:

$ qmake-qt5 testQActionDownUp.pro

$ make && ./testQActionDownUp
Qt Version: 5.9.4




回答2:


For remove border of QToolButton you can use style sheet like this ( i tested it is work ):

QToolButton { border: 0px;}
QToolButton:pressed {background-color: red;}

It is remove border and fill background of pressed tool button.

You can not change image of pressed button because usally we add QAction on QToolBar in QtDesignet, and need to change QAction image, but QAction have not signal from "pressed" like in QPushButton.

But if you adding QToolButton to QToolBar manually - you can do that, because QToolButton have signal "pressed" and have property "icon". In some slot connected to "pressed" signal you can change it.



来源:https://stackoverflow.com/questions/53202892/how-to-change-icon-image-of-qaction-when-press-qtoolbutton

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!