最新大型开源项目-云游戏,云桌面系统,欢迎关注
本项目代码地址
1.单个按钮
我们使用QWidget自己绘制按钮,不使用QPushButton,可以做更漂亮的效果和更灵活的使用。
下图表示按钮的2个状态,左边为未选中状态,右边为选中状态。
2.实现
实现一个SideBarItem类,继承自QWidget。因为之前的文章已经介绍过鼠标进入,离开,按下,抬起以及绘制的事件,我们就直接实现这些方法。
我们希望点击之后,能有一个回调,因此也设置一个回调函数。
// 回调函数定义
using SideBarItemClickCallback = std::function<void()>;
class SideBarItem : public QWidget {
public:
// 将未选中和选中的颜色传进来。并且把要显示的文字也传进来。
SideBarItem(int nc, int ec, const QString& title, QWidget* parent = nullptr);
~SideBarItem() = default;
// 实现以下几个方法
// 绘制
void paintEvent(QPaintEvent *event) override;
// 鼠标进入
void enterEvent(QEvent *event) override;
// 鼠标离开
void leaveEvent(QEvent *event) override;
// 鼠标按下
void mousePressEvent(QMouseEvent *event) override;
// 鼠标抬起
void mouseReleaseEvent(QMouseEvent *event) override;
// 设置回调函数
void SetOnClickCallback(SideBarItemClickCallback&& cbk);
private:
// 保存回调函数
SideBarItemClickCallback click_cbk_;
// 未选中的按钮颜色
int normal_color_ = 0xcc0000;
// 选中的按钮颜色
int enter_color_ = 0xdd0000;
// 鼠标状态
bool enter_ = false;
bool pressed_ = false;
// 为了相对美观一点,左右2侧,流几个像素的空白
int left_right_padding_ = 8;
// 要显示的文字
QString title_ = "";
};
SideBarItem::SideBarItem(int nc, int ec, const QString& title, QWidget* parent) : QWidget(parent) {
//保存信息
this->normal_color_ = nc;
this->enter_color_ = ec;
this->title_ = title;
}
void SideBarItem::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.setRenderHint(QPainter::RenderHint::Antialiasing);
painter.setPen(Qt::NoPen);
// 鼠标进入和离开不同的状态下,设置不同的画刷,设置不同的颜色
if (enter_) {
painter.setBrush(QBrush(this->enter_color_));
}
else {
painter.setBrush(QBrush(this->normal_color_));
}
// 将左右2侧的空隙减去,留一点空白,上下填满即可
// 注意:因为是左右2侧都要留空隙,所以QRect的第3,4个参数,要减去2倍的padding值
QRect inner_rect(left_right_padding_, 0, this->rect().width()-left_right_padding_*2, this->rect().height());
// 我们需要圆角矩形的圆角大一些,达到高度的一半后,则可以绘制出一个半圆,可以自己调整需要的大小
painter.drawRoundedRect(inner_rect, this->height()/2, this->height()/2);
// 设置画笔和使用的字体,这种方式一定是自己系统上安装的字体,如果是自己定义的字体,需要自己加载。
QPen pen;
QFont font = QFont("Microsoft YaHei", 13, QFont::Bold, false);
// 不同的状态下,设置不同的颜色
if (enter_) {
pen.setColor(this->normal_color_);
}
else {
pen.setColor(this->enter_color_);
}
painter.setPen(pen);
painter.setFont(font);
// 将字体居中绘制
painter.drawText(this->rect(), Qt::AlignCenter, title_);
}
// 不同的状态下分别设置不同的值,然后刷新UI
void SideBarItem::enterEvent(QEvent *event) {
enter_ = true;
repaint();
}
void SideBarItem::leaveEvent(QEvent *event) {
enter_ = false;
repaint();
}
void SideBarItem::mousePressEvent(QMouseEvent *event) {
pressed_ = true;
repaint();
}
// 鼠标抬起时,触发回调
void SideBarItem::mouseReleaseEvent(QMouseEvent *event) {
pressed_ = false;
repaint();
if (this->click_cbk_) {
this->click_cbk_();
}
}
// 将回调函数保存起来
void SideBarItem::SetOnClickCallback(SideBarItemClickCallback&& cbk) {
this->click_cbk_ = std::move(cbk);
}
3.添加选中,未选中的状态
因为时多个按钮排列在一起,所以要指定哪个按钮被按下了,同时其他的按钮要置为未选中的状态。
class SideBarItem : public QWidget {
public:
...
void Select();
void Unselect();
private:
...
bool selected_ = false;
};
void SideBarItem::Select() {
selected_ = true;
repaint();
}
void SideBarItem::Unselect() {
selected_ = false;
repaint();
}
同时要将selected_的状态与界面关联起来,在这里我们将选中和鼠标进入设置为相同的状态。
void SideBarItem::paintEvent(QPaintEvent *event) {
...
// 这里再加一个条件即可
if (enter_ || selected_) {
painter.setBrush(QBrush(this->enter_color_));
}
else {
painter.setBrush(QBrush(this->normal_color_));
}
...
// 这里再加一个条件即可
if (enter_ || selected_) {
pen.setColor(this->normal_color_);
}
else {
pen.setColor(this->enter_color_);
}
...
}
4.添加到SideBar中
我们需要将4个按钮都保存起来,以便随时切换或者其他操作。SideBar添加一个vector保存它们。
因为4个按钮时互斥的,所以只有一个可以选中,所以再SideBar里控制这个逻辑
class SideBar : public QWidget
{
...
// 控制选中哪个按钮
void Select(int idx);
private:
std::vector<SideBarItem*> side_bar_items_;
};
因为时互斥的,所以选中一个,另外三个必然是未选中的状态
void SideBar::Select(int idx) {
if (idx < 0 || idx >= side_bar_items_.size()) {
return;
}
for (int i = 0; i < side_bar_items_.size(); i++) {
if (i == idx) {
side_bar_items_[i]->Select();
}
else {
side_bar_items_[i]->Unselect();
}
}
}
接下来修改SideBar的构造函数,添加四个按钮
SideBar::SideBar(QWidget *parent) : QWidget(parent) {
this->setFixedSize(QSize(Settings::kSideBarWidth, 530));
// item layout
auto item_layout = new QVBoxLayout();
LayoutHelper::ClearMarginSpacing(item_layout);
// Logo ...
item_layout->addSpacing(30);
// 添加4个按钮到布局中
// 还记得吗,用一对大括号包起来,相似的代码,看起来更直观。
// 当然这里更好的做法是用for循环来做,现在是demo,所以写的更直观一点,方便理解。
{
auto item = new SideBarItem(Settings::kSideBarNormalColor, Settings::kSideBarEnterColor, "睡眠", this);
item->setFixedSize(QSize(Settings::kSideBarWidth, 35));
item_layout->addWidget(item);
side_bar_items_.push_back(item);
item->SetOnClickCallback([=]() {
Select(0);
});
}
{
auto item = new SideBarItem(Settings::kSideBarNormalColor, Settings::kSideBarEnterColor, "专注",this);
item->setFixedSize(QSize(Settings::kSideBarWidth, 35));
item_layout->addSpacing(5);
item_layout->addWidget(item);
side_bar_items_.push_back(item);
item->SetOnClickCallback([=]() {
Select(1);
});
}
//相同的代码,不再展示
...
...
item_layout->addStretch();
setLayout(item_layout);
// 默认选中第一个
Select(0);
}
现在运行代码,就可以看见效果了: