【Qt C++自绘制界面音乐播放器-9】播放控制区-播放按钮和音量控制

29 阅读3分钟

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

GammaRay源码地址

本项目代码地址

源码

1.播放按钮

包括一个圆形的背景和一个图片,以及点击后的事件回调

using OnButtonClickCallback = std::function<void()>;

class CircleButton : public QWidget
{
    Q_OBJECT
public:
    // icon_url: 正常状态下,鼠标未点击或者未进入控件区域时的图片
    // active_icon_url: 鼠标进入控件区域或者点击时的图片
    CircleButton(const QString& icon_url, const QString& active_icon_url = "", QWidget *parent = nullptr);

    void paintEvent(QPaintEvent *event) override;

    void enterEvent(QEvent *event) override;
    void leaveEvent(QEvent *event) override;

    void mouseReleaseEvent(QMouseEvent *event) override;
    // 设置回调函数
    void SetOnClickCallback(OnButtonClickCallback&& cbk);
    // 切换图片,播放和暂定是2套不同的图片
    void ChangeIcon(const QString& icon_url, const QString& active_icon_url = "");

signals:

private:

    bool enter_ = false;
    // 2个状态下的图片
    QPixmap pixmap_;
    QPixmap pixmap_active_;
    // 保存回调函数
    OnButtonClickCallback callback_;

};
CircleButton::CircleButton(const QString& icon_url, const QString& active_icon_url, QWidget *parent) : QWidget(parent) {
    // 加载图片并缩放到合适的比例
    this->ChangeIcon(icon_url, active_icon_url);
}

void CircleButton::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::RenderHint::Antialiasing);

    int pen_width = 1;
    QPen pen;
    pen.setWidth(1);
    // 鼠标进入和离开时,分别设置不同的画笔和画刷
    if (enter_) {
        pen.setColor(QColor(0x334466));

        painter.setBrush(QBrush(QColor(0x334466)));
    }
    else {
        pen.setColor(QColor(0xcccccc));

        painter.setBrush(Qt::NoBrush);
    }
    painter.setPen(pen);

    // 绘制一个圆形的背景
    int radius = (this->width()-2*pen_width)/2;
    painter.drawEllipse(QPointF(this->width()/2, this->height()/2), radius, radius);

    //鼠标进入和离开时,绘制不同的图片
    if (enter_ && !pixmap_active_.isNull()) {
        painter.drawPixmap((this->width() - pixmap_.width())/2, (this->height() - pixmap_.height())/2, pixmap_);
    } else {
        painter.drawPixmap((this->width() - pixmap_active_.width())/2, (this->height() - pixmap_active_.height())/2, pixmap_active_);
    }

}

void CircleButton::enterEvent(QEvent *event) {
    enter_ = true;
    repaint();
}

void CircleButton::leaveEvent(QEvent *event) {
    enter_ = false;
    repaint();
}

void CircleButton::mouseReleaseEvent(QMouseEvent *event) {
    if (callback_) {
        callback_();
    }
    repaint();
}

void CircleButton::SetOnClickCallback(OnButtonClickCallback&& cbk) {
    this->callback_ = std::move(cbk);
}

// 加载图片,并缩放到合适的比例
void CircleButton::ChangeIcon(const QString& icon_url, const QString& active_icon_url) {
    QImage image;
    image.load(icon_url);
    pixmap_ = QPixmap::fromImage(image);
    pixmap_ = pixmap_.scaled(24, 24, Qt::KeepAspectRatio, Qt::SmoothTransformation);

    if (!active_icon_url.isEmpty()) {
        QImage image;
        image.load(active_icon_url);
        pixmap_active_ = QPixmap::fromImage(image);
        pixmap_active_ = pixmap_active_.scaled(24, 24, Qt::KeepAspectRatio, Qt::SmoothTransformation);
    }
    repaint();
}

2.音量控制

