【Qt设计开发】Qt入门,GUI界面设计开发

【Qt设计开发】Qt入门,GUI界面设计开发Qt 是一个 1991 年由 QtCompany 开发的跨平台 C 图形用户界面 GraphicUserI GUI 应用程序开发框架

大家好,欢迎来到IT知识分享网。

  本文是我在学习QT的GUI界面设计过程当中的心得和学习笔记,在学习时已经有C, C++,Python的基础。文章附上了学习的代码,仅供大家参考。如果有问题,有错误欢迎大家留言。此外,博主还有另外几篇文章,分别关于 Python基础知识、 Python的具体应用、 C语言指针结构体的难点、 C++入门和进阶知识点和 C++高阶知识点,大家点击即可翻阅。

一、Qt简介和下载安装

  Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面(Graphic User Interface, GUI)应用程序开发框架。QT包括但不仅限于GUI的开发,也包含了诸如系统调用、网络编程、数据库编程,2D/3D图形处理等等。QT具有强大的跨平台运行的性能,几乎囊括了所有的操作系统,例如Linux、Windows、Mac OS、Android、IOS。我们所熟知的金山WPS、Google Earth谷歌地图、SKype网络电话就是用Qt开发的。

  博主使用的版本是Qt 5.14.2, 下载、安装 参照B站视频。

二、Qt入门

2.1 创建第一个项目

  第一步,选择new->Application->Qt Widgets Application->Choose:

https://img-blog.csdnimg.cn/899345d535f048ca97822f0ebd0130ab.png

  第二步,修改项目名称和项目路径,点击下一步。

请添加图片描述

  第三步,修改类名称,其中基类有三种,分别是QMainWindow(菜单类), QWidget, QDialog(对话框类),表示创建的类继承的基类,例如,图中所示mywidget类的父类就是QWidget。QDialog和QMainWindow是QWidget的子类。QMainWindow是菜单类,左上角有一些菜单选项,右上角有最小化最大化按钮。QDialog是对话框类,下图所示就是一个对话框类。

请添加图片描述

  第四步,选择MinGW 64-bit 编译器,32位和64位的区别在于32位能在64位的机器上跑,64位不能在32位的机器上跑,初始项目选择任意一个就可以,点击下一步,然后在点击完成,就可以产生一个名为Qt_test的项目,项目底下有一个Qt_test.pro的项目文件。
请添加图片描述

  之后的步骤默认就可以,一直点下一步,然后编译运行,出现一个空白窗口,创建完毕。

Alt

  编译成功后,会在项目目录底下生成build文件,然后点击debug文件,里面有生成的.exe可执行文件,点击即可运行,结果就是一个空白图窗。博主在运行.exe的时候碰到了错误弹窗,显示程序“无法找到入口”,添加了环境变量之后还需要将环境变量上移,具体解决参考解决Qt生成exe错误:无法定位程序输入点。

# QT_hello.pro文件 QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets # 版本大于4以上的添加widgets模块 CONFIG += c++11 # 用C++11标准来解释代码 # The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ mywidget.cpp HEADERS += \ mywidget.h TARGET = UAV # 生成的.exe文件名称 TEMPLATE = app # 应用程序模板 Application # Default rules for deployment. qnx: target.path = /tmp/$${ 
   TARGET}/bin else: unix:!android: target.path = /opt/$${ 
   TARGET}/bin !isEmpty(target.path): INSTALLS += target 
// mywidget.h文件 #ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> class mywidget : public QWidget // 公共继承 { 
    Q_OBJECT // Q_OBJECT宏,允许类中使用信号和槽的机制 public: mywidget(QWidget *parent = nullptr); // 构造函数 ~mywidget(); // 析构函数 }; #endif // MYWIDGET_H 
// main.cpp文件 # include <QApplication> # include <QtWidgets> # include <QDebug> // main程序入口 argc命令行变量数量, argv命令行变量的数组 int main(int argc, char *argv[]) { 
    QApplication a(argc, argv); // 应用程序对象, 在QT中,应用程序对象有且仅有一个  mywidget w; // 窗口对象 mywidget是Qwidget的子类 w.show(); // show方法, 窗口对象默认不会显示 qDebug()<<"hello world"; // 在控制台输出, 用于调试  return a.exec(); // 让应用程序对象进入消息循环 } 
// mywidget.cpp文件 #include "mywidget.h" mywidget::mywidget(QWidget *parent) : QWidget(parent) { 
    } mywidget::~mywidget() { 
    } 

2.2 快捷键和命名规范

  QT如下,可以提高编码效率:

/* 快捷键 * 运行代码: Ctrl + r * 编译: Ctrl + b * 注释: Ctrl + / * 缩放字体:Ctrl + 滚轮 * 查找/替换字体: Ctrl + f * 整行移动代码: Ctrl + Shift + 上/下键 * 自动对齐:Ctrl + i * 在同名文件和源文件之间切换: F4 * 快速添加函数定义:鼠标移到声明的那一行,按Alt + Enter * 修改变量名,并应用到所有:Ctrl + Shift + r * 快捷打开输出窗口: Alt + number(1-8) * 书签功能: 快速跳到代码 * Ctrl + M 添加/删除标签 * Ctrl + . 查找并移动到下一个标签处 * 查看帮助文档: * 第一种:Qt Creator 查看 F1 * 第二种:独立的帮助文档程序查看 */ /* 类名: 首字母大写,单词和单词之间首字母大写 函数名和变量名称: 首字母小写,单词和单词之间首字母大写 */ 

在这里插入图片描述

  Qt设计师创建的文件后缀为.ui, 无法直接运用到C++中,因此引入一个uic工具,可以将.ui文件转换为.c文件。rcc moc同样是这样类型的工具,将一些qt文件转换成C++语法格式的文件

