C++ Qt 学习(四):自定义控件与 qss 应用

1. qss 简介

  • Qt style sheet(qss,Qt 样式表),不需要用 C++ 代码控件进行重载,就可以修改控件外观,类似于前端的 css

2. qss 选择器

2.1 通配符选择器

/* 设置后控件窗口背景色都被修改为黄色 */
* {
    background-color:yellow;
}

/* 指明子类 */
* QPushButton{
    background-color:yellow;
}

2.2 类型选择器

/* 通过控件类型来匹配控件的 (包括子类) */
QWidget {
    background-color:yellow
}

/* 禁止父窗口影响子窗口样式 (不包括子类) */
setAttribute(Qt::WA_StyledBackground);

/* 在类前面加个 .(不包括子类),这样就只对 QWidget 生效,如果界面上有其它控件则不生效 */
.QWidget {
    background-color:yellow
}

2.3 ID 选择器

  • ID 选择器是结合控件的 objectname 来匹配控件的,qss 里 objectname 前加个 “#” 来表示
    /* 只对 pushButton_2 有效*/
    QPushButton#pushButton_2 {
        background-color:blue
    }
    

2.4 属性选择器

  • 属性选择器是结合控件的属性值来匹配控件的,首先要设定控件的属性,qss 里属性用 [proterty = attitude] 来限制
    label1.setProperty('notice_level', 'error')
    label2.setProperty('notice_level', 'warning')
    
    .QLabel {
        background-color:pink;
    }
    
    .QLabel[notice_level='warning'] {
        border:5px solid yellow;
    }
    
    .QLabel[notice_level='error'] {
        border:5px solid red;
    }
    

3. QLabel

/* 设置普通样式 */
QLabel {
    font-family: "Microsoft YaHei";  /*字体类型*/
    font-size: 18px;                 /*字体大小*/
    color: #BDC8E2;                  /*字体颜色*/    
    font-style: normal;              /*字体斜体样式*/
    font-weight: normal;             /*字体加粗样式*/
 
    /*设置边框属性*/
    border-style: solid;
    border-width: 2px;
    border-color: aqua;
    border-radius: 20px;
 
    /*设置文字显示位置*/
    padding-left: 20px;
    padding-top: 3px;
 
    /*设置背景样式*/
    background-color: #2E3648;
    background-image: url("./res/image/123.png");
    background-repeat: no-repeat;
    background-position: left center;
}

/* 设置鼠标悬浮样式*/
QLabel:hover {
    color: red;
    border-color: green;
    background-color: aqua;
}

/* 设置禁止样式 */
QLabel:disabled {
	color: blue;
	border-color: brown;
    background-color: #363636;
}

4. QLineEdit

C++ Qt 学习(四):自定义控件与 qss 应用_第1张图片

  • widget.ui
    C++ Qt 学习(四):自定义控件与 qss 应用_第2张图片

  • qss 样式

QWidget{
    background-color:rgb(54,54,54);
}

QLineEdit{
    border: 1px solid #ABCDA0;      /* 边框宽度为1px,颜色为#A0A0A0 */
    border-radius: 3px;             /* 边框圆角 */
    padding-left: 5px;              /* 文本距离左边界有5px */
    background-color: #F2F2F2;      /* 背景颜色 */
    color: black;                   /* 文本颜色 */
    selection-background-color: #A0A0A0;  /* 选中文本的背景颜色 */
    selection-color: #F2F2F2;         /* 选中文本的颜色 */
    font-family: "Microsoft YaHei";   /* 文本字体族 */
    font-size: 10pt;                  /* 文本字体大小 */
}
    
QLineEdit:hover { /* 鼠标悬浮在QLineEdit时的状态 */
    border: 1px solid #298DFF;
    border-radius: 3px;
    background-color: #F2F2F2;
    color: #298DFF;
    selection-background-color: #298DFF;
    selection-color: #F2F2F2;
}
    
QLineEdit[echoMode="2"] { /* QLineEdit有输入掩码时的状态 */
    lineedit-password-character: 9679;
    lineedit-password-mask-delay: 2000;
}

QLineEdit:disabled { /* QLineEdit在禁用时的状态 */
    border: 1px solid #CDCDCD;
    background-color: #CDCDCD;
    color: #B4B4B4;
}

QLineEdit:read-only { /* QLineEdit在只读时的状态 */
    background-color: #CDCDCD;
    color: #F2F2F2;
}
  • widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include 

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget {
    Q_OBJECT

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

private slots:
    void on_lineEdit_rex_returnPressed();
    void on_lineEdit_rex_inputRejected();
    void on_btnCheck_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include   // 使用正则表达式限制输入
#include 
#include 

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

    ui->lineEdit_1->setText("Hello LineEdit");

    ui->lineEdit_2->setReadOnly(true);  // 设置为只读状态
    ui->lineEdit_2->setText("Hello LineEdit");

    ui->lineEdit_3->setText("Hello LineEdit");
    ui->lineEdit_3->setEchoMode(QLineEdit::Password);  // 设置密码为圆点隐藏状态

    ui->lineEdit_4->setDisabled(true);  // 设置为不可用状态

    // 使用正则表达式(QRegExp)来验证电子邮件地址的格式
    QRegExp regx("\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}");
    // 指定验证的目标为 ui->lineEdit_rex(一个文本输入框)
    QValidator *validator = new QRegExpValidator(regx, ui->lineEdit_rex);
    // 将验证器应用到 lineEdit_rex上,这样用户在该输入框中输入文本时,将根据正则表达式进行验证
    ui->lineEdit_rex->setValidator(validator);
}

Widget::~Widget() {
    delete ui;
}

// 当用户在 lineEdit_rex 中按下回车键并释放时触发这个函数
void Widget::on_lineEdit_rex_returnPressed() {
    qDebug() << "on_lineEdit_rex_returnPressed";
}

// 当 lineEdit_rex 中的输入被验证器拒绝时触发这个函数
void Widget::on_lineEdit_rex_inputRejected() {
    qDebug() << "on_lineEdit_rex_inputRejected";
}

// 获取 lineEdit_rex 文本框的验证器,并根据输入的文本内容进行验证
void Widget::on_btnCheck_clicked() {
    // 验证器 validator 用于限制用户输入的内容符合特定的模式或规则
    const QValidator *v = ui->lineEdit_rex->validator();
    int pos = 0;
    
    // 获取 lineEdit_rex 文本框中的文本内容
    QString text = ui->lineEdit_rex->text();
    // 用于检查刚才获取的文本内容是否满足验证器的要求
    if (v->validate(text, pos) != QValidator::Acceptable) {
        ui->lineEdit_rex->setText(QString::fromLocal8Bit("邮箱格式不正确"));
    } else {
        QMessageBox::information(this, u8"标题", u8"邮箱格式正确");
    }
}

5. QPushButton

5.1 点击按钮弹出菜单

C++ Qt 学习(四):自定义控件与 qss 应用_第3张图片

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include 
#include 
#include 
#include "qss.h"

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);
    this->setStyleSheet("background-color:rgb(54,54,54)");

    // 1、创建 文件 菜单项
    QMenu *fileMenuItems = new QMenu;
    fileMenuItems->setIcon(QIcon(":/resources/file.png"));
    fileMenuItems->setTitle(u8"文件");
    fileMenuItems->setStyleSheet(QString::fromStdString(menuItemQss));
    QList<QAction*> acList;  // 用于存放子级菜单

    // 为 文件 菜单项添加 action 构成子菜单
    QAction *openFileAc = new QAction(QIcon(":/resources/file.png"), u8"打开文件", this);
    //openFileAc->setShortcuts(QKeySequence::Print);   // 设置快捷键,系统内置枚举量
    openFileAc->setShortcut(QKeySequence("Ctrl+8"));   // 随意指定快捷键
    QAction *openFloderAc = new QAction(u8"打开文件夹", this);
    QAction *openUrlAc = new QAction(u8"打开url", this);

    // QMenu 添加 QMenu 构成多级菜单
    acList << openFileAc << openFloderAc << openUrlAc;
    fileMenuItems->addActions(acList);

    // 0、主菜单(父菜单,包括 文件 播放 工具 设置 退出)
    QMenu *pMenu = new QMenu;
    pMenu->addMenu(fileMenuItems);

    // 2、创建 播放 工具 菜单项(无子级菜单)
    QAction *play = new QAction(QIcon(":/resources/play.png"), u8"播放", this);
    QAction *tools = new QAction(QIcon(":/resources/tools.png"), u8"工具", this);
    pMenu->addAction(play);
    pMenu->addAction(tools);

    pMenu->addSeparator();  // 添加分割线

    // 3、创建 设置 菜单项
    QMenu *setMenuItems = new QMenu;
    setMenuItems->setTitle(u8"设置");
    setMenuItems->setIcon(QIcon(":/resources/set.png"));
    QList<QAction*> setList;

    // 为 设置 菜单项添加 action 构成子菜单
    QAction *sysSetAc = new QAction(u8"系统设置", this);
    QAction *playSetAc = new QAction(u8"播放设置", this);
    QAction *zimuSetAc = new QAction(u8"字幕设置", this);
    setList << sysSetAc << playSetAc << zimuSetAc;
    setMenuItems->addActions(setList);
    setMenuItems->setStyleSheet(QString::fromStdString(menuItemQss));
    pMenu->addMenu(setMenuItems);

    pMenu->addSeparator();  // 添加分割线

    // 4、创建 退出 菜单项
    QAction *exitAc = new QAction(QIcon(":/resources/exit.png"), u8"退出", this);
    pMenu->addAction(exitAc);

    ui->pushButton->setMenu(pMenu);

    connect(openFileAc, &QAction::triggered, [=]{
        QString fileName = QFileDialog::getOpenFileName(this,
                                                        u8"请选择视频文件",
                                                        "D:/",
                                                        "视频(*.mp4 *.flv);;");
        if (fileName.isEmpty()) {
            return;
        }
    });

    ui->pushButton->setText(u8"QW影音");
    ui->pushButton->setFixedSize(100, 32);
    ui->pushButton->setStyleSheet(QString::fromStdString(button_qss));
    pMenu->setStyleSheet(QString::fromStdString(menuQss));
}

Widget::~Widget() {
    delete ui;
}
  • qss.h
#ifndef QSS_H
#define QSS_H

#include 

using namespace std;

string button_qss = R"(
    QPushButton {
        font:18px "Microsoft YaHei";
        color:rgb(255,255,255);
        border:none
    }

    QPushButton::menu-indicator:open {
        image:url(:/resources/down_arrow.svg);
        subcontrol-position:right center;
        subcontrol-origin:padding;border:none;
    }

    QPushButton::menu-indicator:closed {
        image:url(:/resources/up_arrow.svg);
        subcontrol-position:right center;
        subcontrol-origin:padding;border:none;
    }
)";

string menuQss = R"(
    QMenu {
        background-color:rgb(53, 63, 73);
    }

    QMenu::item {
        font:16px;
        color:white;
        background-color:rgb(53, 63, 73);
        padding:8px 32px;
        margin:8px 8px;
        /*border-bottom:1px solid #DBDBDB;  item底部颜色*/
    }

    /*选择项设置*/
    QMenu::item:selected {
        background-color:rgb(54, 54, 54);
    }
)";

string menuItemQss = R"(
    QMenu {
        background-color:rgb(73, 73, 73);
    }

    QMenu::item {
        font:16px;
        color:white;
        background-color:rgb(73, 73, 73);
        padding:8px 32px;
        margin:8px 8px;
        /*border-bottom:1px solid #DBDBDB;  item底部颜色*/
    }

    /*选择项设置*/
    QMenu::item:selected {
         background-color:rgb(54, 54, 54);
    }
)";

#endif // QSS_H

5.2 鼠标悬浮弹出对话框

C++ Qt 学习(四):自定义控件与 qss 应用_第4张图片

5.2.1 主窗口
  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include 
#include "CVolumeButton.h"

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

    resize(800, 600);

    QHBoxLayout *pHlay = new QHBoxLayout(this);

    // 音量调节按钮
    CVolumeButton* pVolumeButton = new CVolumeButton(this);
    pHlay->addWidget(pVolumeButton);
}

Widget::~Widget() {
    delete ui;
}
5.2.2 音量按钮
  • CVolumeButton.h
/*
音量调节按钮
功能:
    1. 鼠标悬浮到音量时显示 slider dialog
    2. 点击时 mute
注意问题:
    重写按钮类,样式表无效
*/
#pragma once

#include 
#include "CVolumeSliderDialog.h"

class CVolumeButton : public QPushButton {
    Q_OBJECT

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

    bool getMute() const {
        return m_isMute;
    }
    // 设置静音
    void setMute(bool mute) { 
        m_isMute = mute;
    }

signals:
    void sig_VolumeValue(int value);

protected:
    void paintEvent(QPaintEvent* event) override;
    void enterEvent(QEvent* event) override;
    //void leaveEvent(QEvent* event) override;
    void mousePressEvent(QMouseEvent* event) override;
    void timerEvent(QTimerEvent* event) override;

private:
    bool m_isMute = false;  // 是否静音
    CVolumeSliderDialog* m_pVolumeSliderDlg = nullptr;

    int m_timerId = -1;
};
  • CVolumeButton.cpp
#include "CVolumeButton.h"
#include 
#include 
#include 
#include 
#include 

using namespace std;

CVolumeButton::CVolumeButton(QWidget* parent) : QPushButton(parent) {
    this->setFixedSize(32, 32);  // 音量按钮大小

    setStyleSheet("QPushButton{background-image:url(:/resources/audio_open.svg);border:none;}"
        "QPushButton:hover{background-image:url(:/resources/audio_open_hover.svg);border:none;}"
        "QPushButton:pressed{background-image:url(:/resources/audio_open.svg);border:none;}");
}

CVolumeButton::~CVolumeButton() {}

// 绘制一个样式化的按钮,以便与应用程序的整体外观和主题保持一致
void CVolumeButton::paintEvent(QPaintEvent*) {
    QStylePainter p(this);  // QStylePainter 用于样式化绘制 widget
    QStyleOptionButton option;  // 用于存储按钮的样式选项
    initStyleOption(&option);   // 初始化 option 对象,以便正确设置按钮的样式选项
    p.drawControl(QStyle::CE_PushButton, option);  // 绘制一个样式化的按钮
}

// 在鼠标进入 CVolumeButton 对象时显示一个音量滑动条,其位置在 CVolumeButton 的正上方
void CVolumeButton::enterEvent(QEvent* event) {
	if (!m_pVolumeSliderDlg)
        m_pVolumeSliderDlg = new CVolumeSliderDialog(this);

    // 将 widget 坐标 pos 转换为全局屏幕坐标
    QPoint p1 = this->mapToGlobal(QPoint(0, 0));  // 声音按钮左上角相对于桌面的绝对位置
	QRect rect1 = this->rect();
    QRect rect2 = m_pVolumeSliderDlg->rect();     // rect 包含标题栏,去掉标题栏后 height 不变

	int x = p1.x() + (rect1.width() - rect2.width()) / 2;
	int y = p1.y() - rect2.height() - 5;
    m_pVolumeSliderDlg->move(x, y);               // move 是相对于桌面原点的位置

	m_pVolumeSliderDlg->show();
    m_timerId = startTimer(250);

	connect(m_pVolumeSliderDlg, &CVolumeSliderDialog::sig_SliderValueChanged, [=](int value) {
		emit sig_VolumeValue(value);
    });
}

// 鼠标移出控件或窗口
//void CVolumeButton::leaveEvent(QEvent* event)
//{
//	根据鼠标的位置判断音量调节窗口是否消失
//	//QPoint p1 = QCursor::pos();   // 绝对位置
//
//	//cout << "QCursor x= " << p1.x() << " y = " << p1.y() << endl;
//
//	//if (m_pVolumeSliderDlg) {
//	//	QRect rect1 = this->rect();  // 按钮矩形
//	//	QRect rect2 = m_pVolumeSliderDlg->rect();
//	//	QRect rect3 = m_pVolumeSliderDlg->geometry();
//
//	//	QPoint p2 = this->mapToGlobal(QPoint(0, 0));   // 声音按钮左上角相对于桌面的绝对位置
//
//	//	// 已知:音量框宽 40 > 按钮宽 30
//	//  // 左上宽高
//	//	QRect area(rect3.left(), rect3.top(), rect2.width(), p2.y() + rect1.height() - rect3.top());
//	//	cout << "p1 x = " << p1.x() << " y = " << p1.y() << endl;
//
//	//	if (!area.contains(p1)) {
//	//		m_pVolumeSliderDlg->hide();
//	//	}
//	//}
//}

// 鼠标按下事件,用于处理音量按钮的交互行为
void CVolumeButton::mousePressEvent(QMouseEvent* event) {
    if (event->button() == Qt::LeftButton) {  // 检测鼠标事件的按钮类型是否为左键
        m_isMute = !m_isMute;
        if (m_isMute) {  // 如果是左键点击,则切换静音状态
			if (m_pVolumeSliderDlg)
				m_pVolumeSliderDlg->setSliderValue(0);
        } else {         // 如果静音状态为假,将滑动条的值设置为 50
			if (m_pVolumeSliderDlg)
				m_pVolumeSliderDlg->setSliderValue(50);
		}
	}
}

/**
 * @brief 用定时器模拟 leaveEvent
 * 直接在 leaveEvent 里让 m_pVolumeSliderDlg 消失,效果不太好
 * 用鼠标移动事件也不太好,定时器是比较好的做法
 */
