【QT】《Qt5.9 C++开发指南》准备做个视觉小说

前言

准备开新坑了。这个坑可大可小,而且我不急于一个月两个月写完这第一篇。目前在学QT,所以打算用QT做。先做个视觉小说来设计一个简单的框架,然后在年底开始第二阶段,打算做个小游戏编辑工具——按去年那时我的想法来说,这个工具可以算是引擎了——虽然我以前下载了GameMaker和RPGMaker但是不怎么会用,但是最近几乎每天都在B站上看一个UP直播恐怖游戏,而且似乎都是RPGMaker做的。看的多了,又让我想起了曾经的激情。


视图、场景、图形项

在这里插入图片描述
首先需要被介绍的概念是视口窗口。这两个概念都是用来表示window,并不一定指同一个,两个概念中的window的座标系不同。
视口以左上角为原点,窗口以中心为原点。

视图就是一个视口,场景和图形项都是窗口。视图是场景的父项,场景项是图形项的父项,而图形项下还可以有图形项。图形项包括矩形、椭圆、多边形、直线、文字、图片等。

My_GraphicsView

新建一个继承于QMainWindow的项目,在编辑UI时删除工具栏和状态栏,菜单栏可能之后有用。设置MainWindow的初始大小为805X505,这是为了保证里面的窗口大小是800X500。还需要设置最小大小为805X505。

在MainWindow中先放入一个Scroll Area,然后右键布局为栅格布局。然后在其中放入一个Graphics View控件,右键布局为栅格布局。把两个布局的属性,比如布局宽度等设置为零。
对QGraphicsView类进行提升,更改为一个自定义的子类,比如My_GraphicsView(之后会定义)
在这里插入图片描述
然后新建C++ Class,命名为My_GraphicsView,继承于QGraphicsView

//my_graphicsview.h
#pragma once
#include <QGraphicsView>
class My_GraphicsView : public QGraphicsView
{

    Q_OBJECT

public:
    My_GraphicsView(QWidget* parent=nullptr);

}

//my_graphicsview.cpp
#include "my_graphicsview.h"
My_GraphicsView::My_GraphicsView(QWidget* parent):QGraphicsView (parent)
{

}

自定义消息与自定义槽

1、QT两大系统,信号与槽,属性系统。属性系统没怎么用过,但是信号与槽很熟悉了。在编辑UI时,在Signals and Slots Editor可以定义一整套信号发送和接收流程,当然是简单的。比如定义当Pushbutton按下时,发送信号到窗口,触发窗口的关闭。这种自动的、非代码的信号与槽操作只能做一些简单的效果。

2、想要个性的话需要在某个控件或者Action上右击,选择转到槽,然后从跳出的窗口中多个slot函数中选择一个,这样就可以在选择的slots函数内进行代码操作。这是第二个阶段。
第二个阶段的信号与槽的使用还存在些限制,一是自动创建的slot函数是固定的,只能从有限的几个里面选择,二是只能操作在ui界面的控件或者是Action上,对于不出现在ui界面上的动作没有办法。

3、然后更高级一点就是自定义slot和自定义关联。自动创建的slot是不用自己去手动关联的,其信号发送者和接收者是一一对应的。除此之外的其他关联就需要自己做,自己定义的槽函数也需要自己关联。
简而言之,自动创建的slot只能一对一,而复杂的情况还有很多——多对一、一对多、多对多。

4、之后就是自定义signals、自定义slots、自定义关联。所谓的信号,不过是个命名而已,不需要实现,信号函数的名字只用作区分其与其他信号。

//my_graphicsview.h 
protected:
    void keyPressEvent(QKeyEvent *event);
    void mouseDoubleClickEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);

signals:
    void s_keyClicked(QKeyEvent* event);
    void s_mouseDoubleClicked(QPoint point);//这个用不到
    void s_mouseClicked(QPoint point);
    void s_mouseMove(QPoint point);

//my_graphicsview.cpp

void My_GraphicsView::keyPressEvent(QKeyEvent *event){

//    static int i=0;
//    qDebug()<<"keyPressEvent被执行了 "<<++i;
    emit s_keyClicked(event);
    QGraphicsView::keyPressEvent(event);

}

void My_GraphicsView::mouseDoubleClickEvent(QMouseEvent *event){

//    static int i=0;
//    qDebug()<<"mouseDoubleClickEvent "<<++i;
    if(event->button()==Qt::LeftButton){
        QPoint p=event->pos();
        emit s_mouseDoubleClicked(p);
    }
    QGraphicsView::mouseDoubleClickEvent(event);

}
void My_GraphicsView::mousePressEvent(QMouseEvent *event){

//    static int i=0;
//    qDebug()<<"mousePressEvent "<<++i;
    if(event->button()==Qt::LeftButton){
        QPoint p=event->pos();
        emit s_mouseClicked(p);
    }

    QGraphicsView::mousePressEvent(event);


}
void My_GraphicsView::mouseMoveEvent(QMouseEvent *event){

    //    static int i=0;
    //    qDebug()<<"mouseMoveEvent "<<++i;
    QPoint p=event->pos();
    emit s_mouseMove(p);
    QGraphicsView::mouseMoveEvent(event);
}