2.3 Qt项目和VS2022项目相互转换

  博主最近需要使用QT和VS 2022联合编程,大家有需要也可以参考视频 VS项目和QT项目相互转换。

  使用VS2022创建的QT项目,输出为.pro文件,利用Qt createor打开,需要在.pro文件中加载模块(添加如下代码),因为VS2022是在项目配置的时候加载的。如下图所示。

# ---------------------------------------------------- # This file is generated by the Qt Visual Studio Tools. # ------------------------------------------------------ QT += core gui widgets # 模块加载, 使用VS2022创建的项目,输出为.pro文件,利用Qt createor打开,需要在.pro文件中加载模块 # 因为VS2022是在项目配置的时候加载的。 TEMPLATE = app TARGET = QtWidgetsTest # 以下代码可以不要 DESTDIR = ../x64/Debug CONFIG += debug LIBS += -L"." DEPENDPATH += . MOC_DIR += GeneratedFiles/$(ConfigurationName) OBJECTS_DIR += debug UI_DIR += GeneratedFiles RCC_DIR += GeneratedFiles # HEADERS += ./qtwidgetstest.h SOURCES += ./qtwidgetstest.cpp \ ./main.cpp FORMS += ./qtwidgetstest.ui RESOURCES += qtwidgetstest.qrc 

请添加图片描述

 w.setWindowTitle(u8"VS2022 QT 窗口"); // 不乱码 /* 产生乱码, 英文不会有乱码,英文编码格式都是同意的ASCII,QT中文编码格式是UTF-8, windows中文编码格式是GB2312,u8为转换成UTF-8,QT就可以识别了 */ 

三、Qt基础

3.1 Qt对象树和窗口坐标系概念

  当创建的对象在堆区的时候,如果指定的父亲是QObject派生下来的类或者QObject子类派生下来的类,可以不用管理释放操作,QT会对象会放入到对象树中,会自动释放内存,一定程度上简化了内存回收机制。这也是QT的优点之一,因此,我们在构造时候就指定parent对象,就不需要操心内存释放问题。

  Qt窗口的坐标系:以左上角顶点为原点(0, 0),X向右增加,Y向下增加。对于嵌套窗口,其坐标系是相对于父窗口而言。

3.2 QPushButton

  在编写这部分代码时,博主的编辑器竟然没有代码补全功能,于是又在网上找了解决办法,这里给出链接。
  在学习QT的各种类的过程中,最重要的是 学会如何查找帮助文档以及看懂帮助文档。例如QPushButton类,帮助文档中给出详细解释:添加头文件,同时要在.pro文件中加入widgets模块,其父类是QAbstractButton,其子类是QCommandLinkButton等等信息。

在这里插入图片描述

3.3 信号和槽(signals and slots)

3.3.1 pushbutton关闭窗口

  信号和槽是学习Qt的一个非常重要知识点,在信号和槽当中,我们引入一个连接函数connect( ),将信号发送者和信号接收者链接起来。connect( )一共有四个参数

  • 参数1:信号发送者;
  • 参数2:发送的信号(函数地址);
  • 参数3:信号接收者;
  • 参数4:处理的槽函数(函数地址)。

  在空白项目的基础上改写mywidget.cpp函数,实现点击按钮,关闭窗口案例:

# include "mywidget.h" # include <QPushButton> # include <QDebug> mywidget::mywidget(QWidget *parent) : QWidget(parent) { 
    qDebug() << "hello world"; // 调试信息 // 创建一个按钮 QPushButton * btn = new QPushButton; //btn->show(); // show以顶层(新窗口)的方式弹出窗口控件 btn->setParent(this); // 让btn对象依赖在mywidget窗口中 btn->setText("第一个按钮"); QPushButton *btn2 = new QPushButton("第二个按钮",this); btn2->move(100,100); // 移动btn2按钮 resize(600,400); // 重置窗口大小 btn2->resize(50,50); // 设置btn2按钮大小 setWindowTitle("第一个窗口应用"); // 设置窗口名称 connect(btn,&QPushButton::clicked, this, &mywidget::close); } mywidget::~mywidget() { 
    } 

3.3.2 自定义信号和槽

  创建一个下课,老师饿了,学生请客的案例。添加自定义的老师类和学生类,选择C++类,这个两个类不是窗口类,直接继承QObject类。分别产生了teacher和student的.cpp .h文件。

在这里插入图片描述

  头文件中定义,变量名称和函数声明,在.cpp文件中写实现。

// mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> # include "teacher.h" # include "student.h" class mywidget : public QWidget // 公共继承 { 
    Q_OBJECT // Q_OBJECT宏,允许类中使用信号和槽的机制 public: mywidget(QWidget *parent = nullptr); // 构造函数 ~mywidget(); // 析构函数 Teacher *t; // 在头文件中声明变量和函数 Student *s; void ClassIsOver(); }; #endif // MYWIDGET_H 
// student.h #ifndef STUDENT_H #define STUDENT_H #include <QObject> class Student : public QObject { 
    Q_OBJECT public: explicit Student(QObject *parent = nullptr); signals: public slots: /* 早期的QT版本必须要写到,public slots,高级版本可以写到public或全局下 * 返回值void,需要声明,也需要实现 * 可以有参数,可以发生重载 */ void treat(); void treat(QString foodName); }; #endif // STUDENT_H 
// teacher.h #ifndef TEACHER_H #define TEACHER_H #include <QObject> class Teacher : public QObject { 
    Q_OBJECT public: explicit Teacher(QObject *parent = nullptr); signals: /* 自定义信号类,没有返回值 * 只需要声明,不需要实现 * 可以有参数,可以重载 */ void hungry(); void hungry(QString foodName); public slots: /* 早期的QT版本必须要写到,public slots,高级版本可以写到public或全局下 * 返回值void,需要声明,也需要实现 * 可以有参数,可以发生重载 */ }; #endif // TEACHER_H 
