学习视频链接
完整项目链接:
https://github.com/lejunXTS/QT.git
https://github.com/lejunXTS/coinfilp/tree/main
此系列博客代码是我在学习过程中自己手敲实现一遍的,作为一个学习过程的记录。为了防止误解,我在此再次说明一下。如有侵权,请私信我。
概述 什么是QT QT是一个跨平台 的C++图像用户界面应用程序框架
为应用程序开发者提供建立艺术级图形界面所需的所有功能,它是完全面向对象的,很容易扩展,并且允许真正的组件编程
Qt的发展史 1991年 Qt最早由奇趣科技开发
1996年 进入商业领域,它也是目前流行的Linux桌面环境KDE的基础
2008年 奇趣科技被诺基亚公司收购,Qt称为诺基亚旗下的编程语言
2012年 Qt又被Digia公司收购
2014年4月 跨平台的集成开发环境Qt Creator3.1.0发布,同年5月20日配发了Qt5.3正式版,至此Qt实现了对iOS、Android、WP等各平台的全面支持
当前Qt最新版本为 Qt 6
支持的平台 Windows – XP、Vista、Win7、Win8、Win2008、Win10、Win11
Uinux/X11 – Linux、Sun Solaris、HP-UX、Compaq Tru64 UNIX、IBM AIX、SGI IRIX、FreeBSD、BSD/OS、和其他很多X11平台
Macintosh – Mac OS X
Embedded – 有帧缓冲支持的嵌入式Linux平台,Windows CE
Qt版本 Qt按照不同的版本发行,分为商业版和开源版
商业版:
为商业软件提供开发,他们提供传统商业软件发行版,并且提供在商业有效期内的免费升级和技术支持服务。
开源的LGPL版本:
为了开发自有而设计的开放源码软件,它提供了和商业版本同样的功能,在GNU通用公共许可下,它是免费的。
QT的优点 跨平台,几乎支持所有的平台
接口简单,容易上手
一定程度上简化了内存回收机制
开发效率高,能够快速的构建应用程序
有很好的社区氛围
可以进行嵌入式开发
成功案例 Linux桌面环境KDE
WPS Office 办公软件
Skype 网络电话
Google Earth 谷歌地图
VLC多媒体播放器
VirtualBox虚拟机软件
创建项目 Qt中的构建⼯具有三种:qmake CMake Qbs
qmake:qmake是⼀个构建⼯具(build tool),⽤于⾃动⽣成makefile⽂件。qmake⽀持跨平台构建。qmake编辑的是⼀个后缀名为.pro的⽂件。
CMake:CMake是⼀个跨平台的构建⼯具。CMake本⾝不是⼀个编译器,其实就是⽣成⼀个让编译器能读懂编译流程的⽂件⼯具。让CMake⾃动⽣成构建系统,例如Makefile和Visual Studio项⽬⽂件。CMake是⼀个第三⽅⼯具,有⾃⼰的⽂档。
Qbs:Qbs(Qt Build Suite:Qt构建套件)同qmake、CMake⼀样都是构建⼯具。Qbs号称是新⼀代的构建⼯具,⽐qmake编译速度更快。Qbs没有绑定Qt版本,它从项⽬⽂件的⾼级项⽬描述中⽣成⼀个正确的依赖表。⽽传统的MakeFile⽣成⼯具如qmake和CMake,其在⽣成MakeFile⽂件后将实际的命令交给Make⼯具去执⾏。
基类选择
QWidget(父类)——最简单、最基本的窗体程序,里面可以放置多个控件实现程序功能
QMainWindow(子类)——主窗口类,一般用于较为复杂的应用程序,除了中央客户区界面,还包括菜单栏、工具栏、状态栏以及多个可停靠的工具对话框等
QDialog(子类)——基于对话框的程序,对话框一般用于弹窗,也可以用于主界面显示,对话框是从QWidget继承而来,并丰富了一些功能,如模态显示和返回值等
版本控制系统:
svn vss git
创建QT项目 第一个QT项目 #include "mywidget.h" #include <QApplication> int main (int argc, char *argv[]) { QApplication a (argc, argv) ; myWidget w; w.show (); return a.exec (); }
Qt系统提供的标准类名声明头文件没有.h后缀
Qt一个类对应一个头文件,类名就是头文件名
QApplication应用程序类:
管理图形用户界面应用程序的控制流和主要设置。
是Qt的整个后台管理的命脉它包含主事件循环,在其中来自窗口系统和其它资源的所有事件处理和调度。它也处理应用程序的初始化和结束,并且提供对话管理。
对于任何一个使用Qt的图形用户界面应用程序,都正好存在一个QApplication对象,而不论这个应用程序在同一时间内是不是有0、1、2或更多个窗口。
a.exec() 程序进入消息循环,等待对用户输入进行响应。这里main()把控制权转交给Qt,Qt完成事件处理工作,当应用程序退出的时候exec()的值就会返回。在exec()中,Qt接受并处理用户和系统的事件并且把它们传递给适当的窗口部件。
#ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> class myWidget : public QWidget{ Q_OBJECT public : myWidget (QWidget *parent = nullptr ); ~myWidget (); }; #endif
#include "mywidget.h" myWidget::myWidget (QWidget *parent) : QWidget (parent) {} myWidget::~myWidget () {}
.pro 文件介绍
QT += core gui greaterThan (QT_MAJOR_VERSION, 4 ): QT += widgetsCONFIG += c++17 # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ mywidget.cpp HEADERS += \ mywidget.h # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else : unix:!android: target.path = /opt/$${TARGET}/bin!isEmpty (target.path): INSTALLS += target
按钮控件的常用API myWidget::myWidget (QWidget *parent) : QWidget (parent) { QPushButton * btn = new QPushButton; btn->setParent (this ); btn->setText ("the first button" ); QPushButton * btn2 = new QPushButton ("the second button" ,this ); btn2->move (100 ,100 ); resize (500 ,300 ); setFixedSize (500 ,300 ); setWindowTitle ("the first window" ); }
对象树 当创建的对象在堆区的时候,如果指定的父类是QObject派生下来的类或者QObject子类派生下来的类,可以不用管理释放的操作,对象会放入到对象树中。
一定的程度上简化了内存回收机制
窗口坐标体系 坐标体系:
以左上角为原点(0,0),X向右增加,Y向下增加
对于嵌套窗口,其坐标是相对于父窗口来说的
信号和槽机制 连接方式 connect(信号的发送者,发送的具体信号,信号的接受者,信号的处理(槽函数))
信号槽的优点:松散耦合,信号发送端和接收端本身是没有关联的,通过connect连接将两端耦合在一起
MyPushButton * myBtn = new MyPushButton; myBtn->setText ("my own button" ); myBtn->move (100 ,200 ); myBtn->setParent (this ); connect (myBtn, &QPushButton::clicked, this , &QWidget::close);
自定义信号和槽 #ifndef TEACHER_H #define TEACHER_H #include <QObject> class Teacher : public QObject{ Q_OBJECT public : explicit Teacher (QObject *parent = nullptr ) ; signals: void hungry () ; }; #endif
#include "teacher.h" Teacher::Teacher (QObject *parent) : QObject{parent} {}
#ifndef STUDENT_H #define STUDENT_H #include <QObject> class Student : public QObject{ Q_OBJECT public : explicit Student (QObject *parent = nullptr ) ; void treat () ; signals: }; #endif
#include "student.h" #include <QDebug> Student::Student (QObject *parent) : QObject{parent} {} void Student::treat () { qDebug () << "please eat teacher" ; }
#ifndef WIDGET_H #define WIDGET_H #include "teacher.h" #include "student.h" #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui {class Widget ;} QT_END_NAMESPACE class Widget : public QWidget{ Q_OBJECT public : Widget (QWidget *parent = nullptr ); ~Widget (); private : Ui::Widget *ui; Teacher * cc; Student * ll; void classIsOver () ; }; #endif
#include "widget.h" #include "./ui_widget.h" Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); this ->cc = new Teacher (this ); this ->ll = new Student (this ); connect (cc, &Teacher::hungry, ll, &Student::treat); classIsOver (); } void Widget::classIsOver () { emit cc->hungry (); } Widget::~Widget () { delete ui; }
#include "widget.h" #include <QApplication> int main (int argc, char *argv[]) { QApplication a (argc, argv) ; Widget w; w.show (); return a.exec (); }
重载
当自定义信号和槽出现重载,需要利用函数指针明确指向函数的地址
void hungry () ;void hungry (QString foodName) ;
void treat () ;void treat (QString foodName) ;
emit cc->hungry ("sandwich" );
void (Teacher:: *teacherSignal)(QString) = &Teacher::hungry;void (Student:: *studentSlot)(QString) = &Student::treat;connect (cc, teacherSignal, ll, studentSlot);classIsOver ();
点击一个下课的按钮再触发下课 & 信号连接信号
Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); this ->cc = new Teacher (this ); this ->ll = new Student (this ); void (Teacher:: *teacherSignal)(QString) = &Teacher::hungry; void (Student:: *studentSlot)(QString) = &Student::treat; connect (cc, teacherSignal, ll, studentSlot); QPushButton * btn = new QPushButton ("class over" , this ); resize (600 ,400 ); void (Teacher:: *teacherSignal2)(void ) = &Teacher::hungry; void (Student:: *studentSlot2)(void ) = &Student::treat; connect (cc, teacherSignal2, ll, studentSlot2); connect (btn, &QPushButton::clicked, cc, teacherSignal2); }
其他 QString->char * 先转成QByteArray (.toUtf8()) 再转成char * ()
connect (cc, SIGNAL (hungry ()), ll, SLOT (treat ()));
Lambda表达式 []标识符 匿名函数
= 值传递
& 引用传递
() 参数 {}实现体
mutable 修饰值传递变量,可以修改拷贝出的数据,改变不了本体
返回值 ->int{}
最常用 = {}
C++11中的Lambda表达式(这个再找资料看一下)
QPushButton * btn2 = new QPushButton; btn2->setText ("close" ); btn2->move (100 ,0 ); btn2->setParent (this ); connect (btn2, &QPushButton::clicked, this , [=](){ this ->close (); emit cc->hungry ("sandwich" ); });
QMainWindow 简介 QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个锚接部件(dock widgets)(浮动窗口)、一个状态栏(status bar)及一个中心部件(central widget),是许多应用程序的基础,如文本编辑器,图片编辑器等。
基础&常用操作 #include "mainwindow.h" #include <QMenuBar> #include <QToolBar> #include <QDebug> #include <QPushButton> #include <QStatusBar> #include <QLabel> #include <QDockWidget> #include <QTextEdit> MainWindow::MainWindow (QWidget *parent) : QMainWindow (parent) { resize (600 ,400 ); QMenuBar * bar = menuBar (); setMenuBar (bar); QMenu * fileMenu = bar->addMenu ("文件" ); QMenu * editMenu = bar->addMenu ("编辑" ); QAction * newAction = fileMenu->addAction ("新建" ); fileMenu->addSeparator (); QAction * openAction = fileMenu->addAction ("打开" ); QToolBar * toolBar = new QToolBar (this ); addToolBar (Qt::LeftToolBarArea, toolBar); toolBar->setAllowedAreas (Qt::LeftToolBarArea | Qt::RightToolBarArea); toolBar->addAction (newAction); toolBar->addSeparator (); toolBar->addAction (openAction); QPushButton * btn = new QPushButton ("aa" , this ); toolBar->addWidget (btn); QStatusBar * stBar = statusBar (); setStatusBar (stBar); QLabel * label = new QLabel ("提示信息" , this ); stBar->addWidget (label); QLabel * label2 = new QLabel ("右侧提示信息" , this ); stBar->addPermanentWidget (label2); QDockWidget * dockWidget = new QDockWidget ("浮动" , this ); addDockWidget (Qt::BottomDockWidgetArea, dockWidget); dockWidget->setAllowedAreas (Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); QTextEdit * edit = new QTextEdit (this ); setCentralWidget (edit); } MainWindow::~MainWindow () {}
资源文件
将图片文件拷贝到项目文件下
创建新文件->QT->QT resource file->给资源文件起名
res生成res.qrc
open in editor 编辑资源
添加前缀 添加文件
使用”: + 前缀名 + 文件名”
#include "mainwindow.h" #include "./ui_mainwindow.h" MainWindow::MainWindow (QWidget *parent) : QMainWindow (parent) , ui (new Ui::MainWindow) { ui->setupUi (this ); ui->actionnew->setIcon (QIcon (":/image/001VEnKKgy1gsu6mbym6vj60t41dan4j02.jpg" )); ui->actionopen->setIcon (QIcon (":/image/005y0Ylbly1hhdrfsc6pbj32p51pinpe.jpg" )); }
对话框QDialog 自定义对话框 模态与非模态对话框的创建
#include "mainwindow.h" #include "./ui_mainwindow.h" #include <QDialog> #include <QDebug> MainWindow::MainWindow (QWidget *parent) : QMainWindow (parent) , ui (new Ui::MainWindow) { ui->setupUi (this ); connect (ui->actionnew,&QAction::triggered, [=](){ QDialog * dlg2 = new QDialog (this ); dlg2->resize (300 ,200 ); dlg2->show (); dlg2->setAttribute (Qt::WA_DeleteOnClose); qDebug () << "非模态对话框弹出了" ; }); } MainWindow::~MainWindow () { delete ui; }
标准对话框 消息对话框
#include "mainwindow.h" #include "./ui_mainwindow.h" #include <QDialog> #include <QDebug> #include <QMessageBox> MainWindow::MainWindow (QWidget *parent) : QMainWindow (parent) , ui (new Ui::MainWindow) { ui->setupUi (this ); connect (ui->actionnew,&QAction::triggered, [=](){ QMessageBox::warning (this , "war" , "waring" ); }); } MainWindow::~MainWindow () { delete ui; }
其他标准对话框
QColorDialog: 选择颜色
QColor color = QColorDialog::getColor (QColor (255 ,0 ,0 )); qDebug () << "r = " << color.red () << "g = " << color.green () << "b = " << color.blue ();
QFileDialog: 选择文件或者目录
QString str = QFileDialog::getOpenFileName (this , "open file" , "C:\\Users\\****\\Desktop" , "(*.txt)" ); qDebug () << str;
QFontDialog: 选择字体
bool flag;QFont font = QFontDialog::getFont (&flag, QFont ("华文彩云" , 36 )); qDebug () << "字体:" << font.family ().toUtf8 ().data () << "字号:" << font.pointSize () << "是否加粗:" << font.bold () << "是否倾斜:" << font.italic ();
QInputDialog: 允许用户输入一个值,并将其值返回
QMessageBox: 模态对话框,用于显示信息、询问问题等
QPageSetupDialog: 为打印机提供纸张相关的选项
QPrintDialog: 打印机配置
QPrintPreviewDialog:打印预览
QProgressDialog: 显示操作过程
界面布局 实现登录窗口 利用布局方式给窗口进行美化
选取widget进行布局,水平布局,垂直布局,栅格布局
给用户名、密码、登录、退出按钮进行布局
默认窗口和控件之间有6像素,可以调整layoutLeftMargin
利用弹簧进行布局
调整固定高度
常用控件 按钮组 QPushButton 常用按钮
QToolButton 工具按钮 用于显示图片 显示文字
toolButtonStyle 凸起风格 autoRaise
radioButton 单选按钮 默认ui->radioButtonMan->setChecked(true);
checkBox 多选按钮 监听状态
ui->radioButtonMan->setChecked (true ); connect (ui->radioButtonWoman, &QRadioButton::clicked, [=](){ qDebug () << "select woman!" ; }); connect (ui->checkBox_4, &QCheckBox::stateChanged, [=](int state){ qDebug () << state; });
QListWidgetItem * item 一行内容 ui->listWidget->addItem(item)
可以利用additems一次性添加整个内容
QStringList list; list << "人生天地间" << "忽如远行客" << "斗酒相娱乐" << "聊厚不为薄" ; ui->listWidget->addItems (list);
ui->treeWidget->setHeaderLabels (QStringList ()<< "一" << "内容" ); QTreeWidgetItem * liTtem = new QTreeWidgetItem (QStringList ()<< "一" ); QTreeWidgetItem * liTtem2 = new QTreeWidgetItem (QStringList ()<< "二" ); ui->treeWidget->addTopLevelItem (liTtem); ui->treeWidget->addTopLevelItem (liTtem2); QTreeWidgetItem *childItem1 = new QTreeWidgetItem (QStringList () <<"" <<"一帆一江一渔舟,一个渔翁一个钩" ); QTreeWidgetItem *childItem2 = new QTreeWidgetItem (QStringList () << "" <<"一俯一仰一场笑,一江明月一江秋" ); liTtem->addChild (childItem1); liTtem2->addChild (childItem2);
ui->tableWidget->setColumnCount (3 ); ui->tableWidget->setHorizontalHeaderLabels (QStringList ()<<"姓名" <<"性别" <<"年龄" ); ui->tableWidget->setRowCount (5 ); QStringList nameList; nameList<<"小李" <<"小王" <<"小杨" <<"小刘" <<"小赵" ; QList<QString>sexList; sexList<<"女" <<"女" <<"女" <<"女" <<"女" ; for (int i = 0 ; i <5 ; i++) { int col = 0 ; ui->tableWidget->setItem (i,col++,new QTableWidgetItem (nameList[i])); ui->tableWidget->setItem (i,col++,new QTableWidgetItem (sexList.at (i))); ui->tableWidget->setItem (i,col++,new QTableWidgetItem (QString::number (i+18 ))); }
其他控件 connect (ui->Button, &QPushButton::clicked, [=](){ ui->stackedWidget->setCurrentIndex (0 ); }); connect (ui->buttonTabBox, &QPushButton::clicked, [=](){ ui->stackedWidget->setCurrentIndex (1 ); }); connect (ui->ButtonTabWidget, &QPushButton::clicked, [=](){ ui->stackedWidget->setCurrentIndex (2 ); }); ui->comboBox->addItem ("杜鹃" ); ui->comboBox->addItem ("樱花" ); ui->comboBox->addItem ("海棠" ); connect (ui->btnSelect, &QPushButton::clicked, [=](){ ui->comboBox->setCurrentText ("海棠" ); }); ui->labelImage->setPixmap (QPixmap (":/image/005y0Ylbly1hj9kxm09i0j31kw1kw1ky.jpg" )); QMovie * movie = new QMovie (":/image/小太阳gif.jpg" ); ui->labelMovie->setMovie (movie); movie->start ();
自定义控件封装 添加新文件——QT——设计师界面类(.h .cpp .ui)
.ui中设计QStringBox 和 QSlider 两个控件 组成一个控件
Widget中使用自定义控件 拖拽一个Widget控件 点击提升为 添加类名等
实现功能 改变数字 滑动条跟着移动
提供getNumber和setNumber两个对外接口
#ifndef SMALLWIDGET_H #define SMALLWIDGET_H #include <QWidget> namespace Ui {class SmallWidget ;} class SmallWidget : public QWidget{ Q_OBJECT public : explicit SmallWidget (QWidget *parent = nullptr ) ; ~SmallWidget (); void setNumber (int num) ; int getNumber () ; private : Ui::SmallWidget *ui; }; #endif
#include "smallwidget.h" #include "ui_smallwidget.h" SmallWidget::SmallWidget (QWidget *parent) : QWidget (parent) , ui (new Ui::SmallWidget) { ui->setupUi (this ); connect (ui->spinBox, &QSpinBox::valueChanged, ui->horizontalSlider, &QSlider::setValue); connect (ui->horizontalSlider, &QSlider::valueChanged, ui->spinBox, &QSpinBox::setValue); } void SmallWidget::setNumber (int num) { ui->spinBox->setValue (num); } int SmallWidget::getNumber () { return ui->spinBox->value (); } SmallWidget::~SmallWidget () { delete ui; }
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui {class Widget ;} QT_END_NAMESPACE class Widget : public QWidget{ Q_OBJECT public : Widget (QWidget *parent = nullptr ); ~Widget (); private : Ui::Widget *ui; }; #endif
#include "widget.h" #include "ui_widget.h" #include <QDebug> Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); connect (ui->btnGet, &QPushButton::clicked, [=](){ qDebug () << ui->widget->getNumber (); }); connect (ui->btnSet, &QPushButton::clicked, [=](){ ui->widget->setNumber (50 ); }); } Widget::~Widget () { delete ui; }
Qt 中的事件 #ifndef MYLABEL_H #define MYLABEL_H #include <QLabel> #include <QEnterEvent> class myLabel : public QLabel{ Q_OBJECT public : explicit myLabel (QWidget *parent = nullptr ) ; void enterEvent (QEnterEvent *event) ; void leaveEvent (QEvent *event) ; signals: }; #endif
#include "mylabel.h" #include <QDebug> myLabel::myLabel (QWidget *parent) : QLabel{parent} {} void myLabel::enterEvent (QEnterEvent *event) { qDebug ()<< "鼠标进入了" ; } void myLabel::leaveEvent (QEvent *event) { qDebug ()<< "鼠标离开了" ; }
其他
virtual void mousePressEvent (QMouseEvent *ev) ;virtual void mouseReleaseEvent (QMouseEvent *ev) ;virtual void mouseMoveEvent (QMouseEvent *ev) ;
#include "mylabel.h" #include <QDebug> #include <QMouseEvent> myLabel::myLabel (QWidget *parent) : QLabel{parent} { setMouseTracking (true ); } void myLabel::mousePressEvent (QMouseEvent *ev) { if (ev->button () == Qt::LeftButton) { QString str = QString ("鼠标按下了 x = %1 y = %2 " "globalx = %3 globaly = %4" ).arg (ev->x ()).arg (ev->y ()) .arg (ev->globalX ()).arg (ev->globalY ()); qDebug ()<< str; } } void myLabel::mouseReleaseEvent (QMouseEvent *ev) { QString str = QString ("鼠标释放了 x = %1 y = %2 " "globalx = %3 globaly = %4" ).arg (ev->x ()).arg (ev->y ()) .arg (ev->globalX ()).arg (ev->globalY ()); qDebug ()<< str; } void myLabel::mouseMoveEvent (QMouseEvent *ev) { QString str = QString ("鼠标移动了 x = %1 y = %2 " "globalx = %3 globaly = %4" ).arg (ev->x ()).arg (ev->y ()) .arg (ev->globalX ()).arg (ev->globalY ()); qDebug ()<< str; }
定时器 第一种定时器 #ifndef WIDGET_H #define WIDGET_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui {class Widget ;} QT_END_NAMESPACE class Widget : public QWidget{ Q_OBJECT public : Widget (QWidget *parent = nullptr ); ~Widget (); void timerEvent (QTimerEvent *) ; int id1; int id2; private : Ui::Widget *ui; }; #endif
#include "widget.h" #include "ui_widget.h" Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); id1 = startTimer (1000 ); id2 = startTimer (2000 ); } void Widget::timerEvent (QTimerEvent *ev) { if (ev->timerId () == id1) { static int num = 1 ; ui->label_2->setText (QString::number (num++)); } if (ev->timerId () == id2) { static int num2 = 1 ; ui->label_3->setText (QString::number (num2++)); } } Widget::~Widget () { delete ui; }
第二种定时器 #include "widget.h" #include "ui_widget.h" #include <QTimer> Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); id1 = startTimer (1000 ); id2 = startTimer (2000 ); QTimer * timer = new QTimer (this ); timer->start (500 ); connect (timer, &QTimer::timeout, [=](){ static int num = 1 ; ui->label_4->setText (QString::number (num++)); }); } void Widget::timerEvent (QTimerEvent *ev) { if (ev->timerId () == id1) { static int num = 1 ; ui->label_2->setText (QString::number (num++)); } if (ev->timerId () == id2) { static int num2 = 1 ; ui->label_3->setText (QString::number (num2++)); } } Widget::~Widget () { delete ui; }
event 事件 用于事件的分发
也可以做拦截操作 不建议
bool myLabel::event (QEvent *e) { if (e->type () == QEvent::MouseButtonPress) { QMouseEvent * ev = static_cast <QMouseEvent *>(e); QString str = QString ("event函数中 鼠标按下了 x = %1 y = %2 " "globalx = %3 globaly = %4" ).arg (ev->x ()).arg (ev->y ()) .arg (ev->globalX ()).arg (ev->globalY ()); qDebug ()<< str; return true ; } return QLabel::event (e); }
事件过滤器 通过事件过滤器,可以在程序分发到event事件之前再做一次高级拦截
两个步骤:
给控件安装事件过滤器
重写eventfilter事件
bool eventFilter (QObject *, QEvent *) ;
#include "widget.h" #include "ui_widget.h" #include <QTimer> Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); ui->label->installEventFilter (this ); } bool Widget::eventFilter (QObject *obj, QEvent *e) { if (obj == ui->label) { if (e->type () == QEvent::MouseButtonPress) { QMouseEvent * ev = static_cast <QMouseEvent *>(e); QString str = QString ("事件过滤器中 鼠标按下了 x = %1 y = %2 " "globalx = %3 globaly = %4" ).arg (ev->x ()).arg (ev->y ()) .arg (ev->globalX ()).arg (ev->globalY ()); qDebug ()<< str; return true ; } } return QWidget::eventFilter (obj,e); } Widget::~Widget () { delete ui; }
QPainter 绘图 基础设置 #ifndef WIDGET_H #define WIDGET_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui {class Widget ;} QT_END_NAMESPACE class Widget : public QWidget{ Q_OBJECT public : Widget (QWidget *parent = nullptr ); ~Widget (); void paintEvent (QPaintEvent *) ; private : Ui::Widget *ui; }; #endif
#include "widget.h" #include "ui_widget.h" #include <QPainter> Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); } void Widget::paintEvent (QPaintEvent *) { QPainter painter (this ) ; QPen pen (QColor(122 ,168 ,200 )) ; pen.setWidth (6 ); pen.setStyle (Qt::DotLine); painter.setPen (pen); QBrush brush (QColor(222 ,222 ,222 )) ; brush.setStyle (Qt::Dense3Pattern); painter.setBrush (brush); painter.drawLine (QPoint (0 ,0 ), QPoint (100 ,100 )); painter.drawEllipse (QPoint (100 ,100 ),36 ,66 ); painter.drawRect (QRect (20 ,20 ,50 ,50 )); painter.drawText (QRect (10 ,200 ,100 ,50 ),"晴天与猫" ); } Widget::~Widget () { delete ui; }
高级设置 QPainter painter (this ) ;painter.drawRect (QRect (20 ,20 ,50 ,50 )); painter.translate (100 ,0 ); painter.save (); painter.drawRect (QRect (20 ,20 ,50 ,50 )); painter.translate (100 ,0 ); painter.restore (); painter.drawRect (QRect (20 ,20 ,50 ,50 ));
#include "widget.h" #include "ui_widget.h" #include <QPainter> Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); connect (ui->pushButton,&QPushButton::clicked,[=](){ posX += 20 ; update (); }); } void Widget::paintEvent (QPaintEvent *) { QPainter painter (this ) ; if (posX > this ->width ()) { posX = 0 ; } QPixmap pixmap (":/image/le.jpg" ) ; QPixmap scaledPixmap = pixmap.scaled (200 , 200 , Qt::KeepAspectRatio); painter.drawPixmap (posX,10 ,scaledPixmap); } Widget::~Widget () { delete ui; }
update() 是一个 QWidget 类的成员函数,用于请求更新窗口部件的绘图
当调用 update() 函数时,Qt 将触发绘图事件,并重新绘制窗口部件
这意味着任何需要更新的视图都会被更新
绘图设备 绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和 QPicture。
QPixmap专门为图像在屏幕上的显示做了优化
QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap
QImage专门为图像的像素级访问做了优化。
QPicture则可以记录和重现QPainter的各条命令
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui {class Widget ;} QT_END_NAMESPACE class Widget : public QWidget{ Q_OBJECT public : Widget (QWidget *parent = nullptr ); ~Widget (); void paintEvent (QPaintEvent *) ; private : Ui::Widget *ui; }; #endif
#include "widget.h" #include "ui_widget.h" #include <QPixmap> #include <QPainter> #include <QImage> #include <QPicture> Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); QPicture pic; QPainter painter; painter.begin (&pic); painter.setPen (QPen (Qt::cyan)); painter.drawEllipse (QPoint (150 ,150 ),100 ,100 ); painter.end (); pic.save ("D:\\cpp\\QT\\pic.zt" ); } void Widget::paintEvent (QPaintEvent *) { QPainter painter (this ) ; QPicture pic; pic.load ("D:\\cpp\\QT\\pic.zt" ); painter.drawPicture (0 ,0 ,pic); } Widget::~Widget () { delete ui; }
文件系统 QFile 对文件进行读写操作
#include "widget.h" #include "ui_widget.h" #include <QFileDialog> #include <QFile> #include <QString> #include <QFileInfo> #include <QDebug> #include <QDateTime> Widget::Widget (QWidget *parent) : QWidget (parent) , ui (new Ui::Widget) { ui->setupUi (this ); connect (ui->pushButton,&QPushButton::clicked,[=](){ QString path = QFileDialog::getOpenFileName (this ,"打开文件" ,"C:\\Users\\lyzhy\\Desktop" ); ui->lineEdit->setText (path); QFile file (path); file.open (QIODevice::ReadOnly); QByteArray array = file.readAll (); ui->textEdit->setText (array); file.close (); file.open (QIODevice::Append); file.write ("茉莉生酪拿铁" ); file.close (); QFileInfo info (path); qDebug ()<<"文件大小:" <<info.size ()<<"文件后缀名:" <<info.suffix () <<"文件名称:" <<info.fileName ()<<"文件路径:" <<info.filePath (); qDebug ()<<"创建日期:" <<info.birthTime ().toString ("yyyy/MM/dd hh:mm:ss" ); qDebug ()<<"最后修改日期:" <<info.lastModified ().toString ("yyyy-MM-dd hh:mm:ss" ); }); } Widget::~Widget () { delete ui; }
翻金币游戏实战 创建项目,添加项目资源
主场景
设置游戏主场景配置
设置背景图标
设置固定大小
设置项目标题
设置背景
背景标题
开始菜单
退出功能
创建开始按钮
封装自定义按钮 MyPushButton
构造函数 参数( 默认显示图片, 按下后显示的图片)
开始按钮 点击开始按钮进入选择关卡场景
开始按钮特效
zoom1 向下跳
zoom2 向上跳
创建选择关卡场景
点击开始按钮后 延时进入到 选择关卡场景
选择关卡场景
场景基本设置
背景设置 图标 标题 大小等
创建返回按钮
选择关卡的返回按钮特效制作
点击后切换另一个图片
重写 void mousePressEvent
重写 void mouseReleaseEvent
开始场景与选择关卡场景的切换
点击选择关卡场景的返回按钮,发送一个自定义信号
在主场景中监听这个信号,并且当触发信号后,重新显示主场景,隐藏掉选择关卡的场景
创建选择关卡按钮
利用一个for循环将所有的按钮布置到场景中
在按钮上面设置一个QLabel显示关卡数
QLabel 设置 大小、显示文字、对齐方式、鼠标穿透
给每个按钮 监听点击事件
翻金币场景
翻金币场景创建
点击选择关卡按钮后,进入到翻金币游戏场景
配置翻金币游戏场景 设置标题、图标、大小、设置背景
实现返回按钮,可以返回到上一个场景(选关场景)
实现三个场景之间的切换
实现显示关卡标签 在左下角显示玩家具体的关卡标签
QLabel创建设置 大小和位置label->setGeometry(30, this->height() - 50,120, 50);
QFont font 设置字体以及字号
给QLabel设置字体 setFont(font)
创建金币类 先将金币的背景图案放入到 PlayScene 中
创建 MyCoin 自定义金币按钮类
MyCoin::MyCoin(QString btnImg) 构造函数中传入默认显示的图片金币
在PlayScene创建所有的金币按钮
每个关卡的默认显示
先引入dataConfig.h 和 dataConfig.cpp文件到项目中
在PlayScene 中写了 int gameArray[4][4]的数组 维护每个关卡的金币状态
初始化每个关卡的显示
金币翻转特效
给每个硬币加属性 posX 坐标x posY 坐标y bool flag 正反面标志
给MyCoin 加函数 changFlag改变标志, 如果是flag为true 改为false 并且开启定时器1 (正面翻反面); 如果flag为false ,改为true,并且开启定时器2 (反面翻正面)
实现定时器中的内容
测试 金币翻银币 以及 银币翻金币
解决快速点击的效果不好
在MyCoin中加入了 isAnimation 判断 是否正在做动画条件
当按下 MyCoin 判断是否在做动画,如果做动画,直接return,保证金币和银币动态切换的完整效果
翻转周围金币 点击金币后 ,延时翻转周围金币实现
判断胜利
PlayScene中 添加 isWin的标志 来判断是否胜利
如果胜利了,打印胜利信息
将所有按钮 屏蔽掉点击
胜利图片特效
将胜利图片放入到游戏场景外
当游戏胜利时,移动到屏幕中央,做胜利效果
添加音效资源
QSoundEffect 所属模块 multimedia 需要在.pro文件中加入这个模块
在三个场景中添加音效
播放 s ->play()
s->setLoop() 设置播放次数 -1代表无限循环播放
项目的优化
将三个场景的切换位置 一致
打包
hm nis edit
enigma
完整项目链接:https://github.com/lejunXTS/coinfilp/tree/main
完结撒花!
完结撒花!
完结撒花!