转载请注明出处 |
---|
作者:Alex Yang
配套视频:黑马程序员张涛老师Qt课程
文末配doc笔记文档、源码。
CSDN:杨莫辞
博客园:ycwforeverdoit
哔哩哔哩:故里旧人我在
QT是跨平台图形界面引擎。
优点:跨平台、接口简单、一定程度简化内存回收。
创建:1991奇趣科技
案例:Linux桌面环境KDE、谷歌地图、VLC多媒体播放器
**安装步骤跳转到主页对应blog**
注意:项目路径及项目名称不能为中文、不能有空格
位置:顾名思义
构建套件:选择用什么开发。用什么版本
详情:创建一个主窗口类(下图myWidget),QWidget是空窗口,QMainWindow多了菜单栏、状态栏,Qdialog有选择性的物件(如下图对话框的下一步按钮),它们三者是继承关系。
创建界面复选框:勾选则要进行界面UI设计,否则就只是像dev一样实现逻辑。
汇总:团队开发时,将团队成员各自的代码,使用版本控制系统svn、vss或git。
QApplication a:创建应用程序对象,名为a,有且仅有一个 myWidget w:实例化窗口对象 w.show():显示窗口
return a.exec():让应用程序对象进入消息循环机制,代码阻塞当前行(窗口不会一闪而过,会一直等待用户操作)
(1)第7行:包含的模块,core是核心模块,gui是图形模块,QT所有模块如下
(2)第9行: greaterThan表示大于 (QT_MAJOR_VERSION, 4)表示版本为4 QT +=
widgets同理第7行表示包含widgets模块。 整行表示“版本大于4就包含widgets模块”。
(3)第11行:生成exe文件的名称为01FirstProject,路径如下
(4)第12行:模板变量告诉qmake为这个应用程序生成哪种makefile。下面是可选择的模板
app:建立一个应用程序的makefile。这是默认值,所以如果模板没有被指定,这个将被使用。
lib:建立一个库的makefile。
vcapp:建立一个应用程序的VisualStudio项目文件。
vclib:建立一个库的VisualStudio项目文件。
Subdirs:特殊的模板,可以创建一个能够进入特定目录并且为一个项目文件生成makefile并且为它调用make的makefile。
(5)第15-16行:项目生成的源文件,在外部创建新文件编译器会在pro文件中该位置后自动增加添加的文件。或者直接在pro文件中该位置后增加文件,保存后外部也会自动创建增加的文件。(注意文件间用“\”分割)
(6)第18行:同上,这里是头文件。
(1)第1-2、15行:防止重复包含
(2)第4行:QWidget窗口类头文件
(3)第8行:Q_OBJECT宏,有了这个才允许类中使用信号和槽机制
(1)注释 ctrl + /
(2)运行 ctrl + r
(3)编译 ctrl + b
(4)字体缩放 ctrl + 鼠标滚轮
(5)查找 ctrl + f
(6)整行移动 ctrl + shift + ↑或↓
(7)帮助文档 F1或左侧“帮助”或
(8)自动对齐 ctrl + i
(9)同名.h和.cpp切换 F4
使用顶层方法show如下:
绑定父窗口如下:
按钮显示文本:
局限是创建的myWidget窗口大小配对按钮大小,所以这里创建出来的窗口大小很小
可以手动调整大小(按钮也可以调整大小):
移动按钮在父窗口中的位置:
设置窗口标题:
设置固定大小,用户不可以改变窗口大小:
(1)可以看到上述代码中,new的对象没有手动释放,其原理就是对象树。
(2)下图是一个对象树,win是窗口,Topic、obj是小控件。
① 不是继承关系中的子类父类。这里的措辞是对象,指对象与对象之间的树状关系,是父对象与子对象的关系。
② 一个父对象可以包含多个子对象,但一个子对象只有一个父对象(原理:根据下图Qt帮助文档可知)
③ 指定父对象(如btn->setParent(this),创建的这个QObject对象btn自动添加到父对象的children()列表)后,子对象就不需要手动释放。未指定的需要手动释放,否则出现内存泄露。
④ 当父对象析构时,其子对象列表children()中所有的对象被析构。当子对象先析构时,子对象自动从父对象的children()列表删除。
堆区:首先delete掉对应对象空间,其子对象按顺序调用析构函数,父对象析构函数不会再调用,如下图
栈区:不允许delete
⑤ 析构≠释放。这里要和C++区分:在C++中,不存在继承关系时,按照创建顺序,先创建的先构造,先构造的后析构,如下图
存在继承关系时,父类先构造,后析构,如下图
在Qt中,不存在对象树时,构造析构顺序和C++不存在继承关系时一样,按照创建顺序,先创建的先构造,先构造的后析构。存在对象树时,若在堆开辟空间,则先创建的先构造先析构,唯一要注意的就是销毁过程相反(即执行某个QObject对象的析构函数时还没有释放内存,在析构函数结束时才释放,这里常常产生错觉);若在栈开辟空间,则先创建后释放。如下图,
堆区:
栈区:
⑥ 正常情况(不使用delete、无父对象子对象创建顺序问题)栈区内存释放过程:先创建后析构。
⑦ 正常情况堆区内存释放过程:用下图案例说明,从根节点开始(即最顶层父对象w1),程序中w1.show()显示窗口(w2、w3、w4包含在里面),这时候点击关闭按钮触发w1窗口的close()槽,进入w1的析构函数,首先执行w1析构函数内的qDebug()宏,输出信息“w1析构”(注意这时候w1还没有被释放),接着查看w1是否有子对象,发现有子对象w2,则继续进入w2的析构函数,输出信息“w2析构”,同理,进入w3析构函数,输出信息“w3析构”,最后进入w4的析构函数,输出信息“w4析构”,发现w4没有子对象,开始回走,首先结束w4析构函数,这时候才释放w4内存空间,接着释放w3,…,直到释放完全。如下图
⑧ QWidget是QObject的子类,在parent机制上无区别,但在实际使用时QWidget更复杂,原因是QWidget和QEventLoop高度配合才能完成工作。实际中,QWidget的关闭流程,首先用户点击关闭按钮触发close()槽,然后Qt向widget发送QCloseEvent,默认的QCloseEvent(用户没有重写,使用默认)将widget隐藏起来,即hide()。所以,通过QWidget关闭流程可知,Widget关闭的实质是隐藏,而没有释放内存。因此,需要设置Qt::WA_DeleteOnClose属性,使得close后调用widget的析构函数,另外一种就是直接手动delete。
①上面说到先构造后析构(C++原理),即先创建后析构。有一种情况,先创建子对象,后创建父对象。对于在栈区开辟空间的情况,根据第一句所说则应该先析构父对象,又遵从Qt对象树原理,则其子对象随之被析构,代码继续执行,按照第一句所说则会再析构一次子对象,这时出现对同一对象调用两次析构函数的情况,而C++中不允许重复调用两次析构函数,因此程序崩溃。如下图,关闭w1后程序crush
对于在堆区开辟空间的情况:子对象和父对象的创建先后无影响
②由注意的④可知delete很危险。
①使用delete:
堆区:delete的对象及其所有子对象,按顺序从父对象到子对象调用析构函数。(与创建顺序无关)
栈区:不允许delete。
②先创建子对象,后创建父对象:
堆区:先创建先析构,但最后释放内存,也就是先析构子对象。
栈区:不允许,应用程序crush。
③正常情况(不使用delete、无父对象子对象创建顺序问题):
堆区:先析构父对象,但子对象先释放内存。
栈区:先创建后析构,也可以理解为先析构子对象后析构父对象,析构的同时释放内存。
不要把Qt和C++完全等比,正常情况下在堆区开辟的对象,其析构函数被调用时不会释放对象空间,即先创建的先调用析构函数,但是最后释放(等调用到最底层子对象结束后开始往回走,一个一个结束析构函数,这时候才真正的释放内存)。
①先创建父对象后创建子对象
②在堆上创建对象
③不要对指定了父对象的对象delete
坐标系是相对父窗口而言的。
格式:connect(信号发送者,信号,信号接收者,处理的槽函数)
注意:信号和槽函数都是函数地址
信号槽优点:松散耦合(信号发送端和接收端是独立无关联的,只不过是通过connect将两端耦合联系起来,即信号端的操作成功与否和接收端无关,信号端成功发送信号给接收端,接收端也不一定成功处理)
案例:关闭窗口,如下图
(1)信号函数:
①返回值是 void
②只需要声明,不需要实现
③可以有参数
④写在signals下
(2)槽函数:
①返回值是void
②需要声明,需要实现
③可以有参数
④早期Qt版本必须写在publicslots下,高级版本可以写到public或全局下
(3)流程:
①定义两个操作类,一个作为信号发送者类,一个作为信号接收者类
②信号发送者类中,在signals下定义信号函数
③信号接收者类中,在public lots下定义槽函数
④定义触发函数,触发函数中触发信号
⑤在主调函数中,创建信号发送者和信号接收者(信号发送者类和信号接收者类的对象)
⑥使用connect函数连接发送者和接收者
⑦调用触发函数
(4)案例:
①信号发送者:学生
②信号:犯错
③信号接收者:老师
④槽:批评
信号函数重载、触发函数重载(触发函数不一定写在信号发送者类中)
槽函数重载
槽函数实现
因为重载,所以连接时不能够直接取函数地址,要使用函数指针
运行结果
可以看到上图运行结果外多了一个引号,这是因为传的是QString,将QString转为char就可以了,方法是先通过.toUtf8()转成QByteArray,再通过.data()转成char
改为事件触发信号连接
案例:按钮点击信号,连接到学生犯错信号,学生犯错信号连接到老师批评槽函数,达到传递效果。
注意:信号和槽参数必须保持一致。
同理connect,断开信号使用disconnect。
①一个信号,可以连接多个槽函数
②多个信号,可以连接同一个槽函数
③信号和槽函数,参数类型不许一一对应
④信号和槽函数,信号的参数个数可以多于槽函数,反之不可
⑤Qt4版本以前,信号和槽连接方式,如
connect(st,SIGNAL(mistake()),te,SLOT(criticize()));
底层原理:将SIGNAL(mistake())和SLOT(criticize())中的mistake()和criticize()转换为字符串”mistake()”和”criticize()”然后去寻找。
Qt4版本优点:参数直观
缺点:类型不做检测
⑥Qt5以上版本支持Qt4版本写法
实质:匿名函数,是C++11的新特性,低版本需要在工程文件中添加 CONFIG += C++11
格式:[ 函数对象参数 ] (操作符重载函数参数)mutable->返回值{函数体}。
(1) Mutable->返回值:根据需要添加,最基本的部分就是{}
(2) 函数对象参数:
① 空。无任何函数对象参数。
② =。函数体内可以使用lambda所在作用范围内的所有可见的局部变量(包括lambda所在类的this)。并且是值传递方式。
③ &。同上,是引用传递方式。
④ this。函数体内可以使用lambda所在类中的成员变量。
⑤ a。将a按值传递,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const,要修改传递进来的a的拷贝可以添加mutable修饰符。
⑥ &a。按引用传递。
⑦ a,&b。a按值传递,b按引用传递。
⑧ =,&a,&b。除了a和b按引用传递,其它参数都按值传递。
⑨ &,a,b。除了a和b按值传递,其它参数都按引用传递。
(3) 操作符重载函数参数:标识重载的()操作符的参数,没有参数时省略。参数可以通过按值传递(如:(a,b))和按引用传递(如:(&a,&b))。
(4) 可修改标识符(mutable):注意添加mutable后能修改的是拷贝,而不是值本身。
(5) 函数返回值(->返回值类型):返回值为void或函数体中只有一处return时,可以省略。
(6) 函数体:无实现时为空,但{}不可省略。
(7) 注意:区分下面两种
QMainWindows是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个链接部件(dock widgets)、一个状态栏(status bar)及一个中心部件(central widget)。如下图
(1)创建菜单栏
menuBar()是函数,函数系统源码内已经创建了对象树,所以这里可以直接调用这个menuBar()函数,而不需要new
(2)菜单栏放入窗口,这时候还看不到
(3)创建菜单
(4)创建菜单项
(5)创建工具栏,工具栏可以拖拽到任意位置
(6)更改工具栏默认位置(Qt::是枚举值)
(7)设置只允许左右停靠
(8)设置浮动、设置移动
(9)设置工具项,也可以设置分割线(未演示)
(10)增加按钮
(11)创建状态栏
(12)放标签控件
(13)逆序放标签控件
(14)创建铆接部件、主体部件,铆接部件又叫浮动部件,铆接部件可以设置停靠区域
(1)建立可以UI设计的项目
(2)UI设计,可以直接拖拽组件
(2)添加icon,首先把icon文件资源复制到项目文件夹,然后新建Qt文件资源文件
模态对话框:弹出对话框后,不能对其它窗口操作
非模态对话框:可以对其它窗口操作
Qt内置对话框:
QColorDialog 选择颜色
QFileDialog 选择文件或目录
QFontDialog 选择字体
QInputDialog 允许用户输入一个值,并返回该值
QMessageBox 模态对话框,用于显示信息,询问问题等
QPageSetupDialog 为打印机提供纸张相关的选项
QPrintDialog 打印机配置
QPrintPreviewDialog 打印预览
QProgressDialog 显示操作过程
普通按钮PushButton
图片按钮(又叫工具按钮)ToolButton,用于显示图片,如果想显示文字,修改属性tooButtonStyle,想凸起,修改属性autoRaise
复选框RadioButton,如果多个复选框要分组,添加控件GroupBox,如果要设置默认选中,在逻辑代码中ui->复选框名->setChecked(true)
多选框CheckBox,监听状态(是否选中,返回对应值)下,选中返回2,半选返回1,未选返回0
列表控件QListWidget:
QListWidgetItem * item = new QListWidgetItem (“内容”);
ui->列表容器名->addItem(item);
设置居中方式 item->setTextAlignment(枚举值);
添加多行 addItems(一个列表)
树控件QTreeWidget:
设置头 ui ->树容器名->setHeaderLabels(一个列表);
创建根节点 QTreeWidgetItem * listItem = new QTreeWidgetItem(“内容”);
添加根节点 ui->树容器名->addTopLevelItem(listItem);
创建子节点 QTreeWidgetItem * l1 = new QTreeWidgetItem(一个列表);
添加子节点 listItem -> addChild(l1);
表格控件QTableWidget:
设置列数 ui -> 表格控件名 -> setColumnCount(总列数);
设置水平表头 ui -> 表格控件名 -> setHorizontalHeaderLabels(表头列表);
设置行数 ui -> 表格控件名 -> setRowCount(总行数);
设置正文 ui -> 表格控件名 -> setItem(行数,列数,元素);
通过按钮控制不同页的跳转:ui->tabwidget名->setCurrentIndex(页号);
(1)新建设计师界面文件
(2)在新建的封装类的ui中,设计要封装的组合控件
(3)回到呈现类的ui设计
(4)封装空间的逻辑在封装类中实现,本案例实现数字选择框和滑动条联动(滑动条变化则数字选择框变化,反之亦然)
QEvent是事件
进入事件 enterEvent
离开事件 leaveEvent
按下事件 mousePressEvent(QMouseEvent ev)
释放事件 mouseReleaseEvent
移动事件 mouseMoveEvent
X坐标 ev->x()
Y坐标 ev->y()
全局x坐标(相对于整个屏幕) ev->globalX()
全局y坐标 ev->globalY()
判断鼠标是哪个键被点击 ev->button() 左键Qt::LeftButton Qt::RightButton
判断组合键(鼠标两个以上键同时点击) ev->buttons()
格式化字符串 QString(%1 %2).arg(显示到1位置的内容).arg(显示到2位置的内容)
(1)查帮助文档
(2)添加两个标签
(3)重写timerEvent函数,启动计时器
(4)多个计时器
(5)利用计时器类QTimer(停止计时的接口是timer->stop())
bool event(QEvent * e)可以用于事件分发,也可用于事件拦截
重写bool event(QEvent * e)函数,在函数中利用if等判断语句,若用户操作为if判断的操作,则执行if语句内的代码,然后return true,则事件分发器不在向下分发,若未执行if语句代码,则return QLabel::event()或其它,即依旧让系统进行事件分发,交给父类事件分发器进行。
在用户操作和事件分发器中间还有一个事件过滤器,同理事件分发器,事件过滤器内用户也可以自己处理某事件,剩下的交给父类事件过滤器处理。
第一步,安装事件过滤器 ui->控件名->installEventFilter(this)
第二步,重写事件过滤器 void eventFilter(QObject*,QEvent*),函数内先判断控件(可能有多个控件安装了事件过滤器,但只有这一个函数使用),然后判断事件(同理事件分发器)
Qt的绘图系统基于QPainter,QPaintDevice和QPaintEngine三个类。
QPainter用来绘制。
QPaintDevice是一个二维空间的抽象,这个二维空间是QPainter的工作空间。
QPaintEngine提供QPainter在不同设备上绘制的统一接口。
(1)提高清晰度
(2)转移绘图圆点
如果在第二个矩形后再加一个画矩形的语句,再次画的矩形也是相对这个新绘图原点画。
(3)保存/恢复绘图状态
使用恢复时必须已经保存绘图状态,恢复可以还原到保存的状态
(1)画照片
(2)手动调用
先创建一个按钮,然后利用connect连接绘图事件和按钮事件,手动调用绘图事件不能直接调用paintEvent(QPaintEvent*),旧版本使用repaint(),新版本使用更优的update()
(3)移动图像与计时器结合
绘图设备有QPixmap、QImage、QBitmap(只有黑白色)、QPicture、QWidget。
(1)QPixmap 对不同平台做了显示的优化
绘制图像:
第一步:创建pixmap图像对象QPixmap pix(长,宽)
第二步:填充颜色(最后生成的图像默认背景为黑色,修改背景填充颜色)pix.fill(颜色)
第三步:创建画笔QPainter painter(&pix)
第四步:利用画笔在图像对象pix上绘画(见16.5)
第五步:保存图像pix.save(路径)
(2)QImage 可以对像素点进行修改
绘制图像:
第一步:创建image图像对象QImage img(长,宽,颜色类型)
第二步:其它和QPixmap相同
修改像素点:
第一步:创建image图像图像QImage img
第二步:加载图片(只能是添加到资源文件的图片)img.load(文件路径)
第三步:设置修改后的图像颜色QRgb value = qRgb(255,0,0)
第四步修改img.setPixel(像素点x,像素点y,value)
下图只能写在绘图事件中,不能写在其它位置
(3)QPicture 记录和重现图像
记录图像:
第一步:创建picture图像对象QPicture pic
第二步:创建画笔QPainter painter
第三步:利用画笔在图像对象pic上绘画painter.begin(&pic)
第四步:结束绘图painter.end()
第五步:保存(任意后缀名)pic.save(路径)重现图像(有些图像正常不能打开,如zt后缀图像,所以要用代码打开):
第一步:创建画笔QPainter painter(this)
第二步:创建pic对象QPicture pic
第三步:关联文件pic.load(文件路径)
第四步:利用画笔painter.drawPicture(重现位置x,重现位置y, pic)
上述操作后记得关闭文件,上面忘记关闭了:file.close()
实战项目:翻金币游戏。
创建QMainWindow项目,添加图片、图标、音效资源,完成后如下
删除工具栏、导航栏,菜单栏添加菜单项,完成后如下
设置窗口固定大小、窗口图标、窗口标题,完成后如下
菜单项“退出”功能实现,完成后如下
先设置背景主图片占满整个窗口,再添加一个背景标题图片,覆盖在主图片上左上角,完成后如下
创建选择关卡窗口类,关联开始按钮和选择关卡窗口,完成后如下
设置选择关卡窗口内容,设置窗口背景,添加按钮,完成后如下
在按钮类中重写鼠标按下释放事件,完成后如下
利用信号和槽实现场景切换,完成后如下
利用循环创建按钮,完成后如下
同样利用循环创建标签覆盖在按钮上,完成后如下
因为标签覆盖了按钮,导致按钮无法生效,设置穿透,使得鼠标可以触发按钮(即标签变成类似透明),完成后如下
首先创建一个类,接着使用自定义构造函数进行窗口、背景、按钮配置,完成后如下
实现退出菜单项、返回按钮功能,完成后如下
在关卡场景类的构造函数中定义标签控件,设置字体格式,标签装备字体格式,设置标签显示字体,调整标签位置,完成后如下
利用标签和循环显示金币背景,完成后如下
创建金币类,并创建金币,完成后如下
创建文件,通过map和vector容器将每一关的数据保存,完成后如下
在游戏场景文件中,创建数据对象,将数据对象的数据拷贝给游戏场景数组,在循环中按数据创建金币银币,完成后如下
在金币类创建定时器、金币标志、金币参数,在金币类中定义点击金币函数,函数内根据金币标志调用定时器开始计时,金币类构造函数中连接定时器计时事件和要实现的内容,在游戏场景中连接点击金币和点击金币函数,完成后如下
在金币类中,定义一个特效执行标志,用来判断特效是否执行完成,然后在对应位置改变其值,重写鼠标点击事件,若特效还在执行则直接返回知道执行完成,从而达到第一次翻完才能翻第二次的效果,提升用户体验,完成后如下
在金币类中,定义金币位置变量,游戏场景中,定义金币状态数组,在创建金币前赋值相应数据,在连接处判断周围情况,实现翻转,最后优化,让周围金币延时翻转,完成后如下
在金币类和游戏场景中添加胜利标志isWin,每次点击金币翻金币后双层循环依次判断每一个金币状态flag,如果全部变为金币则isWin为真,此时将金币类的isWin也赋值为真,在鼠标点击事件中判断如果isWin为真则直接return,即不能再翻金币,完成后如下
首先创建胜利图片,然后在判断胜利的语句中让胜利图片显示,完成后如下
在工程文件添加音效模块multimedia,再在要使用的地方包含头文件、创建音效、播放 音效,完成后如下
格式:即将显示的窗口->setGeometry(当前窗口->geometry())。
首先按release编译,随后文件夹生成一个release文件夹
新建文件夹,将release中的exe文件复制到新建文件夹
保证qt安装路径bin有下图文件,打开cmd,进入新建文件夹,输入图中内容,等待片刻就生成了很多文件
若要打包成安装包,则使用HM NIS Edit软件按步骤打包即可。
「Qt从下载 到实战」https://www.aliyundrive.com/s/gcHge1y6Zun |
---|
点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。 |