// mywidget.cpp # include "mywidget.h" # include <QPushButton> # include <QDebug> mywidget::mywidget(QWidget *parent) : QWidget(parent) { 
    // 创建老师和学生对象 this->t = new Teacher(this); this->s = new Student(this); // 无参 // //ClassIsOver(); // 调用下课函数 没有链接,没有任何响应 // connect(t,&Teacher::hungry, s, &Student::treat); // 先链接后触发信号,才能响应 // ClassIsOver(); // 调用下课函数 // 链接代参数的函数, void (Teacher:: *f1)(QString) = &Teacher::hungry; void (Student:: *f2)(QString) = &Student::treat; // connect(t,f1, s, f2); // 因为发生了函数重载,不能简单的用取地址符,编译器判断不了是哪个函数,用函数指针 ClassIsOver(); // 调用下课函数 // 点击一个按钮, 触发下课 QPushButton *btn = new QPushButton("下课",this); this->resize(800,600); // 重置窗口大小 connect(btn,&QPushButton::clicked,this, &mywidget::ClassIsOver); // disconnect(btn,&QPushButton::clicked,this, &mywidget::ClassIsOver); // 断开链接 /* 1、信号可以链接信号 * 2、一个信号可以链接到多个槽函数 * 3、多个信号可以链接到一个槽函数 * 4、信号和槽函数的参数必须类型一一对应(槽函数要接收信号的参数) * 5、信号参数个数可以多于槽函数参数个数,但是类型也要一一对应 */ // QT5 6 向下兼容 QT4版本以前的信号和槽的链接方式 connect(t,SIGNAL(hungry()), s, SLOT(treat(QString))); // 优点,直观,缺点,类型不做检测 } mywidget::~mywidget() { 
    } void mywidget::ClassIsOver() { 
    // emit t->hungry(); emit t->hungry("宫保鸡丁"); } 
// student.cpp #include "student.h" # include <QDebug> Student::Student(QObject *parent) : QObject(parent) { 
    } void Student::treat() { 
    qDebug() << "请老师吃饭"; } void Student::treat(QString foodName) { 
    // qDebug() << "请老师吃 :" << foodName; // 带引号,用toUtf8()先将它转成QbyteArray类型,然后用data()在转成char *类型 qDebug() << "请老师吃 :" << foodName.toUtf8().data(); } 

  main.cpp和teacher.cpp默认即可。信号和槽使用时必须先链接后触发信号,才能响应。

3.4 Lambda表达式

  Lambda表达式是C++11中用来定义并创建匿名的函数对象。实际上是一个匿名方法,用来声明一个只在此次使用的匿名函数
[函数对象参数](操作符重载函数参数)mutable->返回值(函数体)

  • 1、函数对象参数:[],标识一个lambda的开始,这部分不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构迨函数。函数对象参数只能使用那些到定义Lambda 为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下几种形式:
参数 作用
没有使用任何函数对象参数
= 函数体可以使用lambda所在作用范围内所有可见的局部变量
this 函数体可以使用lambda所在类中的成员变量
a 将a按值进行传递
&a 将a按引用进行传递
a, &b 将a按值传递,b按引用传递
=, &a, &b 除a b按引用传递外,其余值按值进行传递
&, a, b 除a b按值进行传递外,其余值按引用进行传递

  其中=传递了包括Lambda所在类的this,并且是引用传递方式,相当于编译器自动为我们引用传递了所有局部变量。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。

  • 2、操作符重载函数参数:标识函数重载的()参数,没有参数时,可以省略。参数可以用过按值传递和按引用两种方式进行传递。
  • 3、可修改标识符:mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符,可以修改按值传递进来的拷贝(注意仅仅是能修改拷贝, 而不是修改值本身)
  • 4、函数返回值:->返回值类型, 标识函数返回值的类型,当返回值为void,或者函数体中只有溢出return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
  • 5、函数体: {},标识函数的实现,这部分不能省略,但函数体可以为空。
[capture](parameters)mutable->return-type { 
    statement; } 

  在空项目的基础之上改变mywidget.cpp函数:

#include "mywidget.h" #include <QPushButton> mywidget::mywidget(QWidget *parent) : QWidget(parent) { 
    // 利用lambda表达式,点击按钮,关闭窗口 QPushButton *btn = new QPushButton("关闭", this); btn->move(100,100); connect(btn,&QPushButton::clicked,this, [=](){ 
   this->close();}); } mywidget::~mywidget() { 
    } 

3.5 菜单栏工具栏的创建

  在空项目的基础之上改变mywidget.cpp函数:

#include "mainwindow.h" #include <QPushButton> #include <QMenuBar> #include <QToolBar> #include <QStatusBar> #include <QDockWidget> #include <QLabel> #include <QTextEdit> #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { 
    resize(1400,900); // 重置窗口大小 / 菜单栏创建 / 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->setFloatable(false);// 设置浮动 toolBar->setMovable(false); // 允许移动(总开关) toolBar->addAction(newAction); toolBar->addSeparator(); toolBar->addAction(openAction); QPushButton *btn = new QPushButton("aaa",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::BottomDockWidgetArea | Qt::TopDockWidgetArea); // 设置停靠区域,只允许上下 / 设置中心部件,只能有一个 / QTextEdit *edit = new QTextEdit(this); setCentralWidget(edit); /*总结: 使用set加入窗口的部件智能有一个,而add加入的能有多个*/ } MainWindow::~MainWindow() { 
    } 

3.6 资源文件添加和UI界面使用

  在新建空白项目的第三步点击generate form,生成项目后就会产生一个.ui文件。UI界面可以直接拖拽控件,输入文本,我们开发窗口应用就变得很方便。
在这里插入图片描述

  在此界面的基础上,创建文件,编辑,工具,帮助等菜单,菜单的一级目录是无法键入中文的,只能输入英文,然后在创建好的对象中将文本改成中文,建立完成后的文件如下。点击项目添加文件,add new file -> Qt -> Qt resource file -> choose,然后更改文件名称,一般设置为res,然后会在Resources底下生成一个res.qrc的文件。

