c++ - 按下 QToolButton 时如何更改 QAction 的图标图像

标签 c++ qt

  1. 我想在鼠标悬停时移除 QToolButton 的边框。 enter image description here

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

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


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

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

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

enter image description here

最佳答案

为了“热身”,我从 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

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

$ qmake-qt5 testQPushButtonDownUp.pro

$ make && ./testQPushButtonDownUp
Qt Version: 5.9.4

Snapshot of testQPushButtonDownUp Snapshot of testQPushButtonDownUp while button pressed

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

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

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

此列表会针对每次出现的 QToolButton 进行扫描,它(派生自 QAbstractButton 以及 QPushButton 并且)提供相同的信号。

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

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

$ qmake-qt5 testQToolButtonDownUp.pro

$ make && ./testQToolButtonDownUp
Qt Version: 5.9.4

Snapshot of testQToolButtonDownUp Snapshot of testQToolButtonDownUp while button pressed

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

一旦此类响应的关联小部件自动连接新的 QToolButton 会很好。 QAction 已更改。我滚动浏览了文档。上下寻找合适的东西——没有运气。我试过是否QAction::changed() signal 可能会提供所需的行为(尽管文档给出的希望较小)但它不起作用。

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

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

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

$ qmake-qt5 testQActionDownUp.pro

$ make && ./testQActionDownUp
Qt Version: 5.9.4

Snapshot of testQActionDownUp Snapshot of testQActionDownUp while button pressed

Snapshot of testQActionDownUp while 2nd button added Snapshot of testQActionDownUp while 2nd button added and pressed

关于c++ - 按下 QToolButton 时如何更改 QAction 的图标图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53202892/

相关文章:

c++ - 检查 QByteArray 的特定位

c++ - 在构造函数中分配数组时出错

c++ - 如何检查用户是否输入了两次相同的数字? C++ 帮助?

qt - QML 中是否有类似于 QT 的 setMask() API 的 API?

c++ - 正则表达式选择扩展

c++ - 是否有免费的 C++ xsl-fo 转 PDF 引擎?

C++ 大括号分隔的初始化列表

c++ - 是首先调用 init 部分函数还是构造函数?

c++ - 在 C++ 中初始化多个类属性的最佳实践

qt - 在 Qt 中创建帮助文件