【Qt C++自绘制界面音乐播放器-3】标题栏添加控制按钮

58 阅读4分钟

最新大型开源项目-云游戏,云桌面系统,欢迎关注

GammaRay源码地址

本项目代码地址

源码

1.绘制控制按钮小圆圈

实现一个类,继承自QWidget,然后实现paintEvent方法,在此方法内进行绘制。

鼠标进入,离开后,按钮有不同的颜色,因此需要实现进入和离开的事件监听

enterEvent 鼠标进入

leaveEvent 鼠标离开

因为是绘制圆形的按钮,所以需要一个半径,同时需要鼠标进入时的颜色和正常无鼠标进入的颜色。

class OperationIcon : public QWidget {
public:
    // 绘制半径,普通状态下颜色,鼠标进入时颜色
    OperationIcon(int radius, int normal_color, int enter_color, QWidget* parent = nullptr);
    // 绘制事件
    void paintEvent(QPaintEvent *event) override;
    // 鼠标进入事件
    void enterEvent(QEvent *event) override;
    // 鼠标离开事件
    void leaveEvent(QEvent* event) override;

private:

    int radius_ = 10;
    int normal_color_ = 0xff0099;
    int enter_color_ = 0xff000000;

};

绘制圆形可以使用绘制椭圆或者圆角矩形的方式,在paintEvent中,我们使用绘制圆角矩形的方式。

OperationIcon::OperationIcon(int radius, int normal_color, int enter_color, QWidget* parent) : QWidget(parent) {
    this->radius_ = radius;
    this->normal_color_ = normal_color;
    this->enter_color_ = enter_color;
}

void OperationIcon::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    // 开启抗锯齿,绘制的图形更加细腻
    painter.setRenderHint(QPainter::RenderHint::Antialiasing);
    // 不需要描边,只需填充颜色即可
    painter.setPen(Qt::NoPen);
    // 进入和离开(普通状态),分别设置颜色画刷
    if (enter_) {
        painter.setBrush(QBrush(QColor(this->enter_color_)));
    }
    else {
        painter.setBrush(QBrush(QColor(this->normal_color_)));
    }
    // 绘制圆角矩形
    painter.drawRoundedRect(this->rect(), this->width()/2, this->height()/2);
}

void OperationIcon::enterEvent(QEvent *event) {
   //进入时,设置状态重绘
   enter_ = true;
   repaint();
}

void OperationIcon::leaveEvent(QEvent* event) {
   //离开时,设置状态重绘
    enter_ = false;
    repaint();
}

2.添加到TitleBar中

添加2个按钮到一个水平的Layout中,然后将Layout添加到TitleBar中,并添加合适的空隙。

TitleBar::TitleBar(QWidget* parent) : QWidget(parent) {
    setFixedHeight(Settings::kTitleBarHeight);
    setFixedWidth(Settings::kWindowWidth);

    // 生成一个垂直布局,是为了让TitleBar的内容,上下调整更方便
    auto root_layout = new QVBoxLayout();
    // 生成一个水平布局,按钮添加在这个布局里
    auto item_layout = new QHBoxLayout();
    // 给按钮所在的布局,添加一个弹簧,将按钮挤压到右边
    item_layout->addStretch();

    auto btn_size = Settings::kOperationBtnRadius;

    // 生成最小化按钮
    {
        min_btn_ = new OperationIcon(btn_size,
                                         Settings::kOperationBtnMinNomralColor,
                                         Settings::kOperationBtnMinEnterColor,
                                         this);
        // 设置大小
        min_btn_->setFixedSize(QSize(btn_size, btn_size));
        // 添加到水平Layout中
        item_layout->addWidget(min_btn_);
    }
    // 添加一个空隙
    item_layout->addSpacing(6);

    // 同理添加一个关闭按钮
    {
        close_btn_ = new OperationIcon(btn_size,
                                           Settings::kOperationBtnCloseNomralColor,
                                           Settings::kOperationBtnCloseEnterColor,
                                           this);
        close_btn_->setFixedSize(QSize(btn_size, btn_size));
        item_layout->addWidget(close_btn_);
    }
    item_layout->addSpacing(6);
    root_layout->addLayout(item_layout);
    setLayout(root_layout);
}

运行后,得到的效果如下

3.增加点击事件

鼠标点击事件是通过以下两个方法获得的

mousePressEvent 鼠标按下

mouseReleaseEvent 鼠标抬起

3.1 定义一个回调函数

当鼠标点击时,通过回调函数通知。

当然,也可以使用Qt的信号和槽实现,但C++11之后,可以方便使用Lambda表达式,故此使用functional函数(仿函数)。

using ClickCallback = std::function<void()>;
3.2 实现鼠标事件和回调函数的设置
class OperationIcon : public QWidget {
public:
...
    //鼠标按下
    void mousePressEvent(QMouseEvent *event) override;
    // 鼠标抬起
    void mouseReleaseEvent(QMouseEvent *event) override;
    // 设置点击回调函数
    // 注意这里用的是右值参数
    void SetOnClickCallback(ClickCallback&& cbk);

private:
...
    // 按下状态
    bool pressed_ = false;
    // 回调函数
    ClickCallback click_cbk_;

};

实现很简单,只需要在鼠标抬起的时候,调用一下回调函数即可。

自定义Widget在这里也体现了一定的灵活度,因为有的需求在按下立刻触发,如果使用Qt的clicked信号,则无法实现了。

void OperationIcon::mousePressEvent(QMouseEvent *event) {
    pressed_ = true;
    repaint();
}

void OperationIcon::mouseReleaseEvent(QMouseEvent *event) {
    pressed_ = false;
    repaint();

    if (click_cbk_) {
        click_cbk_();
    }
}

void OperationIcon::SetOnClickCallback(ClickCallback&& cbk) {
    // 注意这里是move,因为回调函数只有一个,直接move过来保存到click_cbk_这个变量里
    click_cbk_ = std::move(cbk);
}
3.3 处理逻辑

我们的按钮添加在了TitleBar的Layout里,添加了一个最小化,一个关闭。

有2个位置可以处理这两个事件:

1.在TitleBar中直接处理

2.TitleBar暴露接口,给外面处理

我们选择暴露接口,给外面处理,以获取更大的灵活性

class TitleBar : public QWidget
{
public:
    void SetMinClickCallback(ClickCallback&& cbk);
    void SetCloseClickCallback(ClickCallback&& cbk);
}

直接把回调函数设置给按钮即可:

void TitleBar::SetMinClickCallback(ClickCallback&& cbk) {
    min_btn_->SetOnClickCallback(std::move(cbk));
}

void TitleBar::SetCloseClickCallback(ClickCallback&& cbk) {
    close_btn_->SetOnClickCallback(std::move(cbk));
}

在MainWindow中,我们处理这2个事件

MainWindow::MainWindow(QWidget *parent)
    : QWidget(parent) {
...
    // 最小化
    title_bar_->SetMinClickCallback([=]() {
        this->showMinimized();
    });
    
    //直接退出应用
    title_bar_->SetCloseClickCallback([=]() {
        qApp->exit(0);
    });
}