void CVolumeButton::timerEvent(QTimerEvent* event) {
    if ((m_pVolumeSliderDlg != nullptr) && (m_pVolumeSliderDlg->isVisible())) {
        QPoint p1 = QCursor::pos();      // 鼠标绝对位置
        if (m_pVolumeSliderDlg) {
            QRect rect1 = this->rect();  // 按钮矩形
			QRect rect2 = m_pVolumeSliderDlg->rect();
			QRect rect3 = m_pVolumeSliderDlg->geometry();

            QPoint p2 = this->mapToGlobal(QPoint(0, 0));   // 声音按钮左上角相对于桌面的绝对位置

            // 已知:音量框宽 40 > 按钮宽 30
            // 左上宽高
            QRect area(rect3.left(), rect3.top(), rect2.width(), p2.y() + rect1.height() - rect3.top());

            if (!area.contains(p1)) {
				m_pVolumeSliderDlg->hide();
			}
		}
    } else {
        killTimer(m_timerId);
	}
}
5.2.3 音量调节滑动条
  • CVolumeSliderDialog.h
#ifndef CVOLUMESLIDERDIALOG_H
#define CVOLUMESLIDERDIALOG_H

#include 
#include 

class CVolumeSliderDialog : public QDialog {
	Q_OBJECT

public:
	CVolumeSliderDialog(QWidget *parent = Q_NULLPTR);
	~CVolumeSliderDialog();

    void setSliderValue(int value) {
		m_pSlider->setValue(value);
	}

protected:
	bool event(QEvent* event) override;

signals:
	void sig_SliderValueChanged(int value);

private:
	QSlider* m_pSlider = nullptr;
};

#endif
  • CVolumeSliderDialog.cpp
#include "CVolumeSliderDialog.h"
#include 
#include 
#include 

// 注意由于此类使用了 windows 的函数 SetClassLong,需要包含 user32.lib
// 如果是在 vs2019 中使用则不需要包含 user32.lib
#pragma comment(lib, "user32.lib")

CVolumeSliderDialog::CVolumeSliderDialog(QWidget* parent) : QDialog(parent) {
    // 创建竖直滑动条并添加进竖直布局中
	this->setFixedSize(40, 200);
	QVBoxLayout* pVLay = new QVBoxLayout(this);
	m_pSlider = new QSlider(this);
	m_pSlider->setOrientation(Qt::Vertical);
	pVLay->addWidget(m_pSlider);

    // 创建滑动按钮
	setFixedSize(40, 120);
    setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip);   // ToolTip : 悬浮是显示,离开时消失
    // 0.5 表示透明度,0 表示全透明、1 表示不透明;也可以使用百分比表示
    setStyleSheet("QDialog{background-color: rgba(54, 54, 54, 0.5);}");

	connect(m_pSlider, &QSlider::valueChanged, [=](int value) {
		emit sig_SliderValueChanged(value);
    });
}

CVolumeSliderDialog::~CVolumeSliderDialog() {}

// 参考 qt 文档:bool QWidget::event(QEvent *event)
// 设置 popup 后,dialog 有窗口阴影,需要去除就重写 event 函数
bool CVolumeSliderDialog::event(QEvent* event) {
    static bool class_amended = false;

    if (event->type() == QEvent::WinIdChange) {
        HWND hwnd = (HWND)winId();

        if (class_amended == false) {
            class_amended = true;
            DWORD class_style = ::GetClassLong(hwnd, GCL_STYLE);
            class_style &= ~CS_DROPSHADOW;
            ::SetClassLong(hwnd, GCL_STYLE, class_style); // windows 系统函数
        }
    }

	return QWidget::event(event);
}

6. QCheckBox 实现打开、关闭状态按钮

C++ Qt 学习(四):自定义控件与 qss 应用_第5张图片

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include 

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

    ui->checkBox->setFixedSize(128, 64);

    QString qss = "QCheckBox::indicator:unchecked{ \
        image:url(:/resources/status_close.png); \
        } \
        QCheckBox::indicator:checked { \
        image: url(:/resources/status_open.png); \
    }";

    ui->checkBox->setStyleSheet(qss);
    ui->checkBox->setChecked(true);
    //ui->checkBox->setTristate(true);

    connect(ui->checkBox, &QCheckBox::stateChanged, [=](int state){
        qDebug() << state;
    });
}

Widget::~Widget() {
    delete ui;
}

7. QComboBox 样式表

C++ Qt 学习(四):自定义控件与 qss 应用_第6张图片

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include 
#include 

using namespace std;

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

    QStringList strList;
    strList << "item1" << "item2" << "item3" << "item4"
            << "item1" << "item2" << "item3" << "item4"
            << "item1" << "item2" << "item3" << "item4"
            << "item1" << "item2" << "item3" << "item4";

    ui->comboBox->addItems(strList);
    // 将 QComboBox 的下拉列表视图设置为 QListView,并将此视图限定在当前的 QWidget 类的范围内显示
    ui->comboBox->setView(new QListView(this));
    ui->comboBox->setEditable(true);
    //ui->comboBox->insertSeparator(10);
    //ui->comboBox->insertSeparator(12);
}

Widget::~Widget() {
    delete ui;
}
  • qss
/* 未下拉时,QComboBox的样式 */
QComboBox {
    border: 1px solid gray;   /* 边框 */
    border-radius: 5px;   /* 圆角 */
    padding: 1px 18px 1px 3px;   /* 字体填衬 */
    color: white;
    font: normal normal 24px "Microsoft YaHei";
    background:rgb(54,54,54);
}

/* 下拉后,整个下拉窗体样式 */
QComboBox QAbstractItemView {
    outline: 0px solid gray;   /* 选定项的虚框 */
    border: 1px solid yellow;   /* 整个下拉窗体的边框 */
    color: rgb(250,251,252);
    background-color: rgb(70,80,90);   /* 整个下拉窗体的背景色 */
    selection-background-color: lightgreen;   /* 整个下拉窗体被选中项的背景色 */
}

/* 下拉后,整个下拉窗体每项的样式 */
/* 项的高度(设置 ComboBox->setView(new QListView(this)); 后该项才起作用) */
QComboBox QAbstractItemView::item {
    height: 50px;   
}

/* 下拉后,整个下拉窗体越过每项的样式 */
QComboBox QAbstractItemView::item:hover {
    color: rgb(90,100,105);
    background-color: lightgreen;   /* 整个下拉窗体越过每项的背景色 */
}

/* 下拉后,整个下拉窗体被选择的每项的样式 */
QComboBox QAbstractItemView::item:selected {
    color: rgb(12, 23, 34);
    background-color: lightgreen;
}

