@TOC

前言

这个东西是学期的课程设计,因为最近几天不是很想写题,所以就提前把这个东西做了,这是本人第一次用qt做东西,因此此blog 仅供参考不建议任何人学习

成品图

图案-茶壶选择拼图
移动拼图
变换拼图

要求

要求
要求

思路

利用QPaintEvent在主窗口中绘制图形,用容器(咱用的vector)中的结构体储存每一个图形的坐标、类型、偏转角,图形操作(例如移动,选择,变换)用QAction绑定快捷键实现按键操作,每次修改完参数再update()重新绘制图形。

实现流程

图形绘制

因为QPainter只能在paintEevent中创建(在别的地方麻烦),所以我的操作是遍历结构体容器,根据类型再进行下一步操作
其中elementList是结构体Vector容器,paintObject是结构体,rota是偏转角

void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter:: Antialiasing, true);//反取样 or 抗锯齿?
    for(int i = 0; i < elementList.size(); i++)//遍历容器
    {
        paintObject nowPaint = elementList[i];
        int nextX = nowPaint.mouseX;
        int nextY = nowPaint.mouseY;
        int nextT = nowPaint.type;
        int nextR = nowPaint.rota;
        int tempX,tempY;
        tempX = nextX;
        tempY = nextY;
        painter.setBrush(elementColor[i+1]);
        painter.translate(nextX,nextY);
        painter.rotate(nextR);
        nextX = nextY = 0;
        if(nextT == 1)
            paintRectangle(&painter,nextX,nextY);
        if(nextT == 2)
            paintTriangle(&painter,nextX,nextY,200,100);
        if(nextT == 3)
            paintTriangle(&painter,nextX,nextY,282,141);
        if(nextT == 4)
            paintTriangle(&painter,nextX,nextY,400,200);
        if(nextT == 5)
            paintParallelogram(&painter,nextX,nextY);
        painter.rotate(-nextR);
        painter.translate(-tempX,-tempY);
    }
}

具体图形

正方形

void MainWindow::paintRectangle(QPainter *painter,int X,int Y)
{
    painter->drawRect(X,Y,141,141);
}

三角形

void MainWindow::paintTriangle(QPainter *painter,int X,int Y,int Height,int Width)
{
    QPainterPath triangle;
    triangle.moveTo(X,Y);
    triangle.lineTo(X+Height/2,Y-Width);
    triangle.lineTo(X+Height,Y);
    triangle.lineTo(X,Y);
    painter->drawPath(triangle);
}

平行四边形

void MainWindow::paintParallelogram(QPainter *painter,int X,int Y)
{
    QPainterPath parallelogram;
    parallelogram.moveTo(X,Y);
    parallelogram.lineTo(X+100,Y-100);
    parallelogram.lineTo(X+300,Y-100);
    parallelogram.lineTo(X+200,Y);
    parallelogram.lineTo(X,Y);
    painter->drawPath(parallelogram);
}

图形颜色

先预处理一遍颜色,用map让每一个拼图对应唯一颜色
然后在遍历容器进行具体绘制是设置颜色
elementColor是<int,QColor>的映射

预处理

这个QColor开始不会用,写的应该有些复杂

void MainWindow::preworkColor()
{
    QColor red,yellow,green,cyan,blue,violet,orange;
    red.setRed(255);
    yellow.setRed(255),yellow.setGreen(255);
    green.setGreen(255);
    cyan.setGreen(255),cyan.setBlue(255);
    blue.setBlue(255);
    violet.setRed(255),violet.setBlue(255);
    orange.setRed(255),orange.setGreen(165);
    elementColor[1] = red;
    elementColor[2] = yellow;
    elementColor[3] = green;
    elementColor[4] = cyan;
    elementColor[5] = blue;
    elementColor[6] = violet;
    elementColor[7] = orange;
}

染色

因为vector是从0开始的,所以是i+1

for(遍历容器)
	//do something
	painter.setBrush(elementColor[i+1]);
	//paintElement

图形旋转

利用painter的translate设置坐标原点,然后用rotate旋转坐标轴,绘制图形,复位

for(遍历容器)
	//do something
    painter.translate(nextX,nextY);
    painter.rotate(nextR);
    //paintElement
    painter.translate(-nextX,-nextY);
    painter.rotate(-nextR);

值得注意的是,我们定义图形的坐标为原点后,之后绘制图形的坐标应当为(0,0),而且在复位时,应当按照类似“先进先出”的顺序复位

拼图选择

定义一个nowSelect储存当前选择的拼图
当QAction触发信号时改变nowSelect的值
信号:triggered()
槽:

void MainWindow::on_action_select1_triggered()
{
    nowSelect = 1;
}

拼图操作

与拼图的选择类似,当QAction触发信号时改变当前选择的拼图的属性,改变完update()更新绘制
同样因为vector从0开始,所以下标 [nowSelect-1] 对应的才是第nowSelect个拼图

