网站建设优化服务机构,苏州市做网站,建设网站那家公司好,西安微动免费做网站✍Qt自定义带图标按钮
#x1f4dd;问题引入 近段时间的工作中#xff0c;有遇到这样一个需求
#x1f4dd;#xff1a;
一个按钮#xff0c;有normal、hover、pressed三种状态的样式#xff0c;并且normal和hover样式下#xff0c;字体颜色和按钮图标不一样。
分析…✍Qt自定义带图标按钮
问题引入 近段时间的工作中有遇到这样一个需求 一个按钮有normal、hover、pressed三种状态的样式并且normal和hover样式下字体颜色和按钮图标不一样。
分析这个需求背景色和文字颜色容易实现复杂的点在于图标和隐藏的一个点——文字和图标的间距以及文字的换行。常规的样式表只能实现背景色、文字颜色无法设置hover和normal状态下不同的icon。当然我们有很多种方式来实现这个需求例如创建一个继承自QWidget的类并在里面添加文字label和图标label。但这种方式需要处理按钮的点击事件毕竟一个按钮不可能光秃秃的放在那里肯定会有点击操作。
我采用的方案是创建一个QPushButton并创建一个布局在布局里塞入文字label和图标label最后在将这个布局设置给创建的QPushButton。代码可能如下 当然还要处理一下按钮的hover事件:
// ... 假设你为这个按钮安装了事件过滤器
bool eventFilter(QObject* watched, QEvent* e)
{if (e-type() QEvent::HoverEnter) {iconLabel-setProperty(LabelStatus, Hover);textLabel-setProperty(LabelStatus, Hover);this-style()-unpolish(iconLabel);this-style()-polish(iconLabel);this-style()-unpolish(textLabel);this-style()-polish(textLabel);} else if (e-type() QEvent::HoverLeave) {iconLabel-setProperty(LabelStatus, Normal);textLabel-setProperty(LabelStatus, Normal);this-style()-unpolish(iconLabel);this-style()-polish(iconLabel);this-style()-unpolish(textLabel);this-style()-polish(textLabel);}return QWidget::eventFilter(watched, e);
}你可以通过样式表来设置图标和文字样式
QLabel#iconLabel[LabelStatusNormal]
{border-image: url(xxx);
}QLabel#iconLabel[LabelStatusHover]
{border-image: url(xxx_hover);
}QLabel#textLabel[LabelStatusNormal]
{color: #FFFFFF;
}QLabel#textLabel[LabelStatusHover]
{color: #FF0000;
}这样做一个好处就是你就不需要去单独处理点击事件虽然都已经处理了HoverEnter和HoverLeave还有一个好处是在按钮过多时你可以将按钮加入到QButtonGroup里统一对按钮的点击事件进行处理。
当然代码还可以优化你可以将这些内容封装成类
class MyButton : public QPushButton
{Q_OBJECTpublic:MyButton(QWidget* parent): QPushButton(parent), m_pIconLabel{nullptr}, m_pTextLabel{nullptr}{auto hLayout new QHBoxLayout();hLayout-setContentMargins(4, 4, 4, 4);hLayout-setSpacing(8); // 设置文字和图标的间距m_pTextLabel new QLabel(this);m_pTextLabel-setObjectName(m_pTextLabel);m_pIconLabel new QLabel(this);m_pIconLabel-setObjectName(m_pIconLabel);hLayout-addWidget(m_pTextLabel);hLayout-addWidget(m_pIconLabel);this-setLayout(hLayout);this-installEventFilter(this);this-setStyleSheet(R(QLabel#iconLabel[LabelStatusNormal]{border-image: url(xxx);}QLabel#iconLabel[LabelStatusHover]{border-image: url(xxx_hover);}QLabel#textLabel[LabelStatusNormal]{color: #FFFFFF;}QLabel#textLabel[LabelStatusHover]{color: #FF0000;}));}private:bool eventFilter(QObject* watched, QEvent* e) override{if (e-type() QEvent::HoverEnter) {m_pIconLabel-setProperty(LabelStatus, Hover);m_pTextLabel-setProperty(LabelStatus, Hover);this-style()-unpolish(m_pIconLabel);this-style()-polish(m_pIconLabel);this-style()-unpolish(m_pTextLabel);this-style()-polish(m_pTextLabel);} else if (e-type() QEvent::HoverLeave) {m_pIconLabel-setProperty(LabelStatus, Normal);m_pTextLabel-setProperty(LabelStatus, Normal);this-style()-unpolish(m_pIconLabel);this-style()-polish(m_pIconLabel);this-style()-unpolish(m_pTextLabel);this-style()-polish(m_pTextLabel);}return QPushButton::eventFilter(watched, e);}private:QLabel* m_pIconLabel;QLabel* m_pTextLabel;
};但当把这个按钮加到到实际的界面时问题出现了
// ...创建界面
auto hLayout new QHBoxLayout(this);auto title new QLabel(this);
title-setText(XXXXXX);auto btn new MyButton(this);hLayout-addWidget(title);
hLayout-addStrect();
hLayout-addWidget(btn);我想要的效果是
在切换语言后按钮的宽度能够跟随文字的长度变换而相应变宽或变窄。 但实际情况与预期有所不同。即使我将按钮或文字的sizePolicy设置为Expanding效果依然不理想。而原生的QPushButton是可以根据文字宽度自动调整大小的。这是为什么呢按理说我已经把自定义按钮内的文字标签等组件放入了一个水平布局中它应该能够自动调整宽度但实际上水平布局并未起作用。这种情况让我很好奇想要去了解Qt的布局管理系统是如何工作的。
Qt布局探秘 在Qt关于布局管理的[官方文档](https://doc.qt.io/qt-5/layout.html)中有这样一句话
:::success
Adding Widgets to a Layout
When you add widgets to a layout, the layout process works as follows:
All the widgets will initially be allocated an amount of space in accordance with their QWidget::sizePolicy() and QWidget::sizeHint().
:::
大致意思就是
所有界面的初始大小将根据其尺寸策略sizePolicy和尺寸建议sizeHint进行设定。
这让我突然有了一个灵感是不是因为QPushButton的sizeHint中计算的文字控件与我所显示的文字控件不是同一个呢而外层布局又是直接调用QPushButton的sizeHint函数来获取宽度的进而导致按钮的大小不如人意。为了验证我的想法我决定到QPushButton的源码中一探究竟。
QSize QPushButton::sizeHint() const
{Q_D(const QPushButton);// ...QString s(text());bool empty s.isEmpty();if (empty)s QStringLiteral(XXXX);QFontMetrics fm fontMetrics();QSize sz fm.size(Qt::TextShowMnemonic, s);if(!empty || !w)w sz.width();if(!empty || !h)h qMax(h, sz.height());opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height// ...d-sizeHint (style()-sizeFromContents(QStyle::CT_PushButton, opt, QSize(w, h), this).expandedTo(QApplication::globalStrut()));return d-sizeHint;
}事实确实是这样QPushButton中计算的文本是通过text函数来获取的而这个文本又是通过调用setText来设置的我们绕过了这一步那计算的大小就肯定不是我们想要的了。
️问题的解决 看到这里相信各位同学也都已经知道解决方法了**实现自己的sizeHint。**
最终我们的按钮类变成了
class MyButton : public QPushButton
{Q_OBJECTpublic:MyButton(QWidget* parent): QPushButton(parent), m_pIconLabel{nullptr}, m_pTextLabel{nullptr}, m_pLayout{nullptr}{m_pLayout new QHBoxLayout();m_pLayout-setContentMargins(4, 4, 4, 4);m_pLayout-setSpacing(8); // 设置文字和图标的间距m_pTextLabel new QLabel(this);m_pTextLabel-setObjectName(m_pTextLabel);m_pIconLabel new QLabel(this);m_pIconLabel-setObjectName(m_pIconLabel);m_pLayout-addWidget(m_pTextLabel);m_pLayout-addWidget(m_pIconLabel);this-setLayout(m_pLayout);this-installEventFilter(this);this-setStyleSheet(R(QLabel#iconLabel[LabelStatusNormal]{border-image: url(xxx);}QLabel#iconLabel[LabelStatusHover]{border-image: url(xxx_hover);}QLabel#textLabel[LabelStatusNormal]{color: #FFFFFF;}QLabel#textLabel[LabelStatusHover]{color: #FF0000;}));}QSize sizeHint(){return m_pLayout-sizeHint();}private:bool eventFilter(QObject* watched, QEvent* e) override{if (e-type() QEvent::HoverEnter) {m_pIconLabel-setProperty(LabelStatus, Hover);m_pTextLabel-setProperty(LabelStatus, Hover);this-style()-unpolish(m_pIconLabel);this-style()-polish(m_pIconLabel);this-style()-unpolish(m_pTextLabel);this-style()-polish(m_pTextLabel);} else if (e-type() QEvent::HoverLeave) {m_pIconLabel-setProperty(LabelStatus, Normal);m_pTextLabel-setProperty(LabelStatus, Normal);this-style()-unpolish(m_pIconLabel);this-style()-polish(m_pIconLabel);this-style()-unpolish(m_pTextLabel);this-style()-polish(m_pTextLabel);}return QPushButton::eventFilter(watched, e);}private:QLabel* m_pIconLabel;QLabel* m_pTextLabel;QHBoxLayout* m_pLayout;
};通过返回内部布局的尺寸来控制按钮在布局中的尺寸。