/* QComboBox中的垂直滚动条 */
QComboBox QAbstractScrollArea QScrollBar:vertical {
    width: 13px;
    background-color: #d0d2d4;   /* 空白区域的背景色*/
}

QComboBox QAbstractScrollArea QScrollBar::handle:vertical {
    border-radius: 5px;   /* 圆角 */
    background: rgb(60,60,60);   /* 小方块的背景色深灰lightblue */
}

QComboBox QAbstractScrollArea QScrollBar::handle:vertical:hover {
    background: rgb(90, 91, 93);   /* 越过小方块的背景色yellow */
}

/* 设置为可编辑(setEditable(true))editable时,编辑区域的样式 */
QComboBox:editable {
    background: green;
}

/* 设置为非编辑(setEditable(false))!editable时,整个QComboBox的样式 */
QComboBox:!editable {
    background: rgb(54,54,54);
}

/* 设置为可编辑editable时,点击整个QComboBox的样式 */
QComboBox:editable:on {
    background: rgb(54,54,54);
}

/* 设置为非编辑!editable时,点击整个QComboBox的样式 */
QComboBox:!editable:on {
    background: rgb(54,54,54);
}

/* 设置为可编辑editable时,下拉框的样式 */
QComboBox::drop-down:editable {
    background: rgb(54,54,54);
}

/* 设置为可编辑editable时,点击下拉框的样式 */
QComboBox::drop-down:editable:on {
    background: rgb(54,54,54);
}

/* 设置为非编辑!editable时,下拉框的样式 */
QComboBox::drop-down:!editable {
    background: rgb(54,54,54);
}

/* 设置为非编辑!editable时,点击下拉框的样式 */
QComboBox::drop-down:!editable:on {
    background: rgb(54,54,54);
    image: url(:/resources/up.png); /* 显示上拉箭头 */ 
}

/* 下拉框样式 */
QComboBox::drop-down {
    subcontrol-origin: padding;   /* 子控件在父元素中的原点矩形。如果未指定此属性,则默认为padding。 */
    subcontrol-position: top right;   /* 下拉框的位置(右上) */
    width: 32px;   /* 下拉框的宽度 */

    border-left-width: 1px;   /* 下拉框的左边界线宽度 */
    border-left-color: darkgray;   /* 下拉框的左边界线颜色 */
    border-left-style: solid;   /* 下拉框的左边界线为实线 */
    border-top-right-radius: 3px;   /* 下拉框的右上边界线的圆角半径(应和整个QComboBox右上边界线的圆角半径一致) */
    border-bottom-right-radius: 3px;   /* 同上 */
    image: url(:/resources/down.png); 
}

/* 越过下拉框样式 */
QComboBox::drop-down:hover {
    background: rgb(80, 75, 90);
}

/* 下拉箭头样式 */ 
QComboBox::down-arrow {  
    width: 32px; /* 下拉箭头的宽度(建议与下拉框drop-down的宽度一致) */   
    background: rgb(54,54,54); /* 下拉箭头的的背景色 */   
    padding: 0px 0px 0px 0px; /* 上内边距、右内边距、下内边距、左内边距 */  
    image: url(:/resources/down.png); 
} 

/* 点击下拉箭头 */ 
QComboBox::down-arrow:on {   
    image: url(:/resources/up.png); /* 显示上拉箭头 */ 
}

8. QProgressBar 用法及样式表

C++ Qt 学习(四):自定义控件与 qss 应用_第7张图片

  • widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include 
#include 

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget {
    Q_OBJECT

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

private slots:
    void on_btnStart_clicked();
    void on_btnStop_clicked();

private:
    Ui::Widget *ui;

    QTimer *m_pTimer = nullptr;
};
#endif // WIDGET_H
  • widget.cpp
#include "widget.h"
#include "ui_widget.h"

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

    m_pTimer = new QTimer(this);

    ui->progressBar->setValue(0);
    ui->progressBar->setRange(0, 100);
    ui->progressBar->setFormat("%p%");
    
    // lambda 表达式用于每个定时器超时时更新 QProgressBar 组件的当前进度值,其中的 step 变量用于存储当前进度值
    // 当定时器超时时,step 的值会自增 1,并将新的进度值设置到 QProgressBar 组件上
    connect(m_pTimer, &QTimer::timeout, [=]{
        static int step = 0;
        ui->progressBar->setValue(step++);
    });

    ui->progressBar_2->setOrientation(Qt::Vertical);
    ui->progressBar_2->setFixedWidth(60);
    ui->progressBar_2->setFixedHeight(300);
}

Widget::~Widget() {
    delete ui;
}

void Widget::on_btnStart_clicked() {
    m_pTimer->start(50);
}

void Widget::on_btnStop_clicked() {
    m_pTimer->stop();
}
  • qss
QProgressBar {
      background:rgb(54,54,54);
      border:none;   /*无边框*/
      border-radius:5px;
      text-align:center;   /*文本的位置*/
      color: rgb(229, 229, 229);  /*文本颜色*/
}
 
QProgressBar::chunk  {
      background-color:rgb(58, 154, 255);
      border-radius:4px;
}
QProgressBar:vertical {
	border-radius:5px;
	background-color:darkgray;
	text-align:center;
	padding-left: 5px; padding-right: 4px; padding-bottom: 2px; 
}

QProgressBar::chunk:vertical {
	background-color:QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #00ff58,stop: 1 #034f1f);
	margin:1px;
}

9. QSlider 用法及样式表

C++ Qt 学习(四):自定义控件与 qss 应用_第8张图片

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "qss.h"

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

    ui->hSlider->setStyleSheet(QString::fromStdString(hslider_qss));
}

Widget::~Widget() {
    delete ui;
}
  • qss.h
#ifndef QSS_H
#define QSS_H

#include 

using namespace std;

