@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文件
2.编辑qrc文件
用记事本打开
添加以下内容
<RCC>
<qresource prefix="/">
<file>Config/finishNeves.txt</file>
</qresource>
</RCC>
其中 Config/finishNeves.txt 便是我们要添加的资源的路径
其实推荐使用notepad++打开
使用资源
右键资源库中的文件 可以直接获取文件路径
窗口图标的添加
先把图标文件(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 可以设置窗口大小。
效果图:
文件图标的添加
回到项目文件夹 复制你的文件图标(XX.ico)进来,然后再创建一个XX.rc的文件
编辑XX.rc文件
IDI_ICON1 ICON DISCARDABLE "XX.ico"
回到qt creator里 打开项目的pro文件(xx.pro)
到末尾添加
RC_FILE = XX.rc
总结
个人感觉这个七巧板还是比较好做的,虽然需要baidu很多前置知识,但入了门(真的吗?)后会发现轮子真是太好用了,无脑写就行了(比写题爽~),但写之前应当对整个流程有一个大概的了解,不然很可能会出现牵一发而动全身的情况
因为多文件以及ui类的特殊性就不放代码了,但仍希望大家能多多指正,因为自己本身也是第一次接触qt这种东西,难免会有一些片面的理解,见谅,见谅