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