功能需求:
自由图形绘制;基本图形绘制(直线,矩形,椭圆);能够选择图形绘制颜色。
解决方案:
1、以QWidget为基类创建绘图主窗口;
2、使用QGroupBox创建图形设置区域;
3、使用单选按钮QRadioBox实现目标图形的选择;
4、使用组合框QCombox实现绘图颜色的选择。
定义一个枚举类型的变量用来保存绘图类型:
enum DrawType
{
NONE,
FREE,
LINE,
RECT,
ELLIPSE
};
定义一个结构体变量,用来保存绘图所需的类型、颜色、点坐标等参数:
struct DrawParam
{
DrawType type;
Qt::GlobalColor color;
QList<QPoint> points;
};
根据图形视图框架的设计原则,无需创建新的类,所以直接就可以在widget类中完成所有视图组件的编写。
在绘图的过程中,必须跟踪鼠标的移动踪迹,因此我们需要获得鼠标移动时经过的所有点坐标,并且选取合适的数据结构进行数据保存。
QList<DrawParam> m_drawList;
DrawParam m_current;
在main()函数中,对成员变量进行初始化:
m_current.type = NONE;
m_current.color = Qt::white;
m_current.points.clear();
我们需要编写以下函数:
1、以鼠标按下为开始,记录开始坐标:mousePressEvent();
2、记录鼠标移动时经过的像素坐标:mouseMoveEvent();
3、以鼠标释放为结束,记录结束坐标:mouseReleaseEvent();
4、按照记录顺序在每个坐标之间进行图形绘制,重写paintEvent()函数。
在实现上述函数之前,针对不同绘图类型,实现一个append()函数,对不同类型绘图的点进行进队列。
void Widget::append(QPoint p)
{
if( m_current.type != NONE )
{
if( m_current.type == FREE )
{
m_current.points.append(p);
}
else
{
if( m_current.points.count() == 2 )
{
m_current.points.removeLast();
}
m_current.points.append(p);
}
}
}
因为基础图形的目标是固定的,只是开始点的坐标和结束点的坐标会对形状起决定性作用,所以在鼠标按下时,确定起始坐标,松开时确定最终图形。
void Widget::mousePressEvent(QMouseEvent *evt)
{
m_current.type = drawType();
m_current.color = drawColor();
m_current.points.append(evt->pos());
}
void Widget::mouseMoveEvent(QMouseEvent *evt)
{
append(evt->pos());
update();
}
void Widget::mouseReleaseEvent(QMouseEvent *evt)
{
append(evt->pos());
m_drawList.append(m_current);
m_current.type = NONE;
m_current.color = Qt::white;
m_current.points.clear();
update();
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
for(int i=0; i<m_drawList.count(); i++)
{
draw(painter, m_drawList[i]);
}
draw(painter, m_current);
}
定义一个绘图函数,用于具体绘图的实现。
void Widget::draw(QPainter& painter, DrawParam& param)
{
if( (param.type != NONE) && (param.points.count() >= 2)) // 2 point to draw
{
int x = (param.points[0].x() < param.points[1].x()) ? param.points[0].x() : param.points[1].x();
int y = (param.points[0].y() < param.points[1].y()) ? param.points[0].y() : param.points[1].y();
int w = qAbs(param.points[0].x() - param.points[1].x()) + 1;
int h = qAbs(param.points[0].y() - param.points[1].y()) + 1;
painter.setPen(QPen(param.color));
painter.setBrush(QBrush(Qt::NoBrush));
switch (param.type)
{
case FREE:
for(int i=0; i<param.points.count()-1; i++)
{
painter.drawLine(param.points[i], param.points[i+1]);
}
break;
case LINE:
painter.drawLine(param.points[0], param.points[1]);
break;
case RECT:
painter.drawRect(x, y, w, h);
break;
case ELLIPSE:
painter.drawEllipse(x, y, w, h);
break;
default:
break;
}
}
}
颜色选择函数:
Qt::GlobalColor Widget::drawColor()
{
Qt::GlobalColor ret = Qt::black;
if( m_colorBox.currentText() == "Black" ) ret = Qt::black;
if( m_colorBox.currentText() == "Blue" ) ret = Qt::blue;
if( m_colorBox.currentText() == "Green" ) ret = Qt::green;
if( m_colorBox.currentText() == "Red" ) ret = Qt::red;
if( m_colorBox.currentText() == "Yellow" ) ret = Qt::yellow;
return ret;
}
其他组件的定义和设置比较简单,最终的实现效果如下:
绘图程序的简单实现如上所述,读者若有兴趣,可以在此基础上实现更多的功能,比如更多图形的绘制、橡皮擦的功能实现,或者使用工具栏、菜单栏等windows自带绘图工具的样式进行设计。
程序启动画面(QSplashScreen)
在上述程序的基础上,在main()函数中添加代码:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPixmap pixmap("Kobe.jpg");
pixmap = pixmap.scaled(QSize(pixmap.width()/3, pixmap.height()/3));
QSplashScreen splash(pixmap);
splash.show();
Sleep(1000);
Widget w;
w.show();
return a.exec();
}
其中Sleep()函数是windows.h文件下的函数,表示此窗口暂时持续一段时间,其他程序处于休眠状态。
实现效果就是在图片加载1000ms后,主程序开始启动加载。
注:图片必须保存在相应工程的debug文件夹下