QT学习笔记:

QT学习笔记:

相关介绍:
1	Qt简介
1.1	跨平台图形界面引擎
1.2	历史
1.2.1	1991  奇趣科技
1.3	优点
1.3.1	跨平台
1.3.2	接口简单,容易上手
1.3.3	一定程度上简化了内存回收
1.4	版本
1.4.1	商业版
1.4.2	开源版
1.5	成功案例
1.5.1	Linux桌面环境 KDE
1.5.2	谷歌地图
1.5.3	VLC多媒体播放器
1.5.4	
2	创建第一个Qt程序
2.1	点击创建项目后,选择项目路径以及 给项目起名称
2.2	名称 -  不能有中文 不能有空格
2.3	路径 -  不能有中文路径
2.4	默认创建有窗口类,myWidget,基类有三种选择: QWidget 、QMainWindow、QDialog
	QWidget类是所有用户界面对象的基类。
	QDialog类是对话框窗口的基类。
	QMainWindow类提供一个有菜单条、工具栏、状态条的主应用程序窗口(例如:开发Qt常用的IDE-Visual Studio、Qt Creator等)。
2.5	main函数

main函数:

#include "mywidget.h"
#include <QApplication>// 包含一个应用程序类的头文件


//main程序入口  argc命令行变量的数量  argv命令行变量的数组
int main(int argc, char *argv[])
{
    //应用程序对象,在Qt中,应用程序对象 有且仅有一个
    QApplication a(argc, argv);
    //窗口对象  myWidget父类  -> QWidget
    myWidget w;
    //窗口对象 默认不会显示,必须要调用show方法显示窗口
    w.show();

    //让应用程序对象进入消息循环
    //当代码阻塞到这行,就是只要不点击x,一直捕获鼠标或点击事件
    return a.exec();


//    while(true)
//    {
//         if(点击叉子)
//        {
//            break;
//        }
//    }

}

.pro文件解释:

QT       += core gui   Qt包含的模块

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets  //大于4版本以上 包含 widget模块

TARGET = 01_FirstProject  //目标   生成的.exe程序的名称
TEMPLATE = app       	  //模板   应用程序模板  Application  


SOURCES += main.cpp\      //源文件 会随着项目内容增加而增加,下同
        mywidget.cpp

HEADERS  += mywidget.h    //头文件
	    

Qt5模块:

Qt5 模块

mywidget.cpp:

#include "mywidget.h"
#include <QPushButton> //按钮控件的头文件
#include "mypushbutton.h"
#include <QDebug>
// 命名规范
// 类名 首字母大写,单词和单词之间首字母大写
// 函数名 变量名称 首字母小写,单词和单词之间首字母大写

//快捷键
//注释  ctrl + /
//运行  ctrl + r
//编译  ctrl + b
//字体缩放 ctrl + 鼠标滚轮
//查找  ctrl + f
//整行移动 ctrl + shift + ↑ 或者↓
//帮助文档 F1
//自动对齐  ctrl + i;
//同名之间的.h 和 .cpp切换  F4

//帮助文档 第一种方式 F1  第二种 左侧按钮  第三种 C:\Qt\Qt5.6.0\5.6\mingw49_32\bin

myWidget::myWidget(QWidget *parent)
    : QWidget(parent)//初始化列表
{
}

myWidget::~myWidget()
{
}

mywidget.h:

#ifndef MYWIDGET_H
#define MYWIDGET_H


#include <QWidget> //包含头文件 QWidget 窗口类

class myWidget : public QWidget
{
    Q_OBJECT // Q_OBJECT宏,允许类中使用信号和槽的机制

public:
    myWidget(QWidget *parent = 0); //构造函数
    ~myWidget(); //析构函数
};

#endif // MYWIDGET_H
第一个Qt小程序:

QPushButton创建按钮:

widget.cpp:

#include "widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //创建一个按钮
    QPushButton *btn = new QPushButton;
    btn->show();//以顶层方式弹出窗口控件
    //让btn对象依赖在mywidget窗口中
    btn->setParent(this);//设置到父类对象树中
    //显示文本
    btn->setText("第一个按钮");
    //创建第二个按钮  按照空间大小创建按钮
    QPushButton * btn2 = new QPushButton("第二个按钮",this);

    //移动btn2按钮
    btn2->move(100,100);
    //重置窗口大小
    //resize(600,400);
    //设置固定窗口大小
    setFixedSize(600,600);
    //设置窗口标题
    setWindowTitle("第一个窗口");

}

Widget::~Widget()
{
}

对象树:

在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。
	QObject是以对象树的形式组织起来的。
	当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。
这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。
	当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)
这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。
	QWidget是能够在屏幕上显示的一切组件的父类。
	QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
	当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。
Qt 引入对象树的概念,在一定程度上解决了内存问题。
	当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
	任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。
我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。

Qt对象树

Qt中的坐标系:

以左上角为原点(0,0),X向右增加,Y向下增加。

img

对于嵌套窗口,其坐标是相对于父窗口来说的。
信号和槽机制:

系统自带信号与槽:

点击按钮关闭窗口:

#include "widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //创建第二个按钮  按照空间大小创建按钮
    QPushButton * btn2 = new QPushButton("第二个按钮",this);

    //需求 点击我的按钮 关闭窗口
    //需求1 信号的发送者 参数2 发送的信号 (函数的地址) 参数3 信号的接受者 参数4 处理的槽函数
    connect(btn2,&QPushButton::clicked,this,&Widget::close);

}

Widget::~Widget()
{
}

解析图:

信号和槽解析图

自定义信号和槽:

案例:下课后,老师触发饿了信号,学生响应信号,请客吃饭

1.创建一个老师类:

teacher.h

#ifndef TEACHER_H
#define TEACHER_H

#include <QObject>

class Teacher : public QObject
{
    Q_OBJECT
public:
    explicit Teacher(QObject *parent = nullptr);

signals:
    //自定义信号 写到signal下
    //返回值是void,只需要声明,不需要实现
    //可以有参数,可以重载
    void hungary();
};

#endif // TEACHER_H

teacher.cpp

#include "teacher.h"

Teacher::Teacher(QObject *parent) : QObject(parent)
{

}

2.创建一个学生类:

student.h

#ifndef STUDENT_H
#define STUDENT_H

#include <QObject>

class Student : public QObject
{
    Q_OBJECT
public:
    explicit Student(QObject *parent = nullptr);

    //早期Qt版本 必须写到public slots,高级版本可以写到 public 或者全局下
    //返回值 void,需要声明,也需要实现
    //可以有参数,可以发生重载
    void treat();
signals:

};

#endif // STUDENT_H

student.cpp

#include "student.h"
#include <QDebug.h>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::treat(){
    qDebug()<<"请老师吃饭";
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建一个老师对象
    this->zt = new Teacher(this);

    //创建一个学生对象
    this->st = new Student(this);
    //老师饿了 学生请客的连接
    connect(zt,&Teacher::hungary,st,&Student::treat);

    classIsOver();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::classIsOver(){
    //下课函数,调用后 触发老师饿了的信号
    emit zt->hungary();
};

自定义信号和槽出现重载:

teacher.h:

#ifndef TEACHER_H
#define TEACHER_H

#include <QObject>

class Teacher : public QObject
{
    Q_OBJECT
public:
    explicit Teacher(QObject *parent = nullptr);

signals:
    //自定义信号 写到signal下
    //返回值是void,只需要声明,不需要实现
    //可以有参数,可以重载
    void hungary();

    void hungry(QString foodName);
};

#endif // TEACHER_H

student.h

#ifndef STUDENT_H
#define STUDENT_H

#include <QObject>

class Student : public QObject
{
    Q_OBJECT
public:
    explicit Student(QObject *parent = nullptr);

    //早期Qt版本 必须写到public slots,高级版本可以写到 public 或者全局下
    //返回值 void,需要声明,也需要实现
    //可以有参数,可以发生重载
    void treat();

    void treat(QString foodName);
signals:

};

#endif // STUDENT_H

student.cpp

#include "student.h"
#include <QDebug.h>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::treat(){
    qDebug()<<"请老师吃饭";
}

void Student::treat(QString foodName){
    //输出不加*
    //QString -> char *先转成QByteArray (.toUtf8()) 再转char * (data())
    qDebug()<< "请老师吃饭,老师要吃" << foodName.toUtf8().data();
};

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建一个老师对象
    this->zt = new Teacher(this);

    //创建一个学生对象
    this->st = new Student(this);
    //老师饿了 学生请客的连接
//    connect(zt,&Teacher::hungary,st,&Student::treat);

    //连接带参的信号和槽
    //指针 ->地址
    //函数指针 ->函数地址
    void (Teacher:: *teacherSignal) (QString) = &Teacher::hungry;
    void (Student:: *studentSlot) (QString) = &Student::treat;
    connect(zt,teacherSignal,st,studentSlot);

    classIsOver();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::classIsOver(){
    //下课函数,调用后 触发老师饿了的信号
//    emit zt->hungary();
    emit zt->hungry("华子");
};

信号连接信号:

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "QPushButton"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建一个老师对象
    this->zt = new Teacher(this);

    //创建一个学生对象
    this->st = new Student(this);
    //老师饿了 学生请客的连接
//    connect(zt,&Teacher::hungary,st,&Student::treat);

    //连接带参的信号和槽
    //指针 ->地址
    //函数指针 ->函数地址
//    void (Teacher:: *teacherSignal) (QString) = &Teacher::hungry;
//    void (Student:: *studentSlot) (QString) = &Student::treat;
//    connect(zt,teacherSignal,st,studentSlot);

//    classIsOver();

    //点击一个下课的按钮,再触发下课
    QPushButton * btn = new QPushButton("下课",this);
    //重置窗口大小
    this->resize(600,400);

    //点击按钮 触发下课
//    connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);

    //无参信号和槽连接
    void(Teacher:: *teacherSignal2)(void) = &Teacher::hungry;
    void(Student:: *studentSlot2)(void)  = &Student::treat;
    connect(zt,teacherSignal2,st,studentSlot2);
    //信号连接信号
    connect(btn,&QPushButton::clicked, zt, teacherSignal2);

    //断开信号
    disconnect(zt,teacherSignal2,st,studentSlot2);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::classIsOver(){
    //下课函数,调用后 触发老师饿了的信号
//    emit zt->hungary();
    emit zt->hungry("华子");
};

拓展:

//拓展
    //1.信号可以连接信号
    //2.一个信号可以连接多个槽函数
    //3.多个信号可以连接同一个槽函数
    //4.信号和槽函数的参数 必须类型一一对应
    //5.信号和槽函数的参数 信号的个数 可以多于槽函数的个数 对应的位置保证类型一致

    //Qt4版本以前信号和槽的连接方式
    connect(zt,SIGNAL(hungry()),st,SLOT(treat()));
//    优点:参数直观  缺点:类型不做检测

Lambda表达式:

C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。首先看一下Lambda表达式的基本构成:

[capture](parameters) mutable ->return-type
{
statement
}

[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}

① 函数对象参数;
[],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:
	空。没有使用任何函数对象参数。
	=。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
	&。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
	this。函数体内可以使用Lambda所在类中的成员变量。
	a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
	&a。将a按引用进行传递。
	a, &b。将a按值进行传递,b按引用进行传递。
	=,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
	&, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
② 操作符重载函数参数;
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
③ 可修改标示符;
mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
QPushButton * myBtn = new QPushButton (this);
    QPushButton * myBtn2 = new QPushButton (this);
    myBtn2->move(100,100);
    int m = 10;

    connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });

    connect(myBtn2,&QPushButton::clicked,this,[=] ()  { qDebug() << m; });

    qDebug() << m;

④ 函数返回值;
->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
⑤ 是函数体;
	{},标识函数的实现,这部分不能省略,但函数体可以为空。

实例:

 //通过lambda表达式 实现点击按钮 关闭窗口
    QPushButton * btn2 = new QPushButton ;
    btn2->setText("关闭");
    btn2->move(100,0);
    btn2->setParent(this);
    connect(btn2,&QPushButton::clicked,this,[=](){
//       this->close();
        emit zt->hungry("华子");
    });

总结:

Qt信号和槽

QMainWindow:

QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)及一个中心部件(central widget),是许多应用程序的基础,如文本编辑器,图片编辑器等。

img

mainWindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMenuBar>
#include <QToolBar>
#include <qpushbutton>
#include <QStatusBar>
#include <QLabel>
#include <QDockWidget>
#include <QTextEdit>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //重置窗口大小
    resize(600,400);
    //菜单栏创建  菜单栏最多只能有一个
    QMenuBar * bar = menuBar();
    //将菜单栏放入到窗口中
    setMenuBar(bar);
    //创建菜单
    QMenu * fileMenu = bar->addMenu("文件");
    QMenu * editMenu = bar->addMenu("编辑");

    //创建菜单项
    QAction *addAction = fileMenu->addAction("新建");
    //添加分隔符
    fileMenu->addSeparator();
    QAction *editAction = 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(addAction);
    toolBar->addSeparator();
    toolBar->addAction(editAction);

    //工具栏中添加控件
    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);
}

MainWindow::~MainWindow()
{
    delete ui;
}

资源文件添加:

Qt 资源系统是一个跨平台的资源机制,用于将程序运行时所需要的资源以二进制的形式存储于可执行文件内部。如果你的程序需要加载特定的资源(图标、文本翻译等),那么,将其放置在资源文件中,就再也不需要担心这些文件的丢失。也就是说,如果你将资源以资源文件形式存储,它是会编译到可执行文件内部。

使用 Qt Creator 可以很方便地创建资源文件。我们可以在工程上点右键,选择“添加新文件…”,可以在 Qt 分类下找到“Qt 资源文件”:

img

点击“选择…”按钮,打开“新建 Qt 资源文件”对话框。在这里我们输入资源文件的名字和路径:

img

点击下一步,选择所需要的版本控制系统,然后直接选择完成。我们可以在 Qt Creator 的左侧文件列表中看到“资源文件”一项,也就是我们新创建的资源文件:

img

右侧的编辑区有个“添加”,我们首先需要添加前缀,比如我们将前缀取名为 images。然后选中这个前缀,继续点击添加文件,可以找到我们所需添加的文件。这里,我们选择 document-open.png 文件。当我们完成操作之后,Qt Creator 应该是这样子的:

img

接下来,我们还可以添加另外的前缀或者另外的文件。这取决于你的需要。当我们添加完成之后,我们可以像前面一章讲解的那样,通过使用 : 开头的路径来找到这个文件。比如,我们的前缀是 /images,文件是 document-open.png,那么就可以使用:/images/document-open.png找到这个文件。

这么做带来的一个问题是,如果以后我们要更改文件名,比如将 docuemnt-open.png 改成 docopen.png,那么,所有使用了这个名字的路径都需要修改。所以,更好的办法是,我们给这个文件去一个“别名”,以后就以这个别名来引用这个文件。具体做法是,选中这个文件,添加别名信息:

img

这样,我们可以直接使用:/images/doc-open引用到这个资源,无需关心图片的真实文件名。

引用示例:

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("D:/Image/Luffy.png"));

    //使用添加Qt资源 ": + 前缀名 + 文件名 "
    ui->actionnew->setIcon(QIcon(":/Luffy.png"));
    ui->actionopen->setIcon(QIcon(":/LuffyQ.png"));
}

MainWindow::~MainWindow()
{
    delete ui;
}
对话框QDialog:

模态对话框和非模态对话框创建:

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 dlg(this);
//        dlg.resize(200,100);
//        dlg.exec();

//        qDebug() <<"模态对话框弹出";
        //非模态对话框  创建到堆区 防止一闪而过
        QDialog *dlg2 = new QDialog(this);
        dlg2->show();
        dlg2->setAttribute(Qt::WA_DeleteOnClose);//设置关闭dlg窗口 就删除dlg对象
        qDebug() <<"非模态对话框弹出";
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

消息对话框:

mainWindow.cpp

#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::critical(this,"标题","错误");
        //信息对话框
//        QMessageBox::information(this,"标题","信息");
        //提问对话框
        //参数1 父亲 参数2 标题 参数3 提示内容 参数4 案件类型 参数5 默认关联回车按键
//       if(QMessageBox::Save == QMessageBox::question(this,"标题","提问",QMessageBox::Save|QMessageBox::Cancel,QMessageBox::Cancel)) {
//           qDebug() <<"选择的是保存!";
//       }
//       else{
//           qDebug() <<"选择的是取消!";
//       }
        //警告对话框
        QMessageBox::warning(this,"标题","警告");
    });

}

MainWindow::~MainWindow()
{
    delete ui;
}

其他标准对话框:

mainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
#include <QDebug>
#include <QMessageBox>
#include <QColorDialog>
#include <QFileDialog>
#include <QFontDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //点击一个新建按钮 弹出一个对话框
    connect(ui->actionnew,&QAction::triggered,[=](){
       
        //其他标准对话框
        //颜色对话框
//        QColor color = QColorDialog::getColor(QColor(255,0,0));
//        qDebug() <<"r=" <<color.red();

        //文件对话框  参数1 父亲  参数2  标题  参数3 默认打开路径  参数4 过滤文件格式
//        QString str = QFileDialog::getOpenFileName(this,"打开文件","D:/","(*.txt)");
//        qDebug() <<str;
        //字体对话框
        bool flag;
        QFont font = QFontDialog::getFont(&flag,QFont("宋体",36));
        qDebug() <<"字体:" <<font.family();
    });

}

MainWindow::~MainWindow()
{
    delete ui;
}
登录窗口部件:

image-20220717211922934

image-20220717212358961

效果:

image-20220717213102464

控件:按钮组:

image-20220718205136710

其他代码设置:

ui->setupUi(this);
    //设置单选按钮 男默认选中
    ui->radioButtonMan->setChecked(true);
    //设置选择女 打印信息
    connect(ui->radioButton_2,&QRadioButton::clicked,[=] (){
        qDebug() <<"选中了女!";
    });
    //多选按钮 选择老板娘  2是选中 0是未选中
    connect(ui->checkBox_3,&QCheckBox::stateChanged,[=](int state){
        qDebug() <<state;
    });

image-20220718205319548

5.1.1	QPushButton  常用按钮 
5.1.2	QToolButton  工具按钮  用于显示图片,如图想显示文字,修改风格:toolButtonStyle , 凸起风格autoRaise
5.1.3	radioButton  单选按钮,设置默认 ui->rBtnMan->setChecked(true); 
5.1.4	checkbox多选按钮,监听状态,2 选中  1 半选 0 未选中

QListWidget:

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include "QDebug"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //利用ListWidget写诗
//    QListWidgetItem * item = new QListWidgetItem("锄禾日当午");
//    //将一行诗放入到listWidget控件中
//    ui->listWidget->addItem(item);
        //设置居中方式
//    item->setTextAlignment(Qt::AlignHCenter);

    //QStringList  QList<QString>
    QStringList list;
    list <<"锄禾日当午" <<"汗滴禾下土" <<"谁知盘中餐" <<"粒粒皆辛苦" ;
    ui->listWidget->addItems(list);
}

Widget::~Widget()
{
    delete ui;
}

image-20220718211504717

TreeWidget:

widget.cpp:

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //treeWidget树控件的使用

    //设置水平头
    ui->treeWidget->setHeaderLabels(QStringList()<<"英雄"<<"英雄介绍");
    //创建每一行
    QTreeWidgetItem *item1 = new QTreeWidgetItem(QStringList()<<"力量");
    QTreeWidgetItem *item2 = new QTreeWidgetItem(QStringList()<<"敏捷");
    QTreeWidgetItem *item3 = new QTreeWidgetItem(QStringList()<<"智力");
    //加载顶层节点
    ui->treeWidget->addTopLevelItem(item1);
    ui->treeWidget->addTopLevelItem(item2);
    ui->treeWidget->addTopLevelItem(item3);
    //追加子节点
    QStringList heroL1;
     heroL1 << "刚被猪" << "前排坦克,能在吸收伤害的同时造成可观的范围输出";
    QTreeWidgetItem * l1 = new QTreeWidgetItem(heroL1);
    item1->addChild(l1);
}

Widget::~Widget()
{
    delete ui;
}

实现:

image-20220718214124468

QTableWidget:

widget.cpp:

#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("亚瑟"));
}

Widget::~Widget()
{
    delete ui;
}

实现:

image-20220719193605613

其他常用控件:

image-20220719195530111

stackWidget:

将之前的组件拖到stackWidget里面,修改代码:

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //栈控件使用

    //设置打开默认页面
    ui->stackedWidget->setCurrentIndex(2);
    //按钮使用
    connect(ui->b1,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(1);
    });

    connect(ui->b2,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(2);
    });

    connect(ui->b3,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(3);
    });

    connect(ui->b4,&QPushButton::clicked,[=](){
        ui->stackedWidget->setCurrentIndex(0);
    });
}

Widget::~Widget()
{
    delete ui;
}

实现页面:

image-20220719201004093

image-20220719203527850

image-20220719205300193

自定义控件:

image-20220719212746149

在打开的界面设计自定义控件样式:

image-20220719212849447

设计完成后在主控件ui界面添加一个与自定义控件同类的控件,在此控件右击选择 提升为 在页面填入 提升的相关信息(类名)

image-20220719213521089

再去实现自定义控件中两个控件之间的关联以及提供对外接口:

smallWidget.h:

#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 setNum(int num);

    int getNum();

private:
    Ui::SmallWidget *ui;
};

#endif // SMALLWIDGET_H

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;
    connect(ui->spinBox,spSignal,ui->horizontalSlider,&QSlider::setValue);

    //QSlide滑动  QSpinBox数字跟着改变
    connect(ui->horizontalSlider,&QSlider::valueChanged,ui->spinBox,&QSpinBox::setValue);
}

SmallWidget::~SmallWidget()
{
    delete ui;
}

void SmallWidget::setNum(int num)
{
    ui->spinBox->setValue(num);
}

int SmallWidget::getNum()
{
   return ui->spinBox->value();
}

在主widget中调用接口实现功能:

实现效果:

image-20220720202943112

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include <QMovie>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //点击获取  获取当前控件的值
    connect(ui->btn_get,&QPushButton::clicked,[=](){
       qDebug()<< ui->widget->getNum();
    });

   //设置到一半
    connect(ui->btn_set,&QPushButton::clicked,[=](){
        ui->widget->setNum(50);
    });
}

Widget::~Widget()
{
    delete ui;
}
Qt中的鼠标事件:

实现一个部件,对于鼠标的移入移出做出响应:

实现效果:

image-20220720205850585

创建一个自定义组件QLabel,并实现其中的虚继承函数;

myLabel.h:

#ifndef MYLABEL_H
#define MYLABEL_H

#include <QLabel>

class myLabel : public QLabel
{
    Q_OBJECT
public:
    explicit myLabel(QWidget *parent = nullptr);

    //鼠标进入事件
    void enterEvent(QEvent *event);
    //鼠标离开事件
    void leaveEvent(QEvent *evnet);

signals:

};

#endif // MYLABEL_H

myLabel.cpp:

#include "mylabel.h"
#include <QDebug>
myLabel::myLabel(QWidget *parent) : QLabel(parent)
{

}

void myLabel::enterEvent(QEvent *event)
{
    qDebug()<<"我进来啦!";
}

void myLabel::leaveEvent(QEvent *evnet)
{
    qDebug()<<"我出去啦!";
}

在页面添加一个Label部件,并设置将其提升为自定义的组件名:

image-20220720210911360

其他鼠标事件:

2.2	鼠标进入事件  enterEvent
2.3	鼠标离开事件  leaveEvent
2.4	鼠标按下   mousePressEvent ( QMouseEvent  ev)
2.5	鼠标释放   mouseReleaseEvent
2.6	鼠标移动   mouseMoveEvent
2.7	ev->x() x坐标  ev->y() y坐标
2.8	ev->button() 可以判断所有按键 Qt::LeftButton  Qt::RightButton
2.9	ev->buttons()判断组合按键  判断move时候的左右键  结合 & 操作符
2.10	格式化字符串  QString( “ %1  %2 ” ).arg( 111 ).arg(222)
2.11	设置鼠标追踪    setMouseTracking(true);
定时器:

1、实现在一个label中定时显示数字:

实现效果:

image-20220720214330472

widget.cpp

#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 *ev);

    //定时器的唯一标识
    int id1;
    int id2;

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //启动定时器
    id1 = startTimer(1000); //参数1 间隔  单位 毫秒

    id2 = startTimer(2000);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::timerEvent(QTimerEvent *ev)
{
    if(ev->timerId() ==id1){
    //label2 每隔一秒加1
    static int num = 1;
    ui->label_2->setText(QString::number((num++)));
    }
    if(ev->timerId() ==id2){
    //label3 每隔两秒加1
    static int num2 = 1;
    ui->label_3->setText(QString::number((num2++)));
    }
}

第二种定时器:

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QTimer> //定时器类

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //定时器第二种方式
    QTimer * timer = new QTimer(this);
    //启动定时器
    timer->start(500);

    connect(timer,&QTimer::timeout,[=](){
        static int num =1;

        //label4 每隔0.5s加1
        ui->label_4->setText(QString::number(num++));
    });

    //点击暂停按钮  实现停止定时器
    connect(ui->pushButton,&QPushButton::clicked,[=](){
        timer->stop();
    });
}

Widget::~Widget()
{
    delete ui;
}

实现效果:

image-20220721194140019

event事件分发器:

用途:

用于事件的分发

也可以做拦截操作,不建议

解析图:

image-20220721201356423

代码实现:

mylabel.cpp

#include "mylabel.h"
#include <QDebug>
#include <QEvent>
#include <QMouseEvent>
myLabel::myLabel(QWidget *parent) : QLabel(parent)
{

}

void myLabel::enterEvent(QEvent *event)
{
    qDebug()<<"我进来啦!";
}

void myLabel::leaveEvent(QEvent *evnet)
{
    qDebug()<<"我出去啦!";
}

bool myLabel::event(QEvent *e)
{
   //如果是鼠标按下,在event事件分发中做拦截操作
    if(e->type() ==QEvent::MouseButtonPress){
        QString str = QString("event函数拦截");
        qDebug() << str;

        return true; //true代表用户自己处理这个事件 不向下分发
    }
    //其他事件 交给 父类处理 默认处理
    return QLabel::event(e);

}

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;
    }
}

实现效果:

image-20220721201823851

所有点击事件都被拦截。

事件过滤器:

可以参考上面的图,在程序事件分发之前再做一次高级拦截。