绘制一个三角形,唯一需要注意的是,绘制方向是从上向下,从左到右,而我们的三角形是从下往上,越来越高的。因此计算斜率时,需要注意,我们计算得到的是蓝色三角形上,两个红圈代表的点之间的距离,因此上面红圈的坐标就是:高度-2个红圈之间的距离 = x轴到第一个红圈的距离

class VolumeController : public QWidget
{
    Q_OBJECT
public:
    explicit VolumeController(QWidget *parent = nullptr);

    void paintEvent(QPaintEvent *event) override;

    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    
    // 设置当前音量,0~100
    void SetCurrentVolume(int volume);

private:

    int current_volume_ = 30;

};
void VolumeController::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::RenderHint::Antialiasing);

    QPen pen;
    pen.setWidth(1);
    pen.setColor(QColor(0x334466));
    painter.setPen(pen);
    painter.setBrush(Qt::NoBrush);

    // 绘制一个三角形,仅有边框
    QPainterPath path;
    path.moveTo(0, this->height()-1);
    path.lineTo(this->width()-1, this->height()-1);
    path.lineTo(this->width()-1, 0);
    path.lineTo(0, this->height()-1);
    painter.drawPath(path);
    
    // 绘制填充区域
    painter.setBrush(QBrush(QColor(0x334466)));
    {
        // 计算斜率,注意此时时数学上的方向
        float k = this->height() * 1.0f / this->width();
        // 计算当前音量下,x轴上的长度
        int target_width = current_volume_*1.0f / 100.0f * this->width();
        // 计算当前斜率下,y轴上的大小
        int target_y = k * target_width;
        QPainterPath path;
        path.moveTo(0, this->height()-1);
        // 高度-2个红圈之间的距离 = x轴到第一个红圈的距离
        path.lineTo(target_width, this->height() - target_y);
        path.lineTo(target_width, this->height() - 0);
        path.lineTo(0, this->height()-1);
        painter.drawPath(path);
    }
}

void VolumeController::mousePressEvent(QMouseEvent *event) {

}

void VolumeController::mouseReleaseEvent(QMouseEvent *event) {
    int percent = event->pos().x() * 1.0f / this->width() * 100;
    SetCurrentVolume(percent);
    repaint();
}

void VolumeController::SetCurrentVolume(int volume) {
    this->current_volume_ = volume;
    repaint();
}

3.添加到PlayController中

PlayController::PlayController(QWidget *parent) : QWidget(parent) {
    auto root_layout = new QHBoxLayout();
...
    {
        auto item_layout = new QVBoxLayout();
        LayoutHelper::ClearMarginSpacing(item_layout);

        play_btn_ = new CircleButton(":/images/resources/play_arrow.svg", ":/images/resources/play_arrow_active.svg");
        play_btn_->setFixedSize(QSize(30, 30));
        play_btn_->SetOnClickCallback([=]() {
            // 不同的状态,切换不同的图片
            if (is_playing_) {
                is_playing_ = false;
                play_btn_->ChangeIcon(":/images/resources/play_arrow.svg", ":/images/resources/play_arrow_active.svg");
            }
            else {
                is_playing_ = true;
                play_btn_->ChangeIcon(":/images/resources/pause.svg", ":/images/resources/pause_active.svg");
            }
        });

        item_layout->addStretch();
        item_layout->addWidget(play_btn_);
        item_layout->addStretch();

        root_layout->addSpacing(25);
        root_layout->addLayout(item_layout);
    }

    {
        auto item_layout = new QVBoxLayout();
        LayoutHelper::ClearMarginSpacing(item_layout);

        volume_controller_ = new VolumeController(this);
        volume_controller_->setFixedSize(QSize(100, 25));
        item_layout->addSpacing(32);
        item_layout->addWidget(volume_controller_);
        item_layout->addStretch();

        root_layout->addSpacing(25);
        root_layout->addLayout(item_layout);
    }

    root_layout->addStretch();

    setLayout(root_layout);
}

运行后,所有UI即可正常工作