【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)”解決方案

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