Qt

《总之,好记性不如烂笔头!把你遗忘的都记下来吧!》

Qt基础

Qt元对象编辑器moc

元对象编辑器moc是负责解析c++中不存在的例如:signals、slot、emit等关键字,moc通过解析包含Q_OBJECT宏的类,生成能够处理信号和槽连接代码。

解析属性宏,生成相应的代码。

能使得Qt在运行时进行反射操作,比如查找类的方法、属性以及信号的槽。

Qt元对象系统:

元对象系统是Qt框架中独特的拓展,用来增强C++语言动态特性。

使用元对象系统必须包括以下三个方面:

1、QObject类是所有使用元对象系统的类的基类

2、必须在一个类的开头部分插入宏Q_OBJECT,这样这个类才能使用元对象系统的特性。

3、MOC为每个QObject的子类提供必要的代码实现元对象系统的特性。

支持的特性:

  • 动态属性系统
  • 信号和槽机制
  • 反射机制
  • 动态类型信息

坐标系统

image-20240528103326318

获取当前标签位置:

pos()函数返回的是子窗口的左上角相对于父窗口的位置:

QPoint pos = ui->pushButton->pos();

mapToGlobal()函数返回控件的左上角坐标相对于整个屏幕的坐标

1
2
QPoint pos = ui->pushButton->pos(); 
QPoint posLeftUp = w.mapToGlobal(pos);

而对于事件QEvent,通过gloPos()函数可以直接获取事件的屏幕坐标

1
QPoint point = event->globalPos();

类继承关系

在这里插入图片描述

QWidget

属性:

sizePolicy 类型 QSizePolicy定义了组件在水平和垂直方向上的尺寸变化策略

QSizePolicy::fixed: 固定尺寸, QWidget的sizeHint()函数返回组件的建议尺寸作为组件的固定尺寸,即使使用了布局管理,组件也不会被放大或者缩小

作为窗口:

windowOpacity 设置窗口的透明度

信号:

void customContextMenuRequested(const QPoint &pos) 在组件上点击鼠标右键时发射,可以用于创建组件的快捷菜单

QPushButton

设置按钮背景图的方式

​ 0、通过设置图标Icon来设置图片背景

​ 1、通过QSS 设置 border-image

​ 2、自定义继承按钮类,通过重写paintEvent,重画背景图片

QAction

QMenu

QLineEdit

QToolButton

QTimer

创建定时器

1
2
3
4
5
6
7
QTime *m_timeWait = new QTimer(this);
// 定时器时间
m_timeWait->setInterval(5000);
// 开启定时器
m_timeWait->start();
// 定时结束绑定事件处理
connect(m_timeWait, SIGNAL(timeout()), this, SLOT(doTimeSwitch()));

创建单次定时器

1
2
3
4
m_animWait = new QTimer(this);
m_animWait->singleShot(m_animContinue, Qt::PreciseTimer, this, [this](){
m_isAllowedLR = true;
});

QTreeWidget

一个QTreeWidget组件显示内容分为表头和目录树两部分,表头和目录树结点都是QTreeWidgetItem对象

image-20240707225650834

顶层节点:目录树中一行就是一个节点,目录树中最上层的节点成为顶层节点,顶层节点没有父节点,一个目录树中可以有多个顶层节点

1
2
void addTopLevelItem(QTreeWidgetItem *item);	// 添加一个顶层节点
int indexOfTopLevelItem(QTreeWidgetItem *item); // 返回一个顶层节点的序列号

**次级节点:**所有次级节点都直接或者间接挂在某个顶层节点下面。

隐藏的根节点:目录树中有一个隐藏的根节点,是所有顶层节点的父节点。

1
QTreeWidgetItem *QTreeWidget::invisibleRootItem(); // 获取隐藏的根节点

QTreeWidgetItem

构造QTreeWidgetItem

1
QTreeWidgetItem(int type = Type);

可以传递一个整数表示节点类型,这个类型是自定义的

1
2
3
4
5
enum TreeItemType{
TreeItemPro = 1,
TreeItemDir = 2,
TreeItemPic = 3,
};

然后可以通过QTreeWidget::addTopLevelItem()添加到顶层节点,或者通过QTreeWidgetItem::addChild()将其添加为一个节点的子节点。

设置节点中每一列数据:

1
2
3
4
5
6
7
item->setText(0, name);
item->setIcon(0, QIcon(":/icon/icon/dir.png"));
item->setToolTip(0, src_path);
// 使用另一种方式
item->setData(0, Qt::DisplayRole, name); // 使用DisplayRole设置文本
item->setData(0, Qt::DecorationRole, QIcon(":/icon/icon/dir.png")); // 使用DecorationRole设置图标
item->setData(0, Qt::ToolTipRole, src_path); // 使用ToolTipRole设置提示