string hslider_qss = R"(
    QSlider {
        background-color: #FF0000;
        border-style: outset;
        border-radius: 1px;
    }

    QSlider::groove:horizontal {
        height: 12px;
        background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B1B1B1, stop:1 #c4c4c4);
        margin: 2px 0
    }

    QSlider::handle:horizontal {
        background: QRadialGradient(cx:0, cy:0, radius: 1, fx:0.5, fy:0.5, stop:0 white, stop:1 green);
        width: 16px;
        height: 16px;
        margin: -5px 6px -5px 6px;
        border-radius:11px;
        border: 3px solid #ffffff;
    }
)";

#endif // QSS_H

10. qss 加载方式详解

  • 方式一:在 qt 设计器里写

  • 方式二:C++ 代码写

    • QString 或 R 字符串
  • 方式三:写到 qss 文件里,读文件

    • 放到程序外部,暴露给用户
    • 加到 qrc 文件里,编译到 exe 里
  • widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include 

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

    QFile file(":/res/skin.qss");

    QString lineStr;
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QTextStream txtInput(&file);
        while (!txtInput.atEnd()) {
            lineStr += txtInput.readLine();  // 逐行读取 qss 代码
        }
    }
    file.close();

    this->setStyleSheet(lineStr);
}

Widget::~Widget() {
    delete ui;
}
  • skin.css
QWidget {
    background-color: rgb(54, 54, 54);
    border-top:2px;
    border-bottom:2px;
    border-left:2px;
    border-right:2px;
}
 
QLineEdit {
    background-color: rgb(249, 249, 249); 
    border: 1px solid black;
    border-radius:5;
    font:14px;
}
 
QLabel {
    background-color: rgb(54, 54, 54); 
    font:12px;
    color:white;
}
 
QPushButton {
    color:rgb(251,251,251);    
    font:12px, "微软雅黑";
    background-color:rgb(105, 105, 105);
    border-radius:4px;
	padding:2px; 
}
 
QPushButton:hover {
    color:#0000ff;
    background-color:rgb(210, 205, 205); /*改变背景色*/
    border-style:inset;/*改变边框风格*/
    padding-left:2px;
    padding-top:2px;
} 
 
QPushButton:flat {  
    border:2px solid red;  
} 
 
QPushButton:pressed {
    color:green;
} 
 
QPlainTextEdit {
    background-color: rgb(169, 169, 169); 
    font:14px;
    color:white;
}

11. 控件提升与自定义控件

  • 控件提升相当于把控件变成另一个控件,或者说称为自定义控件,控件提升需要在 qt 设计器里操作
  • 案例:按钮图片在上,文字在下

在这里插入图片描述

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"

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

    ui->pushButton->setText("");
    ui->pushButton->setFixedSize(64, 88);
}

Widget::~Widget() {
    delete ui;
}

C++ Qt 学习(四):自定义控件与 qss 应用_第9张图片

  • cmybutton.h
#ifndef CMYBUTTON_H
#define CMYBUTTON_H

#include 
#include 
#include 

class CMyButton : public QPushButton {
    Q_OBJECT

public:
    CMyButton(QWidget *parent = nullptr);
    void set_Text(const QString& text);

private:
    QLabel *m_pIconLabel;
    QLabel *m_pTextLabel;
};

#endif // CMYBUTTON_H
  • cmybutton.cpp
#include "cmybutton.h"
#include 

CMyButton::CMyButton(QWidget *parent) : QPushButton(parent) {
    this->setFixedSize(64, 88);
    this->setText("");

    m_pTextLabel = new QLabel(this);
    m_pIconLabel->setFixedSize(64, 64);
    m_pIconLabel->setPixmap(QPixmap(":/resources/save.png"));

    m_pTextLabel = new QLabel(this);
    m_pTextLabel->setFixedSize(64, 24);
    m_pTextLabel->setText(u8"保存");

    QVBoxLayout* pVlay = new QVBoxLayout(this);
    pVlay->addWidget(m_pIconLabel);
    //pVlay->addSpacing(5);
    pVlay->addWidget(m_pTextLabel);
    pVlay->setContentsMargins(0, 0, 0, 0);
}

void CMyButton::set_Text(const QString& text) {
    m_pTextLabel->setText(text);
}

12. Qt 鼠标、控件、窗口位置详解

C++ Qt 学习(四):自定义控件与 qss 应用_第10张图片

  • 桌面原点:在电脑桌面左上角(上图红点处)
  • 应用程序原点:在应用程序左上角
  • 坐标系一般都是 x 向左为正,y 向下为正
  • 绝对位置:相对于电脑桌面左上角的位置
  • 相对位置:相对于应用程序左上角的位置

12.1 鼠标的位置

  • 获取鼠标相对于桌面左上角的绝对位置

    QCursor::pos()
    
  • 获取 mousePressEvent 的参数 event 的位置

    event->pos()        // 鼠标相对于应用程序的位置,相对位置
    event->globalPos()  // 鼠标相对于桌面原点的位置,绝对位置
    

12.2 控件的位置

  • 相对位置
    • 按钮相对于应用窗口原点的位置
    QPoint p = ui->pushButton->pos();
    
  • 绝对位置
    • 按钮相对于桌面原点的位置
    QPoint p = ui->pushButton->mapToGlobal(QPoint(0, 0));
    
  • 控件大小
    QRect rect = ui->pushButton->rect();
    

12.3 应用程序窗口的位置

  • 相对位置
    QRect rect = m_pDlg->pos();
    
  • 绝对位置
    • 对话框相对于桌面原点的 rect
    QRect rect = m_pDlg->geometry();
    
  • 应用窗口大小
    QRect rect = m_pDlg->rect();
    

12.4 案例

C++ Qt 学习(四):自定义控件与 qss 应用_第11张图片

C++ Qt 学习(四):自定义控件与 qss 应用_第12张图片

  • widget.cpp
#ifndef WIDGET_H
#define WIDGET_H

#include 

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget {
    Q_OBJECT

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

private slots:
    void on_pushButton_clicked();

private:
    void mousePressEvent(QMouseEvent *event) override;

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include 
#include 
#include "tempdialog.h"
#include 

using namespace std;

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

    ui->pushButton->setFixedSize(100,200);
}

Widget::~Widget() {
    delete ui;
}

