在项目中经常遇到需要高度定制的QPushButton,互斥性的QPushButton菜单便是其中一例。老手就不用看了,没啥技术含量,希望能给新手点思路吧。再复杂的控件,也可以通过这种思路去构造。
效果图如下:
基本需求就是鼠标三态(normal,hover,press),布局样式就是图片+文字。这种样式的按钮应用场景还是挺多的,比如tab页,工具栏,小到单个按钮的需求。
基本思路:单个按钮封装成控件,这个控件包含这个按钮的类别,文字,以及图片路径的类信息成员。然后实现这个控件的mouseReleaseEvent,enterEvent, leaveEvent等。再提供一个setStyle接口,用于设置不同状态下的样式。最后再把这些单个控件放到布局里,或者说是上层封装的一个控件(比如工具栏控件)。 上面的样式我只用到了两态,实现的也有复杂一些的,不过道理都是一样的。上代码吧。
NaviBtn.h
#include <QWidget>
class QLabel;
enum ENavi
{
Navi_ClassRoom,
Navi_Courseware,
};
struct NaviInfo
{
ENavi type;
QString name;
QString imagePath;
};
class NaviBtn : public QWidget
{
Q_OBJECT
public:
NaviBtn(QWidget *parent, const NaviInfo &naviInfo);
~NaviBtn();
void setChecked(bool checked = true);
ENavi getType() { return m_NaviInfo.type; }
protected:
virtual void mouseReleaseEvent(QMouseEvent * event);
void paintEvent(QPaintEvent *event);
private:
void setStyle(bool checked = false);
signals:
void sig_NaviBtnClick(ENavi type);
private:
NaviInfo m_NaviInfo;
QLabel* m_ImageLbl = nullptr;
QLabel* m_NameLbl = nullptr;
bool m_IsChecked = false;
};
NaviBtn.cpp
NaviBtn::NaviBtn(QWidget *parent, const NaviInfo &naviInfo)
: QWidget(parent),m_NaviInfo(naviInfo)
{
setFixedSize(104, 104);
setObjectName("NaviBtn");
m_ImageLbl = new QLabel(this);
m_ImageLbl->setFixedSize(48, 48);
m_NameLbl = new QLabel(this);
m_NameLbl->setText(m_NaviInfo.name);
m_NameLbl->setObjectName("NaviBtnName");
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setSpacing(0);
layout->setContentsMargins(0, 14, 0, 0);
layout->setAlignment(Qt::AlignHCenter|Qt::AlignTop);
layout->addWidget(m_ImageLbl, 0, Qt::AlignHCenter);
layout->addSpacing(4);
layout->addWidget(m_NameLbl, 0, Qt::AlignHCenter);
layout->addStretch();
setStyle(m_NaviInfo.type == Navi_ClassRoom);
}
NaviBtn::~NaviBtn()
{
}
void NaviBtn::mouseReleaseEvent(QMouseEvent * event)
{
m_IsChecked = true;
setStyle(true);
emit sig_NaviBtnClick(m_NaviInfo.type);
}
void NaviBtn::setChecked(bool checked)
{
m_IsChecked = checked;
setStyle(checked);
}
void NaviBtn::setStyle(bool checked)
{
QString imageStyle;
if (checked)
{
setProperty("isCheck", true);
m_NameLbl->setProperty("isCheck", true);
imageStyle = QString("background-image:url(:/common/%1_check)").arg(m_NaviInfo.imagePath);
}
else
{
setProperty("isCheck", false);
m_NameLbl->setProperty("isCheck", false);
imageStyle = QString("background-image:url(:/common/%1)").arg(m_NaviInfo.imagePath);
}
style()->polish(this);
m_NameLbl->style()->polish(m_NameLbl);
m_ImageLbl->setStyleSheet(imageStyle);
}
void NaviBtn::paintEvent(QPaintEvent *event)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
对应的qss样式如下:
QWidget#NaviBtn
{
border-radius:8px;
background-color:transparent;
}
QWidget#NaviBtn[isCheck = "true"]
{
background-color:rgba(0,0,0,0.06);
}
QLabel#NaviBtnName
{
font-size:18px;
color:rgba(0,0,0,0.6);
font-weight:400;
background-color:transparent;
}
QLabel#NaviBtnName[isCheck = "true"]
{
color:#FF6000;
}
是不是很简单呢~