Для «разминки» я начал с 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
Это было легко. Применение того же к QAction
немного сложнее & ndash; QAction
обеспечивает только один сигнал triggered()
. Я не проверял, испускается ли он для pressed()
или released()
& ndash; один из обоих необходимых сигналов наверняка отсутствует.
Для решения этой проблемы я использовал QAction::associatedWidgets()
, который
Возвращает список виджетов, к которым было добавлено это действие.
Этот список сканируется для каждого вхождения 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
Это работает, но немного неудобно для обслуживания & ndash; функция connectAction()
должна вызываться после добавления QAction
ко всем виджетам. (Двойной вызов для одного и того же экземпляра QAction
может потребовать дополнительных усилий для предотвращения дублированных обработчиков сигналов для одного и того же экземпляра QToolButton
.)
Было бы неплохо автоматически подключить новые QToolButton
s, как только связанные виджеты такого соотв. QAction
были изменены. Я пролистал документ. вверх и вниз, чтобы найти что-то подходящее & ndash; без удачи Я пытался определить, может ли сигнал QAction::changed()
обеспечить требуемое поведение (хотя документ давал меньше надежды), но он не работал.
Наконец, я решил изменить это - ndash; то есть обнаружение, когда QAction
добавлено к QToolButton
. Тем не менее, в моем коде я добавляю QAction
к QToolBar
и соотв. QToolButton
появляется автоматически. Таким образом, я сделал фильтр событий , установленный на qApp
. Этот фильтр событий получит любое событие и, следовательно, будет полезен для обнаружения любых QAction
, добавленных к любому QWidget
. Все, что я должен сделать дополнительно, это отфильтровать эти события для QAction
с и QToolButton
с, для которых требуются значки вниз.
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
.
A QTimer
используется для периодического добавления / удаления образца Action qCmd
в QToolBar qToolbar2
для проверки / демонстрации правильности работы автоматического управления.
testQActionDownUp.pro
SOURCES = testQActionDownUp.cc
QT += widgets
Скомпилировано и протестировано снова в cygwin64 в Windows 10:
$ qmake-qt5 testQActionDownUp.pro
$ make && ./testQActionDownUp
Qt Version: 5.9.4