在这里插入图片描述

  将图片复制到项目目录底下的Image文件(所有图片文件都放进去),以资源编辑器的方式打开res.qrc,添加前缀(可以直接使用默认或者“/”),添加文件,使用“ : + 前缀名 + 文件名称” 。mainwindow.cpp文件如下所示:

// mainwindow.cpp文件 #include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { 
    ui->setupUi(this); // ui->actionnew->setIcon(QIcon("C:/Users/19080/Pictures/Camera Roll/文件图标.JPEG") ); // 绝对路径 // 使用添加Qt资源文件 ui->actionnew->setIcon(QIcon(":Image\\fileIcon.JPEG") ); ui->actionopen->setIcon(QIcon(":Image\\Luffy.jpg") ); } MainWindow::~MainWindow() { 
    delete ui; } 

3.7 对话框

3.7.1 模态和非模态

  mainwindow.cpp文件如下所示:

 #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 dig(this); // dig.resize(200,100); // dig.exec(); // qDebug() <<"模态对话框弹出"; /* 非模态创建 */ QDialog *dig2 = new QDialog(this); dig2->resize(200,100); dig2->show(); dig2->setAttribute(Qt::WA_DeleteOnClose); // 55号属性 qDebug() << "非模态对话框弹出"; }); } MainWindow::~MainWindow() { 
    delete ui; } 

3.7.2 消息对话框

  目前Qt内置对话框有:

名称 作用
QColorDialog 选择颜色
QFileDialog 选择文件或者目录
QFontDialog 选择字体
QInputDialog 允许用户输入一个值,并将值返回
QMessageBox 模态对话框,用于显示信息、询问信息等等
QPageSetupDialog 为打印机提供纸张相关的选项
QPrintDialog 打印机配置
QPrintPreviewDialog 打印预览
QProgressDialog 显示操作过程

  mainwindow.cpp文件如下所示,其中QMessageBox::question的返回值是QMessageBox::StandardButton类型,我们就可以利用if语句去判断返回值是否为QMessageBox::Save,从而进一步做其他操作。

#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDialog> #include <QMessageBox> #include <QDebug> #include <QColorDialog> #include <QFileDialog> #include <QFontDialog> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { 
    ui->setupUi(this); connect(ui->actionnew,&QAction::triggered,[=](){ 
    // 错误对话框 // QMessageBox::critical(this, "critical", "错误"); // 信息对话框 // QMessageBox::information(this, "info", "信息"); // 提问对话框 参数1:父类,参数2:title, 参数3:提示信息, 参数4:按键选项, 参数5: 默认选项(关联回车选项) // if(QMessageBox::Save == QMessageBox::question(this, "ques", "提问",QMessageBox::Save|QMessageBox::Cancel,QMessageBox::Save)) // { 
    // qDebug() << "选择的是保存"; // } // else // { 
    // qDebug() << "选择的是取消"; // } // 警告对话框 //QMessageBox::warning(this,"warning","警告"); // 其他标准对话框 // 颜色对话框 // QColor color = QColorDialog::getColor(QColor(255, 0, 0)); // qDebug() << " r = " << color.red() << " g = "<< color.green() << " b = " << color.blue(); // 文件对话框 参数1: 父类, 参数2: 对话框标题 参数3:默认打开路径,参数4:过滤器(仅能选取该类型文件) 返回值是选取文件路径 // QString str= QFileDialog::getOpenFileName(this, "打开文件", "C:\\Users\\19080\\Desktop", "(*.txt)"); // qDebug() << str; // 字体对话框 bool flag; QFont font = QFontDialog::getFont(&flag, QFont("华文云彩", 36) ); qDebug() <<" 字体: "<< font.family() <<" 大小:"<< font.pointSize()<< "是否加粗:"<< font.bold() << "是否倾斜:"<< font.italic(); }); } MainWindow::~MainWindow() { 
    delete ui; } 

3.8 设计登录界面

  首先我们现在UI界面创建如下控件,用户名和密码用Label控件,输入框用Line Edit控件,登录和退出用PushButton控件。

在这里插入图片描述

  然后在左侧工具栏Containers中选择Widget控件,将用户名、密码和输入框拖入Widget中选择,在上方工具栏中选择栅格布局(适用于多行多列的,如果是单行或单列可以选择水平布局或垂直布局),布局之后就变得更整齐。登录和退出就选择水平布局。为了在窗口缩放是保持各个空间的相对位置不变,可以加入Spacers控件(也可以不加),其效果于弹簧。

在这里插入图片描述

  登录界面一般开发时就确定大小,我们找到MainWindow->sizePolicy->水平和垂直策略都选择Fixed,然后将minnumSize和maxiumSize都选择固定的尺寸(具体数值任意,大小合适即可)。操作完毕后窗口大小就固定下来。
  最后修改窗口名称,选中密码对应的编辑框,QLineEdit->echoMode->Password(输入密码的编程一个个黑圈圈),到目前为止我们将登录窗口的UI界面设计完毕,但是具体的功能还需要底层代码才能实现。
在这里插入图片描述

3.9 各类控件

3.9.1 按钮组

  Qt的UI设计界面的按钮组有:PushButton,ToolButton, Radio Button, Check Box等等。
  ToolButton建立后,可以添加图片和修改文本,选择Icon->选择资源文件(前面部分有介绍),然后选择QToolButton->autoRaise,其效果是当光标移动到该按钮时,按钮自动高光亮起。
  依次创建4个Radio Button,分别命名为男 女, 已婚, 未婚。然后添加Group Box, 将男女添加今一个Group Box, 修改文本为性别。同理,已婚和未婚这两个Radio Button为另外一组。
  创建4个Check Box依次修改文本,放入Group Box中,设置垂直布局。然后在添加一个ListWidget,在预览图中显示为白色框。
在这里插入图片描述

  在mainwindow.cpp中输入如下代码,形成代码和界面的联动:

#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { 
    ui->setupUi(this); // 设置单选按钮 默认男选中 ui->radioButton->setChecked(true); // 选中女后打印信息 connect(ui->radioButton_2, &QRadioButton::clicked,[=](){ 
    qDebug() << "选中了女性按钮"; }); // 多选按钮 2:选中 1:半选中 0:未选中 // 信号为stateChanged, 槽函数为lambda表达式, 信号的参数会自动传给槽函数 connect(ui->checkBox_4, &QCheckBox::stateChanged,[=](int state){ 
    qDebug() << state; }); // 利用listWidget写诗 QListWidgetItem *item = new QListWidgetItem("窗前明月光"); // 将一行诗放到listWidget控件中 ui->listWidget->addItem(item); item->setTextAlignment(Qt::AlignHCenter); // 设置为水平居中 // QStringList QList<QString> QStringList list; list << "疑是地上霜"<< "举头望明月"<<"低头思故乡"; // 将这几句诗加入链表类中 ui->listWidget->addItems(list); // 这种方法无法设置对齐格式 } MainWindow::~MainWindow() { 
    delete ui; } 

