Qt是一个基于C++的可跨平台的App和UI开发框架。
平台:Windows、Ubuntu、CentOS、Android、鸿蒙、UOS(统信)
跨平台:就是可以在不同平台上直接运行的方式
两种跨平台的方式:
程序级:编译好的程序可以直接在不同平台上运行,需要装解释器来支持,如Java、Python、Js
程序级(速度比较慢) 程序 解释器 系统 源码级:一份代码无需修改,可直接在不同平台上编译运行,需要编译环境或交叉编译支持,如C、C++。
源码级(速度比较快)
程序 系统
Qt的输出
#include <QDebug>
qDebug()<<123; //默认末尾自动换行
qDebug()<<123<<456<<789; //多段自动加空格,实际输出123 456 789
Qt工具
1、Qt助手:文档阅读、发布(一般想给项目生成帮助文档,先对源码进行注释)
2、Qt语言家:用于国际化翻译多语言文字切换。
3、Qt设计师:用于拖拽式的可视化ui界面的设计
1、工程文件
生成目标的目录一般和源码目录同级,固定名字格式:build-工程名-编译器-版本。
源码目录下工程文件结构如图所示:
2、解决可视化界面按钮显示不全的两种方法:
1、使用布局自适应大小
2、在主函数设置DPI属性
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
3、添加并使用资源问题
添加:
点击工程名->添加新文件->QT->Qt Resourse->取名->完成->添加前缀->添加文件->保存
使用的三种方法:
通过属性选择;在样式中使用,用的是资源路径;在代码中添加资源路径。
4、鼠标点击并移动窗口问题
原理如下:
代码解析:
.h文件需要添加:
#include <QPoint>
#include <QMouseEvent>
QPoint m_offset //声明一个成员变量或者全局变量接受位置
void mousePressEvent(QMouseEvent *event); //函数原型声明
void mouseMoveEvent(QMouseEvent *event); //函数原型声明
.c文件需要添加:
void TestWidget::mousePressEvent(QMouseEvent *event)
{
m_offset = event->globalPos() - this->pos();//鼠标相对部件当前位置的偏移量
}
void TestWidget::mouseMoveEvent(QMouseEvent *event)
{
this->move(event->globalPos() - m_offset); //即A'=B'- offset
}
0902学习问题解决:
1、可视化界面操作设计该界面
拖动Tab Widget到底板,双击设置行列数、内容和图标
右图三个依次:行标签头设置可见不可见、标签宽度设置、列标签头设置可见不可见。
设置表格边框可见不可见。
点击表格时,设置按行选中。
图标设置如图:
2、信号是什么,如何声明?
信号是函数,需要加关键字signal声明,只声明不实现,作用是作为触发者。
signals:
void 信号名(形参列表...); //只声明不实现。
emit 信号名(实参...); //使用。
3、槽是什么,如何声明和实现?
槽是成员函数,需要加slots来实现:作用是作为功能执行者。
4、信号槽的作用是什么?
实现对象间的通信。
5、connect函数
信号槽的关联函数是connect(),关联函数的原型以及连接方式和写法。
connect()函数原型如下:
bool QObject::connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection )
//五个参数分别:信号发送者、信号、信号接收者、槽、连接方式。
发送者和接收者都要是指针、信号发送者拥有信号,接受者拥有槽
连接方式有五种:1、自动连接(单线程切直接连接、多线程切队列连接)
2、直接连接(单线程同步方式+阻塞)
3、队列连接(主要多线程异步方式+非阻塞)
4、阻塞队列连接(只能用与多线程同步,如果单线程就死锁)
5、唯一连接(防止重复连接)
四种写法:
1、宏写法(不安全)预处理阶段只进行替换,不进行校验,在编译过程中不会报错。
connect(信号发送者,SIGNA(信号名(参数类型..)),信号接受者,SLOT(槽名(参数类型..)));
2、一般的函数指针,前提是信号槽无重载 (不安全)
connect(信号发送者,&类名::信号名,信号接受者,&类名::槽名);
3、若有重载,使用QOverload加载函数指针 (安全)
connect(信号发送者,QOverload<参数类型>::of(&类名::信号名),this,QOverload<参数类型>::of(&类名::槽名));
4、若槽功能简单,则可使用lambada表达式取代槽 (相对安全)
connect(信号发送者,QOverload<参数类型>::of(类名::信号名),this,[=](){函数体});
示例:
connect(te3,SIGNAL(sign5()),this,SLOT(slot5()));
connect(te3,&testMainWindow4::sign5,this,&testMainWindow::slot5);//一般的函数指针。(要求不能有重载)
connect(te3,QOverload<>::of(&testMainWindow4::sign5),this,QOverload<>::of(&testMainWindow::slot5));//若有重载,使用Qoverload
connect(te3,QOverload<>::of(&testMainWindow4::sign5),this,[=]{
this->show();
te->show();
te1->show();
te2->show();
te3->show();});
0903学习问题解决:
1、实现界面切换
A界面按钮切换到B界面,B界面按钮切换到C界面...E界面按钮切回A界面
1、创建新界面过程:
新建界面
在主界面的头文件中添加新界面头文件并创建对象
在主界面的源文件中的构造函数中初始化对象,最后可以在析构函数中释放对象。
2、信号与槽的实现:
界面1的按钮->转到槽->发射信号(emit 信号名)
界面1的头文件中需要声明信号(signal: void 信号名())
主界面的头文件需要声明一个槽(权限 slots:void 槽名()) //对象在哪,优先在哪里实现功能。
主界面的源文件构造函数中连接 connect(信号发送者,SIGNAL(信号名),信号接受者,SLOT(槽名));
2、类的六大设计原则
单一职责原则(类和方法,接口)
开闭原则 (扩展开放,修改关闭)
里氏替换原则(基类和子类之间的关系)
依赖倒置原则(依赖抽象接口,而不是具体对象)
接口隔离原则(接口按照功能细分)
迪米特法则 (类与类之间的亲疏关系)
0904学习问题解决:
1、滚动照片
//1、在主界面头文件中添加头文件
#include <QTimer>
//2、声明成员变量
QTimer m_timer;
//3、主界面源文件构造函数中初始化成员变量
m_timer = new Timer(this);
//4、声明槽
private slots:
void timeactive();
//5、在主界面源函数中实现槽函数
void 类名::timeactive(){
static int cnt =0;
if(cnt % 2 == 0){
ui->label->setPixmap(QPixmap("图片1相对路径"));
}else{
ui->label->setPixmap(QPixmap("图片2相对路径"));
}
cnt++;
}
//6、主界面构造函数中槽与信号连接
connect(m_timer,SIGNAL(timeout()),this,SLOT(timeactice()));
//7、开启定时器
m_timer->start(5000) //定时5秒
2、滚动文字
1、主界面头文件中添加头文件
#include <QTimer>
2、创建对象
QTimer * m_timer1;
QString m_str;
3、主界面源文件构造函数中初始化对象
m_timer1 = new QTimer(this);
m_str = "今天是个大晴天!"
4、声明槽
private slots:
void timeactive1();
5、在主界面源函数中实现槽函数
void 类名::timeactive1(){
static int nPos=0;
if(nPos>m_str.length()){
nPos = 0;
}
ui->label_10->setText(m_str.mid(nPos));
nPos++;
}
6、主界面构造函数中槽与信号连接
connect(m_timer1,SIGNAL(timeout()),this,SLOT(timeactice1()));
7、开启定时器
m_timer1->start(5000) //定时5秒
0905学习问题解决:
1、A、B界面求和放C
有三个界面A 、B、C,A上点击按钮,需要将A、B的输入框内的数求和放到C的标签上。
字符串转数字:QString 对象.toInt()
数字转字符串:QString::number(数字)
对应一下关键代码;
A中
//1、添加A信号
signals:
void aSign(QString str);
//2、按钮槽中发射A信号
void A::on_pushButton_clicked()
{
emit aSign(ui->->lineEdit->text());
}
B中
//添加B信号
signals:
void bSign(int);
//添加B槽
public slots:
void bSlot(QString str);
//B槽接受A信号
void B::bSlot(QString str)
{
int val1 = str.toInt();
int val2 = ui->lineEdit->text().toInt(); //将两个文本字符串转为数据
emit bSign(val1+val2);
}
C中
//添加C槽
public slots:
void cSlot(int sum);
//C槽实现功能
void C::cSlot(int sum)
{
ui->lable->setText(QString::number(sum));
}
最后在A构造函数中关联一下信号与槽
connect(this,SIGNAL(aSign(QString)),m_b,SLOT(bSlot(QString)));
connect(m_b,SIGNAL(bSign(int)),m_c,SLOT(cSlot(int)));
2、A下拉框选颜色改B标签背景色
下拉框(combox):相关理论:Qt6.5类库详解:QComboBox_qt combox-CSDN博客
在上面基础上,在添加一个界面C,C中有一个文本浏览器,在此空间上记录下拉框颜色调整效果
0906学习问题解决:
1、验证五种连接方式
Qt5 connect()函数的最后一个参数表明了关联的方式,有五种连接方式
1、默认为Qt::AutoConnection(自动连接)信号和槽不同线程则选择队列连接
信号和槽在同一个线程选择直接连接
2、Qt::DirectConnection(直接连接) 发射完信号后立即执行槽,槽执行完后信号发生后面的代码才可以执行
3、Qt::QueuedConnection(队列连接) 接收部件所在线程的事件循环返回后才执行槽,无论槽执行与否,后面代码都立即执行
4、Qt::BlockingQueuedConnection(阻塞队列连接) 只能用在信号与槽不同线程的情况下,单线程情况下会发生死锁。
5、Qt::UniqueConnection(唯一连接),相同的信号和槽只能有唯一一个关联,解决重复关联问题。
验证上述结论的过程关键程序:
void connnectMainWindow::on_pushButton_clicked()
{
connect(this,SIGNAL(mySignal()),this,SLOT(mySlot()),Qt::QueuedConnection);
qDebug()<<"发射前";
emit mySignal();
qDebug()<<"发射后";
}
根据上述理论,在该鼠标点击槽中切换不同连接方式,会有不同的输出结果
另 由于是在鼠标点击槽中实现信号与槽的连接
因此,每点击一次鼠标,信号都会连接该槽,
只有唯一连接方式可以避免出现一个信号对多个槽的输出结果。
队列连接与阻塞队列连接区别:
阻塞调用会导致程序在等待数据或资源时无法执行其他任务。同步
非阻塞调用允许程序在等待数据或资源的同时,执行其他任务。 异步
2、信号关联多个槽的调用顺序
主要两种情况
Qt4信号被触发时会随机并依次执行一遍
Qt5按关联的先后顺序执行一遍
考虑Qt符合C++,在Qt4向Qt5升级中,其实也是STL库容器的选择变换
适用于Qt4的容器:map、set
适用于Qt5的容器:vector、list、deque
STL容器主要分为以下几类:
- 顺序容器(Sequence Containers):这些容器存储的元素是有序的,可以通过位置来访问元素。顺序容器包括:
vector
:动态数组,支持随机访问,尾部插入和删除效率高,但中间插入和删除效率低。deque
(双端队列):支持在两端快速插入和删除操作的序列容器,但随机访问效率略低于vector
。list
:双向链表,支持在任何位置快速插入和删除元素,但不支持随机访问。forward_list
(C++11引入):单向链表,与list
类似,但只能向前遍历,占用空间更小。string
:专门用于存储字符序列的容器,实际上是对vector<char>
的封装,但提供了更丰富的字符串操作功能。array
(C++11引入):固定大小的数组,支持随机访问,但大小在编译时确定,不支持动态改变大小。- 关联容器(Associative Containers):这些容器存储的元素是键值对,支持通过键来快速检索元素。关联容器包括:
set
:基于红黑树实现的集合,元素唯一,且自动排序。multiset
:与set
类似,但允许存储重复元素。map
:存储键值对的集合,键唯一,且自动排序,支持通过键快速检索值。multimap
:与map
类似,但允许键重复。unordered_set
、unordered_multiset
、unordered_map
、unordered_multimap
(C++11引入):基于哈希表实现的关联容器,提供平均常数时间复杂度的查找、插入和删除操作,但不保证元素顺序。- 容器适配器(Container Adapters):这些容器不是真正的容器,而是对容器的封装,提供了特定的接口。容器适配器包括:
stack
:后进先出(LIFO)的容器适配器,通常基于deque
或list
实现。queue
:先进先出(FIFO)的容器适配器,通常基于deque
或list
实现。priority_queue
:优先队列,基于堆实现,元素按照优先级顺序出队。
3、使用信号和槽的条件:
1、类得继承QObject类 [直接或间接]
2、类首行得使用宏 Q_OBJECT
结论:1、QObject类是所有QT类的基类
2、QWidget类是所有可视界面类的基类
3、Qt的三大核心机制:信号槽、元对象系统、事件模型
4、使用信号和槽的注意事项:
当信号槽的参数为自定义类型时,需要对自定义类型进行元类型注册,可以直接使用API注册:
qRegisterMetaType<类型>("类型")
若要使用引用类型,也需要对引用注册
qRegisterMetaType<类型>("&类型")
5、信号和槽的自动关联
void on_对象名_信号名(形参列表...); //槽函数
on_pushButton_clicked() //由on、部件的objectName 、信号三部分组成
这种自动关联需要满足两个条件
//1、给对象注册名字:
对象.setObjectName("对象")
//2、调用 connectSlotsByName()
0907学习问题解决:
1、sender函数
作用:返回信号发送者的对象指针。
功能:可在槽中拿到对象指针,配合属性系统可实现对象间通信。例如:多个不同信号关联同一个槽就可以用该方式。
工程:test_sender_0907
类:SenderMainWindow
.h添加自定义信号:void mySigned();
.cpp添加槽:void mySlot();功能:输出sender()返回值
构造中关联信号与槽
connect(this,SIGNAL(mySigned()),this,SLOT(mySlot()));
在添加一个ui界面类:Second
基类:QMainWindow
ui上有一个按钮,点击则发射信号:eecondSignal()
该信号和主界面的槽mySlot关联
平台 | 跨平台 | 通信方式 | 源码 | 循环 | |
MFC | Windows | 否 | 函数回调 | 闭源 | 消息循环 |
Qt | 多平台(Linux Windows Android IOS) | 是 | 信号槽 | 开源 | 事件循环 |
2、属性系统
作用:1、可配合sender()函数实现对象间通信
2、可用来掌握控件背后的实现原理
添加属性的方式:1、宏 Q_PROPERTY
2、用API:对象.setProperty() //添加、修改
对象.property() //读取用map存名值对,若已存在则覆盖,若不存在则新增
工程:test_property_0907
类:PropertyMainWindow
添加C++类:MyClass
基类:QObject
使用宏的方式添加属性:userName
添加自定义槽:showChanged(QString str)
输出参数:qDebug<<"变化"<<str;
补充项目二:
在上基础上添加ui界面
类:Second 基类:QMainWindow
在Second中使用无参的信号,配合属性系统给主界面传递编辑框内容
先使用宏
后使用API方式动态添加属性:“textData”,内容为编辑框内容。
0908学习作业整理:
1、按钮属性整理以及函数调用:
QAbstractButton类
//QAbstractButton是一个抽象基类,提供了按钮的通用功能,如文本、图标、检查状态(对于可切换按钮如复选框和单选按钮)、快捷键等。由于它是一个抽象类,不能直接实例化,但你可以使用它的派生类,如 QPushButton、QCheckBox、QRadioButton 等。
1、text(按钮文本)
text() //获取按钮显示文本
setText(const QString &text) //设置按钮显示文本
示例:
QPushButton button("Hello, World!"); // 使用text()方法获取按钮文本并打印
qDebug() << button.text(); // 输出: "Hello, World!"
button.setText("Goodbye, World!"); // 使用setText()方法设置新的按钮文本
qDebug() << button.text(); // 输出: "Goodbye, World!"
2、icon(按钮图标)
icon() //返回当前按钮上设置的图标
setIcon(const QIcon &icon) //设置按钮显示图标
示例:
QPushButton button("My Button");
QIcon icon(":/path/to/myIcon.png");
button.setIcon(icon); // 使用setIcon()方法设置按钮图标
if (!button.icon().isNull()) {
qDebug() << "Button has an icon set.";
}
// 如果你想要检查按钮当前是否设置了图标,可以使用icon()方法并打印它(但直接打印QIcon对象可能不会很有用)一种更实用的方式是检查图标是否为空
3、iconSize(按钮图标大小)
iconSize() //返回按钮图标大小
setIconSize() //设置按钮图标大小
示例:
ui->pushButton->setIconSize(QSize(32, 32));
还可以通过样式表(StyleSheet)
示例:
ui->pushButton->setStyleSheet("QPushButton { icon-size: 24px 24px; }");
4、shortcut(按钮快捷键)
示例:
QShortcut *shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_O), this);
// 假设this指向你的窗口或对话框
QObject::connect(shortcut, &QShortcut::activated, pushButton, &QPushButton::click);
// 将快捷键的activated信号连接到按钮的clicked槽
5、checkable(按钮可切换状态)
bool checkable() const; //用于检查按钮是否设置为可切换的(checkable)状态
void setCheckable(bool); //任何QAbstractButton(QPushButton)设置为可切换的。
示例:
QPushButton button("Toggle Me");
// 检查按钮是否可切换(默认情况下,QPushButton是不可切换的)
if (!button.checkable()) {
qDebug() << "Button is not checkable by default.";
}
// 将按钮设置为可切换
button.setCheckable(true);
// 再次检查按钮是否可切换
if (button.checkable()) {
qDebug() << "Button is now checkable.";
}
5、autoRepeat(自动重复功能)
自动重复功能更多是与键盘输入和某些特定的小部件(如QSlider或QSpinBox的编辑区域)相关联,它们会在用户按住键盘上的某个键或鼠标按钮时自动重复执行某个动作。QPushButton本身并没有内置的自动重复功能,需要结合QTimer和鼠标点击释放信号实现自动重复功能
6、autoExclusive(排他方式工作,即当多个按钮中选中一个时,其他按钮自动取消选中状态。)
使用QButtonGroup实现,他允许你将多个按钮组合在一起,并可以设置这些按钮为互斥的(exclusive)。
7、autoRepeatDelay(这个属性通常与键盘事件相关,用于设置当用户按下并持续按住某个键盘键时,开始自动重复发送该键的按键事件之前的延迟时间(以毫秒为单位)。
8、autoRepeatinterval(一旦自动重复功能被激活(例如,用户长按了一个键或触发了一个支持自动重复的操作),随后的重复事件之间的时间间隔将是100毫秒。)
QPushButton类
1、autoDefault
bool autoDefault() const //获取自动默认状态
void setAutoDefault(bool) //设置自动默认状态
//用于设置按钮是否应该成为其所在窗口(或对话框)的默认按钮。如果autoDefault为true,那么当窗口获得焦点且没有其他控件处于编辑状态时,按下回车键将触发该按钮的点击事件。
2、default
bool isDefault() const //返回按钮默认状态
void setDefault(bool) //设置按钮默认状态
3、flat
bool isFlat() const //获取按钮是否无边框凸起状态
void setFlat(bool) //设置按钮是否为无边框凸起状态
QWidget类
1、enable
bool isEnable() //返回控件的启用状态。
void setEnabled(bool enabled) //设置控件的启用状态。
//当控件被禁用时,它不会响应用户输入,也不会接收任何焦点。默认情况下,控件是启用的。
示例:
QApplication app(argc, argv);
QPushButton button("点击我");
button.show();
// 检查按钮是否启用(默认是启用的)
if (button.isEnabled()) {
qDebug() << "按钮是启用的";
}
// 禁用按钮
button.setEnabled(false);
// 再次检查按钮的启用状态
if (!button.isEnabled()) {
qDebug() << "按钮现在是禁用的";
}
// 重新启用按钮
button.setEnabled(true);
2、geometry(控件的位置和大小)
QRect geometry() //返回控件的当前几何形状,包含控件的x()(水平位置)、y()(垂直位置)、width()(宽度)和height()(高度)。
setGeometry(int x, int y, int width, int height):同时设置控件的位置(x,y)和大小(width,height)。
move(int x, int y):仅设置控件的位置(x,y),不改变其大小。
resize(int width, int height):仅改变控件的大小(width,height),不改变其位置。
示例:
QApplication app(argc, argv);
QPushButton button("点击我");
button.show();
// 获取并打印按钮的当前几何形状
QRect geometry = button.geometry();
qDebug() << "按钮的几何形状:" << geometry.x() << "," << geometry.y() << " " << geometry.width() << "x" << geometry.height();
// 设置按钮的新位置和大小
button.setGeometry(100, 100, 200, 50);
3、sizePolicy(决定了当布局管理器需要调整控件大小时(例如,窗口大小改变时),控件应该如何响应。)
void setSizePolicy(QSizePolicy) //设置控件的sizePolicy。
QSizePolicy::Fixed:控件保持其固定的大小,不会随布局空间的改变而改变。
QSizePolicy::Minimum:控件至少保持其最小大小,但如果布局空间允许,它可以变得更大。
QSizePolicy::Expanding:表示控件将尝试扩展以填充可用空间,但如果空间不足,它将至少保持其最小大小。
示例:
QPushButton button("点击我");
// 设置按钮的sizePolicy为水平和垂直方向上都是Expanding
// 这意味着按钮将尝试在水平和垂直方向上扩展以填充其父布局的空间
button.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
4、minimumSize(定义了控件可以拥有的最小尺寸)
maximumSize(定义了控件可以拥有的最大尺寸)
minimumSize和maximumSize是与sizePolicy紧密相关但又有所区别的两个属性,它们共同作用于控件(widget)在布局(layout)中的大小调整行为。
5、sizelncrement(用于定义控件尺寸调整的增量值)
setSizeIncrement(const QSize &sizeIncrement)
setSizeIncrement(int w, int h) //设置控件的尺寸增量值。
QSize是一个包含宽度和高度的结构体
w和h则分别代表宽度和高度的增量值。
示例:
QPushButton button("点击我");
// 设置控件的尺寸增量为宽度50像素,高度30像素
button.setSizeIncrement(QSize(50, 30));
button.show();
6、baseSize(控件的基础大小或最小大小(单位:像素)。)
baseSize() //获取当前控件的基础大小值。
setBaseSize(const QSize &baseSize)
setBaseSize(int baseWidth, int baseHeight) //设置控件的基础大小。
QSize是一个包含宽度和高度的结构体
baseWidth和baseHeight则分别代表基础宽度和基础高度。
示例:
button.setBaseSize(QSize(100, 50));
7、palette(用于管理控件(widgets)的外观显示设置,相当于调色板)
palette() //获取控件当前的调色板对象。
setPalette(QPalette) //传入一个已设置好的QPalette对象,为控件设置新的颜色方案。
修改调色板:在获取到调色板对象后,可以使用QPalette类提供的方法来修改其颜色属性,如setColor()用于设置指定颜色角色的颜色值,setBrush()用于设置指定颜色角色或颜色组的画刷。
示例:
QPushButton button("点击我");
QPalette palette = button.palette(); // 获取当前调色板
// 设置按钮的背景颜色为浅蓝色
palette.setColor(QPalette::Button, QColor(173, 216, 230));
// 设置按钮文本颜色为白色
palette.setColor(QPalette::ButtonText, Qt::white);
// 应用新的调色板
button.setPalette(palette);
button.show();
8、font(设置控件(如标签、按钮等)文本显示属性)
setFont(const QFont &font)
示例:
QLabel *label = new QLabel("Hello, Qt!");
label->show();
// 设置字体
QFont font;
font.setFamily("Microsoft YaHei"); // 设置字体家族为微软雅黑
font.setPointSize(12); // 设置字体大小为12磅
font.setBold(true); // 设置字体加粗
// 应用字体到标签
label->setFont(font);
9、cursor(用于设置和获取窗口或控件的鼠标指针形状的属性)
setCursor(const QCursor &cursor) //设置当前光标形状
cursor() //将返回一个QCursor对象,表示当前的光标形状。
示例:
widget->setCursor(Qt::ArrowCursor);
// 设置鼠标为手形形状
widget->setCursor(Qt::PointingHandCursor);
// 使用QCursor的构造函数
QCursor cursor(Qt::BusyCursor);
widget->setCursor(cursor);
10、mouseTracking(用于控制控件是否跟踪鼠标的移动)
setMouseTracking(true);
鼠标移动跟踪:当mouseTracking属性设置为true时,控件会实时捕捉鼠标的移动事件,即使鼠标没有按下任何按钮。这意味着,只要鼠标在控件的区域内移动,控件就会接收到鼠标移动事件(mouseMoveEvent)。
默认行为:默认情况下,mouseTracking属性是关闭的(即值为false)。此时,控件只会在鼠标按钮被按下时才捕捉鼠标移动事件。这有助于减少不必要的事件处理,从而提高应用程序的性能。
示例:
通过调用setMouseTracking(true)启用了鼠标跟踪。然后,我们重写了mouseMoveEvent函数来处理鼠标移动事件
#include <QWidget>
#include <QMouseEvent>
#include <QLabel>
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr) : QWidget(parent) {
// 启用鼠标跟踪
setMouseTracking(true);
// 初始化其他控件...
}
protected:
// 重写鼠标移动事件处理函数
void mouseMoveEvent(QMouseEvent *event) override {
// 处理鼠标移动事件...
// 例如,更新某个标签的文本以显示鼠标位置
QLabel *label = findChild<QLabel *>("myLabel"); // 假设已经有一个名为"myLabel"的标签
if (label) {
label->setText(QString("(%1, %2)").arg(event->x()).arg(event->y()));
}
// 调用基类的mouseMoveEvent以确保事件被正确处理
QWidget::mouseMoveEvent(event);
}
};
11、tableTracking(用于控制控件是否跟踪触控笔(或类似触摸设备)的移动。)
12、focusPolicy(用于控制窗口部件(控件)如何接收和处理键盘焦点。)
setFocusPolicy(Qt::FocusPolicy policy) //设置焦点策略。
focusPolicy()方法。 //返回一个Qt::FocusPolicy枚举值
Qt::NoFocus:控件永远不会接收键盘焦点。这意味着,无论用户如何操作,该控件都不会成为键盘输入的接收者。
Qt::TabFocus:控件可以通过Tab键接收焦点。用户可以使用Tab键在支持Tab导航的控件之间切换焦点。
Qt::ClickFocus:控件在鼠标点击时接收焦点。用户可以通过鼠标点击控件来使其获得焦点。
Qt::StrongFocus:控件可以通过Tab键和鼠标点击接收焦点。这是大多数可编辑控件(如文本框)的默认行为。
Qt::WheelFocus:类似于Qt::StrongFocus,但控件还可以通过鼠标滚轮接收焦点。然而,这个选项在实际应用中较少使用。
示例:
QPushButton *button = new QPushButton("Click Me");
button->setFocusPolicy(Qt::ClickFocus);
13、contextMenuPolicy(定义了如何响应和处理鼠标右键点击事件,即上下文菜单的策略。)
Qt::NoContextMenu:
表示禁用上下文菜单。当设置为这个值时,即使用户右键点击控件,也不会弹出菜单。这可以用于那些不需要额外菜单选项的控件。
Qt::DefaultContextMenu:
表示使用默认的上下文菜单行为。当用户右键点击控件时,Qt 会自动弹出一个包含 actions() 返回的所有动作(QAction 对象)的菜单。这是大多数控件的默认行为。
Qt::CustomContextMenu:
当你想要自定义上下文菜单时,应该使用这个策略。设置为 Qt::CustomContextMenu 后,你需要重写控件的 contextMenuEvent(QContextMenuEvent *event) 事件处理函数来创建和显示自己的菜单。这提供了最大的灵活性,允许开发者根据需求创建复杂的上下文菜单。
Qt::ActionsContextMenu:
这个策略是 Qt::DefaultContextMenu 的一个特例,它只显示与当前控件相关联的 QAction 对象。这意味着,只有那些通过 addAction 方法添加到控件中的动作才会出现在上下文菜单中。这通常用于那些具有内置动作的控件,例如按钮或工具栏。
Qt::PreventContextMenu:
完全阻止上下文菜单的显示。即使用户执行了通常会引发上下文菜单的操作(如右键点击),也不会有任何菜单弹出。这个选项可以用来确保某些控件不接受任何上下文菜单交互,例如,一个简单的标签或静态文本控件。
14、acceptDrops(控制部件(widget)是否接受拖放(drag and drop)事件)
当设置为True时,表示部件将接受拖放事件,即该部件可以成为拖放操作的目标,允许用户将其他部件或外部应用程序中的数据拖放到该部件上。相反,当设置为False时,部件将忽略拖放事件,这些事件可能会被传递给其父部件或其他接受拖放事件的部件。
应用场景:
在文件管理器中,用户可以将文件或文件夹从一个窗口拖放到另一个窗口;在绘图软件中,用户可以将画布上的图形元素拖放到不同的位置;在表格或列表中,用户可以通过拖放来重新排序项目等。
15、toolTip(用于提供该控件功能的简短说明或额外信息)
16、toolTipDuration(用于控制ToolTip(工具提示)显示的时长,单位是毫秒。当toolTipDuration属性被设置为-1时,它表示ToolTip的显示时长将根据ToolTip内容的长度来自动计算。)
17、statusTip(用于提供当鼠标悬停在控件上时,在应用程序的状态栏(status bar)中显示的文本信息,通常用于向用户传达控件的当前状态、功能描述或额外的上下文信息。)
示例:
#include <QWidget>
#include <QString>
class MyCustomWidget : public QWidget {
Q_OBJECT
public:
explicit MyCustomWidget(QWidget *parent = nullptr) : QWidget(parent) {}
// 重写 statusTip() 函数
QString statusTip() const override {
// 返回要在状态栏中显示的文本
return QString("这是自定义控件 %1 的状态提示信息").arg(objectName());
}
};
// 在某处使用 MyCustomWidget
MyCustomWidget *widget = new MyCustomWidget(this);
widget->setObjectName("ExampleWidget"); // 设置对象名称,以便在 statusTip 中使用
// ...
// 当鼠标悬停在 widget 上时,状态栏将显示 "这是自定义控件 ExampleWidget 的状态提示信息"
18、whatsThis(供有关控件的详细帮助信息。)
setWhatsThis(QString) //设置whatsThis信息。
示例:
QPushButton *button = new QPushButton("帮助按钮", this);
button->setWhatsThis("<h3>帮助信息</h3><p>这是关于帮助按钮的详细描述。</p>");
19、accessibleName(主要用于为屏幕阅读器和其他辅助技术提供关于界面元素的简短、可读的名称。)
20、accessibleDescription(允许开发者为界面元素提供比 accessibleName 更详细的信息。这些信息有助于用户更好地理解元素的用途和功能)
21、layoutDirection(用于指定部件(widget)的布局方向,即部件在其布局中的放置方式)
Qt.LeftToRight:从左到右的布局方向
Qt.RightToLeft:从右到左的布局方向,
22、autoFillBackground(用于控制部件是否自动填充背景颜色)
当autoFillBackground属性设置为True时,部件会根据其调色板(QPalette)中的背景颜色自动填充自己的背景区域。
当设置为False时,部件的背景将不会自动填充颜色,可能显示为透明(如果父部件或窗口也允许透明背景的话)。
23、styleSheet()
对于单个控件:setStyleSheet()
示例:
myPushButton->setStyleSheet("QPushButton { color: red; }");
24、locale(用于定义用户语言、国家(或地区)以及其他与用户界面相关的语言和国家特性,如日期表示、货币表示等)
25、inputMethodHints(用于指定Qt(包括PyQt等Python绑定)中部件(widget)与输入法交互时的行为和提示。)
Qt::ImhNone:无特定的输入方法提示,系统默认。
Qt::ImhHiddenText:输入的文本应被隐藏,如密码输入。
Qt::ImhNoAutoUppercase:禁用自动大写字母功能。
Qt::ImhPreferNumbers:偏好输入数字,某些输入法可能会据此调整其显示或行为。
Qt::ImhDigitsOnly:仅允许输入数字。
Qt::ImhEmailCharactersOnly:仅允许输入电子邮件地址相关的字符。
2、行编辑框属性整理
QLineEdit类
1、inputMask(inputMask属性允许你指定一个字符串,该字符串定义了输入字段中允许用户输入的字符类型及其格式。这对于限制用户输入(如电话号码、日期、时间等)非常有用。)
0:允许输入任何数字(0-9)。
9:允许输入任何数字或空格(常用于电话号码中的可选部分)。
#:允许输入任何ASCII字符。
A:允许输入任何大写字母(A-Z)。
a:允许输入任何小写字母(a-z)。
L:允许输入任何大写字母(A-Z),如果大写锁定被激活,则输入小写字母也会转换为大写。
l:允许输入任何小写字母(a-z),如果大写锁定被激活,则输入大写字母也会转换为小写。
&:允许输入任何字符,并且该字符将作为占位符显示(即使没有输入任何内容也会显示)。
>:允许输入任何字符,且该字符将作为占位符显示,但与&不同的是,如果控件为空,则不显示占位符。
!:允许输入任何字符,但输入的字符将不会显示,这在输入密码时很有用。
C:仅允许输入大写字母(A-Z),且会自动转换为大写,即使大写锁定未激活。
c:仅允许输入小写字母(a-z),且会自动转换为小写,即使大写锁定激活。
示例:
你想要一个QLineEdit用于输入电话号码,格式为(XXX) XXX-XXXX,其中X代表数字。你可以这样设置inputMask:
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setInputMask("(000) 000-0000");
2、text(显示控件文本)
text();
setText();
示例:
QString currentText = lineEdit->text(); // 假设lineEdit是一个QLineEdit指针
lineEdit->setText("Hello, World!"); // 将QLineEdit控件中的文本设置为"Hello, World!"
3、maxLength(用于指定编辑框中允许输入的最大字符数)
setMaxLength(int len) //设置允许出现的最大字符数
示例:
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setMaxLength(10); // 设置最大长度为10个字符
4、frame()
5、echoMode(用于控制文本输入时的显示方式)
setEchoMode(QLineEdit::EchoMode mode) //设置echoMode
Normal:正常显示模式,用户输入什么就显示什么,这是默认模式。
Password:密码模式,不显示用户输入的字符,而是用圆点(或其他占位符)代替,以保护密码等敏感信息的隐私。
PasswordEchoOnEdit:在编辑时显示字符,但失去焦点时以密码模式显示。这种模式允许用户在输入时看到字符,但一旦完成输入并离开编辑框,字符就会被隐藏。
NoEcho:不显示任何输入内容,即使用户在输入,编辑框中也不会显示任何字符。这通常用于需要完全隐藏输入内容的场景。
示例:
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setEchoMode(QLineEdit::Password); // 设置为密码模式
6、cursorPosition (当前光标在编辑框中的位置)
cursorPosition()
//获取当前光标的位置,返回一个整数,表示光标从编辑框文本开始处(即第一个字符之前)到当前光标位置之间的字符数
setCursorPosition(int pos) //设置光标的位置。pos指定了光标应该移动到的新位置。
如果pos的值超出了文本范围,光标将移动到文本的末尾或开头(具体取决于pos是大于文本长度还是小于0)。
7、alignment (用于设置编辑框中文本的对齐方式)
setAlignment(Qt::Alignment alignment) //设置文本的对齐方式
alignment属性可以使用Qt的Qt::AlignmentFlag枚举类型进行设置
Qt::AlignLeft:文本左对齐,即文本从编辑框的左侧开始显示,这是默认的对齐方式。
Qt::AlignRight:文本右对齐,即文本从编辑框的右侧开始显示。
Qt::AlignCenter(或Qt::AlignHCenter):文本水平居中对齐,即文本在编辑框的水平方向上居中显示。
Qt::AlignJustify:虽然这个值在Qt的某些上下文中存在,但在QLineEdit的上下文中,它通常等同于Qt::AlignLeft,因为QLineEdit不支持文本两端对齐的复杂布局。
Qt::AlignTop:文本上对齐,这主要用于垂直方向上的对齐,但在QLineEdit中,由于它通常只处理单行文本,这个值的效果可能不如在多行文本控件中那么明显。
Qt::AlignBottom:文本下对齐,同样,这主要用于垂直方向上的对齐,但在QLineEdit中效果有限。
Qt::AlignVCenter:文本垂直居中对齐,但在QLineEdit中,由于它默认就是单行文本,并且垂直方向上的空间通常被固定分配,因此这个值的效果可能不明显。然而,在某些自定义绘制或特定布局的场景中,它仍然可以被使用。
示例:
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setAlignment(Qt::AlignRight); // 设置文本右对齐
8、dragEnabled (用于控制行编辑器在用户选择了文本后按下鼠标开始移动时是否开始拖动。)
bool dragEnabled() //检查dragEnabled属性的当前值。返回bool,表示拖动操作是否被允许。
setDragEnabled(bool enabled) //enabled为true,则允许拖动;如果为false,则禁止拖动。
当dragEnabled属性被设置为true时,如果用户在QLineEdit中选择了文本,并且按下鼠标按钮开始移动,那么这段文本就可以被拖动。
如果dragEnabled属性被设置为false,则即使用户选择了文本并尝试拖动,也不会发生拖动操作。这可以用于防止用户意外地移动或复制文本
9、readOnly (用于指定编辑框的内容是否为只读)
setReadOnly(bool readOnly) //设置readOnly属性的值
示例:
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setReadOnly(true); // 设置编辑框为只读
10、placeholderText (设置当输入框为空时显示的占位符文本。这个占位符文本在视觉上提示用户应该在这个输入框中输入什么内容)
setPlaceholderText(const QString &text) //设置占位符文本
示例:
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setPlaceholderText("请输入用户名");
11、 cursorMoveStyle(用于控制行编辑器中光标的移动样式)
Qt::LogicalMoveStyle:逻辑移动样式,这是默认选项。在这种模式下,光标按照逻辑顺序移动,即按照文本的方向和顺序进行移动。例如,在从左到右的文本中,按下左箭头键会使光标向左移动一个字符位置。
Qt::VisualMoveStyle:视觉移动样式。在这种模式下,光标的移动可能会根据文本的方向进行调整。例如,在处理从右到左的文本时,按下左箭头键可能会使光标向右移动(如果文本是从右到左书写的)。
setCursorMoveStyle(Qt::CursorMoveStyle style) // 设置光标为逻辑移动样式
示例:
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setCursorMoveStyle(Qt::LogicalMoveStyle); // 设置光标为逻辑移动样式
12、clearButtonEnabled(用于控制是否在行编辑器右侧显示一个清除按钮)
当QLineEdit控件中的文本为空时,清除按钮通常不会显示。
示例:
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setClearButtonEnabled(true); // 启用清除按钮
3、标签属性整理:
QFrame类:
1、frameShape (用于设置控件边框的形状)
setFrameShape(QFrame::Shape shape) //来设置frameShape属性的值
frameShape属性可以设置为QFrame类中定义的多种边框形状,这些形状包括但不限于:
QFrame::NoFrame:无边框。
QFrame::Box:简单的矩形边框。
QFrame::Panel:面板边框,通常比Box边框更宽或具有不同的视觉效果。
QFrame::StyledPanel:样式化面板边框,其外观取决于当前的GUI样式。
QFrame::HLine:水平线边框。
QFrame::VLine:垂直线边框。
QFrame::WinPanel:Windows风格的面板边框(仅在Windows平台上有效)。
QFrame::PopupPanel:弹出窗口风格的面板边框(其外观可能因平台而异)。
QFrame::Plain:平坦边框,通常用于需要简单边框但不希望边框突出的场景。
QFrame::Raised:凸起边框,边框看起来像是从控件表面凸出的。
QFrame::Sunken:凹陷边框,边框看起来像是嵌入到控件表面中的。
示例:
QLabel *label = new QLabel(this);
label->setFrameShape(QFrame::Box); // 设置标签为矩形边框
2、frameShadow (用于设置控件边框的阴影类型)
setFrameShadow(QFrame::Shadow shadow) //设置frameShadow属性的值。
QFrame::Plain:无阴影效果,边框看起来是平坦的。
QFrame::Raised:凸起效果,边框看起来像是从控件表面凸出的,通常与较亮的边框颜色一起使用以增强凸起效果。
QFrame::Sunken:凹陷效果,边框看起来像是嵌入到控件表面中的,通常与较暗的边框颜色一起使用以增强凹陷效果。
示例:
QLabel *label = new QLabel(this);
label->setFrameShadow(QFrame::Sunken); // 设置标签边框为凹陷效果
3、lineWidth(用于设置控件边框的宽度。)
setFrameWidth(int width) //设置边框宽度
示例:
QFrame *frame = new QFrame(this);
frame->setFrameShape(QFrame::Box); // 设置边框形状
frame->setFrameShadow(QFrame::Raised); // 设置边框阴影
frame->setFrameWidth(2); // 设置边框宽度为2像素
4、midLineWidth (用于设置控件边框中额外插入的一条线的宽度)
setMidLineWidth(int width) //设置中线的宽度。表示中线的宽度(以像素为单位)。
示例:
QFrame *frame = new QFrame(this);
frame->setFrameShape(QFrame::Box); // 设置边框形状为矩形
frame->setFrameShadow(QFrame::Raised); // 设置边框阴影为凸起
frame->setLineWidth(2); // 设置边框宽度为2像素
frame->setMidLineWidth(1); // 设置中线宽度为1像素
QLabel类:
1、textFormat (属性用于设置文本的显示格式)
Qt::PlainText:纯文本格式,不对文本进行任何特殊的格式处理。
Qt::AutoText:自动文本格式,Qt会根据文本内容(如是否包含HTML标签)来决定如何显示文本。如果文本包含HTML标签且Qt认为它是富文本(通过Qt::mightBeRichText()函数判断),则按富文本处理;否则,按纯文本处理。
Qt::RichText:富文本格式,允许文本包含HTML或富文本格式的标签,以提供更丰富的文本显示效果。
Qt::MarkdownText:Markdown文本格式,允许文本使用Markdown语法进行格式化。需要注意的是,Markdown的支持可能取决于Qt的版本和具体实现。
示例:
QLabel *label = new QLabel(this);
label->setText("<b>Hello, World!</b> <i>This is italic text.</i>");
label->setTextFormat(Qt::RichText);
2、pixmap (用于处理和显示图像。)
加载图像:
可以使用QPixmap::load()方法从文件中加载图片,或者通过构造函数直接指定图片路径。
示例代码:QPixmappixmap; pixmap.load("image.png"); 或者 QPixmappixmap("path/to/image.png");
显示图像:
QPixmap对象可以通过QWidget或其子类(如QLabel)的setPixmap()方法显示在界面上。
示例代码:QLabel *label = new QLabel(this); label->setPixmap(pixmap); label->show();
图像操作:
裁剪:使用QPixmap::copy()方法可以从原始图像中裁剪出指定大小的矩形区域,并返回一个新的QPixmap对象。
缩放:使用QPixmap::scaled()方法可以缩放原始图像并返回一个新的QPixmap对象。可以指定新的尺寸,并选择是否保持图像的纵横比。
绘制:可以使用QPainter对象在QPixmap上进行绘制操作,如绘制文本、线条、图形等。
保存图像:
使用QPixmap::save()方法可以将图片保存到文件中。
3、scaledContents(用于控制标签中显示的图像或文本是否自动缩放以适应标签的大小。)
默认情况下,scaledContents的值为false,这意味着标签中的内容将以其原始尺寸显示,不会随标签大小的改变而改变。
示例:
QLabel *label = new QLabel(this);
QPixmap pixmap("path/to/image.png");
label->setPixmap(pixmap);
label->setScaledContents(true); // 设置scaledContents为true,使图像自动缩放以适应标签大小
4、alignment (用于控制标签内文本或图像的对齐方式)
setAlignment(Qt::Alignment) //设置对齐方式
示例:
QLabel *label = new QLabel(this);
label->setText("Hello, World!");
label->setAlignment(Qt::AlignCenter); // 居中对齐文本
Qt::AlignLeft:左对齐
Qt::AlignRight:右对齐
Qt::AlignHCenter:水平居中对齐
Qt::AlignJustify:两端对齐(文本专用)
Qt::AlignTop:顶部对齐
Qt::AlignBottom:底部对齐
Qt::AlignVCenter:垂直居中对齐
Qt::AlignCenter:居中对齐(同时设置水平和垂直居中对齐)
5、wordWrap (用于控制文本是否自动换行。)
默认情况下,QLabel可能不会根据控件的宽度自动换行显示长文本,这可能会导致文本被截断或显示不全。通过设置wordWrap属性为true,可以实现文本的自动换行,从而确保文本内容能够完整地显示在QLabel中。
setWordWrap()方法来设置wordWrap属性。该方法接受一个布尔值作为参数,true表示启用自动换行,false表示禁用自动换行。
示例:
QLabel *label = new QLabel(this);
label->setText("This is a very long text that needs to be wrapped automatically.");
label->setWordWrap(true); // 启用自动换行
6、margin (用于设置标签内部文本与边框之间的边距)
setMargin(int margin) //来设置边距
示例:
QLabel *label = new QLabel(this);
label->setText("Some text content");
label->setMargin(10); // 设置边距为10像素
如果你需要为不同方向设置不同的边距,可以考虑使用
setContentsMargins(int left, int top, int right, int bottom)
示例:
QLabel *label = new QLabel(this);
label->setText("Some text content");
// 设置左、上、右、下的边距分别为10、20、30、40像素
label->setContentsMargins(10, 20, 30, 40);
7、indent(文本缩进)
8、openExternalLinks (用于控制当点击QLabel中嵌入的HTML超链接时,是否自动使用默认浏览器打开这些链接。)
被设置为true时,QLabel会自动检测其文本内容中的HTML超链接(即<a href="...">标签),并在用户点击这些链接时,使用QDesktopServices::openUrl()函数来打开默认的网页浏览器以访问链接指向的URL。
示例:
QLabel *label = new QLabel(&window);
// 设置QLabel的openExternalLinks属性为true
label->setOpenExternalLinks(true);
// 设置QLabel的文本为包含HTML超链接的字符串
label->setText("<a href='https://www.example.com'>Visit Example.com</a>");
9、textinteractionFlags (用于控制用户与文本内容之间的交互方式)
setTextInteractionFlags(Qt.TextInteractionFlags flags) //设置QLabel的文本交互标志。
Qt.NoTextInteraction:不能与文本进行任何交互。
Qt.TextSelectableByMouse:允许用户使用鼠标选择文本,并可能通过上下文菜单或标准键盘快捷键复制到剪贴板。
Qt.TextSelectableByKeyboard:允许用户使用键盘上的光标键选择文本,并显示一个文本光标。
Qt.LinksAccessibleByMouse:链接高亮显示,并可用鼠标点击激活。
Qt.LinksAccessibleByKeyboard:链接可以使用Tab键获得焦点,并通过Enter键激活。
Qt.TextEditable:文本完全可编辑,用户可以在文本框中输入和修改文本。
Qt.TextEditorInteraction:文本编辑器的默认值,结合了多种交互方式。
Qt.TextBrowserInteraction:QTextBrowser的默认值,也结合了多种交互方式,适用于浏览器风格的文本显示。
示例:
QLabel *label = new QLabel(&window);
// 设置QLabel的文本内容
label->setText("这是一段可选择的文本内容。");
// 允许用户通过鼠标选择文本
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
// 将QLabel添加到窗口中(这里只是示例,实际添加方式可能有所不同)
window.show();
10、buddy()
0909学习问题解决:
1、按钮、标签、行编辑框属性以及读写函数
2、对象树
1、本质:多叉树,利用了栈的特点去自动销毁父节点时,其孩子列表的对象(一般是堆上的内容)也一起被销毁。
2、作用:防止内存泄漏
若对象有界面,还可以控制界面显示层次[子部件在父部件上显示,这个父子是非继承关系,只是树上的结点关系]
3、添加对象到树的方式:
(1)实例化时,传父节点指针
如: A*a1 = new A(this); //this作为a1的父节点指针。
(2)使用API来指定父节点指针,然后对象.setParent(父节点指针)、
如:A*a2 = new A;
a2->setParent(this);//this作为a2的父节点指针。
工程:test_object_tree_0909
类:ObjectTreeMainWindow
添加C++类:MyButton
基类:QPushButton
添加析构函数:~MyButton()
3、元对象系统(MOS)
1、元对象:描述另一个对象结构的对象
2、作用:提供了对象间通信的信号和槽机制、运行时类型信息、动态属性系统。
3、使用条件:
(1)类要直接或间接继承QObject类。
(2)类的首行使用Q_OBJECT宏
(3)使用MOC【元对象编译器】补全QT特性代码
MOC会对符合条件的自定义类进行代码补全,会将Q_OBJECT宏进行展开,并将静态元元对象进行全局初始化,同时也会对三个操作元对象的虚函数进行代码补全实现,若有自定义信号,则会对该信号进行函数体补全并激活信号,最终在生成目录中产生一个源文件,名为:moc_类名.cpp,最后再和其他源文件一起进行真正的编译。
0911学习问题解决:
1、事件的概念,都有哪些事件
事件是对各种应用程序需要知道的由应用程序内部或者外部产生的事情或者动作的通称。
硬件级事件:鼠标事件、键盘事件、滚轮事件
软件级事件:定时事件、上下文菜单事件、关闭事件、拖放事件、绘制事件、重调大小等。
2、事件的处理方式
1、重写具体的事件处理函数(需继承某个部件类之后来实现)
创建工程qt_event_0911
类:EventMainWindow 基类:QMainWindow
使用自定义的EventMainWindow作为主窗口(继承自MainWindow)
然后在上面放置一个自定义的MyLineEdit(继承自QLineEdit)。
添加C++类:MyLineEdit 基类:QLineEdit
在mylineedit.h中完成以下操作:
1、添加头文件#include <QLineEdit>
2、添加Q_ObJECT宏
3、MyLineEdit(QWidget *p = 0);
//参数p用于指定MyLineEdit控件的父控件,参数0/nullptr则允许在不需要父控件时创建独立的MyLineEdit 实例,他就可以是一个顶级窗口。
4、protected: //这个函数只能在类内部、派生类(子类)中访问
void keyPressEvent(QKeyEvent *event);
在mylineedit.cpp中实现:
1、委托构造
MyLineEdit::MyLineEdit(QWidget *p)://调用基类QLineEdit的构造函数,并将传入的父控件指针p传递给它。
QLineEdit(p)
{
}
2、重写keyPressEvent具体事件的处理函数
#include <qevent.h>
void MyLineEdit::keyPressEvent(QKeyEvent *event)
{
qDebug()<<"MyLineEdit键盘按下" ;
QLineEdit::keyPressEvent(event);
event->ignore();
}
在eventmainwindow.h中完成以下操作:
protected:
void keyPressEvent(QKeyEvent *event);
MyLineEdit * m_myLineEdit;
在eventmainwindow.cpp中完成以下操作:
构造函数中:
m_myLineEdit = new MyLineEdit(this);
m_myLineEdit->move(100,100);
void EventMainWindow::keyPressEvent(QKeyEvent *event)
{
qDebug() <<"EventMainWindow键盘按下事件";
}
事件是先传递给指定窗口部件的,这里确切的说应该是先传递给获得焦点的窗口部件的。但是如果该部件忽略掉该事件,那么这个事件就会传递给这个部件的父部件。在重新实现事件处理函数时,一般要调用父类的相应的事件处理函数来实现默认的操作。
2、重写notify通知函数 (一次只能处理一个事件)
3、给QApplication对象安装事件过滤器 (能同时处理多个事件)
4、重写event函数 (到达默认事件处理函数之前,拿到事件(一般位于子部件))
在myLineEdit.h中添加函数声明:
bool event(QEvent *e);
myLineEdit.cpp中添加
bool MyLineEdit::event(QEvent *e)
{
if(e->type()== QEvent::KeyPress){
qDebug()<<"MyLineEdit event函数";
}
return QLineEdit::event(e);//执行默认的事件处理函数
}
5、给任意对象安装事件过滤器(可同时过滤多个对象,多个事件)
安装事件过滤器分两步:
(1)先给目标对象安装事件过滤器:
对象.installEventFilter(this);
(2)重写事件过滤函数:
bool eventFilter(QObject * watched,QEvent *event)
在eventmainwindow.h中完成以下操作:
bool eventFilter(QObject *watched, QEvent *event);//进行事件过滤器函数的声明
eventmainwindow.cpp的构造函数中
m_myLineEdit->installEventFilter(this); // 为lineEdit安装事件过滤器
下面是事件过滤器函数的定义:
bool Widget::eventFilter(QObject *obj, QEvent *event) // 事件过滤器
{
if(obj == lineEdit) { // 如果是lineEdit部件上的事件
if(event->type() == QEvent::KeyPress)
qDebug() << tr("Widget的事件过滤器");
}
return QWidget::eventFilter(obj, event);
}
在事件过滤器中,先判断该事件的对象是不是lineEdit,如果是,再判断事件类型。最后返回了QWidget类默认的事件过滤器的执行结果。
事件的传递顺序是这样的:先是事件过滤器,然后是该部件的event()函数,最后是该部件的事件处理函数。这里还要注意,event()函数和事件处理函数,是在该部件内进行重新定义的,而事件过滤器却是在该部件的父部件中进行定义的。
0912学习问题解决:
实现一个自定义的单行文本输入框,要求是:只能输入数字,非数字不能显示,想办法通过事件来处理此效果
1、普通事件处理
2、QLineEdit有一个验证器规则:正则表达式
窗口状态
窗口状态有四种:
正常状态、最大化状态、最小化状态、全屏
最大化状态:中间部件占满
全屏:内容占满屏幕[看不到标题栏]
取事件相关函数
取事件相关函数:
对象.type() 取类型
对象.button() 取鼠标左右键(按钮)
对象.buttons() 取正在按的鼠标左右键
对象.key() 取键盘按下的键
对象.modifiers() 取键盘按下的功能键
定时器
定时器[周期性的执行功能,相当于有个循坏在跑]
1、作用:可在主线程中实现多个惹怒操作,类似于多线程
2、类:<QTimer>
3、QTimer的API:
启动:start()/start(毫秒) //启动时设定周期,中间无法修改
停止:stop()
设定周期:setInterval(毫秒)//可在任何时刻调整
溢出信号:timeout()
要实现定时功能,得自定义一个槽和其关联[槽中不能写死循环]
随机数
随机数
取随机数:qrand()
设定随机种子;qsrand()
取特定范围:[begin,end]
qrand()%(end-begin+1)+begin
[begin,end)
qrand()%(end-begin)+begin
随机种子:
一般用能动变化的值,用时间戳作为种子
0913学习问题解决:
1、界面有三个标签,背景色分别红、绿、蓝,双击某个标签时,标签会全屏显示,再双击则恢复原始位置。
(全屏显示某个子部件,需要将该部件标志位改为独立后的窗口,在恢复则改为子窗口的标志)
#ifndef ABCMAINWINDOW_H
#define ABCMAINWINDOW_H
#include <QLabel>
#include <QMainWindow>
namespace Ui {
class ABCMainWindow;
}
class ABCMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit ABCMainWindow(QWidget *parent = 0);
~ABCMainWindow();
void showLabel(QLabel *label);
protected:
bool eventFilter(QObject *watched, QEvent *event);
private:
Ui::ABCMainWindow *ui;
};
#endif // ABCMAINWINDOW_H
#include "abcmainwindow.h"
#include "ui_abcmainwindow.h"
ABCMainWindow::ABCMainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ABCMainWindow)
{
ui->setupUi(this);
ui->label->installEventFilter(this);
ui->label_2->installEventFilter(this);
ui->label_3->installEventFilter(this);
}
ABCMainWindow::~ABCMainWindow()
{
delete ui;
}
void ABCMainWindow::showLabel(QLabel *label){
static QRect rect;
if(label->isFullScreen()){
label->setWindowFlags(Qt::SubWindow);
label->showNormal();
label->setGeometry(rect);
}else{
rect = label->geometry();
label->setWindowFlags(Qt::Window);
label->showFullScreen();
}
}
bool ABCMainWindow::eventFilter(QObject *watched, QEvent *event)
{
if(event->type() == QEvent::MouseButtonDblClick){
if(ui->label==watched){
showLabel(ui->label);
}else if(ui->label_2==watched){
showLabel(ui->label);
}else if(ui->label_3==watched){
showLabel(ui->label);
}
}
return QMainWindow::eventFilter(watched,event);
}
2、在界面创建二维表格QTableWidget(10行10列),每项内容为随机数,范围[18,98],且每3秒刷新一次
testMainWindow.h
#ifndef TESTMAINWINDOW_H
#define TESTMAINWINDOW_H
#include <QMainWindow>
#include <QTableWidget>
#include <QTimer>
#include <QTime> // 用于获取当前时间作为随机数种子
#include <cstdlib> // 包含qrand()和qsrand()
namespace Ui {
class testMainWindow;
}
class testMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit testMainWindow(QWidget *parent = nullptr);
~testMainWindow();
private slots:
void updateTable();
private:
Ui::testMainWindow *ui;
QTableWidget *tableWidget;
};
#endif // TESTMAINWINDOW_H
=========================================================================================
testMainWindow.cpp
#include "testmainwindow.h"
#include "ui_testmainwindow.h"
testMainWindow::testMainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::testMainWindow)
{
ui->setupUi(this);
// 初始化随机数生成器
qsrand(QTime::currentTime().msec());
tableWidget = new QTableWidget(10, 10, this);
tableWidget->setGeometry(10, 10, 700, 300); // 设置位置和大小
for (int row = 0; row < 10; ++row) {
for (int column = 0; column < 10; ++column) {
QTableWidgetItem *item = new QTableWidgetItem(QString::number(qrand() % 82 + 18)); // 生成18到99之间的随机数
tableWidget->setItem(row, column, item);
}
}
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &testMainWindow::updateTable); // 注意这里使用正确的类名
timer->start(3000); // 每3秒触发一次
}
testMainWindow::~testMainWindow()
{
delete ui;
}
void testMainWindow::updateTable()
{
for(int row = 0; row < 10; ++row) {
for (int column = 0; column < 10; ++column) {
int randomValue = qrand() % 82 + 18; // 生成18到99之间的随机数
QTableWidgetItem *item = tableWidget->item(row, column);
if (item) {
item->setText(QString::number(randomValue));
}
}
}
}
3、Qt多线程
线程类:<QThread>
创建线程的方式:
(1)重写run()函数
自定义一个类,继承QThread,重写run()函数,并在run中实现线程功能,使用时,实例化该类,并调用start函数启动线程。
(2)使用moveToThread函数
自定义一个类,继承QObject,添加槽函数来实现线程功能,使用时,实例化两个对象,分别类自定义对象和Qthread对象,并使用自定义类对象,moveToThread(QThread对象指针),使用关联函数connect关联QThread对象的Started信号和自定义类的槽,最后,使用QThread对象调用start函数启动线程。
QThread类的重要API:
1、启动:start()
2、停止:terminate()
3、阻塞等待:wait()
4、睡眠:静态成员函数
秒级:Sleep(秒数)
毫秒级:msleep(毫秒数)
微妙级:usleep(微秒级)
5、查看线程[静态]
指针:currentThread()
id:currentThreadId()
6、信号
已启动:started() [start函数中发出]
已完成:finished()[线程结束时发出]
0914学习问题解决:
1、有两条线程,可以分别输出“A”、“B”需要控制顺序输出“ABABABABABAB”到主界面的标签上显示,使用重写run函数的方式实现
1、先创建两个线程
以a.h为例,b.h b.cpp\c.h c.cpp也都一样套路
#ifndef A_H
#define A_H
#include <QThread>//1、添加QThread头文件
class A : public QThread
{
Q_OBJECT //2、添加Q_OBJECT宏
public:
A();
protected:
void run();//重写run();
};
#endif // A_H
=========================================================================================
a.cpp
#include "a.h"
int g_flag = 1;
A::A()
{
}
void A::run(){//重写run函数,通过发射信号联结槽实现在标签上显示的功能
for(int i=0;i<6;){//需要六组数据,因此要发送六次信号,带参,参数为要发送的A
if(g_flag == 1){//设置全局变量,作为切换的判断条件,1发送A,2发送B
emit aSignal("A");
g_flag = 2;
i++;
}
}
for(int i=0;i<12;){//如果要输出两个一致的字符,需要发射两次信号,可以用全局变量设两个标志量都发送该信号。
if(g_flag == 2 || g_flag ==3){
emit bSignal("B");
g_flag ++;
if(g_flag>3){
g_flag=1;
}
i++;
}
}
}
=========================================================================================
testMainWindow.h
#ifndef TESTMAINWINDOW_H
#define TESTMAINWINDOW_H
#include <QMainWindow>
#include "a.h" //1、包A类的头文件
#include "b.h"
namespace Ui {
class testMainWindow;
}
class testMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit testMainWindow(QWidget *parent = 0);
~testMainWindow();
public slots:
void showAB(QString str); //3、a和b线程发射的信号需要槽来接收
private:
Ui::testMainWindow *ui;
A *m_aThread; //2、实例化A的对象
B *m_bThread;
};
#endif // TESTMAINWINDOW_H
=========================================================================================
testMainWindow.cpp
#include "testmainwindow.h"
#include "ui_testmainwindow.h"
testMainWindow::testMainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::testMainWindow)
{
ui->setupUi(this);
m_aThread = new A; //1、初始化线程a对象
m_aThread->start(); //2、开启线程
connect(m_aThread,SIGNAL(aSignal(QString)),this,SLOT(showAB(QString)));//4、连接信号槽
m_bThread = new B;
m_bThread->start();
connect(m_bThread,SIGNAL(bSignal(QString)),this,SLOT(showAB(QString)));
}
testMainWindow::~testMainWindow()
{
delete ui;
m_aThread->deleteLater; //5、安全释放线程
m_bThread->deleteLater;
}
void testMainWindow::showAB(QString str) //3、实现槽函数功能:在标签上显示字符
{
ui->label->setText(ui->label->text()+str);
}
2、 有三条线程分别输出A、B、C,按顺序输出“ABCABCABCABCABCABC”显示到标签,使用moveToThread方法实现。
自定义一个类,继承QObject,添加槽函数来实现线程功能。需要添加三个槽函数实现三个线程
使用时,每个线程实例化两个对象,分别类自定义对象和Qthread对象,
使用自定义类对象.moveToThread(QThread对象指针)
使用关联函数connect关联QThread对象的Started信号和自定义类的槽
使用QThread对象调用start函数启动线程。
abc.h
#ifndef ABC_H
#define ABC_H
#include <QObject>
class ABC : public QObject
{
Q_OBJECT
public:
explicit ABC(QObject *parent = nullptr);
static int flag;
signals:
void aSignal2(QString);
void bSignal2(QString);
void cSignal2(QString);
public slots:
void aSlot2();
void bSlot2();
void cSlot2();
};
#endif // ABC_H
=========================================================================================
abc.cpp
#include "abc.h"
int ABC::flag=1; //这里使用静态成员变量作为标志位,静态成员变量需要在类外初始化
ABC::ABC(QObject *parent) : QObject(parent)
{
}
void ABC::aSlot2()
{
for(int i =0;i<6;){
if(flag ==1){
emit aSignal2("A");
flag = 2;
i++;
}
}
}
void ABC::bSlot2()
{
for(int i =0;i<6;){
if(flag ==2){
emit bSignal2("B");
flag = 3;
i++;
}
}
}
void ABC::cSlot2()
{
for(int i =0;i<6;){
if(flag ==3){
emit cSignal2("C");
flag = 1;
i++;
}
}
}
=========================================================================================
testMainWindow.h
#ifndef TESTMAINWINDOW_H
#define TESTMAINWINDOW_H
#include <QMainWindow>
#include "a.h"
#include "b.h"
#include "abc.h"
#include <QThread>
namespace Ui {
class testMainWindow;
}
class testMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit testMainWindow(QWidget *parent = 0);
~testMainWindow();
public slots:
void showAB(QString str);
void show2(QString str); //实现功能槽,在标签上显示ABC
private:
Ui::testMainWindow *ui;
A *m_aThread;
B *m_bThread;
ABC*m_a; //声明线程要执行的对象指针
ABC*m_b;
ABC*m_c;
QThread*m_Threada; // 声明线程指针
QThread*m_Threadb;
QThread*m_Threadc;
};
#endif // TESTMAINWINDOW_H
=========================================================================================
testMainWindow.cpp
#include "testmainwindow.h"
#include "ui_testmainwindow.h"
testMainWindow::testMainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::testMainWindow)
{
ui->setupUi(this);
m_a = new ABC; ///实例化指针
m_b = new ABC;
m_c = new ABC;
m_Threada = new QThread; 实例化线程指针
m_Threadb = new QThread;
m_Threadc = new QThread;
m_a->moveToThread(m_Threada); ///使用QObject类自带的函数moveToThread
connect(m_Threada,SIGNAL(started()),m_a,SLOT(aSlot2())); //连接线程启动后要执行的槽信
m_Threada->start();
connect(m_a,SIGNAL(aSignal2(QString)),this,SLOT(show2(QString)));
m_b->moveToThread(m_Threadb);
connect(m_Threadb,SIGNAL(started()),m_b,SLOT(bSlot2()));
m_Threadb->start();
connect(m_b,SIGNAL(bSignal2(QString)),this,SLOT(show2(QString)));
m_c->moveToThread(m_Threadc);
connect(m_Threadc,SIGNAL(started()),m_c,SLOT(cSlot2()));
m_Threadc->start();
connect(m_c,SIGNAL(cSignal2(QString)),this,SLOT(show2(QString)));
}
testMainWindow::~testMainWindow()
{
delete ui;
delete m_a;
delete m_b;
delete m_c;
m_aThread->deleteLater();
m_bThread->deleteLater();
m_Threada->deleteLater();
m_Threadb->deleteLater();
m_Threadc->deleteLater();
}
void testMainWindow::showAB(QString str)
{
ui->label->setText(ui->label->text()+str);
}
void testMainWindow::show2(QString str)
{
ui->label_2->setText(ui->label_2->text()+str);
}
3、当多条线程操作统一资源会产生争夺资源导致脏数据或可能崩溃
线程同步
解决问题:使其同步,同步的方法有以下几种:
0915学习问题解决:
死锁现象:
1、一把互斥锁连续锁两次,实现死锁场景。
void QMutexMainWindow::on_pushButton_clicked()
{
mtex1.lock();
qDebug()<<"拿到了第一次锁";
mtex1.unlock(); //这里如果不解锁,只在最后解锁,会导致死锁
qDebug()<<"解锁了";
mtex1.lock();
qDebug()<<"拿到了第二次锁";
mtex1.unlock();
qDebug()<<"解锁了";
}
2、两条线程,两把锁各自锁上,还想要对方的锁。(相互等待的死锁)
创建两个线程A、B,使用重写run()函数,
a.cpp
#include "a.h"
QMutex mutex1;
extern QMutex mutex2;
A::A()
{
}
void A::run()
{
mutex1.lock();
qDebug() << "拿到mutex1锁"; //这里如果拿到1锁,继续去拿2锁,就会导致相互等待的死锁情况
mutex2.lock();
qDebug()<<"拿到mutex2锁";
mutex1.unlock();
qDebug()<<"解mutex1锁";
}
=========================================================================================
b.cpp
#include "b.h"
QMutex mutex2;
extern QMutex mutex1;
B::B()
{
}
void B::run()
{
mutex2.lock();
qDebug() << "拿到mutex2锁";
mutex1.lock();
qDebug()<<"拿到mutex1锁";
mutex2.unlock();
qDebug() << "解mutex2锁";
}
=========================================================================================
qmutexmainwindow.cpp
void QMutexMainWindow::on_pushButton_2_clicked()
{
m_Threada.start();
m_Threadb.start();
}
========================================================================================
qmutexmainwindow.h
#ifndef QMUTEXMAINWINDOW_H
#define QMUTEXMAINWINDOW_H
#include <QMainWindow>
#include "a.h"
#include "b.h"
#include <QDebug>
namespace Ui {
class QMutexMainWindow;
}
class QMutexMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit QMutexMainWindow(QWidget *parent = 0);
~QMutexMainWindow();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::QMutexMainWindow *ui;
A m_Threada;
B m_Threadb;
};
3、一把互斥锁,多次调用,产生了异常来解锁,导致异常死锁2
#include "cthread.h"
#include <QMutex>
#include <QDebug>
QMutex g_mtxC;
CThread::CThread()
{
}
void CThread::test()
{
qDebug()<<"线程ID:"<<currentThreadId()<<"已启动,准备拿C锁";
g_mtxC.lock();
qDebug()<<"线程ID:"<<currentThreadId()<<"拿到C锁";
throw 88;
g_mtxC.unlock();
qDebug()<<"线程ID"<<currentThreadId()<<"解了C锁";
}
void CThread::run()
{
try{
test();
}catch(int){
qDebug()<<"线程ID:"<<currentThreadId()<<"解了C锁";
}
}
由于异常发生在 g_mtxC.unlock() 调用之前。这会导致一个潜在的死锁问题,因为如果在 g_mtxC.lock() 和抛出异常之间发生异常,那么互斥锁将不会被释放,进而阻塞任何尝试获取该锁的其他线程 。
常见的做法是使用RAII(Resource Acquisition Is Initialization)原则,即资源获取即初始化。这可以通过使用智能锁(如 std::lock_guard
或 std::unique_lock
)来实现,它们会在其生命周期结束时自动释放锁。
#include "cthread.h"
#include <QMutex>
#include <QDebug>
#include <mutex>
using namespace std;
QMutex g_mtxC;
mutex g_mtxC1;
//std::mutex 和 QMutex 是两个不同库(C++ 标准库和 Qt)中提供的互斥锁实现,它们之间不兼容。
CThread::CThread()
{
}
void CThread::test()
{
qDebug()<<"线程ID:"<<currentThreadId()<<"已启动";
lock_guard<mutex> lg(g_mtxC1);
qDebug()<<"线程ID:"<<currentThreadId()<<"拿到C锁";
throw 88;
}
void CThread::run()
{
try{
test();
}catch(int){
qDebug()<<"线程ID:"<<currentThreadId()<<"解了C锁";
}
}
死锁形成的条件
(1)互斥条件:一个资源每次只能被一个进程使用
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
解决死锁
破坏形成死锁的条件即可,如剥夺资源、撤销线程。
剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。
避免死锁(银行家算法)
当一个进程申请使用资源的时候,银行家算法通过先试探分配给该进程资源,然后通过安全性算法判断分配后的系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待。
检测死锁
1、建立唯一标识的资源分配表和线程等待表。定时查看两张表的情况,若资源表一直波动,等待表一直不变动,则可能死锁了。
2、还可以查看线程栈的波动情况,一直不波动也可能死锁。
3、人为监视代码。
进程与线程
进程:是系统分配资源和调度的基本单元
与线程的区别:进程包含线程,每个进程默认有一条主线程,而线程不能独立存在。
进程的创建方式:
Qt中创建进程:
<QProcess>,用start函数启动子进程
平台创建进程:
Windows: CreateProcess();
Linux: fork(); //接着后面的代码往后执行
子线程不能创UI,只能在主线程处理并显示UI(UI需要的事件循环在主线程中),子线程使用信号槽交互于主线程。
查看子线程指针、id QThread::currentThread(); QThread::currentThreadId();
哪里属于真正的子线程,就看哪里是真正的线程函数体:
1、重写虚函数run():在run()函数中即为子线程的函数体。
2、moveToThread函数:和线程started()信号相关联的槽函数
多线程的通信方法
1、全局变量(配合同步方式:原子操作)
2、信号槽(传参:sender+属性系统)
3、消息队列
4、管道(命名、匿名)
进程间通信方法
1、共享内存【文件映射等】【上锁来同步】
2、socket网络通信【TCP、UDP协议】【TCP/IP】
3、消息队列
4、管道
5、剪切板
模型视图代理(MVD)
1、MVD设计模式[思想]
M:model模型,用来存放数据
V:view 视图,用来显示数据
D:Delegate代理,用来提供编辑功能
2、作用:将数据的存储和显示进行了分离,使得1份数据可以在不同的视图中显示
3、类、API、代码例子
0918学习问题记录
1、QT操作快捷键
1、同步列输入:按住Alt,拖动鼠标选中多行操作
2、快速给函数添加定义:鼠标点击头文件声明函数,Alt+Enter
3、快速的生成一个函数说明注释格式:在函数名的上一行,输入 /** 然后回车。就会自动根据该函数创建注释。
4、自动换行注释: /// 然后输入注释回车后自动添加下一行注释。
2、线程池代码