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

在这里插入图片描述

  • widget.ui
    在这里插入图片描述

  • 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 <QWidget>

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 <QRegExpValidator>  // 使用正则表达式限制输入
#include <QDebug>
#include <QMessageBox>

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 点击按钮弹出菜单

在这里插入图片描述

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QMenu>
#include <QAction>
#include <QFileDialog>
#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 <string>

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 鼠标悬浮弹出对话框

在这里插入图片描述

5.2.1 主窗口
  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QHBoxLayout>
#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 <QPushButton>
#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 <QMouseEvent>
#include <QStylePainter>
#include <QStyleOptionButton>
#include <iostream>
#include <QThread>

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 <QDialog>
#include <QSlider>

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 <QVBoxLayout>
#include <QEvent>
#include <windows.h>

// 注意由于此类使用了 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 实现打开、关闭状态按钮

在这里插入图片描述

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

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 样式表

在这里插入图片描述

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <string>
#include <QListView>

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 用法及样式表

在这里插入图片描述

  • widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTimer>

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 用法及样式表

在这里插入图片描述

  • 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 <string>

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 <QTextStream>

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;
}

在这里插入图片描述

  • cmybutton.h
#ifndef CMYBUTTON_H
#define CMYBUTTON_H

#include <QPushButton>
#include <QObject>
#include <QLabel>

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 <QVBoxLayout>

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 鼠标、控件、窗口位置详解

在这里插入图片描述

  • 桌面原点:在电脑桌面左上角(上图红点处)
  • 应用程序原点:在应用程序左上角
  • 坐标系一般都是 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 案例

在这里插入图片描述

在这里插入图片描述

  • widget.cpp
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

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 <QDebug>
#include <QMouseEvent>
#include "tempdialog.h"
#include <memory>

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 <QDialog>

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 <QDebug>

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

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

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

在这里插入图片描述

13.1 主窗口

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QVBoxLayout>
#include "csearchlineedit.h"
#include <QDebug>

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 <QLineEdit>
#include <QObject>
#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 <QHBoxLayout>
#include <string>

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 <QPushButton>
#include <QObject>

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 <string>

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 在左,文本水平

在这里插入图片描述

14.1 主窗口

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QHBoxLayout>
#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 <string>

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 <QTabBar>

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 <QStylePainter>
#include <QStyleOptionTab>

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();  // 恢复之前保存的绘图状态
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/135542.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

便捷Benchmark.sh 自动匹配workload(自用)

​ 因为db_bench选项太多&#xff0c;而测试纬度很难做到统一&#xff08;可能一个memtable大小的配置都会导致测试出来的写性能相关的的数据差异很大&#xff09;&#xff0c;所以官方给出了一个benchmark.sh脚本用来对各个workload进行测试。 该脚本能够将db_bench测试结果中…

深度解析NLP定义、应用与PyTorch实战

1. 概述 文本摘要是自然语言处理&#xff08;NLP&#xff09;的一个重要分支&#xff0c;其核心目的是提取文本中的关键信息&#xff0c;生成简短、凝练的内容摘要。这不仅有助于用户快速获取信息&#xff0c;还能有效地组织和归纳大量的文本数据。 1.1 什么是文本摘要&#x…

《詩經别解》——國風·周南·雎鳩​​​​​​​

一、关于古文的一个认识 目前可以阅读的古文经典&#xff0c;大多是经历了几千年的传承。期间的武力战争、文化纷争、宗教侵袭、官僚介入及文人的私人恩怨与流派桎梏&#xff0c;印刷与制作技术&#xff0c;导致这些古文全部都已经面目全非。简单地说&#xff0c;你读到的都是…

树与二叉树作业

1. 已知一个二叉树的中序遍历序列和后序遍历序列&#xff0c;求这棵树的前序遍历序列 【问题描述】 已知一个二叉树的中序遍历序列和后序遍历序列&#xff0c;求这棵树的前序遍历序列。 【输入形式】 一个树的中序遍历序列 该树后序遍历序列&#xff0c;中间用空格分开。输…

el-table实现展开当前行时收起上一行的功能

<el-tableref"tableRef":data"tableData":expand-row-keys"expandRowKeys":row-key"handleRowKey" // 必须指定 row-keyexpand-change"handleExpandChange" // 当用户对某一行展开或者关闭的时候会触发该事件> <…

Creo螺旋扫描/弹簧画法

一&#xff1a;点击螺旋扫描 二&#xff1a;参考–》螺旋轮廓的定义&#xff1a; 三、草绘轮廓线&#xff1a;视图放正 四、草绘弹簧丝线形状&#xff1a; 在非中轴线上画圆&#xff1a; 制螺旋线&#xff1a; 首先理清Creo绘制螺旋线的逻辑&#xff08;不同于UG直接给定直径…

华为ensp:边缘端口并启动BUDU保护

如上图前提是三个交换机都做了rstp&#xff0c;则在边缘的地方做 边缘端口并启动BUDU保护&#xff0c;也就是我用绿色圈出来的地方 边缘1 进入交换机的系统视图 interface e0/0/3 进入接口 stp edged-port enable quit 再退回系统视图 stp bpdu-protection 这样就可以了…