3.9.2 QTreeWidget和QTableWidget控件

  QTreeWidget控件代码如下:

#include "widget.h" #include "ui_widget.h" #include <QDebug> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { 
    ui->setupUi(this); // treeWidget树控件使用 // 设置水平头 // 从下面两行代码可以看出,str相当于一个string类型的列表(例如,vector<string>) <<操作符相当于 append()函数的作用 // QStringList str = QStringList()<<"英雄"<<"英雄介绍"; // qDebug() << str; ui->treeWidget->setHeaderLabels (QStringList()<<"卡牌类型"<<"卡牌介绍"); QTreeWidgetItem *monster = new QTreeWidgetItem(QStringList()<< " 怪兽"); QTreeWidgetItem *magic = new QTreeWidgetItem(QStringList()<< " 魔法"); QTreeWidgetItem *trap = new QTreeWidgetItem(QStringList()<< " 陷阱"); // 加载顶层节点 ui->treeWidget->addTopLevelItem(monster); ui->treeWidget->addTopLevelItem(magic); ui->treeWidget->addTopLevelItem(trap); // 加载子节点 QStringList m1 = QStringList()<< "增殖的G"<< "效果怪兽,每次对方对怪兽的特殊召唤成功,自己从卡组抽1张"; QTreeWidgetItem *monster1 = new QTreeWidgetItem(m1); monster->addChild(monster1); QStringList m2 = QStringList()<< "效果遮蒙者"<< "效果怪兽,以对方场上1只效果怪兽为对象,其效果直到回合结束时无效。"; QTreeWidgetItem *monster2 = new QTreeWidgetItem(m2); monster->addChild(monster2); QStringList ma1 = QStringList()<< "强欲之壶"<< "通常魔法,从卡组抽两张牌"; QTreeWidgetItem *magic1 = new QTreeWidgetItem(ma1); magic->addChild(magic1); QStringList ma2 = QStringList()<< "天使的施舍"<< "通常魔法,从卡组抽三张,然后丢弃两张手牌"; QTreeWidgetItem *magic2 = new QTreeWidgetItem(ma2); magic->addChild(magic2); QStringList t1 = QStringList()<< "技能抽取"<< "永续陷阱,能够使场上表侧表示的怪兽卡效果无效"; QTreeWidgetItem *trap1 = new QTreeWidgetItem(t1); trap->addChild(trap1); QStringList t2 = QStringList()<< "王宫的敕命"<< "永续陷阱,能够使场上的魔法卡效果无效"; QTreeWidgetItem *trap2 = new QTreeWidgetItem(t2); trap->addChild(trap2); } Widget::~Widget() { 
    delete ui; } 

  效果图:

在这里插入图片描述

  QTableWidget控件

#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { 
    ui->setupUi(this); // TableWidget控件 // 设置列数,一定要设置,不然会出现未知错误 ui->tableWidget->setColumnCount(3); // 设置水平表头 ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"姓名"<<"性别"<<"年龄"); // 设置行数 ui->tableWidget->setRowCount(5); // 设置正文 //ui->tableWidget->setItem(0,0,new QTableWidgetItem("张三")); QStringList nameList = QStringList()<<"李大"<<"柳二"<<"张三"<<"刘四"<<"王五"; QStringList sexList = QStringList()<<"男"<<"男"<<"女"<<"女"<<"女"; 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))); } } Widget::~Widget() { 
    delete ui; } 

  效果图:

在这里插入图片描述

3.9.3 其他控件

  主要包括了scroll area、 tool box、tab widget、stacked widget等控件。其中scroll area 是滚动条控件,toolbox是列表页面(例如的联系人列表),tab widget是类似网页页面的控件。stacked widget是栈控件,可以将以上三个页面全部放到栈控件中,然后实现多个页面的切换。代码中还包括了combo box下拉框,QLabel的简单使用。其中,QLabel可以用作显示图片,播放动态图,视频等等。

#include "widget.h" #include "ui_widget.h" #include <QPushButton> #include <QMovie> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { 
    ui->setupUi(this); // stacked Widget栈控件的使用 // 默认定位 ui->stackedWidget->setCurrentIndex(0); // scroll area 按钮 connect(ui->btn_scroll,&QPushButton::clicked, [=](){ 
    ui->stackedWidget->setCurrentIndex(0); }); // toolBox 按钮 connect(ui->btn_toolBox,&QPushButton::clicked, [=](){ 
    ui->stackedWidget->setCurrentIndex(1); }); // tabWidge 按钮 connect(ui->btn_tabWidget,&QPushButton::clicked, [=](){ 
    ui->stackedWidget->setCurrentIndex(2); }); // combo box 下拉框 ui->comboBox->addItem("奔驰"); ui->comboBox->addItem("宝马"); ui->comboBox->addItem("拖拉机"); // 点击按钮选中拖拉机 connect(ui->pushButton_16,&QPushButton::clicked, [=](){ 
    // ui->comboBox->setCurrentIndex(2); ui->comboBox->setCurrentText("拖拉机"); // 两句代码效果一样 }); // 利用QLabel显示图片 ui->imaLabel->setPixmap(QPixmap(":/Image/fileIcon.JPEG").scaled(ui->imaLabel->size())); //利用QLabel显示gif动态图片 QMovie *movie = new QMovie(":/Image/picaqu.gif"); movie->setScaledSize(ui->movieLabel->size()); ui->movieLabel->setMovie(movie); // 播放动图 movie->start(); } Widget::~Widget() { 
    delete ui; } 

  效果图:
在这里插入图片描述

3.9.4 自定义控件封装

  建立一个自定义的控件,将QSpinBox和QSlider联动起来:QSpinBox移动,QSlider跟着移动,QSlider跟着移动,QSpinBox也跟着移动。

  项目文件点击->新建->Qt->设计师界面->widget,然后修改文件名(SmallWidget)。在smallwidget.up界面中添加,QSpinBox和QSlider两个控件:
在这里插入图片描述
  右键SmallWidget窗口->提升为->添加->提升,成功以后,widget类底下就会包含SmallWidget类。
  修改smallwidget.cpp文件:

// smallwidget.cpp文件 #include "smallwidget.h" #include "ui_smallwidget.h" SmallWidget::SmallWidget(QWidget *parent) : QWidget(parent), ui(new Ui::SmallWidget) { 
    ui->setupUi(this); // QSpinBox移动,QSlider跟着移动 void (QSpinBox:: *spSignal)(int) = &QSpinBox::valueChanged; // valueChanged有重载版本,因此需要确定输入参数是哪种版本的,这里需要的是int输入 connect(ui->spinBox, spSignal, ui->horizontalSlider, &QSlider::setValue); // QSlider跟着移动 QSpinBox移动 connect(ui->horizontalSlider, &QSlider::valueChanged, ui->spinBox, &QSpinBox::setValue); } SmallWidget::~SmallWidget() { 
    delete ui; } 

3.10 鼠标和定时器事件以及事件分发器、过滤器

  添加myLabel类->C+±>C++Class,父类选择为QWidget,因为新建项目窗口能够选择父类有限,这里我们就选择QWidget类,项目文件建好后,修改mylabel.h文件中include头文件和父类,mylabel.cpp中构造函数的父类,全部修改为QLabel。

  进入UI界面,拖拽一个Label控件,修改为合适大小,文字删除,此时控件消失,为了方便观察,我们设置控件的边框为Box类型,属性页面QFrame->frameShape->Box,如下图所示。

在这里插入图片描述

  UI界面的设置创建两个label控件,用来显示定时器数字。定时器主要使用到timerEvent(QTimerEvent *ev)函数,多个定时器之间用timeId来区分。

在这里插入图片描述

  多个事件之间通过bool event(QEvent *ev)来进行事件分发,返回值是bool类型,如果返回值是true代表用户要处理这个事件,不向下分发事件。

在这里插入图片描述

// mylabel.h文件 #ifndef MYLABEL_H #define MYLABEL_H #include <QLabel> class myLabel : public QLabel { 
    public: explicit myLabel(QWidget *parent = nullptr); // 声明 // 鼠标进入事件 void enterEvent(QEvent *event); // 鼠标离开事件 void leaveEvent(QEvent *); // 鼠标按下事件 void mousePressEvent(QMouseEvent *event); // 鼠标释放事件 void mouseReleaseEvent(QMouseEvent *event); // 鼠标移动事件 void mouseMoveEvent(QMouseEvent *event); // 通过event事件分发器拦截 鼠标按下事件 bool event(QEvent *e); signals: }; #endif // MYLABEL_H 
// widget.h文件 #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; // 定时器1的唯一标识 int id2; // 定时器2的唯一标识 // 重写事件过滤器的时间 bool eventFilter(QObject *obj, QEvent *e); private: Ui::Widget *ui; }; #endif // WIDGET_H 
// mylabe.cpp文件 #include "mylabel.h" #include <QDebug> #include <QMouseEvent> myLabel::myLabel(QWidget *parent) : QLabel(parent) { 
    // 设置鼠标追踪 setMouseTracking (true); // 原来是点击后鼠标移动才能触发,现在只要鼠标移动就能触发鼠标移动事件。 } // 鼠标进入事件 void myLabel::enterEvent(QEvent *event) { 
    qDebug() << "鼠标进入"; } // 鼠标离开事件 void myLabel::leaveEvent(QEvent *) { 
    qDebug()<< "鼠标离开"; } // 鼠标按下事件 void myLabel::mousePressEvent(QMouseEvent *event) { 
    // 要求:当鼠标左键按下时,打印信息,右键按下不打印 if(event->button() == Qt::LeftButton) { 
    QString str = QString("鼠标按下了 x = %1, y = %2, globalx = %3, globaly = %4").arg(event->x()).arg (event->y()).arg (event->globalX()).arg (event->globalY()); qDebug()<< str; } } // 鼠标释放事件 void myLabel::mouseReleaseEvent(QMouseEvent *event) { 
    if(event->button() == Qt::LeftButton) { 
    qDebug()<< "鼠标释放"; } } // 鼠标移动事件 void myLabel::mouseMoveEvent(QMouseEvent *event) { 
    qDebug()<< "鼠标移动"; // if(event->buttons() & Qt::LeftButton) // &位与操作,buttons用于同时按下多个按钮,只要按下的按钮中包含左键,执行下面的操作。 // { 
    // qDebug()<< "鼠标移动"; // } } // 通过event事件分发器拦截 鼠标按下事件 bool myLabel::event(QEvent *e) { 
    // 如果是鼠标按下,在event中做拦截操作,也就是说在这一层做处理,后面的 鼠标按下 相关代码就不会触发 if(e->type() == QEvent::MouseButtonPress) { 
    QMouseEvent *ev = static_cast<QMouseEvent *>(e); // static_cast是C++的强制类型转换,大精度类型转小精度类型,有损 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; // true代表用户自己处理这个事件,不向下分发 } // 其他事件交给父类处理, 其余事件都正常传给后面的代码处理 return QLabel::event(e); } 
