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

编程入门 行业动态 更新时间:2024-10-26 10:36:02

C++ Qt 学习(四):<a href=https://www.elefans.com/category/jswz/34/1771438.html style=自定义控件与 qss 应用"/>

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_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic: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 dialog2. 点击时 mute
注意问题:重写按钮类,样式表无效
*/
#pragma once#include <QPushButton>
#include "CVolumeSliderDialog.h"class CVolumeButton : public QPushButton {Q_OBJECTpublic: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 用于样式化绘制 widgetQStyleOptionButton 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 {         // 如果静音状态为假,将滑动条的值设置为 50if (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_OBJECTpublic: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_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic: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_OBJECTpublic: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_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic: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_OBJECTpublic: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");// 创建搜索栏实例化对象 pEditCSearchLineEdit* 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_OBJECTpublic: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_pBtnm_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_OBJECTpublic: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();  // 恢复之前保存的绘图状态}
}

更多推荐

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

本文发布于:2023-11-15 17:41:02,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1603572.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:自定义   控件   Qt   qss

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!