Arduino ESP8266使用AliyunIoTSDK.h连接阿里云物联网平台

文章目录 1、AliyunIoTSDK简介2、相关库安装3、阿里云创建产品&#xff0c;订阅发布4、对开源的Arduino ESP8266源代码修改5、使用阿里云点亮一个LED灯6、设备向阿里云上传温度数据7、项目源码 1、AliyunIoTSDK简介 AliyunIoTSDK是arduino的一个库&#xff0c;可以在arduino的…

20分钟搭建Ubertooth One开源蓝牙测试工具

kali linux 2023 安装依赖&#xff08;记得使用root用户搭建环境&#xff09; 1、apt-get update 2、apt install ubertooth 更新共享库缓存 3、ldconfig 安装 Ubertooth 工具和驱动程序 4、插入Ubertooth One工具 5、ubertooth-util -v 备注&#xff1a;出现Firmwate v…

Android系统开发快速寻找代码(如何在文件夹中寻找代码)

很多时候对于Android系统开发小白而言&#xff0c;例如预置APK&#xff0c;知道了APK包名不知道具体代码位置需要去寻找代码&#xff0c;但是Android系统代码十分庞大&#xff0c;如何快速准确查询代码是个问题。 本人目前只探索到了一些方法&#xff0c;如有更有效的办法可以…

CMOS介绍

1 二极管 2 CMOS 2.1 栅极、源极、漏极 2.2 内部结构 2.2 导电原理 - 原理&#xff1a;1.通过门级和衬底加一个垂直电场Ev&#xff0c;从而在两口井之间形成反形层2.如果加的电场足够强&#xff0c;反形层就可以把source&#xff08;源极&#xff09;和drain&#xff08;漏极…

UML软件建模软件StarUML mac中文版软件介绍

StarUML for mac是一款UML建模器&#xff0c;StarUML for mac提供了几个模版&#xff0c;帮助用户建立使用新的图表&#xff0c;是目前最流行的UML建模工具&#xff0c;给开发工作带来大大的便利。 StarUML mac软件介绍 StarUML 是一个流行的软件建模工具&#xff0c;用于创建…

【车载开发系列】AutoSar中的CANTP

【车载开发系列】AutoSar中的CANTP 【车载开发系列】AutoSar中的CANTP 【车载开发系列】AutoSar中的CANTP一. CANTP相关术语二. CANTP相关概念1&#xff09;单帧&#xff1a;SF(Single Frame)2&#xff09;首帧&#xff1a;FF(First Frame)3&#xff09;连续帧CF(Consecutive F…

原生微信小程序学习之旅(一) -来简单的使用

文章目录 取消导航栏标头组件创建添加Component组件接收传入的数据 页面创建(Page)关于tabBartabBar自定义样式 轮播图轮播图指示点样式改变 微信小程序快速获取用户信息路由跳转获取url路径中的参数 bindtap(click)传参wx:if编写用户登陆关于默认工程目前的获取方法尝试一下服…

python 中用opencv开发虚拟键盘------可以只选择一个单词不会出现一下选择多个

一. 介绍 OpenCV是最流行的计算机视觉任务库&#xff0c;它是用于机器学习、图像处理等的跨平台开源库&#xff0c;用于开发实时计算机视觉应用程序。 CVzone 是一个计算机视觉包&#xff0c;它使用 OpenCV 和 Media Pipe 库作为其核心&#xff0c;使我们易于运行&#xff0c…

Apache和Nginx实现虚拟主机的3种方式

目录 首先介绍一下Apache和nginx&#xff1a; Nginx和Apache的不同之处&#xff1a; 虚拟主机 准备工作 Apache实现&#xff1a; 方法1&#xff1a;使用不同的ip来实现 方法2&#xff1a;使用相同的ip&#xff0c;不同的端口来实现 方法3&#xff1a;使用相同的ip&…

解决游戏找不到x3daudio1_7.dll文件的5个方法,快速修复dll问题

在电脑使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“x3daudio1_7.dll丢失”。这个错误通常会导致软件游戏无法正常启动运行。为了解决这个问题&#xff0c;我们需要采取一些措施来修复丢失的文件。本文将详细介绍解决x3daudio1_7.dll丢失的方法…

企业云盘与个人云盘:区别与特点一览

企业云盘是企业在寻找文件协同工具的过程中绕不开的一个选项。企业为什么需要专门购置企业网盘&#xff0c;个人云盘能否满足企业的文件协作需求呢&#xff1f;企业云盘和个人云盘有什么区别呢&#xff1f; 企业云盘与个人云盘的区别 1、使用对象&#xff1a;顾名思义&#xf…

Java 简单实现一个 TCP 回显服务器

文章目录 TCP 服务端TCP 客户端实现效果TCP 服务端(实现字典功能)总结 TCP 服务端 package network;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Soc…

【C++】C++入门详解 II【深入浅出 C++入门 这一篇文章就够了】

C入门 七、引用&#xff08;一&#xff09;引用 概念&#xff08;1&#xff09;引用 概念&#xff08;2&#xff09;引用 使用★☆&#xff08;3&#xff09;引用 特性&#xff08;4&#xff09;常引用 &#xff08;二&#xff09;引用的 实际应用 及 其意义☆&#xff08;1&am…