QLayout

image-20240602003656723

image-20240602003920702image-20240602003951337

setMargins();

setContentMargins();

作用:将多个控件当作一个整体,保证这个整体离布局的距离为(Left, Top, Right, Bottom)这几个参数。

控件间距调整:

setSpacing()

作用:设置某个布局下各个控件之间的间距

弹簧布局:

addStretch() setStretch()

作用:增加弹簧布局,参数大小代表弹簧弹力,不同的弹力大小代表控件比例,同类型的函数还有 setStretchFactor()。

QHBoxLayout

水平布局

QVBoxLayout

垂直布局

QGridLayout

网格布局

QFromLayout

表单布局,适用于两列管理的布局

QStackedLayout

堆叠布局,适用于多个页面切换的布局

QSpacerItem

布局中的占位符,可以用来填充剩余空间

QSplitter

分割条组件类,用来水平或者竖直的分割两个界面组件,使得两个控件所占空间能够随时改变

QEvent

什么情况是事件接受,什么情况是事件被接受

event->button()

返回单次按下的按键,不包括按下

event->buttons()

返回按下的组合按键,包括之前已经按下但未松开的按钮

Event->buttons()的返回是 Qt::MouseButton
返回值为以下几种类型:

Qt::NoButton 0x00000000
Qt::LeftButton 0x00000001
Qt::RightButton 0x00000002
Qt::MidButton 0x00000004

当左键按下时,返回值是1 右键 2 中键 4 左+右 3 左+中 5 右+中 6 左右中 7

QMimeData

QDialog

窗口类的重要特性设置

窗口显示或运行的一些特性可以通过QWidget的一些函数设置,如setAttribute() , setWindowFlag(), setWindowState()

模态(modal)窗口:窗口弹出后,没关闭之前,不可以对同一应用程序的其他窗口进行操作;
非模态(modeless)窗口:窗口弹出后,没关闭之前,可以对同一应用程序的其他窗口进行操作,可以同时和多个窗口进行交互;

setAttribute()

![1717816731342](D:\Documents\Tencent Files\2920611818\FileRecv\MobileFile\1717816731342.jpg)

setWindowFlag()

![1717816795244](D:\Documents\Tencent Files\2920611818\FileRecv\MobileFile\1717816795244.jpg)

1717817924752

setWindowState()

![1717817721460](D:\Documents\Tencent Files\2920611818\FileRecv\MobileFile\1717817721460.jpg)

setWindowModality()

![1717817816783](D:\Documents\Tencent Files\2920611818\FileRecv\MobileFile\1717817816783.jpg)

QFileDialog文件对话框

QString getOpenFileName() 选择打开一个文件,返回选择文件的文件名
QStringList getOpenFileName() 选择打开多个文件,返回选择的所有文件的文件名列表
QString getSaveFileName() 选择保存一个文件,返回保存的文件的文件名
QString getExistingDirectory() 选择一个已有的目录,返回所选目录的完整路径
QUrl getOpenFileUrl() 选择打开一个文件,可选择打开远程网络文件
void SaveFileContent() 将一个QByteArray类型的字节数据数组的内容保存为文件
1
2
3
4
// 使用过滤的方法打开一个文件
QString curPath = QDir::currentPath();
QString filter = "程序文件(*.h *.cpp);;文本文件(*.txt);;所有文件(*.*)";
QString fileLog = QFileDialog::getOpenFileName(this,"选择一个文件 ",curPath, filter);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 保存一个文件
QString curPath = QDir::currentPath();
QString filter = "h 文件(*.h);;C++文件(*.cpp);;文本文件(*.txt);;所有文件(*.*)";
QString aFileName = QFileDialog::getSaveFileName(this,"另存文件夹 ",curPath,filter);
if(aFileName.isEmpty())
return;
// 设置当前目录为打开的另存目录
QFileInfo fileInfo(aFileName);
QDir::setCurrent(fileInfo.absolutePath());
QFile aFile(aFileName);
if(!aFile.open(QIODevice::WriteOnly | QIODevice::Text))
return ;
QString str = ui->plainTextEdit->toPlainText();
QByteArray strBytes = str.toUtf8();
aFile.close();

QColorDialog颜色对话框

1
2
// 使用静态函数打开颜色对话框
QColor QColorDialog::getColor();

QFontDialog字体对话框

1
2
// 打开字体对话框
QFont QFontDialog::getFont();

QInputDialog标准输入对话框

QMessageBox消息对话框

QIODevice

QCoreApplicaton

​ 为无UI应用程序提供事件循环的类,所有应用程序的基类,其子类QGuiApplication是具有GUI的应用程序类,具有主事件循环,能够处理和派发来自操作系统或者其他来源的事件。

QGuiApplication的子类QApplication为QWidget应用程序提供支持,包括界面的初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 设置应用程序组织名
QCoreApplication::setOrganizationName("HAOKS");
// 设置应用程序名
QCoreApplication::setApplicationName("FILE TEST");
// 获取可执行文件所在的完整文件目录
QString str = QCoreApplication::applicationDirPath();
// 获取文件的完整执行目录
str.append("\n applicationFilePath: " + QApplication::applicationFilePath());
// 获取文件名
str.append("\n applicationName: " + QApplication::applicationName());
ui->plainTextEdit->appendPlainText(str + '\n');
// 退出程序
QCoreApplication::exit();

QFile

主要进行文件内容的读写,还可用于文件的操作,如复制文件,删除文件,重命名文件。

QFileInfo

用于获取文件的各种信息。

QSaveFile

QSaveFile专门用来保存文件,可以用来保存文本文件或二进制文件。

在保存文件时,QSaveFile会在目标问价所在的目录下创建一个临时文件,向文件写入数据是先写入临时文件,如果写入操作没有错误,调用QSaveFile的函数Commit()提交修改时临时文件里的内容才被移入目标文件,然后临时文件会被删除。

在调用函数commit()之间,如果写入操作产生异常导致程序异常结束,目标文件不会由损失,避免了破坏文件结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 利用QSaveFile保存文件
QString fileName = ui->lineEdit->text();
QSaveFile aFile(fileName);
if(!aFile.open(QIODevice::WriteOnly | QIODevice::Text))
qDebug()<<"open error";
aFile.setDirectWriteFallback(false);
try{
QString str = ui->plainTextEdit->toPlainText();
QByteArray strBytes = str.toUtf8();
// 写入临时文件
aFile.write(strBytes, strBytes.length());
// 提交
aFile.commit();
}catch(QException &e){
qDebug()<<"发生错误: "<<e.what();
// 取消写入
aFile.cancelWriting();
}

QDir

进行目录操作的类,在构造函数中传递一个目录字符串作为当前目录,或者使用setPath()设置目录。

1
2
QDir dir("C:/Users/web");
dir.setPath("C:/Users/web");

获取当前目录路径:

1
QString curPath = QDir::currentPath();

返回当前目录下的文件fileName的含有绝对路径的文件名

1
QString dist_path = dir.absoluteFilePath(fileName);

QTemporaryDir

用于创建临时目录

QTemporaryFile

用于创建临时文件,临时文件可以保存在系统的临时目录、指定目录或者应用程序当前目录下。QTemporaryFile的父类为QFile。

QFileSystemWatcher

对目录和文件进行监视的类,父类为QObject。把某些目录或者文件添加到QFileSystemWatcher对象的监视列表后;

当目录发生新建、删除文件等操作时,QFileSystemWatcher会发射directoryChanged()信号;

当所监视的文件发生修改、重命名等操作时,QFileSystemWatcher会发射fileChanged()信号。

读写文本文件

1、使用QFile

1
2
3
4
5
6
7
8
9
10
// 进行文件的读取
QFile aFile(ui->lineEdit->text());
if(!aFile.exists())
return ;
if(!aFile.open(QIODevice::ReadOnly | QIODevice::WriteOnly))
return ;
QByteArray info = aFile.readAll();
QString text = QString::fromUtf8(info);
ui->plainTextEdit->appendPlainText(text);
aFile.close();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 文件写入并保存
QString curPath = QDir::currentPath();
QString filter = "h 文件(*.h);;C++文件(*.cpp);;文本文件(*.txt);;所有文件(*.*)";
QString aFileName = QFileDialog::getSaveFileName(this,"另存文件夹 ",curPath,filter);
if(aFileName.isEmpty())
return;
// *设置当前目录为打开的另存目录 (关键)
QFileInfo fileInfo(aFileName);
QDir::setCurrent(fileInfo.absolutePath());
QFile aFile(aFileName);
if(!aFile.open(QIODevice::WriteOnly | QIODevice::Text))
return ;
QString str = ui->plainTextEdit->toPlainText();
QByteArray strBytes = str.toUtf8();
// 写入文件
aFile.write(strBytes, strBytes.length());
aFile.close();

2、使用QFile和QTextStream

1
2
3
4
5
6
7
8
9
10
11
// QTextStream读取文件
QString fileName = ui->lineEdit->text();
QFile aFile(fileName);
if(!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
qDebug()<<"open error";
QTextStream aStream(&aFile);
// 自动检测Unicode
aStream.setAutoDetectUnicode(true);
QString str = aStream.readAll();
ui->plainTextEdit->setPlainText(str);
aFile.close();

读写二进制文件

可以单使用QFile读写二进制文件,一般结合使用QFile和QDataStream读写二进制文件。

Qt SQL

数据库连接 QSqlDataBase 用于建立与数据库连接
数据库中的对象 QSqlRecord 表示数据表中一项记录的类
QSqlField 表示数据库或视图的字段的类
QSqlIndex 表示数据库中索引的类
模型类 QSqlTableModel 表示单个数据表的模型类
QSqlQueryModel 表示SQL查询结果数据的只读模型类
其它功能类 QSqlQuery 运行各种SQL语句的类
QDataWidgetMapper 用于建立界面组件与字段的映射关系的类
QSqlError 用于表示数据库错误信息的类,访问上一次的错误信息
关系模型类 QSqlRelationalTableModel 表示关系数据表的模型类
QSqlRelationalDelegate 用于QSqlRelationalTableModel模型的一个编辑字段的代理类,这个代理类提供一个QComboxBox组件作为编辑器
QSqlRelation 用于表示数据表外键信息的类

以ODBC为例连接MySQL数据库

1
2
3
4
5
6
7
8
9
10
// 连接Mysql数据库
QSqlDatabase DB;
DB = QSqlDatabase::addDatabase("QODBC");
DB.setHostName("127.0.0.1");
DB.setPort(3306);
DB.setUserName("root");
DB.setPassword("123456");
DB.setDatabaseName("hotel");
if(!DB.open()){
ui->labelDataBase->setText("数据库连接失败!");

数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 创建数据模型,打开数据表
tabModel = new QSqlTableModel(this,DB);
// 设置数据表
tabModel->setTable("user");
// 设置数据保存方式
tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
// 数据排序方式
tabModel->setSort(tabModel->fieldIndex("account"),Qt::AscendingOrder);
// 利用数据排序规则和过滤规则刷新数据模型
if(!(tabModel->select())){
qDebug()<<"错误信息:"<<tabModel->lastError().text();
return ;
}
// 设置显示字段标题
tabModel->setHeaderData(tabModel->fieldIndex("id"), Qt::Horizontal, "身份证号");
tabModel->setHeaderData(tabModel->fieldIndex("account"),Qt::Horizontal,"手机号");
tabModel->setHeaderData(tabModel->fieldIndex("pwd"),Qt::Horizontal, "密码");
tabModel->setHeaderData(tabModel->fieldIndex("username"),Qt::Horizontal,"姓名");
tabModel->setHeaderData(tabModel->fieldIndex("permission"),Qt::Horizontal, "权限");
tabModel->setHeaderData(tabModel->fieldIndex("ava"),Qt::Horizontal,"头像");
tabModel->setHeaderData(tabModel->fieldIndex("gender"),Qt::Horizontal, "性别");

选择模型

1
2
3
// 创建选择模型
selModel = new QItemSelectionModel(tabModel, this);
// 在此处绑定选择模型变化的信号和槽

模型/视图结构

1
2
3
4
5
6
// 构建模型视图结构 视图为:QTableView
ui->tableViewUser->setModel(tabModel); // 设置数据模型
// 设置选择模型
ui->tableViewUser->setSelectionModel(selModel);
// 隐藏列
ui->tableViewUser->setColumnHidden(tabModel->fieldIndex("ava"),true);

数据到界面组件映射

1
2
3
4
5
6
7
8
9
10
11
12
13
// 创建数据映射 
QDataWidgetMapper *dataMapper;
dataMapper= new QDataWidgetMapper(this);
dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
dataMapper->setModel(qryModel);

// 界面组件与数据模型的具体字段之间的映射
dataMapper->addMapping(ui->dbSpinEmpNo, rec.indexOf("empNo"));
dataMapper->addMapping(ui->dbEditName, rec.indexOf("Name"));
dataMapper->addMapping(ui->dbComboSex, rec.indexOf("Gender"));
dataMapper->addMapping(ui->dbEditBirth, rec.indexOf("Birthday"));

dataMapper->toFirst(); //移动到首记录

单个数据记录QSqlRecord

1
2
3
4
5
6
7
8
// 获取只有字段信息的单个数据记录
QSqlRecord rec = tabModelRoom->record();
// 将单个数据记录插入数据模型
tabModelRoom->insertRecord(tabModelRoom->rowCount(), rec);
// 提交所有未更新的修改到数据库
tabModelRoom->submitAll();
// 获取第i个数据记录
QSqlRecord curRec = tabModelRoom->record(i);

执行SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
QSqlQuery resultPwd;
// 设置SQL语句,:account为参数占位符
resultPwd.prepare("select pwd, permission from user where user.account = :account");
// 绑定占位符参数
resultPwd.bindValue(":account",account);
// 执行SQL语句
resultPwd.exec();
// 向下移动一个记录
resultPwd.next();
LoginAttri res;
// 只会处理返回的第一条结果
if(resultPwd.value("pwd").toString() != pwd){
QMessageBox::information(this,"info","密码错误");
return;
}
// 处理每一条记录的方法
while (q.next()) {
QString username = q.value("username").toString();
qDebug() << "Username:" << username;
}

QPainter绘图

使用QPainter在绘图设备上绘图,绘图设备见类继承关系,注意:当绘图设备为QWidget时,QPainter只能在paintEvent()内部使用。

image-20240606155743409

QPainter

绘图主要是利用QPainter接口函数在绘图设备上绘制各种基本的图形

QPen

设置绘图时的线条特性,主要包括线宽,颜色,线形状等。

QBrush

定义QPainter绘图时的一个区域的填充特性,包括填充颜色,填充样式,材质填充时的材质图片等。

渐变填充效果

  • QLinearGradient:线性渐变
  • QRadioGradient:辐射渐变
  • QConicalGradient:圆锥形渐变

使用QPainter绘制图形举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// 渲染抗锯齿
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
int W = this->width();
int H = this->height();
QRect rect(W/4, H/4, W/2, H/2);
// 设置画笔
QPen pen;
pen.setWidth(3);
// 画笔颜色
pen.setColor(Qt::blue);
// 设置线条样式
pen.setStyle(Qt::SolidLine);
// 设置线条断点样式
pen.setCapStyle(Qt::FlatCap);
// 设置线条连接样式
pen.setJoinStyle(Qt::BevelJoin);
painter.setPen(pen);
QPixmap texturePixmap(":/209ab910dba69ea54d89a31e10bf82d6_4983283784718022512.png");
// 画刷(填充)
QBrush brush;
// 设置画刷填充样式
brush.setStyle(Qt::TexturePattern);
// 设置QPixmap作为画刷图片
brush.setTexture(texturePixmap);
// 设置画刷颜色
// brush.setColor(Qt::yellow);
// brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
painter.drawRect(rect);
event->accept();
}

QPainter绘制基本图形接口

通过函数接口绘制各种基本的图形,包括绘制图片,具体接口

可以用QPainterPath记录绘制图形的路径

1
2
3
// 使用QPaitner在QPixmap上绘图
QPixmap splice(m_width * (m_imageCount + 2), m_height);
QPainter painter(&splice);

坐标系统和坐标变化

1、平移

2、旋转

3、缩放

4、状态恢复和保存

视口窗口定义:

视口是指绘图设备的任意一个矩形区域,使用物理坐标系。默认情况下,视口等于绘图设备的整个矩形区域。

窗口和视口是同一个矩形区域但是窗口是用逻辑坐标系定义的,窗口可以直接定义矩形区域的逻辑坐标范围。

物理坐标系叫做视口坐标系,逻辑坐标系也叫做窗口。通过内部坐标变化矩阵,QPainter能自动将逻辑坐标变化为绘图设备的物理坐标。

image-20240605121143415

默认情况下视口等于绘图设备的整个矩形区域,它可以被设定为绘图设备的任意一个矩形区域,使用物理坐标系。

QPainter::setViewPort(int x, int y, int width, int height);

窗口可以任意定义矩形区域的逻辑坐标范围,在这个范围内显示个内容会映射到视口中。

QPainter::serWindow(int x, int y, int width, int height);

有关图片处理类

QImage

类说明:与硬件无关的表示图片类,是为设备输入输出优化设计类,可以直接进行图片像素数据的访问和操作。

像素知识补充:

像素
px(pixel),可以理解为一个最小图像单元(只能涂一个颜色)的小方块,就是1px,是一小块面积,但是一般并不强调面积的大小,只是说这是一个最小单元。

1px是一个小方块,但是这个小方块的边长却不是固定的,不同的图片、显示设备都可能是不同的,甚至这个小方块可以是长方形,就是说表示为1px的小方块的宽和高,真实对应的长度可能是不同的。我们可以把一个像素理解为一个点,因为我们经常会忽略像素的大小,平时关注更多的是像素数,也就是分辨率。想象有一个投影,或远或近的打到墙面上,大小会有变化,其实像素数没变。再想象显示器有不同尺寸的,分辨率(像素数)却是相同的。这些都可以理解为是像素大小不同导致的。

分辨率
像素是一个小方块,用这些小方块排成一个大的长方形,这个长方形就是图像,图像的分辨率为:图像宽排列的像素数 × 图像高排列的像素数。如果把像素想象成点,那么分辨率就是 宽像素数 × 高像素数。

4K分辨率:是指水平方向每行像素值达到或者接近4096个(4096×2160)

DPI(Dots Per Inch,每英寸点数),图像每英寸(1 英寸 = 25.4 毫米)长度内的像素点数。

我们还是应该把像素理解为小方块,dpi就可以理解为是一英寸长度内排列的像素数。通过图像的dpi我们就可以计算出在这个图像中像素的边长,如果也知道图像的分辨率(宽高像素数),就可以知道该图像的真实尺寸了。

DPM(Dots Per Meter, 每米点数), 1 DPI = 0.0254 DPM

点距(dot pitch),就是把像素理解为点的时候,点距就是两个像素点的距离。我们把像素理解为小方块,那点距其实就是像素的边长

点距与DPI之间转换,点距一般用毫米表示,DPI表示1英寸(也就是25.4mm)长度内的像素数,所以点距(像素的边长)就等于 25.4 / DPI(总长度/个数=每个长度)

显示器规格:

属性
分辨率 1366 * 768
尺寸 13寸(英寸,对角线长度)
计算过程

我们理解像素是个小正方形,所以显示器的宽高比例也是1366 : 768,

很容易求出,宽:11.33英寸,高:6.37英寸,

DPI :1366 / 11.33(或 768 / 6.37)= 120.55,

点距:25.4 / 120.55 = 0.2107 mm

QPixmap

类说明:为了在屏幕上显示图片而优化设计的类。

缩放到指定高度

1
scaledToHeight(int);

缩放到指定宽度

1
scaledToWidth(int);

缩放到指定宽度和高度

1
scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation) 

