当前位置:   article > 正文

【Qt设计开发】GUI界面设计开发_qt界面开发

qt界面开发


  本文是我在学习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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
// 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
// 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();	// 让应用程序对象进入消息循环
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
// mywidget.cpp文件
#include "mywidget.h"
mywidget::mywidget(QWidget *parent)
    : QWidget(parent)
{
}
mywidget::~mywidget()
{
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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
 * 			第二种:独立的帮助文档程序查看
*/

/*
类名: 首字母大写,单词和单词之间首字母大写
函数名和变量名称: 首字母小写,单词和单词之间首字母大写
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

在这里插入图片描述

  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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

请添加图片描述

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

三、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()
{
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
// 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
// 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
// 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("宫保鸡丁");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
// 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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

  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;
    }
  • 1
  • 2
  • 3
  • 4

  在空项目的基础之上改变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()
{
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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()
{
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

  效果图:

在这里插入图片描述

  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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

  效果图:

在这里插入图片描述

3.9.3 其他控件

  主要包括了scroll area、 tool box、tab widget、stacked widget等控件。其中scroll area 是滚动条控件,toolbox是列表页面(例如QQ的联系人列表),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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

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

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
// 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
// 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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
// 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

在这里插入图片描述

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

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("123456789");
        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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

四、翻金币小游戏

4.1 出现的问题

  如果没有素材可以从网上找,图片资源都可以根据实际变化,不是唯一的。资源网站这里推荐阿里的图标库
  博主在写这个代码的过程中出现了一些错误,第一个错误博主没有解决,将第二个错误解决后,第一个就没有出现了。第二个和第三个错误在头文件前面加上Q_OBJECT成员变量,加在public前面,问题解决参考自博客No Q_OBJECT in the class with the signal错误解决办法,然后在.pro文件末尾加上空格重新编译。

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)) {}
                  ^~~~~~~~~~~~~~~~~~
  • 1
  • 2
  • 3
 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)
                                                ^
  • 1
  • 2
  • 3
error: undefined reference to `vtable for
  • 1

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
  • 1

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

  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安装之后,就会在桌面生成快捷方式,点击即可进行游戏。
在这里插入图片描述

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号