使用步骤:

1.给控件安装事件过滤器

2.重写eventfilter事件

实现效果:

image-20220721204325257

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 *ev);

    //定时器的唯一标识
    int id1;
    int id2;

    //2.重写eventfilter事件
    bool eventFilter(QObject *,QEvent *);
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

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 num =1;

        //label4 每隔0.5s加1
        ui->label_4->setText(QString::number(num++));
    });

    //点击暂停按钮  实现停止定时器
    connect(ui->pushButton,&QPushButton::clicked,[=](){
        timer->stop();
    });

    //给label1安装事件过滤器
    //1.给控件安装事件过滤器
    ui->label->installEventFilter(this);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::timerEvent(QTimerEvent *ev)
{
    if(ev->timerId() ==id1){
    //label2 每隔一秒加1
    static int num = 1;
    ui->label_2->setText(QString::number((num++)));
    }
    if(ev->timerId() ==id2){
    //label3 每隔两秒加1
    static int num2 = 1;
    ui->label_3->setText(QString::number((num2++)));
    }
}
//2.重写eventfilter事件
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("事件过滤器");
            qDebug() <<str;

            return true;
        }
    }
    //其他默认处理
    return QWidget::eventFilter(obj,e);
}
QPaint会话事件:

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 paintEvent(QPaintEvent *event);

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPainter> //画家类

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //  实例化画家类  指定的是绘图设备
    QPainter painter(this);

    //设置画笔颜色
    QPen pen(QColor(255,0,0));
    //设置画家使用这个笔
    painter.setPen(pen);

    //设置画刷
    QBrush brush(Qt::cyan);
    //让画家使用画刷
    painter.setBrush(brush);
    //画线
    painter.drawLine(QPoint(0,0),QPoint(100,100));
    //画圆  椭圆
    painter.drawEllipse(QPoint(100,100),50,50);
    //画矩形
    painter.drawRect(QRect(20,20,40,40));
    //画文字
    painter.drawText(QRect(10,200,250,50),"好好学习,天天向上!");
}

实现效果:

image-20220721211636726

QPainter高级设置:

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPainter> //画家类

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
//    //  实例化画家类  指定的是绘图设备
//    QPainter painter(this);

//    //设置画笔颜色
//    QPen pen(QColor(255,0,0));
//    //设置画家使用这个笔
//    painter.setPen(pen);

//    //设置画刷
//    QBrush brush(Qt::cyan);
//    //让画家使用画刷
//    painter.setBrush(brush);
//    //画线
//    painter.drawLine(QPoint(0,0),QPoint(100,100));
//    //画圆  椭圆
//    painter.drawEllipse(QPoint(100,100),50,50);
//    //画矩形
//    painter.drawRect(QRect(20,20,40,40));
//    //画文字
//    painter.drawText(QRect(10,200,250,50),"好好学习,天天向上!");

    //高级设置
    QPainter painter(this);
    painter.drawEllipse(QPoint(100,50),50,50);
    //设置抗锯齿 效率较低
    painter.setRenderHint(QPainter::Antialiasing);
    painter.drawEllipse(QPoint(200,50),50,50);

    //画矩形
    painter.drawRect(QRect(120,120,50,50));

    //移动画家的位置
    painter.translate(100,0);
    
    painter.drawRect(QRect(120,120,50,50));
    
    //保存画家状态
    painter.save();
    
    //还原画家状态
    painter.restore();
}

实现效果:

image-20220721213305791

手动调用绘图事件:

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 paintEvent(QPaintEvent *event);
	//加入图像的x坐标
    int posX =0;

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#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更新
        update();
    });
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{


    //利用画家画资源图片
    if(posX > this->width()){
        posX = 0;
    }
    QPainter painter(this);
    painter.drawPixmap(posX,0,QPixmap(":/Image/Luffy.png"));

}

实现效果:

image-20220722195720778

绘图设备:
绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和 QPicture。其中,
	QPixmap专门为图像在屏幕上的显示做了优化
	QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
	QImage专门为图像的像素级访问做了优化。 
	QPicture则可以记录和重现QPainter的各条命令。

QPixmap、QBitmap、QImage:

QPixmap继承了QPaintDevice,因此,你可以使用QPainter直接在上面绘制图形。QPixmap也可以接受一个字符串作为一个文件的路径来显示这个文件,比如你想在程序之中打开png、jpeg之类的文件,就可以使用 QPixmap。使用QPainter的drawPixmap()函数可以把这个文件绘制到一个QLabel、QPushButton或者其他的设备上面。QPixmap是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。注意,这里说的显示设备并不是硬件,而是操作系统提供的原生的绘图引擎。所以,在不同的操作系统平台下,QPixmap的显示可能会有所差别。
QBitmap继承自QPixmap,因此具有QPixmap的所有特性,提供单色图像。QBitmap的色深始终为1. 色深这个概念来自计算机图形学,是指用于表现颜色的二进制的位数。我们知道,计算机里面的数据都是使用二进制表示的。为了表示一种颜色,我们也会使用二进制。比如我们要表示8种颜色,需要用3个二进制位,这时我们就说色深是3. 因此,所谓色深为1,也就是使用1个二进制位表示颜色。1个位只有两种状态:01,因此它所表示的颜色就有两种,黑和白。所以说,QBitmap实际上是只有黑白两色的图像数据。
由于QBitmap色深小,因此只占用很少的存储空间,所以适合做光标文件和笔刷。
下面我们来看同一个图像文件在QPixmap和QBitmap下的不同表现:
void PaintWidget::paintEvent(QPaintEvent *)
{
    QPixmap pixmap(":/Image/butterfly.png");
    QPixmap pixmap1(":/Image/butterfly1.png");
    QBitmap bitmap(":/Image/butterfly.png");
    QBitmap bitmap1(":/Image/butterfly1.png");

    QPainter painter(this);
    painter.drawPixmap(0, 0, pixmap);
    painter.drawPixmap(200, 0, pixmap1);
    painter.drawPixmap(0, 130, bitmap);
    painter.drawPixmap(200, 130, bitmap1);
}

img

这里我们给出了两张png图片。butterfly1.png是没有透明色的纯白背景,而butterfly.png是具有透明色的背景。我们分别使用QPixmap和QBitmap来加载它们。注意看它们的区别:白色的背景在QBitmap中消失了,而透明色在QBitmap中转换成了黑色;其他颜色则是使用点的疏密程度来体现的。

QPixmap使用底层平台的绘制系统进行绘制,无法提供像素级别的操作,而**QImage则是使用独立于硬件的绘制系统,实际上是自己绘制自己,因此提供了像素级别的操作,并且能够在不同系统之上提供一个一致的显示形式。

我们声明了一个QImage对象,大小是300 x 300,颜色模式是RGB32,即使用32位数值表示一个颜色的RGB值,也就是说每种颜色使用8位。然后我们对每个像素进行颜色赋值,从而构成了这个图像。我们可以把QImage想象成一个RGB颜色的二维数组,记录了每一像素的颜色。
QImage与QPixmap的区别
	QPixmap主要是用于绘图,针对屏幕显示而最佳化设计,QImage主要是为图像I/O、图片访问和像素修改而设计的
	QPixmap依赖于所在的平台的绘图引擎,故例如反锯齿等一些效果在不同的平台上可能会有不同的显示效果,QImage使用Qt自身的绘图引擎,可在不同平台上具有相同的显示效果
	由于QImage是独立于硬件的,也是一种QPaintDevice,因此我们可以在另一个线程中对其进行绘制,而不需要在GUI线程中处理,使用这一方式可以很大幅度提高UI响应速度。
	QImage可通过setPixpel()和pixel()等方法直接存取指定的像素。
QImage与QPixmap之间的转换:
	QImage转QPixmap
使用QPixmap的静态成员函数: fromImage()
QPixmap	fromImage(const QImage & image, 
Qt::ImageConversionFlags flags = Qt::AutoColor)
	QPixmap转QImage:
使用QPixmap类的成员函数: toImage()
QImage toImage() const

QPicture:

最后一个需要说明的是QPicture。这是一个可以记录和重现QPainter命令的绘图设备。 QPicture将QPainter的命令序列化到一个IO设备,保存为一个平台独立的文件格式。这种格式有时候会是“元文件(meta- files)”。Qt的这种格式是二进制的,不同于某些本地的元文件,Qt的pictures文件没有内容上的限制,只要是能够被QPainter绘制的元素,不论是字体还是pixmap,或者是变换,都可以保存进一个picture中。
QPicture是平台无关的,因此它可以使用在多种设备之上,比如svg、pdf、ps、打印机或者屏幕。回忆下我们这里所说的QPaintDevice,实际上是说可以有QPainter绘制的对象。QPicture使用系统的分辨率,并且可以调整 QPainter来消除不同设备之间的显示差异。
如果我们要记录下QPainter的命令,首先要使用QPainter::begin()函数,将QPicture实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用QPainter::end()命令终止。代码示例如下:
void PaintWidget::paintEvent(QPaintEvent *)
{
    QPicture pic;
    QPainter painter;
	 //将图像绘制到QPicture中,并保存到文件
    painter.begin(&pic);
    painter.drawEllipse(20, 20, 100, 50);
    painter.fillRect(20, 100, 100, 100, Qt::red);
    painter.end();
    pic.save("D:\\drawing.pic");

	 //将保存的绘图动作重新绘制到设备上
    pic.load("D:\\drawing.pic");
    painter.begin(this);
    painter.drawPicture(200, 200, pic);
    painter.end();
}

wiget.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 paintEvent(QPaintEvent * ev);

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#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);
//    //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:\\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:\\img.png");

    //Qpicture绘图设备  可以记录和重现绘图指令
    QPicture pic;
    QPainter painter;
    painter.begin(&pic);//开始往pic上画
    painter.setPen(QPen(Qt::cyan));
    painter.drawEllipse(QPoint(150,150),100,100);
    painter.end();//结束画画
    //保存
    pic.save("D:\\img.tt");
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *ev)
{
//    QPainter painter(this);
//    //利用QImage 对像素进行修改
//    QImage img;
//    img.load(":/Image/Luffy.png");
//    //修改像素点
//    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);

    QPainter painter(this);
    //重现QPicture的绘图指令
    QPicture pic;
    pic.load("D:\\img.tt");
    painter.drawPicture(0,0,pic);
}
QFile文件读写:

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QTextCodec>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //点击选取文件按钮,弹出文件对话框
    connect(ui->pushButton,&QPushButton::clicked,[=](){
      QString path =  QFileDialog::getOpenFileName(this,"打开文件","D:\\file");
      //将路径放入到lineEdit中
      ui->lineEdit->setText(path);
      //编码格式类
//      QTextCodec * codec = QTextCodec::codecForName("gbk");
      //读取内容放入到 textEdit中
      //QFile默认支持的格式是 utf-8
      QFile file(path); //参数就是读取文件的路径
      //设置打开方式
      file.open(QIODevice::ReadOnly);
      //1.一次读取全部
//      QByteArray array = file.readAll();
      //2.按行读
      QByteArray array;
      while(!file.atEnd()){
      array += file.readLine();
      }
      //将读取道德数据放入到textEdit中
      ui->textEdit->setText(array);
//      ui->textEdit->setText(codec->toUnicode(array));

      //对文件对象进行关闭
      file.close();

      //进行写文件操作
      file.open(QIODevice::Append);
      file.write("啊啊啊啊啊");
      file.close();
    });
}

Widget::~Widget()
{
    delete ui;
}

实现效果:

image-20220723200558366

QFileInfo文件信息读取:

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QTextCodec>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //点击选取文件按钮,弹出文件对话框
    connect(ui->pushButton,&QPushButton::clicked,[=](){
      QString path =  QFileDialog::getOpenFileName(this,"打开文件","D:\\file");
      //将路径放入到lineEdit中
      ui->lineEdit->setText(path);

      //QFileInfo 文件信息类
      QFileInfo info(path);
      Debug() <<"大小:" << info.size() <<"后缀名:" <<info.suffix();
    });
}

Widget::~Widget()
{
    delete ui;
}
翻金币案例:

1.创建项目

2.添加资源

项目基本配置:

mainsence.h:

#ifndef MAINSENCE_H
#define MAINSENCE_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainSence; }
QT_END_NAMESPACE

class MainSence : public QMainWindow
{
    Q_OBJECT

public:
    MainSence(QWidget *parent = nullptr);
    ~MainSence();

    //重写paintEvent事件 画背景图
    void paintEvent(QPaintEvent *event);
private:
    Ui::MainSence *ui;
};
#endif // MAINSENCE_H

mainsence.cpp

#include "mainsence.h"
#include "ui_mainsence.h"
#include <QPainter>
MainSence::MainSence(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainSence)
{
    ui->setupUi(this);
    //配置主场景

    //设置固定大小
    setFixedSize(640,1200);

    //设置图标
    setWindowIcon(QIcon(":/res/Coin0001.png"));

    //设置标题
    setWindowTitle("阿伟翻金币!");

    //退出按钮的实现
    connect(ui->actionquit,&QAction::triggered,[=](){
        this->close();
    });

    //设置背景图片

}

MainSence::~MainSence()
{
    delete ui;
}

//设置背景图
void MainSence::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //画背景上图标
    pix.load(":/res/Title.png");
    //对于图标的缩放
//    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap(20,50,pix);
}