image-20240603115014949

image-20240603115040219

QImage与QPixmap的区别

1、QPixmap主要是用于绘图,针对屏幕显示而最佳化设计,QImage主要是为图像I/O、图片访问和像素修改而设计的

2、QPixmap依赖于所在的平台的绘图引擎,故例如反锯齿等一些效果在不同的平台上可能会有不同的显示效果,QImage使用Qt自身的绘图引擎,可在不同平台上具有相同的显示效果

3、目前的Qt会把QPixmap都存储在graphics memory中,QImage是存储在客户端的,是独立于硬件的。在 X11, Mac 以及 Symbian平台上,QPixmap 是存储在服务器端,而QImage则是存储在客户端,在Windows平台上,QPixmap和QImage都是存储在客户端,并不使用任何的GDI资源。

4、由于QImage是独立于硬件的,也是一种QPaintDevice,因此我们可以在另一个线程中对其进行绘制,而不需要在GUI线程中处理,使用这一方式可以很大幅度提高UI响应速度。

5、QImage可通过setPixpel()和pixel()等方法直接存取指定的像素。当图片较大时,我们可以先通过QImage将图片加载进来,然后把图片缩放成需要的尺寸,最后转换成QPixmap 进行显示。

QBitmap

是QPixmap的子类,用于表示一位色深的单色位图