void Widget::mousePressEvent(QMouseEvent *event) {
    qDebug() << QCursor::pos();  // 鼠标绝对位置

    qDebug() << "event->pos()         " << event->pos();
    qDebug() << "event->globalPos()   " << event->globalPos();
}

void Widget::on_pushButton_clicked() {
    qDebug() << u8"控件相对位置" << ui->pushButton->pos();

    QPoint jPos = ui->pushButton->mapToGlobal(QPoint(0,0));
    qDebug() << u8"控件绝对位置" << jPos;


    QRect rect = ui->pushButton->rect();
    qDebug() << rect;

    qDebug() << u8"窗口绝对位置" << this->geometry();  // 绝对位置
    qDebug() << u8"窗口矩形" << this->rect();

    //TempDialog *dlg = new TempDialog(this);
    std::unique_ptr<TempDialog> dlg(new TempDialog(this));

    dlg->move(jPos.x() - dlg->width(), jPos.y());

    dlg->exec();
}
  • tempdialog.h
#ifndef TEMPDIALOG_H
#define TEMPDIALOG_H

#include 

namespace Ui {
    class TempDialog;
}

class TempDialog : public QDialog {
    Q_OBJECT

public:
    explicit TempDialog(QWidget *parent = nullptr);
    ~TempDialog();

private:
    Ui::TempDialog *ui;
};

#endif // TEMPDIALOG_H
  • tempdialog.cpp
#include "tempdialog.h"
#include "ui_tempdialog.h"
#include 

TempDialog::TempDialog(QWidget *parent) : QDialog(parent), ui(new Ui::TempDialog) {
    ui->setupUi(this);
}

TempDialog::~TempDialog() {
    delete ui;
    qDebug() << "123456";
}

13. 自定义 QLineEdit 实现搜索编辑框

C++ Qt 学习(四):自定义控件与 qss 应用_第13张图片

13.1 主窗口

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include 
#include "csearchlineedit.h"
#include 

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

    this->setStyleSheet("background-color:#1A1E21");

    // 创建搜索栏实例化对象 pEdit
    CSearchLineEdit* pEdit = new CSearchLineEdit(this);

    QVBoxLayout* pVLay = new QVBoxLayout(this);
    pVLay->addWidget(pEdit);

    // 链接 搜索栏 搜索信号与 主窗口 控制台打印槽函数
    connect(pEdit, &CSearchLineEdit::sig_Search, this, &Widget::onSearch);
}

Widget::~Widget() {
    delete ui;
}

// 在控制台打印搜索的内容
void Widget::onSearch(const QString& text) {
    qDebug() << u8"搜索的内容是 " << text;
}

13.2 搜索输入栏

  • csearchlineedit.h
#ifndef CSEARCHLINEEDIT_H
#define CSEARCHLINEEDIT_H

#include 
#include 
#include "csearchbutton.h"

class CSearchLineEdit : public QLineEdit {
    Q_OBJECT

public:
    CSearchLineEdit(QWidget *parent = nullptr);

signals:
    void sig_Search(const QString& text);

private:
    CSearchButton* m_pBtn = nullptr;
};

#endif // CSEARCHLINEEDIT_H
  • csearchlineedit.cpp
#include "csearchlineedit.h"
#include 
#include 

using namespace std;

CSearchLineEdit::CSearchLineEdit(QWidget *parent) : QLineEdit(parent) {
    // 将当前的窗口部件设置为使用样式背景
    this->setAttribute(Qt::WA_StyledBackground);

    string qss = R"(
        QLineEdit{
            background-color:#33373E;             /* 背景颜色 */
            border: 1px solid #33373E;            /* 边框宽度为1px,颜色为#A0A0A0 */
            border-radius: 20px;                  /* 边框圆角 */
            padding-left: 10px;                   /* 文本距离左边界有5px */
            color: #FFFFFF;                       /* 文本颜色 */
            selection-background-color: #A0A0A0;  /* 选中文本的背景颜色 */
            selection-color: #F2F2F2;             /* 选中文本的颜色 */
            font-family: \"Microsoft YaHei\";     /* 文本字体族 */
            font-size:18px;                       /* 文本字体大小 */
        }
    )";

    this->setStyleSheet(QString::fromStdString(qss));
    this->setPlaceholderText(u8"请输入搜索内容");  // 只要行编辑为空,行编辑就会显示一个变灰显示占位符文本

    this->setFixedHeight(40);
    this->setMinimumWidth(400);

    // 创建搜索按钮实例化对象 m_pBtn
    m_pBtn = new CSearchButton(this);

    QHBoxLayout* pHlay = new QHBoxLayout(this);
    pHlay->addStretch();  // 添加弹簧挤压占位
    pHlay->addWidget(m_pBtn);
    // 设置控件周围边框大小
    pHlay->setContentsMargins(0, 0, 0, 0);
    // 设置文本周围的边距
    this->setTextMargins(10, 0, 130 + 5, 0);

    // 链接 搜索按钮点击信号 与 搜索栏 搜索信号
    connect(m_pBtn, &CSearchButton::clicked, [=]{
        emit sig_Search(this->text());
    });
}

13.3 搜索按钮

  • csearchbutton.h
#ifndef CSEARCHBUTTON_H
#define CSEARCHBUTTON_H

#include 
#include 

class CSearchButton : public QPushButton {
    Q_OBJECT

public:
    CSearchButton(QWidget *parent = nullptr);

private:
    void normalStyle();  // 按钮控件默认样式

protected:
    // 鼠标进入事件
    void enterEvent(QEvent *event) override;
    // 鼠标离开事件
    void leaveEvent(QEvent *event) override;
};

#endif // CSEARCHBUTTON_H
  • csearchbutton.cpp
#include "csearchbutton.h"
#include 

using namespace std;

CSearchButton::CSearchButton(QWidget *parent) : QPushButton(parent) {
    this->setAttribute(Qt::WA_StyledBackground);

    this->setFixedHeight(40);

    normalStyle();  // 设置默认样式
}