// widget.cpp文件 #include "widget.h" #include "ui_widget.h" #include <QTimer> #include <QMouseEvent> #include <QDebug> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { 
    ui->setupUi(this); id1 = startTimer(1000); // 启动计时器,参数1:间隔,单位毫秒 id2 = startTimer(2000); // 定时器的第二种方式 QTimer *timer = new QTimer(this); // 启动定时器 timer->start(500); connect(timer, &QTimer::timeout, [=](){ 
    static int num3 = 1; // label4 每隔0.5秒+1 ui->label_4->setText(QString::number(num3++)); }); // 点击按钮暂停定时器,第三个暂停 connect(ui->btn1, &QPushButton::clicked, [=](){ 
    timer->stop(); }); // 给label 安装事件过滤器, 实际上是一个比event拦截器更高级的拦截器 // 第一步 ui->label->installEventFilter(this); } // 第二步 重写 eventfiler事件 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; // true代表用户自己处理这个事件,不向下分发 } } // 其他事件交给父类处理, 其余事件都正常传给后面的代码处理 return QWidget::eventFilter(obj, e); } // 定时器事件 void Widget::timerEvent(QTimerEvent *ev) { 
    if(ev->timerId() == id1) { 
    static int num1 = 1; ui->label_2->setText(QString::number(num1++)); } if(ev->timerId() == id2) { 
    static int num2 = 1; ui->label_3->setText(QString::number(num2++)); } } Widget::~Widget() { 
    delete ui; } 

3.11 绘画

3.11.1 绘画设置

  这里主要介绍的是画类操作,画直线,画圆,画矩形等等,画笔,毛刷等等设置,widget.cpp文件如下,此外,还需要在widget.h文件声明函数void paintEvent(QPaintEvent *event)。

// widget.cpp文件 #include "widget.h" #include "ui_widget.h" #include <QPainter> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { 
    ui->setupUi(this); // 点击移动按钮,移动图片 ,paintEvent函数默认会调用一次,然后就是调用repaint函数,即重新绘画函数 connect (ui->pushButton, &QPushButton::clicked, [=](){ 
    // 手动调用绘图事件函数,实际上是调用repaint函数 PosX += 20; // 需要在widget.h中声明, int PosX = 150; update(); }); } // 绘图事件 void Widget::paintEvent(QPaintEvent *event) { 
    // // 实例化画家对象, this制定绘图设备 // QPainter painter(this); // // 设置画笔 // QPen pen(QColor(255,0,0)); // 设置颜色 // pen.setWidth (5); // 设置宽度 // pen.setStyle (Qt::DotLine); // 设置风格 // painter.setPen (pen); // // 设置画刷 // QBrush brush(QColor(0,255,0)); // brush.setStyle (Qt::Dense1Pattern); // painter.setBrush (brush); // // 画了一条线(两个点确定) // painter.drawLine(QPoint(0,0),QPoint(100,100)); // // 画椭圆圆,圆心和长短轴焦点a,b确定 a=b就是圆 // painter.drawEllipse (QPoint(100,100),50,50); // // 画矩形 // painter.drawRect (QRect(20,20,50,50)); // // 画文字 // painter.drawText (QRect(10,200,150,50),"好好学习,天天向上"); 高级设置*/ // QPainter painter(this); // painter.drawEllipse (QPoint(200,200),100,100); // // 设置 抗锯齿能力,即画的仔细一点,毛边少一点,但是效率低一点 // painter.setRenderHint (QPainter::Antialiasing); // painter.drawEllipse (QPoint(400,200),100,100); // // 画矩形 // painter.drawRect (QRect(20,20,50,50)); // painter.translate (100,0); // 画家从0,0开始作画,变成从100,0开始作画 // // 保存画家状态 // painter.save(); // painter.drawRect (QRect(20,20,50,50)); // // 还原画家保存状态 // painter.restore (); // painter.drawRect (QRect(20,20,50,50)); / * 利用画家调用图片资源 // QPainter painter(this); // 如果超过屏幕宽度 ,从0开始 if(PosX > this->width()) { 
    PosX = 0; } painter.drawPixmap(PosX,20,QPixmap(":/Image/Luffy.jpg")); } Widget::~Widget() { 
    delete ui; } 

在这里插入图片描述

3.11.2 绘图设备

  主要有QPixmap,QPicture,QImage三种,需要在widget.h文件声明函数void paintEvent(QPaintEvent *event)

#include "widget.h" #include "ui_widget.h" #include <QPixmap> #include <QPainter> #include <QPicture> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { 
    ui->setupUi(this); // // // // Pixmap绘图设备,专门为平台做了显示的优化 // QPixmap pix(300,300); // 说明画纸大小 // // 填充背景色 // pix.fill(Qt::white); // // 声明画家 // QPainter painter(&pix); // painter.setPen(QPen(Qt::green)); // painter.drawEllipse(QPoint(150,150),100,100); // // 保存 // pix.save("D:\\software\\QT\\QT_Project\\qtDemo10\\pix.png"); // /// // // QImage 绘图设备 可以对每个像素进行访问 // QImage img(300,300, QImage::Format_RGB32); // img.fill(Qt::white); // QPainter painter(&img); // painter.setPen(QPen(Qt::blue)); // painter.drawEllipse(QPoint(150,150),100,100); // // 保存 // img.save("D:\\software\\QT\\QT_Project\\qtDemo10\\img.png");  // QPicture 绘图设备,可以记录和重现绘图指令 QPainter painter; QPicture pic; painter.begin(&pic); // 开始往pic上画画 painter.setPen(QPen(Qt::blue)); painter.drawEllipse (QPoint(150,150),100,100); painter.end(); // 结束画画 // 保存到磁盘 pic.save("D:\\software\\QT\\QT_Project\\qtDemo10\\pic.hyf"); // hyf是博主的姓名缩写,在文件资源管理器中是无法打开这个图片的 // 我们在绘图事件中使用load函数可以打开,准确来说是重新绘制,因此pic保存的不是图片本身而是绘制图片的指令 } // 绘图事件 void Widget::paintEvent(QPaintEvent *event) { 
    // // 利用QImage 对像素进行修改 // QPainter painter(this); // QImage img; // img.load(":/Image/fileIcon.JPEG"); // // 修改像素点 // for(int i=50; i<100; i++) // { 
    // for (int j=50;j<100;j++) // { 
    // QRgb value = qRgb(255,0,0); // img.setPixel(i,j,value); // } // } // painter.drawImage(0,0,img); // 重现QPicture绘图指令 QPainter painter(this); QPicture pic; pic.load("D:\\software\\QT\\QT_Project\\qtDemo10\\pic.hyf"); painter.drawPicture(0,0,pic); } Widget::~Widget() { 
    delete ui; } 