QPicture

可以记录和重现QPainter的各条命令

图形/视图架构

采用QPainter需要在绘图设备事件处理函数paintEvent()编写代码来实现绘图,绘制的是位图。对于绘制复杂可交互的图形使用图形/视图架构,每个图像都是可选择的,可拖放,可修改的。

由三部分组成 1、场景 2、视图 3、图形项

image-20240605164323154

1、QGraphicsScene场景

场景是一个抽象的管理图形的容器,可以向场景中添加图形项,可以获取场景中的图形项。

主要功能:

  • 提供管理大量图形项的快速接口

  • 将事件传播给每个图形项

  • 管理每个图形项的状态,如:选择状态,焦点状态

  • 管理未经变换的渲染功能,主要用于打印

场景中同时除了图形项还存在 背景层前景层

setBackgroundBrush() 设置背景层画刷

setForegroundBrush() 设置前景层画刷

2、QGraphicsView视图

间接父类为QWidget是一个界面组件,用于显示场景中的内容。可以为一个场景设置多个视图,用于对用一个场景提供不同的显示界面。默认情况下,视图大于场景时,场景在视图的中央显示,也可通过设置视图的Alignment属性控制场景在视图中显示的位置。

视图通过接受键盘和鼠标输入并转换为场景的事件,进行坐标变换后这些事件被传送给可视场景。

3、QGraphicsItem图形项