实现效果:

image-20220724161214539

开始按钮的创建:

通过观察发现,案例中的按钮与我们实际使用的按钮有差距,我们通过自己封装一个按钮来实现我们需要的效果

1.封装自定义按钮

mybutton.h

#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H

#include <QPushButton>

class MyPushButton : public QPushButton
{
    Q_OBJECT
public:
//    explicit MyPushButton(QWidget *parent = nullptr);
    //构造函数  参数1 正常显示的图片路径 参数2 按下后显示的图片路径
    MyPushButton(QString normalImg,QString pressImg ="");

    //成员属性 保存用户传入的默认显示路径 以及按下后显示的图片路径
    QString normalImgPath;
    QString pressImgPath;

    //弹跳特效
    void zoom1();  //向下跳
    void zoom2();  //向上跳
signals:

};

#endif // MYPUSHBUTTON_H

mybutton.cpp

#include "mypushbutton.h"
#include <QDebug>
#include <QPropertyAnimation>

MyPushButton::MyPushButton(QString normalImg, QString pressImg)
{
    this->normalImgPath = normalImg;
    this->pressImgPath = pressImg;

    QPixmap pix;
    bool ret = pix.load(normalImg);
    if(!ret){
        qDebug()<<"图片加载失败!";
        return;
    }

    //设置图片固定大小
    this->setFixedSize(pix.width(),pix.height());

    //设置不规则图片样式
    this->setStyleSheet("QPushButton{border:0px}");

    //设置图标
    this->setIcon(pix);

    //设置图标大小
    this->setIconSize(QSize(pix.width(),pix.height()));
}

void MyPushButton::zoom1()
{
    //创建动画对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");
    //设置动画时间间隔
    animation->setDuration(200);
    //起始位置
    animation->setStartValue(QRect(this->x(),this->y(),this->width(),this->height()));
    //结束位置
    animation->setEndValue(QRect(this->x(),this->y()+10,this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();
}

void MyPushButton::zoom2()
{
    //创建动画对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");
    //设置动画时间间隔
    animation->setDuration(200);
    //起始位置
    animation->setStartValue(QRect(this->x(),this->y()-10,this->width(),this->height()));
    //结束位置
    animation->setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();
}

mainsence.cpp

#include "mainsence.h"
#include "ui_mainsence.h"
#include <QPainter>
#include <mypushbutton.h>
#include <QDebug>

MainSence::MainSence(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainSence)
{
    ui->setupUi(this);
    //配置主场景

    //设置固定大小
    setFixedSize(640,1200);

    //设置图标
    setWindowIcon(QIcon(":/res/Coin0001.png"));

    //设置标题
    setWindowTitle("阿伟翻金币!");

    //退出按钮的实现
    connect(ui->actionquit,&QAction::triggered,[=](){
        this->close();
    });

    //开始按钮
    MyPushButton * startBtn = new MyPushButton(":/res/MenuSceneStartButton.png");

    startBtn->setParent(this);
    startBtn->move(this->width() *0.5 - startBtn->width() * 0.5,this->height() * 0.7);

    connect(startBtn,&MyPushButton::clicked,[=](){
        qDebug() <<"点击开始按钮";
        //做弹起特效
        startBtn->zoom1();
        startBtn->zoom2();
    });

}

MainSence::~MainSence()
{
    delete ui;
}

//设置背景图
void MainSence::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //画背景上图标
    pix.load(":/res/Title.png");
    //对于图标的缩放
//    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap(20,50,pix);
}

实现效果:

动画1

选择关卡场景基本配置以及返回按钮设置:

chooselevalsence.h

#ifndef CHOOSELEVELSCENCE_H
#define CHOOSELEVELSCENCE_H

#include <QMainWindow>

class ChooseLevelScence : public QMainWindow
{
    Q_OBJECT
public:
    explicit ChooseLevelScence(QWidget *parent = nullptr);

    //重写绘图事件
    void paintEvent(QPaintEvent * ev);

signals:

};

#endif // CHOOSELEVELSCENCE_H

chooselevelsence.cpp

#include "chooselevelscence.h"
#include <QMenuBar>
#include <QPainter>
#include <mypushbutton.h>
#include <QDebug>

ChooseLevelScence::ChooseLevelScence(QWidget *parent) : QMainWindow(parent)
{
    //配置选择关卡场景
    this->setFixedSize(640,1200);

    //设置图标
    this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

    //设置标题
    this->setWindowTitle("选择关卡场景");

    //创建菜单栏
    QMenuBar * bar = menuBar();
    setMenuBar(bar);

    //创建开始菜单
    QMenu * startMenu = bar->addMenu("开始");

    //创建退出菜单项
    QAction * quitAction = startMenu->addAction("退出");

    //点击退出 实现退出游戏
    connect(quitAction,&QAction::triggered,[=](){
        this->close();
    });

    //返回按钮
    MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
    backBtn->setParent(this);
    backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

    //点击返回
    connect(backBtn,&MyPushButton::clicked,[=](){
       qDebug() <<"点击了返回按钮";

    });
}


void ChooseLevelScence::paintEvent(QPaintEvent *ev)
{
    //加载背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/OtherSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
//    painter.drawPixmap( (this->width() - pix.width())*0.5,30,pix.width(),pix.height(),pix);
    painter.drawPixmap(20,50,pix);

}

mypushbutton.h

#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H

#include <QPushButton>

class MyPushButton : public QPushButton
{
    Q_OBJECT
public:
//    explicit MyPushButton(QWidget *parent = nullptr);
    //构造函数  参数1 正常显示的图片路径 参数2 按下后显示的图片路径
    MyPushButton(QString normalImg,QString pressImg ="");

    //成员属性 保存用户传入的默认显示路径 以及按下后显示的图片路径
    QString normalImgPath;
    QString pressImgPath;

    //弹跳特效
    void zoom1();  //向下跳
    void zoom2();  //向上跳

    //重写按钮 按下和释放事件
    void mousePressEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *e);
signals:

};

#endif // MYPUSHBUTTON_H

mypushbutton.cpp

#include "mypushbutton.h"
#include <QDebug>
#include <QPropertyAnimation>

MyPushButton::MyPushButton(QString normalImg, QString pressImg)
{
    this->normalImgPath = normalImg;
    this->pressImgPath = pressImg;

    QPixmap pix;
    bool ret = pix.load(normalImg);
    if(!ret){
        qDebug()<<"图片加载失败!";
        return;
    }

    //设置图片固定大小
    this->setFixedSize(pix.width(),pix.height());

    //设置不规则图片样式
    this->setStyleSheet("QPushButton{border:0px}");

    //设置图标
    this->setIcon(pix);

    //设置图标大小
    this->setIconSize(QSize(pix.width(),pix.height()));
}