移动:

void MainWindow::on_action_moveUp_triggered()
{
    elementList[nowSelect-1].mouseY -= 25;
    update();
}

变换:

void MainWindow::on_action_elementTypeChange_triggered()
{
    elementList[nowSelect-1].type++;
    if(elementList[nowSelect-1].type == 6)
        elementList[nowSelect-1].type = 1;
   update();
}

旋转:

void MainWindow::on_action_elementRotateChange_triggered()
{
    elementList[nowSelect-1].rota += 45;
    if(elementList[nowSelect-1].rota >= 360)
        elementList[nowSelect-1].rota -= 360;
    update();
}

文件操作

文件格式

第一行应该有一个 n 表示图形数量(虽然七巧板是7个拼图,但刚开始做的是类似画图的东西,因此没有限制数量)
接下来n行,每行有4个数 a b c d
a 表示拼图类型
b 表示拼图的x轴坐标
c 表示拼图的y轴坐标
d 表示拼图的旋转角

打开文件

先用QFileDialog获取文件路径,之后就是常规的文件读取了

void MainWindow::on_action_openFile_triggered()
{
    QString path = QFileDialog::getOpenFileName(this,
                                                tr("Open File"),
                                                ".",
                                                tr("Text Files(*.txt)"));
    if(!path.isEmpty())
    {
        QFile file(path);
        file.open(QIODevice::WriteOnly | QIODevice::Text);
        elementList.clear();
        QTextStream in(&file);
        int totNum;
        in >> totNum;
        for(int i = 0; i < totNum; i++)
        {
            paintObject tempObject;
            in >> tempObject.type >> tempObject.mouseX >> tempObject.mouseY >> tempObject.rota;
            elementList.push_back(tempObject);
        }
        file.close();
        update();
    }
    else
    {
        QMessageBox::warning(this, tr("Path"),
                             tr("You did not select any file."));
    }
}

保存文件

同样是用QFileDialog获取文件路径

void MainWindow::on_action_saveFile_triggered()
{
    QString path = QFileDialog::getSaveFileName(this,
                                                tr("Open File"),
                                                ".",
                                                tr("Text Files(*.txt)"));
    if(!path.isEmpty())
    {
        QFile file(path);
        file.open(QIODevice::WriteOnly | QIODevice::Text);
        QTextStream out(&file);
        out << elementList.size() << endl;
        for(int i = 0; i < elementList.size(); i++)
            out << elementList[i].type << " " << elementList[i].mouseX << " " << elementList[i].mouseY << " " << elementList[i].rota << endl;
        file.close();
    }
    else
    {
        QMessageBox::warning(this, tr("Path"),
                             tr("You did not select any file."));
    }
}

预设图案及初始图案

预设和初始的图案均按照格式储存
利用不同QAction传递的不同参数打开不同的预设图案
预设图案也是类似的道理,在执行构造函数的时候打开文件
值得注意的是,我们的预设图案应当储存在项目的资源库中方便调用(防止篡改

资源库相关

创建资源库

1.在项目文件夹创建qrc文件
qrc

2.编辑qrc文件
用记事本打开
添加以下内容

<RCC>
    <qresource prefix="/">
        <file>Config/finishNeves.txt</file>
    </qresource>
</RCC>

其中 Config/finishNeves.txt 便是我们要添加的资源的路径
其实推荐使用notepad++打开
notepad++

使用资源

右键资源库中的文件 可以直接获取文件路径
path

窗口图标的添加

先把图标文件(XX.ico)扔进资源库,之后再窗口的构造函数中调用 setWindowIcon 即可
在这里插入图片描述

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("Neves");
    setWindowIcon(QIcon(":/Config/Neves.ico"));
    preworkColor();
    readConfig(1);
    resize(1280,720);
}

顺带一提,setWindowTitle 可以设置窗口标题,resize 可以设置窗口大小。
效果图:
效果1
在这里插入图片描述

文件图标的添加

回到项目文件夹 复制你的文件图标(XX.ico)进来,然后再创建一个XX.rc的文件
在这里插入图片描述
编辑XX.rc文件
在这里插入图片描述

IDI_ICON1   ICON   DISCARDABLE   "XX.ico"

回到qt creator里 打开项目的pro文件(xx.pro)
到末尾添加

RC_FILE = XX.rc

在这里插入图片描述

总结

个人感觉这个七巧板还是比较好做的,虽然需要baidu很多前置知识,但入了门(真的吗?)后会发现轮子真是太好用了,无脑写就行了(比写题爽~),但写之前应当对整个流程有一个大概的了解,不然很可能会出现牵一发而动全身的情况

因为多文件以及ui类的特殊性就不放代码了,但仍希望大家能多多指正,因为自己本身也是第一次接触qt这种东西,难免会有一些片面的理解,见谅,见谅

Q.E.D.