【问题标题】:How to change Icon image of QAction when press QToolButton按下QToolButton时如何更改QAction的图标图像
【发布时间】:2018-11-08 07:07:09
【问题描述】:
  1. 我想在鼠标悬停时移除 QToolButton 的边框。

  2. 我想在按下 QToolButton 时更改 QAction 的图标图像。

我更改了 QToolButton 的样式表,但它不起作用,请帮助


  1. 我想在鼠标悬停时移除 QToolButton 的边框。 我更改了 QToolButton 的样式表

QToolButton:悬停{ 边框:无; }
QToolButton:按下{ 边框:无; }

但它显示边框底部和按钮像图像一样向左移动

【问题讨论】:

    标签: c++ qt


    【解决方案1】:

    对于“热身”,我从QPushButton 开始。这个类提供了pressed()released()这两个信号,可以用来改变图标。

    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
    

    在 Windows 10 上的 cygwin64 中编译和测试:

    $ qmake-qt5 testQPushButtonDownUp.pro
    
    $ make && ./testQPushButtonDownUp
    Qt Version: 5.9.4
    

    这很容易。将相同的应用于QAction 有点复杂——QAction 只提供一个信号triggered()。我没有检查它是为pressed() 还是released() 发出的——这两个需要的信号之一肯定丢失了。

    为了解决这个问题,我使用了QAction::associatedWidgets()

    返回已添加此操作的小部件列表。

    扫描此列表以查找每次出现的QToolButton(源自QAbstractButtonQPushButton,并且)提供相同的信号。

    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
    

    在 Windows 10 上的cygwin64 中再次编译和测试:

    $ qmake-qt5 testQToolButtonDownUp.pro
    
    $ make && ./testQToolButtonDownUp
    Qt Version: 5.9.4
    

    这可行,但有点维护不友好——connectAction() 函数必须在 QAction 添加到所有小部件后调用。 (为QAction 的同一实例重复调用它可能需要额外的努力来防止QToolButton 的同一实例的重复信号处理程序。)

    只要这样一个相关的小部件自动连接新的QToolButtons 就好了。 QAction 已更改。我浏览了文档。上下寻找合适的东西——没有运气。我尝试了QAction::changed() 信号是否可以提供所需的行为(尽管文档给出的希望较小)但它没有用。

    最后,我决定扭转局面——即检测何时将QAction 添加到QToolButton。但是,在我的代码中,我将QAction 添加到QToolBar 和resp。 QToolButton 自动出现。因此,我制作了一个event filter,安装到qApp。此事件过滤器将接收任何事件,因此可以很好地检测添加到任何QWidget 的任何QAction。另外,我要做的就是为需要向下图标的QActions 和QToolButtons 过滤这些事件。

    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();
    }
    

    有一个类Action(派生自QAction)将所有必要的东西捆绑在一起。 Action 类在内部使用一个单例(Action::FilterSingleton 类),以便在 Action 的所有实例之间共享一个事件过滤器。

    QTimer 用于定期向QToolBar qToolbar2 添加/删除样本Action qCmd 以测试/演示自动管理是否正常工作。

    testQActionDownUp.pro:

    SOURCES = testQActionDownUp.cc
    
    QT += widgets
    

    在 Windows 10 上的 cygwin64 中再次编译和测试:

    $ qmake-qt5 testQActionDownUp.pro
    
    $ make && ./testQActionDownUp
    Qt Version: 5.9.4
    

    【讨论】:

    • @TuyenNH 我对 Qt 样式表几乎没有经验。 (我更喜欢 Qt 很好模仿的原生外观。)但是,我在 Qt 文档中找到了以下内容:Customizing QToolButton 指的是Customizing QPushButton,这可能会有所帮助。向上/向下滚动,您会发现更多按钮示例。还有一个Qt Style Sheets Reference 可以了解所有可用的内容。
    【解决方案2】:

    要删除 QToolButton 的边框,您可以使用这样的样式表(我测试它是有效的):

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

    它是去除被按下的工具按钮的边框和填充背景。

    您不能更改按下按钮的图像,因为通常我们在 QtDesignet 中的 QToolBar 上添加 QAction,并且需要更改 QAction 图像,但 QAction 没有像 QPushButton 中那样从“按下”发出信号。

    但是,如果您手动将 QToolButton 添加到 QToolBar - 您可以这样做,因为 QToolButton 有信号“按下”并且具有属性“图标”。在某些连接到“按下”信号的插槽中,您可以更改它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-04
      • 2013-06-06
      • 1970-01-01
      相关资源
      最近更新 更多