void MyPushButton::zoom1()
{
    //创建动画对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");
    //设置动画时间间隔
    animation->setDuration(200);
    //起始位置
    animation->setStartValue(QRect(this->x(),this->y(),this->width(),this->height()));
    //结束位置
    animation->setEndValue(QRect(this->x(),this->y()+10,this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();
}

void MyPushButton::zoom2()
{
    //创建动画对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");
    //设置动画时间间隔
    animation->setDuration(200);
    //起始位置
    animation->setStartValue(QRect(this->x(),this->y()-10,this->width(),this->height()));
    //结束位置
    animation->setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();
}

void MyPushButton::mousePressEvent(QMouseEvent *e)
{
    if(this->pressImgPath != ""){ //传入的按下图片不为空  说明需要有按下的状态
        QPixmap pix;
        bool ret = pix.load(this->pressImgPath);
        if(!ret)
        {
            qDebug() <<"图片加载失败";
            return;
        }

        //设置图片固定大小
        this->setFixedSize(pix.width(),pix.height());

        //设置不规则图片样式
        this->setStyleSheet("QPushButton{border:0px}");

        //设置图标
        this->setIcon(pix);

        //设置图标大小
        this->setIconSize(QSize(pix.width(),pix.height()));
    }
    //让父类执行其他内容
    return QPushButton::mousePressEvent(e);
}

void MyPushButton::mouseReleaseEvent(QMouseEvent *e)
{
    if(this->pressImgPath != ""){ //传入的按下图片不为空  说明需要有按下的状态,切换为初始图片
        QPixmap pix;
        bool ret = pix.load(this->normalImgPath);
        if(!ret)
        {
            qDebug() <<"图片加载失败";
            return;
        }

        //设置图片固定大小
        this->setFixedSize(pix.width(),pix.height());

        //设置不规则图片样式
        this->setStyleSheet("QPushButton{border:0px}");

        //设置图标
        this->setIcon(pix);

        //设置图标大小
        this->setIconSize(QSize(pix.width(),pix.height()));
    }

    //让父类执行其他内容
    return QPushButton::mouseReleaseEvent(e);

}

实现效果:

动画1

开始场景与选关场景的切换实现:

chooselevelsence.h

#ifndef CHOOSELEVELSCENCE_H
#define CHOOSELEVELSCENCE_H

#include <QMainWindow>

class ChooseLevelScence : public QMainWindow
{
    Q_OBJECT
public:
    explicit ChooseLevelScence(QWidget *parent = nullptr);

    //重写绘图事件
    void paintEvent(QPaintEvent * ev);

signals:
    //写一个自定义信号 告诉主场景点击了返回
    void chooseScenceBack();
};

#endif // CHOOSELEVELSCENCE_H

chooselevelsence.cpp

#include "chooselevelscence.h"
#include <QMenuBar>
#include <QPainter>
#include <mypushbutton.h>
#include <QDebug>

ChooseLevelScence::ChooseLevelScence(QWidget *parent) : QMainWindow(parent)
{
    //配置选择关卡场景
    this->setFixedSize(640,1200);

    //设置图标
    this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

    //设置标题
    this->setWindowTitle("选择关卡场景");

    //创建菜单栏
    QMenuBar * bar = menuBar();
    setMenuBar(bar);

    //创建开始菜单
    QMenu * startMenu = bar->addMenu("开始");

    //创建退出菜单项
    QAction * quitAction = startMenu->addAction("退出");

    //点击退出 实现退出游戏
    connect(quitAction,&QAction::triggered,[=](){
        this->close();
    });

    //返回按钮
    MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
    backBtn->setParent(this);
    backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

    //点击返回
    connect(backBtn,&MyPushButton::clicked,[=](){
       qDebug() <<"点击了返回按钮";

       //告诉主场景 我返回了,主场景监听ChooseLevelScene的返回按钮
       emit this->chooseScenceBack();

    });
}


void ChooseLevelScence::paintEvent(QPaintEvent *ev)
{
    //加载背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/OtherSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
//    painter.drawPixmap( (this->width() - pix.width())*0.5,30,pix.width(),pix.height(),pix);
    painter.drawPixmap(20,50,pix);

}

mainsence.cpp

#include "mainsence.h"
#include "ui_mainsence.h"
#include <QPainter>
#include <mypushbutton.h>
#include <QDebug>
#include <QTimer>

MainSence::MainSence(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainSence)
{
    ui->setupUi(this);
    //配置主场景

    //设置固定大小
    setFixedSize(640,1200);

    //设置图标
    setWindowIcon(QIcon(":/res/Coin0001.png"));

    //设置标题
    setWindowTitle("阿伟翻金币!");

    //退出按钮的实现
    connect(ui->actionquit,&QAction::triggered,[=](){
        this->close();
    });

    //开始按钮
    MyPushButton * startBtn = new MyPushButton(":/res/MenuSceneStartButton.png");

    startBtn->setParent(this);
    startBtn->move(this->width() *0.5 - startBtn->width() * 0.5,this->height() * 0.7);

    //实例化选择关卡的场景
    chooseLevelScence = new ChooseLevelScence;

    //监听选择关卡的返回按钮的信号
    connect(chooseLevelScence,&ChooseLevelScence::chooseScenceBack,this,[=](){
        chooseLevelScence->hide();//将选择的关卡场景隐藏
        this->show(); //重新显示主场景
    });
    connect(startBtn,&MyPushButton::clicked,[=](){
        qDebug() <<"点击开始按钮";
        //做弹起特效
        startBtn->zoom1();
        startBtn->zoom2();

        //延时进入到选择关卡场景中
        QTimer::singleShot(500,this,[=](){
            //自身隐藏
            this->hide();
            //显式选择关卡场景
            chooseLevelScence->show();

        });

    });

}

MainSence::~MainSence()
{
    delete ui;
}

//设置背景图
void MainSence::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //画背景上图标
    pix.load(":/res/Title.png");
    //对于图标的缩放
//    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap(20,50,pix);
}

实现效果:

动画2

选择关卡按钮的创建:

chooselevelscence.cpp

#include "chooselevelscence.h"
#include <QMenuBar>
#include <QPainter>
#include <mypushbutton.h>
#include <QDebug>
#include <QLabel>

ChooseLevelScence::ChooseLevelScence(QWidget *parent) : QMainWindow(parent)
{
    //配置选择关卡场景
    this->setFixedSize(640,1200);

    //设置图标
    this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

    //设置标题
    this->setWindowTitle("选择关卡场景");

    //创建菜单栏
    QMenuBar * bar = menuBar();
    setMenuBar(bar);

    //创建开始菜单
    QMenu * startMenu = bar->addMenu("开始");

    //创建退出菜单项
    QAction * quitAction = startMenu->addAction("退出");

    //点击退出 实现退出游戏
    connect(quitAction,&QAction::triggered,[=](){
        this->close();
    });

    //返回按钮
    MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
    backBtn->setParent(this);
    backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

    //点击返回
    connect(backBtn,&MyPushButton::clicked,[=](){
       qDebug() <<"点击了返回按钮";

       //告诉主场景 我返回了,主场景监听ChooseLevelScene的返回按钮
       emit this->chooseScenceBack();

    });

    //创建选择关卡的按钮
    for(int i =0; i<20; i++){
       MyPushButton * menuBtn = new MyPushButton(":/res/LevelIcon.png");
       menuBtn->setParent(this);
       menuBtn->move(80 + (i%4) * 140,200 + (i/4) * 140);

       //监听每个按钮的绑定事件
       connect(menuBtn,&MyPushButton::clicked,[=](){
           QString str = QString("您选择的是第 %1 关").arg(i+1);
           qDebug() << str;
       });
       QLabel * label = new QLabel;
       label->setParent(this);
       label->setFixedSize(menuBtn->width(),menuBtn->height());
       label->setText(QString::number(i+1));
       label->move(80 + (i%4) * 140,200 + (i/4) * 140);

       //设置label文字对齐方式 垂直居中 水平居中
       label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
       //设置让鼠标进行穿透,否则在上面的事件监听后,Qlabel无法接受鼠标事件  51号属性
       label->setAttribute(Qt::WA_TransparentForMouseEvents);

    }
}


void ChooseLevelScence::paintEvent(QPaintEvent *ev)
{
    //加载背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/OtherSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
//    painter.drawPixmap( (this->width() - pix.width())*0.5,30,pix.width(),pix.height(),pix);
    painter.drawPixmap(20,50,pix);

}

实现效果:

image-20220726105010931

翻金币场景的基本配置:

1.首先创建翻金币场景的类

2.playscene.h

#ifndef PLAYSCENE_H
#define PLAYSCENE_H
#include <QPaintEvent>
#include <QMainWindow>

class PlayScene : public QMainWindow
{
    Q_OBJECT
public:
    explicit PlayScene(QWidget *parent = nullptr);

    PlayScene(int levelNum);

    int levelIndex; //内部成员属性 记录所选关卡

    //重写绘图事件
    void paintEvent(QPaintEvent * ev);

signals:
    //写一个自定义信号 告诉主场景点击了返回
    void chooseScenceBack();

};

#endif // PLAYSCENE_H

3.playscene.cpp

#include "playscene.h"
#include <QDebug>
#include <QMenuBar>
#include <QPainter>
#include "mypushbutton.h"

//PlayScene::PlayScene(QWidget *parent) : QMainWindow(parent)
//{

//}

PlayScene::PlayScene(int levelNum)
{
   QString str = QString("进入了第 %1 关").arg(levelNum);
   qDebug() << str;
   this->levelIndex = levelNum;

   //初始化游戏场景
   //设置固定大小
   this->setFixedSize(640,1200);

   //设置图标
   this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

   //设置标题
   this->setWindowTitle("金币翻翻翻");

   //创建菜单栏
   QMenuBar * bar = menuBar();
   setMenuBar(bar);

   //创建开始菜单
   QMenu * startMenu = bar->addMenu("开始");

   //创建退出菜单项
   QAction * quitAction = startMenu->addAction("退出");

   //点击退出 实现退出游戏
   connect(quitAction,&QAction::triggered,[=](){
       this->close();
   });

   //返回按钮
   MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
   backBtn->setParent(this);
   backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

   //点击返回
   connect(backBtn,&MyPushButton::clicked,[=](){
      qDebug() <<"游戏界面点击了返回按钮";

//      //告诉主场景 我返回了,主场景监听ChooseLevelScene的返回按钮
      emit this->chooseScenceBack();

   });
}

void PlayScene::paintEvent(QPaintEvent *ev)
{
    //创建背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap( 10,30,pix.width(),pix.height(),pix);

}

实现效果:

动画2

显示关卡数:

playscene.cpp

#include "playscene.h"
#include <QDebug>
#include <QMenuBar>
#include <QPainter>
#include "mypushbutton.h"
#include <QLabel>

//PlayScene::PlayScene(QWidget *parent) : QMainWindow(parent)
//{

//}

PlayScene::PlayScene(int levelNum)
{
   QString str = QString("进入了第 %1 关").arg(levelNum);
   qDebug() << str;
   this->levelIndex = levelNum;

   //初始化游戏场景
   //设置固定大小
   this->setFixedSize(640,1200);

   //设置图标
   this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

   //设置标题
   this->setWindowTitle("金币翻翻翻");

   //创建菜单栏
   QMenuBar * bar = menuBar();
   setMenuBar(bar);

   //创建开始菜单
   QMenu * startMenu = bar->addMenu("开始");

   //创建退出菜单项
   QAction * quitAction = startMenu->addAction("退出");

   //点击退出 实现退出游戏
   connect(quitAction,&QAction::triggered,[=](){
       this->close();
   });

   //返回按钮
   MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
   backBtn->setParent(this);
   backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

   //点击返回
   connect(backBtn,&MyPushButton::clicked,[=](){
      qDebug() <<"游戏界面点击了返回按钮";

//      //告诉主场景 我返回了,主场景监听ChooseLevelScene的返回按钮
      emit this->chooseScenceBack();

   });

   //显示当前关卡数
   QLabel * label = new QLabel;
   label->setParent(this);
   QFont font;
   font.setFamily("华文新魏");
   font.setPointSize(20);
   QString str1 = QString("Level: %1 ").arg(levelNum);
   //将字体设置到标签控件
   label->setFont(font);
   label->setText(str1);
   label->setGeometry(30, this->height() - 50,140, 50);
}

void PlayScene::paintEvent(QPaintEvent *ev)
{
    //创建背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap( 10,30,pix.width(),pix.height(),pix);

}

实现效果:

image-20220726143616745

创建金币背景图片:

1.创建一个金币类:

mycoin.h:

#ifndef MYCOIN_H
#define MYCOIN_H

#include <QPushButton>

class MyCoin : public QPushButton
{
    Q_OBJECT
public:
//    explicit MyCoin(QWidget *parent = nullptr);

    //参数代表传入的金币路径 还是银币的传入路径
    MyCoin(QString btnImg);

signals:

};

#endif // MYCOIN_H

mycoin.cpp

#include "mycoin.h"
#include <QDebug>

//MyCoin::MyCoin(QWidget *parent) : QPushButton(parent)
//{

//}

MyCoin::MyCoin(QString btnImg)
{
    QPixmap pix;
    bool ret = pix.load(btnImg);
    if(!ret){
      qDebug()  <<"图片加载失败";
      return;
    }
    this->setFixedSize(pix.width(),pix.height());
    this->setStyleSheet("QPushButton{border:0px}");
    this->setIcon(pix);
    this->setIconSize(QSize(pix.width(),pix.height()));
}

playsence.cpp

/创建金币的背景图片
       for(int i = 0 ; i < 4;i++)
       {
           for(int j = 0 ; j < 4; j++)
           {
              //绘制背景图片
               QPixmap pix = QPixmap(":/res/BoardNode(1).png");
               QLabel* label = new QLabel;
               label->setGeometry(0,0,pix.width(),pix.height());
               label->setPixmap(pix);
               label->setParent(this);
               label->move(57 + i*50,200+j*50);

               //创建金币
               MyCoin * coin = new MyCoin(":/res/Coin0001.png");
               coin->setParent(this);
               coin->move(59 + i*50,204+j*50);
           }
       }

实现效果:

image-20220726154151671

设置每个关卡的默认实现:

1.引入每个关卡的相关配置:

将文件放入项目目录中,再右键选择添加现有文件

image-20220726162533952

playscene.h:

#ifndef PLAYSCENE_H
#define PLAYSCENE_H
#include <QPaintEvent>
#include <QMainWindow>

class PlayScene : public QMainWindow
{
    Q_OBJECT
public:
    explicit PlayScene(QWidget *parent = nullptr);

    PlayScene(int levelNum);

    int levelIndex; //内部成员属性 记录所选关卡

    //重写绘图事件
    void paintEvent(QPaintEvent * ev);

    //设置二维数组 维护每个关卡的具体数据
    int gameArray[4][4];

signals:
    //写一个自定义信号 告诉主场景点击了返回
    void chooseScenceBack();

};

#endif // PLAYSCENE_H

playscene.cpp

#include "playscene.h"
#include <QDebug>
#include <QMenuBar>
#include <QPainter>
#include "mypushbutton.h"
#include <QLabel>
#include "mycoin.h"
#include "dataconfig.h"

//PlayScene::PlayScene(QWidget *parent) : QMainWindow(parent)
//{

//}

PlayScene::PlayScene(int levelNum)
{
   QString str = QString("进入了第 %1 关").arg(levelNum);
   qDebug() << str;
   this->levelIndex = levelNum;

   //初始化游戏场景
   //设置固定大小
   this->setFixedSize(640,1200);

   //设置图标
   this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

   //设置标题
   this->setWindowTitle("金币翻翻翻");

   //创建菜单栏
   QMenuBar * bar = menuBar();
   setMenuBar(bar);

   //创建开始菜单
   QMenu * startMenu = bar->addMenu("开始");

   //创建退出菜单项
   QAction * quitAction = startMenu->addAction("退出");

   //点击退出 实现退出游戏
   connect(quitAction,&QAction::triggered,[=](){
       this->close();
   });

   //返回按钮
   MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
   backBtn->setParent(this);
   backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

   //点击返回
   connect(backBtn,&MyPushButton::clicked,[=](){
      qDebug() <<"游戏界面点击了返回按钮";

//      //告诉主场景 我返回了,主场景监听ChooseLevelScene的返回按钮
      emit this->chooseScenceBack();

   });

   //显示当前关卡数
   QLabel * label = new QLabel;
   label->setParent(this);
   QFont font;
   font.setFamily("华文新魏");
   font.setPointSize(20);
   QString str1 = QString("Level: %1 ").arg(levelNum);
   //将字体设置到标签控件
   label->setFont(font);
   label->setText(str1);
   label->setGeometry(30, this->height() - 50,180, 50);

   dataConfig config;
   //初始化每个关卡的二维数组
   for(int i = 0;i<4;i++){
       for(int j=0;j<4;j++){
          this->gameArray[i][j] = config.mData[this->levelIndex][i][j];
       }
   }

   //创建金币的背景图片
       for(int i = 0 ; i < 4;i++)
       {
           for(int j = 0 ; j < 4; j++)
           {
              //绘制背景图片
               QPixmap pix = QPixmap(":/res/BoardNode(1).png");
               QLabel* label = new QLabel;
               label->setGeometry(0,0,pix.width(),pix.height());
               label->setPixmap(pix);
               label->setParent(this);
               label->move(57 + i*50,200+j*50);

               //创建金币
               QString coinStr;
               if(this->gameArray[i][j] == 1){
                   coinStr = ":/res/Coin0001.png";
               }else{
                   coinStr = ":/res/Coin0008.png";
               }
               MyCoin * coin = new MyCoin(coinStr);
               coin->setParent(this);
               coin->move(59 + i*50,204+j*50);
           }
       }

}

void PlayScene::paintEvent(QPaintEvent *ev)
{
    //创建背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap( 10,30,pix.width(),pix.height(),pix);

}

实现效果:

image-20220726163036782

金币翻转特效实现:

mycoin.h

#ifndef MYCOIN_H
#define MYCOIN_H
#include <QTimer>
#include <QPushButton>

class MyCoin : public QPushButton
{
    Q_OBJECT
public:
//    explicit MyCoin(QWidget *parent = nullptr);

    //参数代表传入的金币路径 还是银币的传入路径
    MyCoin(QString btnImg);

    //金币的属性
    int posX;  //x坐标位置
    int posY;  //y坐标位置
    bool flag; //正反标识

    //改变标志的方法
   void changeFlag();
   QTimer *timer1; //正面翻反面 定时器
   QTimer *timer2; //反面翻正面 定时器
   int min = 1; //最小图片
   int max = 8; //最大图片


signals:

};

#endif // MYCOIN_H

mycoin.cpp

#include "mycoin.h"
#include <QDebug>

//MyCoin::MyCoin(QWidget *parent) : QPushButton(parent)
//{

//}

MyCoin::MyCoin(QString btnImg)
{
    QPixmap pix;
    bool ret = pix.load(btnImg);
    if(!ret){
      qDebug()  <<"图片加载失败";
      return;
    }
    this->setFixedSize(pix.width(),pix.height());
    this->setStyleSheet("QPushButton{border:0px}");
    this->setIcon(pix);
    this->setIconSize(QSize(pix.width(),pix.height()));

    //初始化定时器对象
    timer1 = new QTimer(this);
    timer2 = new QTimer(this);

    //监听正面翻反面的信号,并且翻转金币
    connect(timer1,&QTimer::timeout,[=](){
       QPixmap pix;
       QString str = QString(":/res/Coin000%1").arg(this->min++);
       pix.load(str);

       this->setFixedSize(pix.width(),pix.height() );
       this->setStyleSheet("QPushButton{border:0px;}");
       this->setIcon(pix);
       this->setIconSize(QSize(pix.width(),pix.height()));

       //判断 如果翻完了,将min重置为1
       if(this->min > this->max) //如果大于最大值,重置最小值,并停止定时器
               {
                   this->min = 1;
                   timer1->stop();
               }

    });

    //监听反面翻正面的信号,并且翻转金币
    connect(timer2,&QTimer::timeout,[=](){
       QPixmap pix;
       QString str = QString(":/res/Coin000%1").arg(this->max--);
       pix.load(str);

       this->setFixedSize(pix.width(),pix.height() );
       this->setStyleSheet("QPushButton{border:0px;}");
       this->setIcon(pix);
       this->setIconSize(QSize(pix.width(),pix.height()));

       //判断 如果翻完了,将min重置为1
       if(this->max < this->min) //如果小于最小值,重置最大值,并停止定时器
               {
                   this->max = 8;
                   timer1->stop();
               }

    });
}

void MyCoin::changeFlag()
{
    if(this->flag) //如果是正面,执行下列代码
        {
            timer1->start(30);
            this->flag = false;
        }
        else //反面执行下列代码
        {
            timer2->start(30);
            this->flag = true;
        }

}

playscene.cpp

connect(coin,&MyCoin::clicked,[=](){
                //qDebug() << "点击的位置: x = " <<  coin->posX << " y = " << coin->posY ;
                coin->changeFlag();
                gameArray[i][j] = gameArray[i][j] == 0 ? 1 : 0; //数组内部记录的标志同步修改
            });

实现效果:

动画2

实现周围金币的翻转:

mycoin.h:

#ifndef MYCOIN_H
#define MYCOIN_H
#include <QTimer>
#include <QPushButton>

class MyCoin : public QPushButton
{
    Q_OBJECT
public:
//    explicit MyCoin(QWidget *parent = nullptr);

    //参数代表传入的金币路径 还是银币的传入路径
    MyCoin(QString btnImg);

    //金币的属性
    int posX;  //x坐标位置
    int posY;  //y坐标位置
    bool flag; //正反标识

    //改变标志的方法
   void changeFlag();
   QTimer *timer1; //正面翻反面 定时器
   QTimer *timer2; //反面翻正面 定时器
   int min = 1; //最小图片
   int max = 8; //最大图片

   //执行动画时候增加标志
   bool isAnimation = false;

   //重写鼠标按下事件
   void mousePressEvent(QMouseEvent *e);
signals:

};

#endif // MYCOIN_H

mycoin.cpp

#include "mycoin.h"
#include <QDebug>

//MyCoin::MyCoin(QWidget *parent) : QPushButton(parent)
//{

//}

MyCoin::MyCoin(QString btnImg)
{
    QPixmap pix;
    bool ret = pix.load(btnImg);
    if(!ret){
      qDebug()  <<"图片加载失败";
      return;
    }
    this->setFixedSize(pix.width(),pix.height());
    this->setStyleSheet("QPushButton{border:0px}");
    this->setIcon(pix);
    this->setIconSize(QSize(pix.width(),pix.height()));

    //初始化定时器对象
    timer1 = new QTimer(this);
    timer2 = new QTimer(this);

    //监听正面翻反面的信号,并且翻转金币
    connect(timer1,&QTimer::timeout,[=](){
       QPixmap pix;
       QString str = QString(":/res/Coin000%1").arg(this->min++);
       pix.load(str);

       this->setFixedSize(pix.width(),pix.height() );
       this->setStyleSheet("QPushButton{border:0px;}");
       this->setIcon(pix);
       this->setIconSize(QSize(pix.width(),pix.height()));

       //判断 如果翻完了,将min重置为1
       if(this->min > this->max) //如果大于最大值,重置最小值,并停止定时器
               {
                   this->min = 1;
                   isAnimation = false;
                   timer1->stop();
               }

    });

    //监听反面翻正面的信号,并且翻转金币
    connect(timer2,&QTimer::timeout,[=](){
       QPixmap pix;
       QString str = QString(":/res/Coin000%1").arg(this->max--);
       pix.load(str);

       this->setFixedSize(pix.width(),pix.height() );
       this->setStyleSheet("QPushButton{border:0px;}");
       this->setIcon(pix);
       this->setIconSize(QSize(pix.width(),pix.height()));

       //判断 如果翻完了,将min重置为1
       if(this->max < this->min) //如果小于最小值,重置最大值,并停止定时器
               {
                   this->max = 8;
                   isAnimation = false;
                   timer2->stop();
               }

    });
}

void MyCoin::changeFlag()
{
    if(this->flag) //如果是正面,执行下列代码
        {
            timer1->start(30);
            isAnimation = true; //开始做动画
            this->flag = false;
        }
        else //反面执行下列代码
        {
            timer2->start(30);
            isAnimation = true; //开始做动画
            this->flag = true;
        }

}

void MyCoin::mousePressEvent(QMouseEvent *e)
{
    if(this->isAnimation){
       return;
    }
    else{
       QPushButton::mousePressEvent(e);
    }
}

playscene.h:

#ifndef PLAYSCENE_H
#define PLAYSCENE_H
#include <QPaintEvent>
#include <QMainWindow>
#include "mycoin.h"


class PlayScene : public QMainWindow
{
    Q_OBJECT
public:
    explicit PlayScene(QWidget *parent = nullptr);

    PlayScene(int levelNum);

    int levelIndex; //内部成员属性 记录所选关卡

    //重写绘图事件
    void paintEvent(QPaintEvent * ev);

    //设置二维数组 维护每个关卡的具体数据
    int gameArray[4][4];

    //维护每一个coin的状态数组
    MyCoin * coinBtn[4][4];

signals:
    //写一个自定义信号 告诉主场景点击了返回
    void chooseScenceBack();

};

#endif // PLAYSCENE_H

playscene.cpp

#include "playscene.h"
#include <QDebug>
#include <QMenuBar>
#include <QPainter>
#include "mypushbutton.h"
#include <QLabel>
#include "mycoin.h"
#include "dataconfig.h"

//PlayScene::PlayScene(QWidget *parent) : QMainWindow(parent)
//{

//}

PlayScene::PlayScene(int levelNum)
{
   QString str = QString("进入了第 %1 关").arg(levelNum);
   qDebug() << str;
   this->levelIndex = levelNum;

   //初始化游戏场景
   //设置固定大小
   this->setFixedSize(640,1200);

   //设置图标
   this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

   //设置标题
   this->setWindowTitle("金币翻翻翻");

   //创建菜单栏
   QMenuBar * bar = menuBar();
   setMenuBar(bar);

   //创建开始菜单
   QMenu * startMenu = bar->addMenu("开始");

   //创建退出菜单项
   QAction * quitAction = startMenu->addAction("退出");

   //点击退出 实现退出游戏
   connect(quitAction,&QAction::triggered,[=](){
       this->close();
   });

   //返回按钮
   MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
   backBtn->setParent(this);
   backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

   //点击返回
   connect(backBtn,&MyPushButton::clicked,[=](){
      qDebug() <<"游戏界面点击了返回按钮";

//      //告诉主场景 我返回了,主场景监听ChooseLevelScene的返回按钮
      emit this->chooseScenceBack();

   });

   //显示当前关卡数
   QLabel * label = new QLabel;
   label->setParent(this);
   QFont font;
   font.setFamily("华文新魏");
   font.setPointSize(20);
   QString str1 = QString("Level: %1 ").arg(levelNum);
   //将字体设置到标签控件
   label->setFont(font);
   label->setText(str1);
   label->setGeometry(30, this->height() - 50,180, 50);

   dataConfig config;
   //初始化每个关卡的二维数组
   for(int i = 0;i<4;i++){
       for(int j=0;j<4;j++){
          this->gameArray[i][j] = config.mData[this->levelIndex][i][j];
       }
   }

   //创建金币的背景图片
       for(int i = 0 ; i < 4;i++)
       {
           for(int j = 0 ; j < 4; j++)
           {
              //绘制背景图片
               QPixmap pix = QPixmap(":/res/BoardNode(1).png");
               QLabel* label = new QLabel;
               label->setGeometry(0,0,pix.width(),pix.height());
               label->setPixmap(pix);
               label->setParent(this);
               label->move(57 + i*50,200+j*50);

               //创建金币
               QString coinStr;
               if(this->gameArray[i][j] == 1){
                   coinStr = ":/res/Coin0001.png";
               }else{
                   coinStr = ":/res/Coin0008.png";
               }
               MyCoin * coin = new MyCoin(coinStr);
               coin->setParent(this);
               coin->move(59 + i*50,204+j*50);

               //给金币属性赋值
               coin->posX = i;
               coin->posY = j;
               coin->flag = this->gameArray[i][j]; //1正面 0反面

               //将金币放到 金币的二维数组里 以便于后期的维护
               coinBtn[i][j] = coin;

               //点击金币 进行翻转
               connect(coin,&MyCoin::clicked,[=](){
                  coin->changeFlag();
                  //更新二维数组对应数据
                  this->gameArray[i][j] = this->gameArray[i][j] =0 ? 1 :0;

                  //翻转周围硬币  延时翻转
                  QTimer::singleShot(300,this,[=](){
                      if(coin->posX +1 <=3)//周围的右侧金币翻转条件
                      {
                          coinBtn[coin->posX+1][coin->posY]->changeFlag();
                          this->gameArray[coin->posX+1][coin->posY] = this->gameArray[coin->posX+1][coin->posY] =0 ? 1 :0;
                      }
                      if(coin->posX-1 >= 0)//周围的左侧金币翻转条件
                      {
                          coinBtn[coin->posX-1][coin->posY]->changeFlag();
                          this->gameArray[coin->posX-1][coin->posY] = this->gameArray[coin->posX-1][coin->posY] =0 ? 1 :0;
                      }
                      if(coin->posY -1 >= 0)//周围上侧硬币翻转条件
                      {
                          coinBtn[coin->posX][coin->posY-1]->changeFlag();
                          this->gameArray[coin->posX][coin->posY-1] = this->gameArray[coin->posX][coin->posY-1] =0 ? 1 :0;
                      }
                      if(coin->posY +1 <= 3)//周围下侧硬币翻转条件
                      {
                          coinBtn[coin->posX][coin->posY+1]->changeFlag();
                          this->gameArray[coin->posX][coin->posY+1] = this->gameArray[coin->posX][coin->posY+1] =0 ? 1 :0;
                      }
                  });

               });
           }
       }

}

void PlayScene::paintEvent(QPaintEvent *ev)
{
    //创建背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap( 10,30,pix.width(),pix.height(),pix);

}

实现效果:

动画2

胜利效果实现:

在MyCoin.h中加入 isWin标志,代表是否胜利。

bool isWin = true; //是否胜利

默认设置为true,只要有一个反面的金币,就将该值改为false,视为未成功。

代码写到延时翻金币后 进行判断

//判断是否胜利
                this->isWin = true;
                for(int i = 0 ; i < 4;i++)
                {
                    for(int j = 0 ; j < 4; j++)
                    {
                        //qDebug() << coinBtn[i][j]->flag ;
                        if( coinBtn[i][j]->flag == false)
                        {
                            this->isWin = false;
                            break;
                        }
                    }
                }

如果isWin依然是true,代表胜利了!

if(this->isWin)
       {
              qDebug() << "胜利";
       }

将胜利的图片提前创建好,如果胜利触发了,将图片弹下来即可

 QLabel* winLabel = new QLabel;
    QPixmap tmpPix;
    tmpPix.load(":/res/LevelCompletedDialogBg.png");
    winLabel->setGeometry(0,0,tmpPix.width(),tmpPix.height());
    winLabel->setPixmap(tmpPix);
    winLabel->setParent(this);
    winLabel->move( (this->width() - tmpPix.width())*0.5 , -tmpPix.height());

如果胜利了,将上面的图片移动下来

if(this->isWin)
    {
       qDebug() << "胜利";
        QPropertyAnimation * animation1 =  new QPropertyAnimation(winLabel,"geometry");
                        animation1->setDuration(1000);
                        animation1->setStartValue(QRect(winLabel->x(),winLabel->y(),winLabel->width(),winLabel->height()));
                        animation1->setEndValue(QRect(winLabel->x(),winLabel->y()+114,winLabel->width(),winLabel->height()));
                        animation1->setEasingCurve(QEasingCurve::OutBounce);
                        animation1->start();
                    }

当胜利后,应该禁用所有按钮的点击状态,可以在每个按钮中加入标志位 isWin,如果isWin为true,MousePressEvent直接return掉即可

MyCoin中.h里添加:

 bool isWin = false;//胜利标志

在鼠标按下事件中修改为

void MyCoin::mousePressEvent(QMouseEvent *e)
{
    if(this->isAnimation|| isWin == true )
    {
        return;
    }
    else
    {
        return QPushButton::mousePressEvent(e);
    }
}
//禁用所有按钮点击事件
    for(int i = 0 ; i < 4;i++)
      {
        for(int j = 0 ; j < 4; j++)
        {
          coinBtn[i][j]->isWin = true;
        }
}

全部实现代码:

mainsence.h

#ifndef MAINSENCE_H
#define MAINSENCE_H

#include <QMainWindow>
#include <chooselevelscence.h>

QT_BEGIN_NAMESPACE
namespace Ui { class MainSence; }
QT_END_NAMESPACE

class MainSence : public QMainWindow
{
    Q_OBJECT

public:
    MainSence(QWidget *parent = nullptr);
    ~MainSence();

    //重写paintEvent事件 画背景图
    void paintEvent(QPaintEvent *event);

    ChooseLevelScence * chooseLevelScence = NULL;
private:
    Ui::MainSence *ui;
};
#endif // MAINSENCE_H

mainsence.cpp

#include "mainsence.h"
#include "ui_mainsence.h"
#include <QPainter>
#include <mypushbutton.h>
#include <QDebug>
#include <QTimer>
#include <QSound> //多媒体模块下的音效文件
MainSence::MainSence(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainSence)
{
    ui->setupUi(this);
    //配置主场景

    //设置固定大小
    setFixedSize(640,1200);

    //设置图标
    setWindowIcon(QIcon(":/res/Coin0001.png"));

    //设置标题
    setWindowTitle("阿伟翻金币!");

    //退出按钮的实现
    connect(ui->actionquit,&QAction::triggered,[=](){
        this->close();
    });

    //准备开始按钮音效
    QSound * startSound = new QSound(":/res/TapButtonSound.wav",this);




    //开始按钮
    MyPushButton * startBtn = new MyPushButton(":/res/MenuSceneStartButton.png");

    startBtn->setParent(this);
    startBtn->move(this->width() *0.5 - startBtn->width() * 0.5,this->height() * 0.7);

    //实例化选择关卡的场景
    chooseLevelScence = new ChooseLevelScence;

    //监听选择关卡的返回按钮的信号
    connect(chooseLevelScence,&ChooseLevelScence::chooseScenceBack,this,[=](){
        this->setGeometry(chooseLevelScence->geometry());
        chooseLevelScence->hide();//将选择的关卡场景隐藏
        this->show(); //重新显示主场景
    });
    connect(startBtn,&MyPushButton::clicked,[=](){
        qDebug() <<"点击开始按钮";
        //播放开始音效资源
        startSound->play();
        //做弹起特效
        startBtn->zoom1();
        startBtn->zoom2();

        //延时进入到选择关卡场景中
        QTimer::singleShot(500,this,[=](){

            //设置chooseLevelScence场景的位置
            chooseLevelScence->setGeometry(this->geometry());
            //自身隐藏
            this->hide();
            //显式选择关卡场景
            chooseLevelScence->show();

        });

    });

}

MainSence::~MainSence()
{
    delete ui;
}

//设置背景图
void MainSence::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //画背景上图标
    pix.load(":/res/Title.png");
    //对于图标的缩放
//    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap(20,50,pix);
}

