Qt6开发离线音乐播放器(一):基本窗口

1. 目标

我们目标是做一个在各个平台上样式高度统一的软件,所以最好的选择是自定义一个基本窗口(QWidget),去掉平台附加的平台样式(边框、按钮等),最后的成品需要具备以下特性:

  • 无边框

  • 窗口阴影

  • 自定义标题栏

  • 可拖动

2. 实现

创建一个basewidget模块,包含一个继承QWidget的BaseWidget类:

basewidget
    - basewidget.pri
    - basewidget.h
    - basewidget.cpp

2.1 无边框

这个实现比较简单,在构造函数设置windowFlag就行

BaseWidget::BaseWidget(QWidget *parent) : QWidget{parent} {
    setWindowFlags(Qt::FramelessWindowHint);
}

2.2 窗口阴影

所有的无边框的窗口阴影实现方案都需要将最下面的widget背景透明:

setAttribute(Qt::WA_TranslucentBackground);

有些方案需要预留出绘制阴影的margin(比如预留8px):

setContentsMargins(8, 8, 8, 8);

之后绘制阴影有以下几种方法:

  • 预留margin,在paintEvent中用贴图的方式绘制四周阴影(需要一张渐变png)

  • 预留margin,在paintEvent或qss中用渐变色绘制阴影

  • 无需预留margin,使用QGraphicsDropShadowEffect设置阴影

这里我们采用第三种方案(因为足够简单)。

首先添加一个带ui文件的QWidget类(Qt 设计器界面类),继承BaseWidget类:

#pragma once

#include <basewidget/basewidget.h>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public BaseWidget {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};

在ui上添加一个QFrame,叫做container吧,设置布局,留出margin:

然后设置阴影:

MainWindow::MainWindow(QWidget *parent) : BaseWidget(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this);
    shadow->setOffset(0, 0);
    shadow->setColor(Qt::gray);
    shadow->setBlurRadius(32);
    ui->container->setGraphicsEffect(shadow);
}

阴影效果就出来了:

在部分linux发行版上,透明效果可能会被系统关闭(比如统信上可以关闭“窗口特效”),此时只能去除阴影效果。

2.3 自定义标题栏

一般标题栏就是左侧logo图片+title,右侧最小化/最大化/还原/关闭按钮,按需添加即可。这里有一个需要注意的事项,当窗口在最大化/非最大化之间切换时,最大化/还原按钮需要进行切换,可以在最大化/还原按钮的点击槽函数中进行判断处理,但是稍显麻烦,比如:

void MainWindow::maxBtnClicked() {
    showMaximized();
    ui->maxBtn->hide();
    ui->restoreBtn->show();
}

void MainWindow::restoreBtnClicked() {
    showNormal();
    ui->maxBtn->show();
    ui->restoreBtn->hide();
}

我们可以利用QWidget的resizeEvent重载函数来实现这一需求:

MainWindow::MainWindow(QWidget *parent) : BaseWidget(parent), ui(new Ui::MainWindow) {
    ...
    connect(ui->maxBtn, &QPushButton::clicked, this, &MainWindow::showMaximized);
    connect(ui->restoreBtn, &QPushButton::clicked, this, &MainWindow::showNormal);
}


void MainWindow::resizeEvent(QResizeEvent *ev) {
    Q_UNUSED(ev)
    ui->maxBtn->setHidden(isMaximized());
    ui->restoreBtn->setHidden(!isMaximized());
}

另外最大化时,窗口阴影一般要关闭,也可以在这里处理。

2.4 可拖动

安装窗口拖动这个需求可以通过mousePressEvent、mouseMoveEvent和mouseReleaseEvent这三个重载函数实现:

  • 定义一个QPoint类型成员变量m_pos,在鼠标按下时(mousePressEvent)记录点击坐标,并设置按下状态为true

  • 鼠标移动时(mouseMoveEvent),计算移动距离,用move设置窗口位置

  • 鼠标松开时(mouseReleaseEvent),取消按下状态

这一部分比较通用,我们写在BaseWidget里:

// basewidget.h
class BaseWidget : public QWidget {
    Q_OBJECT
public:
    explicit BaseWidget(QWidget *parent = nullptr);

protected:
    void mousePressEvent(QMouseEvent *ev) override;
    void mouseMoveEvent(QMouseEvent *ev) override;
    void mouseReleaseEvent(QMouseEvent *ev) override;

private:
    QPoint m_pos;
    bool m_pressed = false;
};

// basewidget.cpp
void BaseWidget::mousePressEvent(QMouseEvent* ev) {
    if ((Qt::LeftButton == ev->button()) && (!isMaximized())) {
        m_pressed = true;
        m_pos = ev->pos();
    }

    QWidget::mousePressEvent(ev);
}

void BaseWidget::mouseMoveEvent(QMouseEvent* ev) {
    if (m_pressed) {
        move(ev->pos() - m_pos + pos());
    }

    QWidget::mouseMoveEvent(ev);
}

void BaseWidget::mouseReleaseEvent(QMouseEvent* ev) {
    m_pressed = false;
    QWidget::mouseReleaseEvent(ev);
}

PS: 代码已经开源在github:linqiaqun/music-player: A cross platform music player (github.com) 欢迎star/fork/issue

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值