3.12 文件读取

  文件读取是主要注意打开文件,也要关闭文件,此外,QFile默认是UTF-8格式类型。widget.cpp文件如下所示:

#include "widget.h" #include "ui_widget.h" #include <QFileDialog> #include <QFile> #include <QTextCodec> #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/19080/Desktop"); // 将路径放到lineEdit中 ui->lineEdit->setText(path); // 编码格式类 QTextCodec *codec = QTextCodec::codecForName("gbk"); // 读取内容,放入到textEdit中 // QFile默认支持的格式是UTF-8 QFile file(path); // 参数就是file path file.open(QIODevice::ReadOnly); // 设置打开方式 //QByteArray array = file.readAll(); QByteArray array; while( !file.atEnd() ) { 
    array += file.readLine(); } // 将读取的数据放入到text Edit中 ui->textEdit->setText(array); //ui->textEdit->setText(codec->toUnicode(array)); file.close(); // 对文件对象进行关闭  文件写入操作 file.open(QIODevice::Append); // 追加的方式写入 file.write(""); file.close();  QFileInfo /// // QFileInfo 文件信息类 QFileInfo info(path); qDebug() << "大小(字节):" << info.size()<< "后缀名:"<< info.suffix()<< "文件名称:"<< info.fileName() <<"文件路径:"<< info.filePath(); qDebug() << "文件创建日期: "<< info.created().toString ("yyyy/MM/dd hh:mm:ss"); // 按格式输出 yyyy/MM/dd hh:mm:ss qDebug() << "文件创建日期: "<< info.lastModified().toString ("yyyy/MM/dd hh:mm:ss"); }); } Widget::~Widget() { 
    delete ui; } 

四、翻金币小游戏

4.1 出现的问题

error: 'QtPrivate::QFunctorSlotObject<Func, N, Args, R>::QFunctorSlotObject(Func) [with Func = MainScene::MainScene(QWidget*)::<lambda()>; int N = 0; Args = QtPrivate::List<>; R = void]', declared using local type 'MainScene::MainScene(QWidget*)::<lambda()>', is used but never defined [-fpermissive] explicit QFunctorSlotObject(Func f) : QSlotObjectBase(&impl), function(std::move(f)) { 
   } ^~~~~~~~~~~~~~~~~~ 
 error: static assertion failed: No Q_OBJECT in the class with the signal # define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message) ^ 
error: undefined reference to `vtable for 

4.2 源码下载

  这里直接给大家分享一下成品,用百度网盘给出,步骤博主就不在一一介绍。
链接:https://pan.baidu.com/s/1QGdOGuyTYMGfSgi81O71vw?pwd=zajg
提取码:zajg

4.3 NSIS打包程序

  当我们写好程序后将编译运行按钮中的debug输出改为release输出,然后得到一个发布版本的.exe程序,如图所示:

在这里插入图片描述

  我们将他单独拎出来放到桌面的release文件夹(自己命名的空文件夹都可以)中,然后找到对应编译器的命令行窗口,如下图所示,博主这里有两个编译器,博主的是MinGW 64-bit的。然后输入windeployqt.exe CoinFlip.exe,按回车键,程序打包成功,之前的release文件就多了一些生成的打包文件。

windeployqt.exe CoinFlip.exe 

在这里插入图片描述
在这里插入图片描述

  windeployqt.exe文件实际上是Qt编译器提供的打包成window程序的可执行文件,可以在对应编译器的bin文件中找到。

在这里插入图片描述

  这里需要注意不能使用普通的命令行窗口执行这个命令,会出现无法找到入口的问题。

在这里插入图片描述

  然后我们使用HM NIS Edit软件进行Setup.exe文件的打包,需要配合NSIS软件一起使用。
  NSIS是”Nullsoft 脚本安装系统”(Nullsoft scriptable Installation System)的缩写,它是是一个免费的win32安装、卸载系统,可以很方便的打包windows应用程序。它的特点:脚本简洁高效;系统开销少;支持安装、卸载、系统设置、解压文件等功能。这里博主直接给出NSIS下载地址和HM NIS Edit下载地址,嫌麻烦的也可以用博主的百度网盘地址下载,链接:https://pan.baidu.com/s/1FrLENkVtB-B2lGslqw33bw?pwd=wybc
提取码:wybc。
  参考博客 手把手教NIS Edit安装向导的使用。当顺着教程做到这一步的时候,点击树形图,选择release文件,将release文件中的所有文件添加进来。其他部分按照教程或者默认即可。最终会在项目目录中生成Setup.exe文件。将Setup.exe安装之后,就会在桌面生成快捷方式,点击即可进行游戏。
在这里插入图片描述

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/160575.html

(0)
上一篇 2024-12-04 21:00
下一篇 2024-12-04 21:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信