mypushbutton.h

#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H

#include <QPushButton>

class MyPushButton : public QPushButton
{
    Q_OBJECT
public:
//    explicit MyPushButton(QWidget *parent = nullptr);
    //构造函数  参数1 正常显示的图片路径 参数2 按下后显示的图片路径
    MyPushButton(QString normalImg,QString pressImg ="");

    //成员属性 保存用户传入的默认显示路径 以及按下后显示的图片路径
    QString normalImgPath;
    QString pressImgPath;

    //弹跳特效
    void zoom1();  //向下跳
    void zoom2();  //向上跳

    //重写按钮 按下和释放事件
    void mousePressEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *e);
signals:

};

#endif // MYPUSHBUTTON_H

mypushbutton.cpp

#include "mypushbutton.h"
#include <QDebug>
#include <QPropertyAnimation>

MyPushButton::MyPushButton(QString normalImg, QString pressImg)
{
    this->normalImgPath = normalImg;
    this->pressImgPath = pressImg;

    QPixmap pix;
    bool ret = pix.load(normalImg);
    if(!ret){
        qDebug()<<"图片加载失败!";
        return;
    }

    //设置图片固定大小
    this->setFixedSize(pix.width(),pix.height());

    //设置不规则图片样式
    this->setStyleSheet("QPushButton{border:0px}");

    //设置图标
    this->setIcon(pix);

    //设置图标大小
    this->setIconSize(QSize(pix.width(),pix.height()));
}

