前言
准备开新坑了。这个坑可大可小,而且我不急于一个月两个月写完这第一篇。目前在学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)”解决方案