C++(week16): C++提高:(六) Qt提高
文章目录
- 四、Qt的元对象系统
- 1.元对象和MOC
- (1)自省 和 反射
- (2)Qt是怎样支持元对象系统的?
- (3)支持元对象系统的三个要求
- (4)元对象系统的功能
- (5)动态属性
- 2.信号和槽机制
- (1)信号与槽机制的基本原理
- (2)自定义信号、自定义槽函数
- ①自定义信号
- ②自定义槽
- ③关联 connect
- (3)信号与槽的特点
- ①槽函数的调用是同步的
- 让信号携带参数
- 处理重载的函数:qOverload
- 如果重复connect
- 交换信号和槽的地位
- (4)减少重复槽函数
- (5)信号与槽的优缺点:信号槽 vs 回调
- 3.对象树
- (1)基础复习:四种指针
- (2)依赖注入
- (3)组合模式
- (4)Qt中的内存申请
- 4.事件系统
- (1)事件和事件循环
- (2)事件到达,处理事件的三个步骤
- ①事件过滤器
- ②event函数
- ③事件处理函数 event handler
- 同时使用event和event handler
- 人造按钮
- 同时使用event filter、event、event handler
- (3)多个对象之间的事件传递
- 事件传递的过程
- 事件系统 + 信号槽,让两个组件互动
- 五、Qt_Widgets模块
- 1.QWidget类的概念
- paintEvent
- 小项目3:打蚊子
- 2.窗口
- (1)定义
- (2)成为窗口的条件
- (3)QWidget当中和窗口相关的属性和方法
- 3.对话框 QDialog
- (1)模态
- (2)自定义方式
- (3)子类:标准对话框
- ①QColorDialog:颜色选择对话框
- ②QFileDialog:文件选择对话框
- ③QFontDialog:字体选择对话框
- ④QMessageBox:信息框
- 4.主窗口 QMainWindow
- (1)菜单栏:QMenubar
- (2)工具栏:QToolbar
- (3)状态栏:QStatusBar
- 5.常见的简单组件
- (1)用来显示的组件:Display Widgets
- Label
- (2)Buttons:按钮
- (3)QComboBox:下拉框
- (4)QLineEdit:单行输入
- (5)容器:containers
- 6.组合组件和界面布局
- (1)布局
- 7.model/view模型:MVC模型
- (1)如何描述一个复杂的界面:分层
- (2)Qt中的MVC:model/view architecture
- ①model
- ②view 视图
- 使用model和view
- 六、Qt的其他模块
- 1.QTimer 定时器
- 2.Qt文件操作:文件IO
- 3.Qt网络编程:网络IO
- 4.Qt多线程
四、Qt的元对象系统
为了实现信号槽 (目的),所以才设计了元对象系统 (手段)。
元数据:不是对象本身的主要内容,而是对象的额外信息(如文件名、文件路径、修改时间)。
元对象系统(Meta-Object System,简称MOS)是一种编程语言特性,用于支持反射(reflection)和元编程(metaprogramming)。它允许程序在运行时获取关于自身结构和行为的信息,甚至能够动态地修改自己的结构和行为。

1.元对象和MOC
元对象:保存了对象的额外信息。额外分配了内存。

(1)自省 和 反射
1.自省

2.反射
反射:为了实现间接访问。运行时,传入字符串,根据字符串调用函数。
反射:在运行时,可以获取并修改对象的结构
(2)Qt是怎样支持元对象系统的?
标准C++不支持元对象
MOC:元对象编译。
Qt需要两次编译:先

(3)支持元对象系统的三个要求
1.继承QObject类,且将QObject作为继承的第一个基类。 以确保内存位置是在第一个。
2.使用Q_OBJECT宏 (Q_OBJECT宏添加了一些函数的定义)

3.使用元对象的类,需要单独使用一个头文件,不能放在main.cpp中


(4)元对象系统的功能
1.支持反射,运行时获取和调整对象的结构
2.信号和槽
3.运行时类型转换
4.对象树
(5)动态属性
反射带来的好处:反射没有带来效率,但带来了更好的灵活性、可维护性。 lose函数不需要修改。
可以在运行的过程中,动态地修改。

反射在工作中的应用场景:
(1)游戏开发:导表工具,转化为python或lua代码
(2)互联网开发:路径路由,根据服务器网页路径调用不同的函数
2.信号和槽机制
信号和槽机制的目的:真正实现对象间的通信。并且是松耦合。
通过connect函数实现耦合。slots、signals、emit是Qt的伪关键字,会通过MOC编译转换为标准C++。
而面向对象的标准C++,实现对象间通信的方式还是函数调用:直接调用 或 回调函数。
(1)信号与槽机制的基本原理
一读一写的流式通信很痛苦,收发错位就有bug。想办法把发和收封装为原子操作。

HTTP协议把四步合为一个事务。发明者参考了函数调用。

A与B之间进行通信

当B的数量很多的时候,这样写起来并不优雅。


A执行发送信息的代码,不要出现任何B的信息。
发送方,发射信号。接收方,调用槽函数。
(2)自定义信号、自定义槽函数
总结:回调函数、观察者模式
A发,B收
自定义信号、自定义槽、在发射信号之前关联信号和槽
①自定义信号
//自定义信号
signals: void signalA();
(1)signals:Qt的伪关键字
(2)自定义信号的设计,就是一个普通的成员函数声明
①放在signals访问权限控制符下面
②返回值必须是void
③只写声明不写定义,信号函数的定义由MOC自动添加
②自定义槽
//自定义槽函数
public slots:void slotsB();void slotB1(int i);void slotB2(int i, int j);
(1)槽函数的访问权限控制符 public/protected/private slots
(2)槽函数的书写和普通的成员函数没有任何区别
(3)槽函数的参数和返回值应当和信号保持一致。槽函数的参数个数必须小于等于信号的参数个数,否则会报错。用SIGNAL宏关联会在运行阶段报错,用指向成员函数的指针关联会在编译阶段报错。
③关联 connect
connect的两种重载形式:
1.字符串:SIGNAL宏、SLOT宏
QObject::connect(&a, SIGNAL(signalA()), &b, SLOT(slotB()));
SIGNAL():把函数名变为字符串
SLOT():把槽函数变为字符串。
2.指向成员函数的指针
QObject::connect(&a, &A::signalA, &b, &B::slotB);
第二种更好。
因为,当槽函数的参数个数大于信号的参数个数时,会报错。
第一种会在运行阶段才报错。第二种在编译阶段就报错。
当然是报错越早越好,所以第二种connect好。
3.第三种重载形式:没有接收者。三个参数是函数对象,不用继承。适合简单功能。

QObject::connect(ui->pushButton, &QPushButton::clicked,[=](){ui->label->setText("world");});
connect相当于在发送方和接收方之间建立了一条通信的信道。
多次connect不会去重。
发射信号,关联的槽函数会自动调用。是同步的。



// 字符串形参
[static] QMetaObject::Connection QObject::connect(const QObject *sender, const
char *signal, const QObject *receiver, const char *method, Qt::ConnectionType
type = Qt::AutoConnection)// 没有接收者的版本
[static] QMetaObject::Connection QObject::connect(const QObject *sender,
PointerToMemberFunction signal, Functor functor)// 函数指针形参
[static] QMetaObject::Connection QObject::connect(const QObject *sender,
PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction
method, Qt::ConnectionType type)
第一种重载形式

SIGNAL():把函数名变为字符串
SLOT():把槽函数变为字符串。
connect的另一种重载形式:指向成员函数的指针



(3)信号与槽的特点
Qt万能头文件
#include <QtWidgets/QtWidgets>
①槽函数的调用是同步的
槽函数的调用是同步的。emit 信号要等对应的槽函数执行完毕后才能返回。



让信号携带参数
信号的参数个数要大于等于槽函数的参数个数
最好,信号的参数和槽一致。
指向成员函数的指针 (C++11)。报错在编译阶段。
SIGNAL宏和SLOT宏,报错在运行阶段。

处理重载的函数:qOverload


如果重复connect
connect几次,发射一次就会执行几次槽函数。
即:一次信号,多个槽函数执行。(即使绑定的完全一致)
connect是建立一条连接。


应该是可以链式调用。在槽函数中也可以发射新的信号
交换信号和槽的地位
1.槽不能当信号使用。但是槽函数里面可以发射信号。
2.信号可以当槽用。

支持元对象系统的类的定义

A信号发射,导致B信号发射,导致C的槽函数执行。链式调用。

不要循环关联,会栈溢出。

(4)减少重复槽函数
1.自定义信号,让其有参数
2.自定义槽函数,并在其中获取发射方的信息:sender() + qobject_cast
(1)用sender()获取发射信号对象的地址 QObject *。
sender只能在槽函数中调用。
[protected] QObject *QObject::sender() const
//Returns a pointer to the object that sent the signal, if called in a slot activated by a signal
(2)用qobject_cast<>将QObject * 向下转型为 自己需要的对应组件的指针,如QPushButtton *
【元对象系统的安全向下转型(基类指针转为派生类指针,如QObject * 转QPushButton*)】
//QObject *向下转型为QPushButton *
QPushButton * pbtn = qobject_cast<QPushButton *> (sender());
举例:
///mainwindow.h
private slots://自定义槽函数void do_btn_clicked();
//mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{ui->setupUi(this); //setUi是创建窗口中的组件。在这之后才能进行connect关联//多个信号,关联一个槽函数QObject::connect(ui->btn1,&QPushButton::clicked,this,&MainWindow::do_btn_clicked);QObject::connect(ui->btn2,&QPushButton::clicked,this,&MainWindow::do_btn_clicked);QObject::connect(ui->btn3,&QPushButton::clicked,this,&MainWindow::do_btn_clicked);
}void MainWindow::do_btn_clicked()
{//具体实现
}
(5)信号与槽的优缺点:信号槽 vs 回调
1.优点
(1)松耦合:把发送方和接收方在代码层面上分开了。
在发送方的代码片段中,没有出现任何接收方的信息【信号槽适合复杂的场景,回调适合性能要求高的场景】
(2)类型安全:connect会检查信号的参数个数要大于等于槽的参数个数
(3)关联自由:信号可以当槽用;槽不能当信号用,但可以在槽函数体中发射信号
(4)生态好:有很多Qt框架自带的信号和槽
2.缺点
性能差。信号槽的时间开销是10倍非虚函数。【但是也就是几纳秒慢到了几十纳秒。比起IO的几十毫秒还是可以忽略的。在IO密集型的场景,可以接受。在CPU密集型场景可能不能接受,如金融量化】
3.对象树
对象树系统,为了内存管理。
内存管理:标准C++用的是RAII,Qt用的是对象树。
所有对象之间构成树状关系。父节点被销毁,孩子结点被自动回收。

(1)基础复习:四种指针
对象如果一直存在,可以用引用。引用不能改变指向,指针可以改变指向
对象一开始有可能不存在,用指针。
负责创建和销毁:用智能指针。
所有权独享:unique_ptr,且效率高。共享所有权,shared_ptr (效率略低,因为有引用计数)。

(2)依赖注入
一种比较好的编程方式:依赖注入
(1)以前,A构造时,在初始化列表中构造B。耦合性太高。两者
(2)现在,用setB中才构造B。后面才注入依赖关系。(避免一个构造函数失败导致整体构造失败)

(3)组合模式
组合模式也是一种设计模式。
1.定义:
(1)所有的对象构成树结构
(2)树中的结点应该拥有公共的类型
2.好处
写一个函数,既可以适用于整棵大树,也可小树、叶子结点
3.适用场景
(1)不适合适用组合模式的场景
叶子和非叶子没有公共的类型

(2)适合适用组合模式的场景
目录也可以当作目录文件。叶子和非叶子有着公共类型。
公共的基类,用容器保存 vector<Base *>

4.对象树使用了组合模式
(1)对象树是一个树形结构
(2)对象树中的所有对象都是QObject的子类

(4)Qt中的内存申请
1.根结点申请在栈上
2.所有的孩子结点可以(最好)申请在堆上。
堆上的数据不需要delete,父结点析构时会自动调用子节点的析构函数。

4.事件系统
信号与槽:对象间通信
事件系统:外界和进程通信

(1)事件和事件循环
1.事件:外界发生了事情,某个fd就绪了
while(1){select/epoll_wait();switch(fd){case fd: call event_handler();... }
}
2.事件的细节
(1)硬件:硬件产生
(2)操作系统:操作系统采集,原生窗口事件,分配给事件队列
(3)进程:Qt进程一直在执行事件循环,如果有事件到来,相当于文件描述符就绪。将原生窗口事件封装为QEvent,发送给具体的QObjet对象
(4)对象:某个QObject对象收到事件后:先eventfilter,再event,再event handler。若event handler未处理完,再交给对象树上的父亲。

3.QObject收到事件后:
立即调用QObject::event(),将对应的事件分配给对应的event handlers。(底层就是分支结构,switch case 或 if else,根据事件的不同,调用不同的event handlers)

(2)事件到达,处理事件的三个步骤
(1)event filter:事件过滤器。
①在不继承的情况下,依然可以添加事件处理的能力。
②制作一个过滤器。继承QObject,再重写eventFilter函数。
③安装过滤器到对象上面。installEventFilter。
(2)event:事件分配。继承并重写event函数,调用基类的event函数(做了switch分配,否则不执行hanler)
(3)event handler:事件处理函数。继承并重写handler。
①事件过滤器
1.定义
事件过滤器:简化事件处理的代码。在不继承的情况下,给对象增加事件处理的能力。
void installEventFilter(QObject *filterObj);
2.实现事件过滤器
写一个过滤器类,继承QObject,重写 eventFilter()
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)//Filters events if this object has been installed as an event filter for the watched object.
class MyFilter : public QObject
{Q_OBJECT
public:explicit MyFilter(QObject *parent = nullptr);bool eventFilter(QObject *watched, QEvent *ev) override{if(ev->type() == QEvent::MouseButtonPress){qDebug() << watched << "is pressed!";}return false;}
signals:};
每个对象安装事件过滤器
filter = new MyFilter(this);
out->installEventFilter(filter);
mid->installEventFilter(filter);
in->installEventFilter(filter);
3.事件过滤器和事件处理器同时存在:
先event filter,再event,最后event handler。若三者都没有处理完,则交给父亲处理。
父亲亦是event filter、event、event handler的顺序进行处理。
4.若安装了多个过滤器,则倒序生效。最后安装的过滤器先生效。
②event函数
4.派生类中,调用基类的虚函数

自定义事件处理函数

坏处是:修改event()可能会对多个事件造成影响,不推荐。
③事件处理函数 event handler
1.event之后,再调用 event handler
2.eventhandler 不是一个函数,而是一堆函数
3.不同类型的事件对应不同的event handler。只修改一个event handler不会影响其他事件。(而修改event函数可能影响其他很多事件)
同时使用event和event handler
event handler 要求调用基类的event()

人造按钮
QLabel : 继承 + 重写


同时使用event filter、event、event handler
代码链接:
(3)多个对象之间的事件传递
(4)事件的传递:当某个对象没有把事件处理完,事件就传递给它对象树上的父亲。
①event filter返回false
②event 返回false
③event handler调用ignore

move是自己的左上顶点和父亲的左上顶点的距离。

event()的返回值bool,代表本对象是否完成了该事件的处理。
只要有event handler,就说明事件可以完成了,不必往上传递。
但如果event handler中有调用ignore(),事件就是未完成的,还需要向上传递。

事件传递的过程
1.鼠标事件先传给离用户最近的对象(z轴)
键盘事件会发给焦点所在的对象
2.对象处理事件:先event(),后event_handler()
(1)event:return true,说明处理完毕,不会调用handler,也不会传给(对象树上的)父亲
(2)event:return false,未处理完毕,不会调用handler,直接传递给父亲
(3)event:调用了基类的event。
①如果handler不存在,会传递给父亲。
②如果handler存在 / accept,不传递给父亲。
③如果handler存在 / ignore,传递给父亲。
事件系统 + 信号槽,让两个组件互动

五、Qt_Widgets模块
1.QWidget类的概念
1.QWidget类是看得见(其上可以绘制图案,支持绘图事件)、摸得着(支持键盘鼠标事件)的组件,是一个ui元素。本质是一个矩形画布。
2.功能:容器、输入内容、显示内容

3.QWidget的继承关系
本质:QWidget继承:
(1)继承QObject:支持元对象系统、信号和槽、对象树
(2)QPaintDevice:画布、矩形。看得见(使用paintEvent绘制),摸得着(可以响应键鼠事件)。
class Q_WIDGETS_EXPORT QWidget : public QObject, public QPaintDevice
4.孩子需要比父亲小,且在父亲的范围之内。否则显示不全或无法显示。

paintEvent
2.paintEvent是绘图事件的event handler

void paintEvent(QPaintEvent *event) override{qDebug() << mosquito_x << "," << mosquito_y;QPainter painter(this); //构造画笔QPixmap pixmap(":/new/prefix1/mosquito.jpeg"); //加载图片到内存painter.drawPixmap(mosquito_x, mosquito_y, 40, 40, pixmap); //绘图:坐标,大小,图QWidget::paintEvent(event); //调用基类的paintEvent,以免漏掉事件
}
小项目3:打蚊子
代码链接:https://github.com/WangEdward1027/Qt/tree/main/kill_mosquitoes
两种方案:
1.QLabel:Label的Pixmap中放蚊子,每次点击移动Label
2.PaintEvent:每次点击事件,event handler之paintEvent临时重绘一次文字图片加载到新的位置。
重写了两个event handler
class MyWidget : public QWidget
{Q_OBJECT
public:explicit MyWidget(QWidget *parent = nullptr);void mousePressEvent(QMouseEvent *event) override{//srand(time(nullptr));if(event->x() - mosquito_x <= 40 && event->x() - mosquito_x >= 0&& event->y() - mosquito_y <= 40 && event->y() - mosquito_y >= 0){emit addPoint();mosquito_x = rand()%560;mosquito_y = rand()%360;update(); // 强制重绘}QWidget::mousePressEvent(event);}void paintEvent(QPaintEvent *event) override{qDebug() << mosquito_x << "," << mosquito_y;QPainter painter(this);QPixmap pixmap(":/new/prefix1/mosquito.jpeg");painter.drawPixmap(mosquito_x,mosquito_y,40,40,pixmap);QWidget::paintEvent(event);}
signals:void addPoint();
private:int mosquito_x;int mosquito_y;
};
2.窗口
(1)定义
没有嵌入到父组件中的组件被称为窗口。
窗口是最大的容器。
(2)成为窗口的条件
(1)没有父节点,调用show()
(2)有父节点,Qt::Window
(3)QDialog、QMainWindow及其子类
如果一个QWidget对象是根结点(没有父结点),调用show方法,则是独立的窗口。任务栏会有两个窗口。
QWidget w1;
QWidget w2;
w1.show();
w2.show();
如果QWidget对象有父亲,则不会是独立窗口,会显示在父窗口里面。
QWidget w1;
QWidget *w2 = new QWidget(&w1);
w1.show();
w2->show();
3.有父亲的QWidget对象,也可以是一个窗口。但任务栏只有一个图标。
父亲窗口销毁,则会将孩子窗口也销毁。
QWidget w1;
QWidget *w2 = new QWidget(&w1, Qt::Window);
w1.show();
w2->show();
4.无边框窗口
QWidget w1;
QWidget *w2 = new QWidget(&w1, Qt::Window | Qt::FramelessWindowHint);
w1.show();
w2->show();
(3)QWidget当中和窗口相关的属性和方法
一、窗口
1.窗口功能:①sho() ②hide(),隐藏为后台进程
2.顶层窗口:最大化、最小化 showMinimized、全屏显示、正常显示
3.窗口内容:update():相当于手动触发绘制事件 (强制重绘)

二、几何位置、大小
1.move():移动位置
2.resize():改变大小


3.对话框 QDialog
QDialog和QMainWindow以及它们的子类对象一定是窗口。
QWidget w1;
QMainWindow *w2 = new QMainWindow(&w1);
QDialog *w3 = new QDialog(&w1);
(1)模态
1.非模态对话框
用show()方法展示。上层的对话框没有退出,不会阻塞整个进程。
2.模态对话框
用exec()。上层的对话框没有退出,会阻塞整个进程。

QDialog一般不好用。可以选择继承QDialog
(1)ui文件
(2)标准对话
下文介绍
(2)自定义方式
addNew,设计师界面类。

(3)子类:标准对话框

①QColorDialog:颜色选择对话框
用其静态成员函数:getColor()。临时生成。
若要经常使用,还是要构建。

②QFileDialog:文件选择对话框
获取文件路径
QString filepath = QFileDialog::getOpenFileName();
qDebug() << filepath;
QString filepath = QFileDialog::getOpenFileName(this, "title", QStrng(),"source code (*.c *.cc *.cp);; header file(*.h)");
qDebug() << filepath;
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),"/home",tr("Images (*.png *.xpm *.jpg)"));
文件选择对话框

③QFontDialog:字体选择对话框
静态函数

④QMessageBox:信息框




4.主窗口 QMainWindow
标题栏、菜单栏、工具栏、中央区域、状态栏
主窗口比一般窗口多出的部分:菜单栏、工具栏、状态栏

(1)菜单栏:QMenubar
1.组成
菜单栏QMenubar由一个个菜单QMenu组成。
菜单由QAction组成。

2.QAction的信号,是triggered()
用ActionEditor进行编辑

3.QAction和QPushButton的区别:
QActon是一个动作,QPushButton是一个实体
同一个QAction可以出现在不同的位置(菜单栏和工具栏),而QPushButton只能出现在一个位置
【ui编辑器中的Action编辑器】
(2)工具栏:QToolbar

加入icon
(3)状态栏:QStatusBar
5.常见的简单组件
(1)用来显示的组件:Display Widgets

Label
QLabel -> QFrame(设置边框) -> Widget
1.设置边框


2.Label显示markdown格式的数据


(2)Buttons:按钮

①QPush Button:文字,clicked()
②QTool Button:图像,clicked()
③QRadio Buttion:单选 (同一容器中的多个Radio Buttion只能有一个被选中),toggled(bool)
④QCheck Box:复选,clicked(bool)


按钮的信号:


在centralwidget中加新的widget作为容器

(3)QComboBox:下拉框
#include <QComboBox>QComboBox *comboBox = new QComboBox(&w);

加选项


信号

处理重载:QOverload<>

设置为可编辑

限制用户的输入:验证器


举例:Int
#include <QIntValidator>

但有漏洞

再加强,精确验证:validator->validate()

(4)QLineEdit:单行输入



分别对应推荐和搜索

(5)容器:containers
容器:一个QWidget,它的对象树中的孩子是其他ui元素
带边框的容器:QGroupBox

6.组合组件和界面布局
(1)布局
1.定义
布局:容器里面的元素的大小位置会随着容器大小的改变而自动改变
2.分类
水平布局、垂直布局、栅格布局


3.布局的使用
(1)先创建容器和里面的元素 【给容器、元素、布局 申请内存】
(2)创建布局:new QHBoxLayout / QVBoxLayout / QGridLayout
(3)把元素加入到布局当中:addWidget
(4)把布局设置为容器的属性:setLayout

栅格布局:addWidget有两种重载形式。可以占多行 (2,2,4,4)

4.弹簧:Spacer


7.model/view模型:MVC模型
模型-视图-控制器(MVC)是一种设计模式,在构建用户界面时经常使用。
MVC由三种对象组成:
①Mode l模型:应用对象,是一个抽象的数据结构,用户无法感知
②View 视图:屏幕表示,是用户看到的界面。
③Controller 控制器:定义了用户界面对用户输入的反应。用户编辑,直接修改了model
在MVC出现之前,用户界面设计往往倾向于将这些对象合并在一起。MVC将它们解耦以增加灵活性和重用性。并降低人思考的复杂度。
(1)如何描述一个复杂的界面:分层



为MVC提出了23种设计模式。
数据变了,视图跟着变。就是观察者模式。
(2)Qt中的MVC:model/view architecture
Qt简化了mvc,控制器controller不是常驻的,而是用户交互时临时生成,称为委托delegate
①model
model是抽象的数据结构,如:①一维数组 ②二维表格 ③树
项item:二维表格的每一个格子称为项,每个项的孩子还是一个二维表格。这样的数据结构称为表格树。

常见的三种模型:对表格树进行限制:①每一个item不准有孩子 ②item只有一列
(1)List Model (线性表):①②
(2)Table Model (二维数组,表格):①
(3)Tree Model (树):②

model的具体类型:
QAbstractItemModel (所有模型的抽象基类,表格树),加限制后退化为QAbstractItemModel、QAbstractTableModel


②view 视图
用户可以看见view。
用户操作view时,会临时生成一个controller,称为委托 Delegate。

QAbstractItemView

使用model和view
1.创建model view
2.根据model设置view
3.model进行增删改,view会自动跟着变



model变了,view自动跟着变
初始化
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//modelmodel = new QStandardItemModel(2, 5, this);//viewview = new QTableView(this);view->setModel(model); //接下来,view的内容,由model决定btn = new QPushButton("add row", this);layout = new QHBoxLayout;layout->addWidget(view);layout->addWidget(btn);ui->centralwidget->setLayout(layout);QStringList headers = {"birth", "name", "age", "job", "image"};model->setHorizontalHeaderLabels(headers);//第一行QStandardItem *birth= new QStandardItem("1027");QStandardItem *name = new QStandardItem("Edward");QStandardItem *age = new QStandardItem("25");QStandardItem *job = new QStandardItem("Research & Development");QStandardItem *image = new QStandardItem(QIcon(":/new/prefix1/images/photo1.jpg"), "pthot1");model->setItem(0, 0, birth);model->setItem(0, 1, name);model->setItem(0, 2, age);model->setItem(0, 3, job);model->setItem(0, 4, image);//第二行birth = new QStandardItem("1202");name = new QStandardItem("Amber");age = new QStandardItem("25");job = new QStandardItem("Product Manager");image = new QStandardItem(QIcon(":/new/prefix1/images/photo2.jpg"), "photo2");model->setItem(1, 0, birth);model->setItem(1, 1, name);model->setItem(1, 2, age);model->setItem(1, 3, job);model->setItem(1, 4, image);QObject::connect(btn ,&QPushButton::clicked, this, &MainWindow::addRow);
}
点击按钮的槽函数
void MainWindow::addRow()
{QStandardItem *birth = new QStandardItem("0811");QStandardItem *name = new QStandardItem("Sam");QStandardItem *age = new QStandardItem("25");QStandardItem *job = new QStandardItem("Quality Assurance");QStandardItem *image = new QStandardItem(QIcon(":/new/prefix1/images/photo3.jpg"),"photo3");QList<QStandardItem *> list;list << birth << name << age << job << image;model->appendRow(list);
}
效果:

六、Qt的其他模块
1.QTimer 定时器
1.QTimer的函数接口 (F1查看帮助手册)
//Public Functions
QTimer(QObject *parent = nullptr); //构造函数void setInterval(int msec); //设置超时间隔//Public Slots
void start();
void stop()
2.举例:一个定时器,每3秒打印一次时间。

#include <QTime>MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{ui->setupUi(this);timer = new QTimer(this);timer->setInterval(3000);timer->start();QObject::connect(timer, &QTimer::timeout, this, &MainWindow::showTime);showTime(); //先手动调用一次槽函数,以在刚开始时就显示时间
}void MainWindow::showTime()
{QString text = QString("current time is %1").arg(QTime::currentTime().toString());ui->textBrowser->setText(text);
}
2.Qt文件操作:文件IO
1.文本文件:人能看懂的,ASCII码序列,数据全为字符串,内存中用string类型
二进制文件:只要数据不是全为字符串,就是二进制文件
2.文件IO的操作:将数据从磁盘读取到内存
①open
②read
③write
④close
3.磁盘IO是非阻塞的,网络IO是阻塞的 (改为非阻塞要配合while循环)。
4.QByteArray
封装char数组,避免了QString进行编码转换,会改变存储的数据。
只要数据在两个设备之间交互,用QByteArray。【内存→磁盘,内存→网卡】
5.QFile
(1)继承关系:QObject -> QIODevice -> QFileDevice -> QFile
①QIODevice:open、read、write、close
②QFile:二级功能

6.举例:
①读文件内容:TextBrowser
②编辑文本内容:TextEdit
void MainWindow::on_pushButton_clicked()
{QString filepath = QFileDialog::getOpenFileName(this, "open file");file = new QFile(filepath, this);file->open(QIODevice::ReadWrite);QByteArray array = file->readAll();ui->textBrowser->setText(QString(array)); //仅浏览ui->textEdit->setText(QString(array)); //可浏览,可编辑
}
3.Qt网络编程:网络IO
只介绍客户端。
1.注意:
①测试时要关代理。
②pro文件中,QT += network
QT += core gui network
2.QTcpSocket
(1)头文件
#include <QTcpSocket>QTcpSocket *socket;
socket = new QTcpSocket(this);
(2)继承关系
QObject -> QIODevice -> QAbstractSockt -> QTcpSocket
①由于QIODevice的open/close/read/write是非阻塞的。希望在可读的时候才读。Qt框架会在可读的时候发射信号,所以将read写在槽函数里。【分离了发射和接受,不需要考虑什么时候才能接收】

②QAbstractSockt的connectToHost()是非阻塞的


3.举例:自己写网络客户端
代码链接:https://github.com/WangEdward1027/Qt/tree/main/QTcpSocket
socket = new QTcpSocket(this);//连接状态一改变,就打印一次
QObject::connect(socket, &QTcpSocket::stateChanged, this, &MainWindow::do_status_changed);
//连接完成,通知可以进行写操作
QObject::connect(socket, &QTcpSocket::connected, this, &MainWindow::do_connected_send);
//可读状态,调用读取的槽函数
QObject::connect(socket, &QTcpSocket::readyRead, this, &MainWindow::do_readyRead_show);
//on是框架生成的,do是自定义的
void MainWindow::on_pushButton_clicked()
{socket->connectToHost("www.baidu.com" ,80);
}void MainWindow::do_status_changed(QAbstractSocket::SocketState socketState)
{qDebug() << "current state = " << socketState;
}void MainWindow::do_connected_send()
{qDebug() << "send request";QByteArray request("GET / HTTP/1.1\r\n\r\n");socket->write(request);
}void MainWindow::do_readyRead_show()
{qDebug() << "show response";QByteArray response = socket->readAll();content += QString(response);ui->textBrowser->setText(content);//ui->webEngineView->setHtml(content);
}
4.Qt多线程
#include <QThread>
相关文章:
C++(week16): C++提高:(六) Qt提高
文章目录 四、Qt的元对象系统1.元对象和MOC(1)自省 和 反射(2)Qt是怎样支持元对象系统的?(3)支持元对象系统的三个要求(4)元对象系统的功能(5)动态属性 2.信号和槽机制(1)信号与槽机制的基本原理(2)自定义信号、自定义槽函数①自定义信号②自定义槽③关联 connect (…...
go 时间转时间戳的时区设置问题
昨天遇到一个问题,在完成时间转换时间戳,在后续测试中发现转换后的时间戳转成时间后,时间发生错误,时间和转换时间不一致问题 如下: package mainimport ("fmt""time" )func main() {Start : &q…...
MySQL 常见日志清理策略
前言: MySQL 数据库服务器使用多种类型的日志来记录操作和事件,这对于故障诊断、审计和性能分析非常重要。然而,这些日志文件会随着时间的推移而不断增长,可能会占用大量的磁盘空间。因此,定期清理这些日志是必要的&a…...
3大管人绝招让你的手下心服口服
3大管人绝招让你的手下心服口服 一:差异化管理,玩弄人性 谁赞成,谁反对,看清楚谁顺从自己,谁反对自己之后,接下来要做的便是区别对待。 给听话的下属最好的资源、最轻松简单的工作、最高的待遇…...
useImperativeHandle 是什么?你可以理解为 vue3 的 expose
useImperativeHandle 确实类似于 Vue 3 的 expose,两者都用于控制子组件向父组件暴露的接口。 在 React 中,useImperativeHandle 需要与 forwardRef 一起使用,原因如下: 转发引用:forwardRef 允许父组件将 ref 传递给…...
《Techporters架构搭建》-Day05 属性校验
属性校验 前言Validated基础用法集合校验分组校验嵌套校验自定义校验器 源码地址 前言 在项目开发过程中,经常遇到需要对传递的参数进行校验,比如某个参数字段是否为空、值的取值是否在约定范围、格式是否合法等等,最原始的写法,…...
HTTP的场景实践
HTTP的场景实践:任选一个浏览器,对于其涉及的请求中的缓存策略展开具体分析 1. 强缓存: Cache-Control用于指定缓存的最长有效时间。 Expires用于指定资源过期的日期。 2. 协商缓存: ETag用于标识资源的唯一标识符,…...
MySQL:表的设计原则和聚合函数
所属专栏:MySQL学习 💎1. 表的设计原则 1. 从需求中找到类,类对应到数据库中的实体,实体在数据库中表现为一张一张的表,类中的属性对应着表中的字段 2. 确定类与类的对应关系 3. 使用SQL去创建具体的表 范式࿱…...
介绍springmvc-水文
Spring MVC 是一个基于 Java 的开源 Web 框架,它是 Spring Framework 的一部分。Spring MVC 提供了一个架构,用于开发灵活、可扩展的 Web 应用程序。 Spring MVC 的主要特点包括: 基于模型-视图-控制器(MVC)的架构&am…...
uni-app学习笔记
一、下载HBuilder https://www.dcloud.io/hbuilderx.html 上述网址下载对应版本,下载完成后进行解压,不需要安装,解压完成后,点击HBuilder X.exe文件进行运行程序 二、创建uni-app项目 此处我是按照文档创建的uni-ui项目模板…...
Windows Server修改远程桌面端口
新建入站规则 填写端口 允许连接 修改远程桌面端口 winR打开注册表 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp修改PortNumber为新端口 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wi…...
界面组件Kendo UI for Vue 2024 Q2亮点 - 发布一系列新组件
随着最新的2024年第二季度发布,Kendo UI for Vue为应用程序开发设定了标准,包括生成式AI集成、增强的设计系统功能和可访问的数据可视化。新的2024年第二季度版本为应用程序界面提供了人工智能(AI)提示,从设计到代码的生产力增强、可访问性改…...
达梦数据库 逻辑备份还原
达梦的逻辑备份还原 1.背景2.要求3.实验步骤3.1 相关术语3.2 dexp逻辑导出3.2.1 使用dexp工具3.2.2 dexp相关参数含义3.2.3 四种级别导出3.2.3.1 FULL3.2.3.2 OWNER3.2.3.3 SCHEMAS3.2.3.4 TABLES 3.2.4 使用范例3.2.4.1 环境准备3.2.4.2 dexp逻辑导出 3.3 dimp逻辑导入3.3.1 使…...
Stable Diffusion绘画 | 图生图-上传重绘蒙版
上传重绘蒙版,可以弥补局部重绘的缺点,能够更精细的修改画面中的指定区域 使用PS制作的蒙版图片为耳朵下方区域,可以为图片中的女生带上不同款式的耳环。 参数配置: 调整提示词: 生成图片如下所示: 调整提…...
打开Office(word、excel、ppt)显示操作系统当前的配置不能运行此应用程序最全解决方案!
我以前用过分区助手把office从c盘挪到d盘了,从那以后office就用不了了,然后我就删了(貌似没删干净)。 最近由于有使用word的需求,所以我从学校官网找到正版软件的安装包,按照步骤重新卸载电脑中office残留…...
猫头虎 分享已解决Bug || TypeError: Cannot read property ‘map‘ of undefined 解决方案
🐯 猫头虎 分享已解决Bug || TypeError: Cannot read property map of undefined 解决方案 摘要: 今天猫头虎带大家深入探讨在前端开发中常见的一个令人头疼的问题:TypeError: Cannot read property map of undefined。这个错误通常出现在我…...
大模型快速部署,以浪潮源2.0为例
step1: 申请PAI-DSW试用 step2:魔塔社区授权 由于本地授权一直失败,于是采用了魔塔免费平台实例进行学习。 搭建好之后,打开就有相关页面了: demo搭建: 按照官方提示的步骤进行搭建,内容如下:…...
Python知识点:使用FastAI进行快速深度学习模型构建
使用FastAI构建深度学习模型非常方便,尤其是对于快速原型开发和实验。以下是一个使用FastAI构建深度学习模型的完整示例,涵盖数据准备、模型训练和评估。 安装依赖 首先,确保你安装了FastAI库和其他必要的库: pip install fast…...
Nginx配置全局https
Nginx配置全局https 要在 Nginx 中配置将 HTTP (80 端口) 请求重定向到 HTTPS (443 端口),可以在 Nginx 的配置文件中添加以下配置。假设你已经配置好了 HTTPS 相关的证书和密钥。 打开你的 Nginx 配置文件,通常是 /etc/nginx/nginx.conf。 在配置文件…...
DBAPI 如何对SQL查询出的日期字段进行统一格式转换
DBAPI 如何对SQL查询出的日期字段进行统一格式转换 mysql有一张订单表,有两个datetime类型的字段create_time update_time 新建一个API,SQL内容是查询所有数据 访问API发现日期字段默认返回时间戳格式 如果修改成自己想要的年月日格式,就要使…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