void MyPushButton::zoom1()
{
    //创建动画对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");
    //设置动画时间间隔
    animation->setDuration(200);
    //起始位置
    animation->setStartValue(QRect(this->x(),this->y(),this->width(),this->height()));
    //结束位置
    animation->setEndValue(QRect(this->x(),this->y()+10,this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();
}

void MyPushButton::zoom2()
{
    //创建动画对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");
    //设置动画时间间隔
    animation->setDuration(200);
    //起始位置
    animation->setStartValue(QRect(this->x(),this->y()-10,this->width(),this->height()));
    //结束位置
    animation->setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();
}

void MyPushButton::mousePressEvent(QMouseEvent *e)
{
    if(this->pressImgPath != ""){ //传入的按下图片不为空  说明需要有按下的状态
        QPixmap pix;
        bool ret = pix.load(this->pressImgPath);
        if(!ret)
        {
            qDebug() <<"图片加载失败";
            return;
        }

        //设置图片固定大小
        this->setFixedSize(pix.width(),pix.height());

        //设置不规则图片样式
        this->setStyleSheet("QPushButton{border:0px}");

        //设置图标
        this->setIcon(pix);

        //设置图标大小
        this->setIconSize(QSize(pix.width(),pix.height()));
    }
    //让父类执行其他内容
    return QPushButton::mousePressEvent(e);
}

void MyPushButton::mouseReleaseEvent(QMouseEvent *e)
{
    if(this->pressImgPath != ""){ //传入的按下图片不为空  说明需要有按下的状态,切换为初始图片
        QPixmap pix;
        bool ret = pix.load(this->normalImgPath);
        if(!ret)
        {
            qDebug() <<"图片加载失败";
            return;
        }

        //设置图片固定大小
        this->setFixedSize(pix.width(),pix.height());

        //设置不规则图片样式
        this->setStyleSheet("QPushButton{border:0px}");

        //设置图标
        this->setIcon(pix);

        //设置图标大小
        this->setIconSize(QSize(pix.width(),pix.height()));
    }

    //让父类执行其他内容
    return QPushButton::mouseReleaseEvent(e);

}

chooselevelsence.h

#ifndef CHOOSELEVELSCENCE_H
#define CHOOSELEVELSCENCE_H
#include "playscene.h"
#include <QMainWindow>

class ChooseLevelScence : public QMainWindow
{
    Q_OBJECT
public:
    explicit ChooseLevelScence(QWidget *parent = nullptr);

    //重写绘图事件
    void paintEvent(QPaintEvent * ev);

    //游戏场景对象指针
    PlayScene * play = NULL;

signals:
    //写一个自定义信号 告诉主场景点击了返回
    void chooseScenceBack();
};

#endif // CHOOSELEVELSCENCE_H

chooselevelsence.cpp

#include "chooselevelscence.h"
#include <QMenuBar>
#include <QPainter>
#include <mypushbutton.h>
#include <QDebug>
#include <QLabel>
#include <QSound>
#include "playscene.h"
ChooseLevelScence::ChooseLevelScence(QWidget *parent) : QMainWindow(parent)
{
    //配置选择关卡场景
    this->setFixedSize(640,1200);

    //设置图标
    this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

    //设置标题
    this->setWindowTitle("选择关卡场景");

    //创建菜单栏
    QMenuBar * bar = menuBar();
    setMenuBar(bar);

    //创建开始菜单
    QMenu * startMenu = bar->addMenu("开始");

    //创建退出菜单项
    QAction * quitAction = startMenu->addAction("退出");

    //点击退出 实现退出游戏
    connect(quitAction,&QAction::triggered,[=](){
        this->close();
    });

    //选择关卡按钮音效
    QSound *chooseSound = new QSound(":/res/TapButtonSound.wav",this);

    //返回按钮音效
    QSound *backSound = new QSound(":/res/BackButtonSound.wav",this);

    //返回按钮
    MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
    backBtn->setParent(this);
    backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

    //点击返回
    connect(backBtn,&MyPushButton::clicked,[=](){
       qDebug() <<"点击了返回按钮";

       backSound->play();
       //告诉主场景 我返回了,主场景监听ChooseLevelScene的返回按钮
       emit this->chooseScenceBack();

    });

    //创建选择关卡的按钮
    for(int i =0; i<20; i++){
       MyPushButton * menuBtn = new MyPushButton(":/res/LevelIcon.png");
       menuBtn->setParent(this);
       menuBtn->move(80 + (i%4) * 140,200 + (i/4) * 140);

       //监听每个按钮的绑定事件
       connect(menuBtn,&MyPushButton::clicked,[=](){
           //播放选择关卡声音
           chooseSound->play();
           QString str = QString("您选择的是第 %1 关").arg(i+1);
           qDebug() << str;

           //进入游戏场景
           this->hide();//将选关场景隐藏
           play = new PlayScene(i+1); //创建游戏的场景
           //设置游戏场景的初始位置
           play->setGeometry(this->geometry());
           play->show();  //显示游戏场景

           connect(play,&PlayScene::chooseScenceBack,[=](){
               this->setGeometry(play->geometry());
              this->show();
               delete play;
               play = NULL;
           });
       });
       QLabel * label = new QLabel;
       label->setParent(this);
       label->setFixedSize(menuBtn->width(),menuBtn->height());
       label->setText(QString::number(i+1));
       label->move(80 + (i%4) * 140,200 + (i/4) * 140);

       //设置label文字对齐方式 垂直居中 水平居中
       label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
       //设置让鼠标进行穿透,否则在上面的事件监听后,Qlabel无法接受鼠标事件  51号属性
       label->setAttribute(Qt::WA_TransparentForMouseEvents);

    }
}


void ChooseLevelScence::paintEvent(QPaintEvent *ev)
{
    //加载背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/OtherSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
//    painter.drawPixmap( (this->width() - pix.width())*0.5,30,pix.width(),pix.height(),pix);
    painter.drawPixmap(20,50,pix);

}

mycoin.h

#ifndef MYCOIN_H
#define MYCOIN_H
#include <QTimer>
#include <QPushButton>

class MyCoin : public QPushButton
{
    Q_OBJECT
public:
//    explicit MyCoin(QWidget *parent = nullptr);

    //参数代表传入的金币路径 还是银币的传入路径
    MyCoin(QString btnImg);

    //金币的属性
    int posX;  //x坐标位置
    int posY;  //y坐标位置
    bool flag; //正反标识

    //改变标志的方法
   void changeFlag();
   QTimer *timer1; //正面翻反面 定时器
   QTimer *timer2; //反面翻正面 定时器
   int min = 1; //最小图片
   int max = 8; //最大图片

   //执行动画时候增加标志
   bool isAnimation = false;

   //重写鼠标按下事件
   void mousePressEvent(QMouseEvent *e);

   //是否胜利的标志
   bool isWin = false;
signals:

};

#endif // MYCOIN_H

mycoin.cpp

#include "mycoin.h"
#include <QDebug>

//MyCoin::MyCoin(QWidget *parent) : QPushButton(parent)
//{

//}

MyCoin::MyCoin(QString btnImg)
{
    QPixmap pix;
    bool ret = pix.load(btnImg);
    if(!ret){
      qDebug()  <<"图片加载失败";
      return;
    }
    this->setFixedSize(pix.width(),pix.height());
    this->setStyleSheet("QPushButton{border:0px}");
    this->setIcon(pix);
    this->setIconSize(QSize(pix.width(),pix.height()));

    //初始化定时器对象
    timer1 = new QTimer(this);
    timer2 = new QTimer(this);

    //监听正面翻反面的信号,并且翻转金币
    connect(timer1,&QTimer::timeout,[=](){
       QPixmap pix;
       QString str = QString(":/res/Coin000%1").arg(this->min++);
       pix.load(str);

       this->setFixedSize(pix.width(),pix.height() );
       this->setStyleSheet("QPushButton{border:0px;}");
       this->setIcon(pix);
       this->setIconSize(QSize(pix.width(),pix.height()));

       //判断 如果翻完了,将min重置为1
       if(this->min > this->max) //如果大于最大值,重置最小值,并停止定时器
               {
                   this->min = 1;
                   isAnimation = false;
                   timer1->stop();
               }

    });

    //监听反面翻正面的信号,并且翻转金币
    connect(timer2,&QTimer::timeout,[=](){
       QPixmap pix;
       QString str = QString(":/res/Coin000%1").arg(this->max--);
       pix.load(str);

       this->setFixedSize(pix.width(),pix.height() );
       this->setStyleSheet("QPushButton{border:0px;}");
       this->setIcon(pix);
       this->setIconSize(QSize(pix.width(),pix.height()));

       //判断 如果翻完了,将min重置为1
       if(this->max < this->min) //如果小于最小值,重置最大值,并停止定时器
               {
                   this->max = 8;
                   isAnimation = false;
                   timer2->stop();
               }

    });
}

void MyCoin::changeFlag()
{
    if(this->flag) //如果是正面,执行下列代码
        {
            timer1->start(30);
            isAnimation = true; //开始做动画
            this->flag = false;
        }
        else //反面执行下列代码
        {
            timer2->start(30);
            isAnimation = true; //开始做动画
            this->flag = true;
        }

}

void MyCoin::mousePressEvent(QMouseEvent *e)
{
    if(this->isAnimation || this->isWin){
       return;
    }
    else{
       QPushButton::mousePressEvent(e);
    }
}

dataconfig.h

#ifndef DATACONFIG_H
#define DATACONFIG_H

#include <QObject>
#include <QMap>
#include <QVector>

class dataConfig : public QObject
{
    Q_OBJECT
public:
    explicit dataConfig(QObject *parent = 0);

public:

    QMap<int, QVector< QVector<int> > >mData;



signals:

public slots:
};

#endif // DATACONFIG_H

dataconfig.cpp

#include "dataconfig.h"
#include <QDebug>
dataConfig::dataConfig(QObject *parent) : QObject(parent)
{

     int array1[4][4] = {{1, 1, 1, 1},
                        {1, 1, 0, 1},
                        {1, 0, 0, 0},
                        {1, 1, 0, 1} } ;

     QVector< QVector<int>> v;
     for(int i = 0 ; i < 4;i++)
     {
         QVector<int>v1;
         for(int j = 0 ; j < 4;j++)
         {

            v1.push_back(array1[i][j]);
         }
         v.push_back(v1);
     }

     mData.insert(1,v);


     int array2[4][4] = { {1, 0, 1, 1},
                          {0, 0, 1, 1},
                          {1, 1, 0, 0},
                          {1, 1, 0, 1}} ;

     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array2[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(2,v);



     int array3[4][4] = {  {0, 0, 0, 0},
                           {0, 1, 1, 0},
                           {0, 1, 1, 0},
                           {0, 0, 0, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array3[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(3,v);


     int array4[4][4] = {   {0, 1, 1, 1},
                            {1, 0, 0, 1},
                            {1, 0, 1, 1},
                            {1, 1, 1, 1}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array4[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(4,v);


     int array5[4][4] = {  {1, 0, 0, 1},
                           {0, 0, 0, 0},
                           {0, 0, 0, 0},
                           {1, 0, 0, 1}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array5[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(5,v);


     int array6[4][4] = {   {1, 0, 0, 1},
                            {0, 1, 1, 0},
                            {0, 1, 1, 0},
                            {1, 0, 0, 1}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array6[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(6,v);


     int array7[4][4] = {   {0, 1, 1, 1},
                            {1, 0, 1, 1},
                            {1, 1, 0, 1},
                            {1, 1, 1, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array7[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(7,v);

     int array8[4][4] = {  {0, 1, 0, 1},
                           {1, 0, 0, 0},
                           {0, 0, 0, 1},
                           {1, 0, 1, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array8[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(8,v);

     int array9[4][4] = {   {1, 0, 1, 0},
                            {1, 0, 1, 0},
                            {0, 0, 1, 0},
                            {1, 0, 0, 1}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array9[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(9,v);



     int array10[4][4] = {  {1, 0, 1, 1},
                            {1, 1, 0, 0},
                            {0, 0, 1, 1},
                            {1, 1, 0, 1}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array10[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(10,v);


     int array11[4][4] = {  {0, 1, 1, 0},
                            {1, 0, 0, 1},
                            {1, 0, 0, 1},
                            {0, 1, 1, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array11[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(11,v);

     int array12[4][4] = {  {0, 1, 1, 0},
                            {0, 0, 0, 0},
                            {1, 1, 1, 1},
                            {0, 0, 0, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array12[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(12,v);


     int array13[4][4] = {    {0, 1, 1, 0},
                              {0, 0, 0, 0},
                              {0, 0, 0, 0},
                              {0, 1, 1, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array13[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(13,v);

     int array14[4][4] = {    {1, 0, 1, 1},
                              {0, 1, 0, 1},
                              {1, 0, 1, 0},
                              {1, 1, 0, 1}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array14[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(14,v);


     int array15[4][4] = {   {0, 1, 0, 1},
                             {1, 0, 0, 0},
                             {1, 0, 0, 0},
                             {0, 1, 0, 1}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array15[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(15,v);


     int array16[4][4] = {   {0, 1, 1, 0},
                             {1, 1, 1, 1},
                             {1, 1, 1, 1},
                             {0, 1, 1, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array16[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(16,v);

     int array17[4][4] = {  {0, 1, 1, 1},
                            {0, 1, 0, 0},
                            {0, 0, 1, 0},
                            {1, 1, 1, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array17[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(17,v);


     int array18[4][4] = { {0, 0, 0, 1},
                           {0, 0, 1, 0},
                           {0, 1, 0, 0},
                           {1, 0, 0, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array18[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(18,v);

     int array19[4][4] = {   {0, 1, 0, 0},
                             {0, 1, 1, 0},
                             {0, 0, 1, 1},
                             {0, 0, 0, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array19[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(19,v);

     int array20[4][4] = {  {0, 0, 0, 0},
                            {0, 0, 0, 0},
                            {0, 0, 0, 0},
                            {0, 0, 0, 0}} ;
     v.clear();
     for(int i = 0 ; i < 4;i++)
     {
          QVector<int>v1;
          for(int j = 0 ; j < 4;j++)
          {
             v1.push_back(array20[i][j]);
          }
          v.push_back(v1);
     }

     mData.insert(20,v);


     //测试数据
//    for( QMap<int, QVector< QVector<int> > >::iterator it = mData.begin();it != mData.end();it++ )
//    {
//         for(QVector< QVector<int> >::iterator it2 = (*it).begin(); it2!= (*it).end();it2++)
//         {
//            for(QVector<int>::iterator it3 = (*it2).begin(); it3 != (*it2).end(); it3++ )
//            {
//                qDebug() << *it3 ;
//            }
//         }
//         qDebug() << endl;
//    }


}

playsence.h

#ifndef PLAYSCENE_H
#define PLAYSCENE_H
#include <QPaintEvent>
#include <QMainWindow>
#include "mycoin.h"


class PlayScene : public QMainWindow
{
    Q_OBJECT
public:
    explicit PlayScene(QWidget *parent = nullptr);

    PlayScene(int levelNum);

    int levelIndex; //内部成员属性 记录所选关卡

    //重写绘图事件
    void paintEvent(QPaintEvent * ev);

    //设置二维数组 维护每个关卡的具体数据
    int gameArray[4][4];

    //维护每一个coin的状态数组
    MyCoin * coinBtn[4][4];

    //判断是否胜利
    bool isWin;
signals:
    //写一个自定义信号 告诉主场景点击了返回
    void chooseScenceBack();

};

#endif // PLAYSCENE_H

playsence.cpp

#include "playscene.h"
#include <QDebug>
#include <QMenuBar>
#include <QPainter>
#include "mypushbutton.h"
#include <QLabel>
#include "mycoin.h"
#include "dataconfig.h"
#include <QPropertyAnimation>
#include <QSound>

//PlayScene::PlayScene(QWidget *parent) : QMainWindow(parent)
//{

//}

PlayScene::PlayScene(int levelNum)
{
   QString str = QString("进入了第 %1 关").arg(levelNum);
   qDebug() << str;
   this->levelIndex = levelNum;

   //初始化游戏场景
   //设置固定大小
   this->setFixedSize(640,1200);

   //设置图标
   this->setWindowIcon(QPixmap(":/res/Coin0001.png"));

   //设置标题
   this->setWindowTitle("金币翻翻翻");

   //创建菜单栏
   QMenuBar * bar = menuBar();
   setMenuBar(bar);

   //创建开始菜单
   QMenu * startMenu = bar->addMenu("开始");

   //创建退出菜单项
   QAction * quitAction = startMenu->addAction("退出");

   //翻金币音效
   QSound *flipSound = new QSound(":/res/ConFlipSound.wav",this);
       //胜利按钮音效
   QSound *winSound = new QSound(":/res/LevelWinSound.wav",this);

   //点击退出 实现退出游戏
   connect(quitAction,&QAction::triggered,[=](){
       this->close();
   });

   //返回按钮
   MyPushButton * backBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
   backBtn->setParent(this);
   backBtn->move(this->width()-backBtn->width(),this->height()-backBtn->height());

   //点击返回
   connect(backBtn,&MyPushButton::clicked,[=](){
      qDebug() <<"游戏界面点击了返回按钮";

//      //告诉主场景 我返回了,主场景监听ChooseLevelScene的返回按钮
      emit this->chooseScenceBack();

   });

   //显示当前关卡数
   QLabel * label = new QLabel;
   label->setParent(this);
   QFont font;
   font.setFamily("华文新魏");
   font.setPointSize(20);
   QString str1 = QString("Level: %1 ").arg(levelNum);
   //将字体设置到标签控件
   label->setFont(font);
   label->setText(str1);
   label->setGeometry(30, this->height() - 50,180, 50);

   dataConfig config;
   //初始化每个关卡的二维数组
   for(int i = 0;i<4;i++){
       for(int j=0;j<4;j++){
          this->gameArray[i][j] = config.mData[this->levelIndex][i][j];
       }
   }

   //胜利图片显示
   QLabel* winLabel = new QLabel;
       QPixmap tmpPix;
       tmpPix.load(":/res/LevelCompletedDialogBg.png");
       winLabel->setGeometry(0,0,tmpPix.width(),tmpPix.height());
       winLabel->setPixmap(tmpPix);
       winLabel->setParent(this);
       winLabel->move( (this->width() - tmpPix.width())*0.5 , -tmpPix.height());


   //创建金币的背景图片
       for(int i = 0 ; i < 4;i++)
       {
           for(int j = 0 ; j < 4; j++)
           {
              //绘制背景图片
               QPixmap pix = QPixmap(":/res/BoardNode(1).png");
               QLabel* label = new QLabel;
               label->setGeometry(0,0,pix.width(),pix.height());
               label->setPixmap(pix);
               label->setParent(this);
               label->move(57 + i*50,200+j*50);

               //创建金币
               QString coinStr;
               if(this->gameArray[i][j] == 1){
                   coinStr = ":/res/Coin0001.png";
               }else{
                   coinStr = ":/res/Coin0008.png";
               }
               MyCoin * coin = new MyCoin(coinStr);
               coin->setParent(this);
               coin->move(59 + i*50,204+j*50);

               //给金币属性赋值
               coin->posX = i;
               coin->posY = j;
               coin->flag = this->gameArray[i][j]; //1正面 0反面

               //将金币放到 金币的二维数组里 以便于后期的维护
               coinBtn[i][j] = coin;

               //点击金币 进行翻转
               connect(coin,&MyCoin::clicked,[=](){
                  flipSound->play();
                  coin->changeFlag();
                  //更新二维数组对应数据
                  this->gameArray[i][j] = this->gameArray[i][j] =0 ? 1 :0;

                  //翻转周围硬币  延时翻转
                  QTimer::singleShot(300,this,[=](){
                      if(coin->posX +1 <=3)//周围的右侧金币翻转条件
                      {
                          coinBtn[coin->posX+1][coin->posY]->changeFlag();
                          this->gameArray[coin->posX+1][coin->posY] = this->gameArray[coin->posX+1][coin->posY] =0 ? 1 :0;
                      }
                      if(coin->posX-1 >= 0)//周围的左侧金币翻转条件
                      {
                          coinBtn[coin->posX-1][coin->posY]->changeFlag();
                          this->gameArray[coin->posX-1][coin->posY] = this->gameArray[coin->posX-1][coin->posY] =0 ? 1 :0;
                      }
                      if(coin->posY -1 >= 0)//周围上侧硬币翻转条件
                      {
                          coinBtn[coin->posX][coin->posY-1]->changeFlag();
                          this->gameArray[coin->posX][coin->posY-1] = this->gameArray[coin->posX][coin->posY-1] =0 ? 1 :0;
                      }
                      if(coin->posY +1 <= 3)//周围下侧硬币翻转条件
                      {
                          coinBtn[coin->posX][coin->posY+1]->changeFlag();
                          this->gameArray[coin->posX][coin->posY+1] = this->gameArray[coin->posX][coin->posY+1] =0 ? 1 :0;
                      }

                      //判断是否胜利
                      this->isWin = true;
                      for(int i = 0;i < 4;i++)
                      {
                          for(int j = 0;j < 4;j++)
                          {
                              if(coinBtn[i][j]->flag == false)
                              {
                                  this->isWin = false;
                                  break;
                              }
                          }
                      }
                      if(this->isWin == true)
                      {
                         //胜利了
                          qDebug() <<"游戏胜利";
                          winSound->play();
                          //将所有按钮的胜利标志改为true 再次点击按钮 直接return 不做响应
                          for(int i = 0;i < 4; i++)
                          {
                              for(int j = 0;j < 4;j++){
                                  coinBtn[i][j]->isWin =true;
                              }
                          }
                          //将胜利的图片移动下来
                          QPropertyAnimation * animation = new QPropertyAnimation(winLabel,"geometry");
                          //设置时间间隔
                          animation->setDuration(1000);
                          //设置开始位置
                          animation->setStartValue(QRect(winLabel->x(),winLabel->y(),winLabel->width(),winLabel->height()));
                          //设置结束位置
                          animation->setEndValue(QRect(winLabel->x(),winLabel->y()+114,winLabel->width(),winLabel->height()));
                          //设置缓和曲线
                          animation->setEasingCurve(QEasingCurve::OutBounce);
                          //执行动画
                          animation->start();
                      }
                  });

               });
           }
       }

}

void PlayScene::paintEvent(QPaintEvent *ev)
{
    //创建背景
    QPainter painter(this);
    QPixmap pix;
    pix.load(":/res/PlayLevelSceneBg.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);

    //加载标题
    pix.load(":/res/Title.png");
    pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
    painter.drawPixmap( 10,30,pix.width(),pix.height(),pix);

}
posted @   小伟99  阅读(148)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示