所有的图像项都是从QGraphicsItem继承而来的,其没有父类。图形项相当于模型中的数据,一个图形项存储了绘制这个图形项的各种参数场景管理所有图形项视图组件负责绘制这些图形项

支持的操作:

  • 鼠标响应事件

  • 键盘输入,以及按键事件

  • 拖放操作

  • 组合,可以是父子图形项关系组合,也可以通过QGraphicsItemGroup类进行组合

一个图形项还可以包含子图形项,图形项之间还支持碰撞检测。

图形/视图架构坐标系

图形/视图架构有三个有效的坐标系:场景坐标系视图坐标系图形项坐标系

1717578701216

1、场景坐标系

场景坐标系定义了所有图形项的基础坐标, 场景坐标系描述了每个顶层图形项的位置.

1
2
// 定义一个左上角坐标为(-400, -300),宽度为800, 长度为600的场景
scene = new QGraphicsScene(-400, -300, 800, 600);

2、视图坐标系

视图坐标系就是视图组件的物理坐标系, 单位为像素, 视图坐标系只与视图组件和视口有关,与场景无关. QGraphicsview视口的左上角坐标总是(0, 0), 所有的鼠标事件,拖放事件的坐标首先是由视图坐标系定义, 然后用户将视图坐标映射为场景坐标, 和图形交互.

3、图形坐标系

图形项使用自己的局部坐标系, 以自己的中心为原点(0,0).

图形项的位置就是指其中心在父对象项坐标系中的坐标, 没有父图形项的图形项, 父对象就是场景.

QGraphicsItem::pos() 返回图形项在父对象中的坐标

4、坐标映射

QGraphicsView::mapToScene() 将视图坐标映射为场景坐标

QGraphicsScene::itemAt() 获取场景中鼠标光标处的图形项

创建图形/视图举例

image-20240605175741852
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 创建场景
QRectF rect(-200, -100, 400, 200);
scene = new QGraphicsScene(rect, this);
ui->graphicsView->setScene(scene);
// 新建图形项
QGraphicsRectItem *item = new QGraphicsRectItem(rect);
item->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
QPen pen;
pen.setWidth(2);
item->setPen(pen);
// 在场景中添加图形项
scene->addItem(item);
QGraphicsEllipseItem *item2 = new QGraphicsEllipseItem(-100, -50, 200, 100);
// 设置图形项在父对象中的位置
item2->setPos(0,0);
item2->setBrush(QBrush(Qt::blue));
// 设置图形项操作标志
item2->setFlag(QGraphicsItem::ItemIsMovable);
item2->setFlag(QGraphicsItem::ItemIsSelectable);
item2->setFlag(QGraphicsItem::ItemIsFocusable);
scene->addItem(item2);
QGraphicsEllipseItem *item3 = new QGraphicsEllipseItem(-50, -50, 100, 100);
item3->setPos(rect.right(),rect.bottom());
item3->setBrush(QBrush(Qt::red));
item3->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable
|QGraphicsItem::ItemIsFocusable);
scene->addItem(item3);
// 清除所有选择
scene->clearSelection();

QChart绘图

QAnimation

Qt常见功能类

QPagedPaintDevice

QPainter

用于打印输出的类

QPdfWriter

用于生成pdf文件的绘图设备类

QGraphicsDropShadowEffect

创建阴影效果

1
2
3
4
5
6
7
8
9
10
QGraphicsDropShadowEffect *shadow_effect = new QGraphicsDropShadowEffect(this);
// 设置阴影偏移量,x 轴方向和 y 轴方向
shadow_effect->setOffset(0, 0);
// 阴影颜色,QColor 属性。默认情况为半透明的深灰色(QColor(63, 63, 63,180))
shadow_effect->setColor(Qt::red);
// 阴影半径。使用较小的半径产生更清晰的阴影,而使用较大的半径产生更模糊的阴影
shadow_effect->setBlurRadius(20);

// 设置阴影
m_pLoginPhoneLabel->setGraphicsEffect(shadow_effect);

常见问题:

字符串编码错误:

使用Qt的时候,时常会遇到中文乱码问题,qDebug打印日志乱码,或者Widget界面乱码等等。

原因呢,大多是因为使用MSVC编译器问题导致,而Qt 自带的MinGW一般不会出现乱码问题。

1
2
3
4
// 定义宏强制编译后的执行文件采用UTF-8编码
#if _MSC_VER >= 1600 //VS2015>VS>VS2010, MSVC VER= 10.0 -14.0
#pragma execution_character_set("utf-8")
#endif

QSS

QSS官方说明:Customizing Qt Widgets Using Style Sheets | Qt Widgets 6.7.1