接收My_GraphicsView信号及相关处理

在加载文件时为什么使用…/untitled/images/*** 而不使用 images/*** 是因为build与项目文件目录不同,而本地路径是从其可执行文件开始的,所以需要跳出 build 文件夹,再到项目文件夹下去选择资源。

这个GALGAME Demo实现了贴图、按钮、文本框、文字、按键处理、按F11全屏、按C切图

//mainwindow.h
#pragma once
#include <QMainWindow>
#include <QFileDialog>
#include <QDebug>
#include <QMainWindow>
#include <QGraphicsScene>
#include <QLabel>
#include <QGraphicsItem>
#include <QTime>
#include <QDialog>
#include <QColorDialog>
#include <QFontDialog>
#include <QFileDialog>
#include <QInputDialog>//输入对话框
#include <QGraphicsRotation>
#include <QGraphicsDropShadowEffect>
#include <QMessageBox>




namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();



private slots: //自定义slots
    void on_keyClicked(QKeyEvent* event);
    void on_mouseClicked(QPoint point);
    void on_mouseMove(QPoint point);



    //重写接口
protected:
    void resizeEvent(QResizeEvent* event);
    void paintEvent(QPaintEvent *event);
    void closeEvent(QCloseEvent* event);

private:
    QGraphicsScene* scene;

    int flag_interface=1;
    void paint1(QPaintEvent* event);
    void paint2(QPaintEvent* event);

private:
    Ui::MainWindow *ui;
};

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    this->setWindowTitle("untitled");//窗口名
    //mouse1.png中心被当做鼠标原点,所以箭头被放在了右下角的位置,保证箭头
    //顶点就是鼠标原点
    setCursor(QCursor(QPixmap("../untitled/images/mouse1.png")));//自定义光标




    //为视图设置一个场景
    int w=width()-5;//右下角座标(895,595)
    int h=height()-5;
    scene=new QGraphicsScene(-w/2,-h/2,w,h);
    ui->view->setScene(scene);
    //ui->view->setCursor(Qt::CrossCursor);
    ui->view->setMouseTracking(true);
    ui->view->setBackgroundBrush(QBrush(Qt::white));
    ui->view->setDragMode(QGraphicsView::NoDrag);






    connect(ui->view,SIGNAL(s_keyClicked(QKeyEvent*)),this,SLOT(on_keyClicked(QKeyEvent*)));
    connect(ui->view,SIGNAL(s_mouseClicked(QPoint)),this,SLOT(on_mouseClicked(QPoint)));
    connect(ui->view,SIGNAL(s_mouseMove(QPoint)),this,SLOT(on_mouseMove(QPoint)));







}

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

void MainWindow::on_keyClicked(QKeyEvent* event){

    if(event->key()==Qt::Key_Escape){
        showNormal();
    }
    else if(event->key()==Qt::Key_F11){
        if(isFullScreen())showNormal();
        else showFullScreen();
    }
    else if(event->key()==Qt::Key_C){
        flag_interface=!flag_interface;
        qDebug()<<"flag_interface="<<flag_interface;
        repaint();//强行触发paintEvent
        //update();//在Qt下一次处理事件时才调用一次绘制事件
    }
}

void MainWindow::on_mouseClicked(QPoint point){

   QGraphicsItem* it=ui->view->itemAt(point);
   if(it!=nullptr){
        static int i=0;

        qDebug()<<"yes "<<++i;
   }

}
void MainWindow::on_mouseMove(QPoint point){
    qDebug()<<"mouse at>>"<<point.x()<<","<<point.y();
}

void MainWindow::resizeEvent(QResizeEvent* event){

    static int i=0;
    qDebug()<<"resizeEvent被执行了 "<<++i;
    //如何同比例放大呢?除了全屏外都是同样的比例,全屏可以不同
	//to be continue

}

void MainWindow::paintEvent(QPaintEvent *event){

    static int i=0;
    qDebug()<<"paint... "<<++i;
    if(flag_interface==1)paint1(event);
    else
        paint2(event);


    QMainWindow::paintEvent(event);

}

void MainWindow::closeEvent(QCloseEvent* event){

    QMessageBox::StandardButton result=QMessageBox::question(this,
                                                             "╭(╯^╰)╮","确定退出游戏吗?",
                                                             QMessageBox::Yes|QMessageBox::Cancel,
                                                             QMessageBox::Cancel);
    if(result==QMessageBox::Yes)event->accept();
    else event->ignore();

}

void MainWindow::paint1(QPaintEvent* event){

    int w=width()-5;
    int h=height()-5;

    //背景
    QGraphicsPixmapItem* it2=new QGraphicsPixmapItem;
    QPixmap pix2;
    pix2.load("../untitled/images/background.jpg");//加载一张图片
    QPixmap p=pix2.scaled(w+2,h+2);
    it2->setPixmap(p);
    it2->setPos(-w/2,-h/2);
    scene->addItem(it2);


    //按钮
    QGraphicsRectItem* it=new QGraphicsRectItem(0,0,100,40);
    QPixmap texturePixmap("../untitled/images/texture.png");
    QBrush brush;
    brush.setStyle(Qt::TexturePattern);
    brush.setTexture(texturePixmap);
    it->setBrush(brush);
    it->setPen(Qt::NoPen);//无边框
    it->setPos(0,0);
    scene->addItem(it);




    //贴一张文本框的图
    QGraphicsPixmapItem* it4=new QGraphicsPixmapItem;
    QPixmap pix;
    pix.load("../untitled/images/text_frame.png");//加载一张图片
    QPixmap p2=pix.scaledToWidth(w+2);
    it4->setPixmap(p2);
    it4->setPos(-w/2,-h/2);
    scene->addItem(it4);




    //文字
    //做一个图形项:文字
    QGraphicsTextItem* it3=new QGraphicsTextItem("小鹏[优纪就由由乃来保护(*^▽^*)]");
    QFont font=this->font();//返回是一个地址
    font.setBold(true);
    font.setPointSize(20);
    it3->setFont(font);
    it3->setDefaultTextColor(QColor(100,100,100));
    it3->setPos(-w/2,h/2-100);
    scene->addItem(it3);





}
void MainWindow::paint2(QPaintEvent* event){

    int w=width()-5;
    int h=height()-5;

    //背景
    QGraphicsPixmapItem* it2=new QGraphicsPixmapItem;
    QPixmap pix2;
    pix2.load("../untitled/images/good.jpg");//加载一张图片
    QPixmap p=pix2.scaled(w+2,h+2);
    it2->setPixmap(p);
    it2->setPos(-w/2,-h/2);
    scene->addItem(it2);

}

执行效果在最后

其他

解决中文乱码

在pro文件里添加下面的内容

win32-msvc*: {
    QMAKE_CFLAGS *= /utf-8
    QMAKE_CXXFLAGS *= /utf-8
}

添加资源

在项目目录里建一个文件夹images,在其中存放一些要用到的图片。
1、在项目里新建一个qrc类型的资源文件file.qrc
2、在file.qrc上右击选择Open in Editor
3、添加一个前缀,把默认前缀 /new/prefix1 改为images
3、添加文件,把文件加images里的图片都添加上

设置程序图标

找张ICO类型的图片,或者使用格式工厂来转换一张图片为ICO类型。把这种图片添加到资源里后,在pro文件里添加下面这一行,即找到这张图片的位置

RC_ICONS = images/app.ico

在这里插入图片描述

设置鼠标光标

在MainWindow类中,可以为这个窗口类设置光标,也可以为视图 ui->view 设置光标。这些光标都可以看做图形项,当设置一张图片为光标时,图片中心就是光标的顶点——所以在画光标的时候,需要把箭头画在图片四分之一的右下角的区域,这样箭头的顶点就是光标的原点了。

setCursor(Qt::CrossCursor);//十字光标
setCursor(Qt::WaitCursor);//圆圈光标
//...
setCursor(QCursor(QPixmap("images/mouse1.png")));//自定义光标

在这里插入图片描述

发布程序

1,首先选择一个Release,编译一下
在这里插入图片描述
2,一般项目目录与build目录是不同的,在Release对应的build目录中找到可执行文件 untitled.exe 。这时候点击但是执行不了,但是没事不用管。
3,把 untitled.exe 放入一个新建的文件夹中,比如E:\DOP
4,找到一个QT部署工具,在开始里面找。必须选择对应Release的版本的。
在这里插入图片描述
4,点击打开这个工具后,找到 untitled.exe ,使用 windeployqt untitled.exe来部署。
这时候程序就自动把要用到的DLL都添加过来了——这里有个images文件夹需要自己复制过来。可是就算这样,点击可执行文件后没有图片!!
因为build与项目目录是分开的,资源文件放在项目文件夹里,而可执行文件在另外的地方。所以这时候可以修改程序里的文件相对路径就行了。修改后再找到新的untitled.exe,复制过来替换这一个,一切OK。
在这里插入图片描述
5、删除点没用的,删除完后这个目录还有18.7MB
在这里插入图片描述

执行效果如下
在这里插入图片描述

参考:
《Qt5.9 C++开发指南》
QT之程序发布以及打包成exe安装包
Qt打包程序提示“应用程序无法正常启动(0xc000007b)”解决方案

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章