// 鼠标进入按钮控件时触发的事件
void CSearchButton::enterEvent(QEvent *event) {
    string qss = R"(QPushButton{
        background-color:#148AFF;
        background-image: url(:/resources/search.png);
        background-repeat: no-repeat;
        background-position:left;
        background-origin:content;
        padding-left:15px; /*图片相对于左边的偏移*/
        text-align:right;  /*文本的对齐方式*/
        padding-right:15px; /*文本相对于右边的偏移*/
        border-radius:20px;
        color:#FFFFFF;
        font-family: \"Microsoft YaHei\";
        font-size: 20px;
    })";

    this->setStyleSheet(QString::fromStdString(qss));

    this->setFixedWidth(130);
    this->setText(u8"搜全网");
}

// 鼠标离开按钮控件时触发的事件
void CSearchButton::leaveEvent(QEvent *event) {
    normalStyle();
}

// 鼠标离开按钮控件时的 默认样式
void CSearchButton::normalStyle() {
    string qss = R"(QPushButton{
        background-color:#148AFF;
        background-image: url(:/resources/search.png);
        background-repeat: no-repeat;
        background-position: center;
        border-radius:20px;
    })";

    this->setStyleSheet(QString::fromStdString(qss));

    this->setFixedWidth(60);
    this->setText(u8"");
}

14. 自定义 QTabWidget 实现 tab 在左,文本水平

C++ Qt 学习(四):自定义控件与 qss 应用_第14张图片

14.1 主窗口

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include 
#include "tabwidget.h"

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

    // 创建 标签窗口 实例对象
    TabWidget *tabWidget = new TabWidget(this);

    // 分别插入三个标签页
    QWidget* w1 = new QWidget;
    w1->setStyleSheet("background-color:rgb(54,54,54)");
    // int insertTab(int index, QWidget *widget, const QString &);
    tabWidget->insertTab(0, w1, u8"参数设置");

    QWidget* w2 = new QWidget;
    w2->setStyleSheet("background-color:rgb(154,54,54)");
    tabWidget->insertTab(1, w2, u8"设备管理");

    QWidget* w3 = new QWidget;
    w3->setStyleSheet("background-color:rgb(154,54,154)");
    tabWidget->insertTab(2, w3, u8"设备管理");

    QHBoxLayout* hLay = new QHBoxLayout(this);
    hLay->addWidget(tabWidget);
}

Widget::~Widget() {
    delete ui;
}

14.2 标签窗口

  • tabwidget.cpp
#include "tabwidget.h"
#include 

using namespace std;

TabWidget::TabWidget(QWidget *parent): QTabWidget(parent) {
    // 创建一个新的选项卡栏对象,并将其设置为当前的选项卡栏
    this->setTabBar(new TabBar);
    // 将选项卡位置设置为西边(左侧)
    this->setTabPosition(QTabWidget::West);

    // 注意在 QTabBar::tab 里不能设置 tab 的大小,否则自定义的 TabBar 无效
    string qss = R"(
        QTabWidget::pane {
            border-top:1px solid #EAEAEA;
            position:absolute;
            top:-0.1px;
        }

        QTabBar::tab {
            font-size:18px;
            font-family:Microsoft YaHei;
            font-weight:400;
            background:#FFFFFF;
            border:2px solid #FFFFFF;
            border-bottom-color:#FFFFFF;
            border-top-left-radius:4px;
            border-top-right-radius:4px;
            padding:2px;
        }

        QTabBar::tab:selected {
            color:#333333;
            border-color:#FFFFFF;
            border-bottom-color:#4BA4F2;
        }

        QTabBar::tab:!selected {
            color:#B2B2B2;
            border-color:#FFFFFF;
            border-bottom-color:#FFFFFF;
        }
    )";

    this->setStyleSheet(QString::fromStdString(qss));
}

14.3 标签栏

  • tabbar.h
#ifndef TABBAR_H
#define TABBAR_H

#include 

class TabBar : public QTabBar {
public:
    TabBar(QWidget* parent = nullptr);

    QSize tabSizeHint(int index) const override;

protected:
    void paintEvent(QPaintEvent *event) override;
};

#endif // TABBAR_H
  • tabbar.cpp
#include "tabbar.h"
#include 
#include 

TabBar::TabBar(QWidget* parent) : QTabBar(parent) {}

// 获取原始选项卡的大小建议,交换宽度和高度,再返回修改后的大小建议,表达了对选项卡大小的自定义设置
QSize TabBar::tabSizeHint(int index) const {
    QSize s = QTabBar::tabSizeHint(index);
    s.transpose();

    // 设置每个 tabBar 中 item 的大小
    // 注意在 qss QTabBar::tab 里不能设置 tab 的大小,否则自定义的 TabBar 无效
    s.rwidth() = 90;
    s.rheight() = 44;
    return s;
}

// 绘制标签栏,将标签栏的形状绘制为横向的条形,标签内容垂直显示
void TabBar::paintEvent(QPaintEvent *event) {
    // QStylePainter 类用于 widget 窗口内部绘制 QStyle 元素
    QStylePainter painter(this);
    // QStyleOptionTab 类用于描述 标签栏 的参数
    QStyleOptionTab opt;

    for (int i = 0; i < count(); i++) {
        initStyleOption(&opt, i);  // i 为标签栏索引
        painter.drawControl(QStyle::CE_TabBarTabShape, opt);  // 绘制标签栏的形状
        painter.save();  // 保存当前绘图状态

        // 根据 opt.rect 的大小创建一个矩形 r,并将其移动到 opt.rect 的中心位置
        QSize s = opt.rect.size();
        s.transpose();
        QRect r(QPoint(), s);
        r.moveCenter(opt.rect.center());
        opt.rect = r;

        // 将绘图原点移到当前标签的中心位置,然后旋转 90 度,再将绘图原点移回初始位置
        QPoint c = tabRect(i).center();
        painter.translate(c);
        painter.rotate(90);
        painter.translate(-c);
        painter.drawControl(QStyle::CE_TabBarTabLabel, opt);  // 绘制标签栏的标签
        painter.restore();  // 恢复之前保存的绘图状态
    }
}

你可能感兴趣的:(C++,Qt,学习,c++,qt,学习,qss选择器,控件提升,QPushbutton,QSlider)