盒子模型

使用样式表时,每个widget都被视为包含四个同心矩形的框边距矩形(margin rectangle)、边框矩形(border rectangle)、填充矩形(padding rectangle)和内容矩形(content rectangle)

image-20240604235720014

常见样式举例:

QPushButton

image-20240531110111671

1
2
3
4
5
6
7
8
9
QPushButton {
border:none;
border-radius: 4px;
color:rgb(244, 216, 168);
background-color: rgb(57, 59, 64);
}
QPushButton::hover {
background-color: rgba(57, 59, 64, 200);
}

QComboBox

image-20240605105400027

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
QComboBox {
border:none;
font: normal normal 16px "Microsoft YaHei";
background-color: rgb(243, 243, 243);
}

QComboBox::drop-down{
subcontrol-origin: padding;
subcontrol-position: top right;
border-left-width: 1px;
border-left-color: darkgray;
width: 20px;
}

QComboBox::down-arrow{
image: url(:/res/down.png);
width: 20px;
height: 20px

}

QCheckButton

image-20240531120044731image-20240531120102467

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
QCheckBox::indicator{
border-radius: 5px;
border:none;
width:30px;
height:30px;
}
QCheckBox::indicator:unchecked{
image:url(:/new/bg/res/unchecked.png);
border: 0px;
}
QCheckBox::indicator:checked{
image:url(:/new/bg/res/checked.png);
border: 0px;
background-color:rgb(57, 59, 64);
}

QRadioButton

image-20240531120346639image-20240531120358497

1
2
3
4
5
6
7
8
9
10
11
12
13
14
`QRadioButton::indicator{
border:none;
width:30px;
height:30px;
}
QRadioButton::indicator:unchecked{
/* 使用border-image会导致图片失真*/
image: url(:/new/bg/res/radio button unselect.png);
border: 0px;
}
QRadioButton::indicator:checked{
image: url(:/new/bg/res/radio button select.png);
border: 0px;
}`

QToolButton

image-20240531120653964

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
QToolButton
{
color:rgb(112, 74, 29);
background-color:rgb(255, 202, 11);
border: 2px;
border-radius: 4px;
}
QToolButton::menu-arrow{
image:none;
}

QToolButton::menu-button{
width: 35px;
image: url(:/res/mession.png);
}

QToolButton::hover {
background-color:rgb(255, 206, 45);
}

QScrollArea

image-20240531104613496

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
QScrollArea{
border: 0px solid;
border-right-width: 1px;
border-right-color: #dcdbdc;
background-color: #f5f5f7;
}
QScrollBar:vertical {
border: none;
background: #ffffff;
width: 10px;
margin: 0px 0 0px 0;
}
QScrollBar::handle:vertical {
background: Gainsboro;
min-height: 20px;
border-radius: 5px;
border: none;
}
QScrollBar::add-line:vertical {
border: 0px solid grey;
background: #32CC99;
height: 0px;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical {
border: 0px solid grey;
background: #32CC99;
height: 0px;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
width: 0px;
height: 0px;
}

QLineEdit

image-20240605105528128

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
QLineEdit {
/*border: 1px solid #A0A0A0; /* 边框宽度为1px,颜色为#A0A0A0 */
border-radius: 3px; /* 边框圆角 */
padding-left: 5px; /* 文本距离左边界有5px */
background-color:rgb(243, 243, 243);
color: rgb(0, 0, 0); /* 文本颜色 */
selection-background-color: #A0A0A0; /* 选中文本的背景颜色 */
selection-color: #F2F2F2; /* 选中文本的颜色 */
font-family: "Microsoft YaHei"; /* 文本字体族 */
font-size: 20px; /* 文本字体大小 */
}

QLineEdit:hover { /* 鼠标悬浮在QLineEdit时的状态 */
border-radius: 3px;
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;
}

QListWidget

QTabWidget

image-20240531120904547

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
QTabWidget::pane 
{
border: 0;
}

QTabBar::tab
{
background: transparent;
font-family:"微软雅黑";
color:rgb(255, 255, 255);
font-size:16px;
font: bold;
padding-left:5px;
padding-right:5px;
min-width: 85px;
min-height:30px;
}

QTabBar::tab:selected
{
border-bottom: 3px solid rgb(255, 210, 132);
}
QTabBar::tab:hover
{
color: rgb(255, 210, 132);
}
QStackedWidget {
background: transparent;
}
QStackedWidget > QWidget {
background-color: rgba(20,20,20,80);
}

Qt
http://example.com/2024/07/31/Qt/
作者
John Doe
发布于
2024年7月31日
许可协议