设为首页 加入收藏

TOP

一个简易画板的实现(Graphics View)(三)
2012-11-13 13:23:21 来源: 作者: 【 】 浏览:1358
Tags:一个 简易 画板 实现 Graphics  View

 

    我们把PaintWidget当做一个scene,因此PaintWidget现在是继承QGraphicsScene,而不是前面的QWidget.

    paintwidget.h

    #ifndef PAINTWIDGET_H

    #define PAINTWIDGET_H

    #include <QtGui>

    #include <QDebug>

    #include "shape.h"

    #include "line.h"

    #include "rect.h"

    class PaintWidget : public QGraphicsScene

    {

    Q_OBJECT

    public:

    PaintWidget(QWidget *parent = 0);

    public slots:

    void setCurrentShape(Shape::Code s)

    {

    if(s != currShapeCode) {

    currShapeCode = s;

    }

    }

    protected:

    void mousePressEvent(QGraphicsSceneMouseEvent *event);

    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);

    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

    private:

    Shape::Code currShapeCode;

    Shape *currItem;

    bool perm;

    };

    #endif // PAINTWIDGET_H

    paintwidget.cpp

    #include "paintwidget.h"

    PaintWidget::PaintWidget(QWidget *parent)

    : QGraphicsScene(parent), currShapeCode(Shape::Line), currItem(NULL), perm(false)

    {

    }

    void PaintWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)

    {

    switch(currShapeCode)

    {

    case Shape::Line:

    {

    Line *line = new Line;

    currItem = line;

    addItem(line);

    break;

    }

    case Shape::Rect:

    {

    Rect *rect = new Rect;

    currItem = rect;

    addItem(rect);

    break;

    }

    }

    if(currItem) {

    currItem->startDraw(event);

    perm = false;

    }

    QGraphicsScene::mousePressEvent(event);

    }

    void PaintWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)

    {

    if(currItem && !perm) {

    currItem->drawing(event);

    }

    QGraphicsScene::mouseMoveEvent(event);

    }

    void PaintWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)

    {

    perm = true;

    QGraphicsScene::mouseReleaseEvent(event);

    }

    我们把继承自QWidget改成继承自QGraphicsScene,同样也会有鼠标事件,只不过在这里我们把鼠标事件全部转发给具体的item进行处理。这个我们会在下面的代码中看到。另外一点是,每一个鼠标处理函数都包含了调用其父类函数的语句。

    shape.h

    #ifndef SHAPE_H

    #define SHAPE_H

    #include <QtGui>

    class Shape

    {

    public:

    enum Code {

    Line,

    Rect

    };

    Shape();

    virtual void startDraw(QGraphicsSceneMouseEvent * event) = 0;

    virtual void drawing(QGraphicsSceneMouseEvent * event) = 0;

    };

    #endif // SHAPE_H

    shape.cpp

    #include "shape.h"

    Shape::Shape()

    {

    }

    Shape类也有了变化:还记得我们曾经说过,Qt内置了很多item,因此我们不必全部重写这个item.所以,我们要使用Qt提供的类,就不需要在我们的类里面添加新的数据成员了。这样,我们就有了不带有额外的数据成员的Shape.那么,为什么还要提供Shape呢?因为我们在scene 的鼠标事件中需要修改这些数据成员,如果没有这个父类,我们就需要按照Code写一个长长的switch来判断是那一个图形,这样是很麻烦的。所以我们依然创建了一个公共的父类,只要调用这个父类的draw函数即可。

    line.h

    #ifndef LINE_H

    #define LINE_H

    #include <QGraphicsLineItem>

    #include "shape.h"

    class Line : public Shape, public QGraphicsLineItem

    {

    public:

    Line();

    void startDraw(QGraphicsSceneMouseEvent * event);

    void drawing(QGraphicsSceneMouseEvent * event);

    };

    #endif // LINE_H

    line.cpp

    #include "line.h"

    Line::Line()

    {

    }

    void Line::startDraw(QGraphicsSceneMouseEvent * event)

    {

    setLine(QLineF(event->scenePos(), event->scenePos()));

    }

    void Line::drawing(QGraphicsSceneMouseEvent * event)

    {

    QLineF newLine(line()。p1(), event->scenePos());

    setLine(newLine);

    }

    Line类已经和前面有了变化,我们不仅仅继承了Shape,而且继承了QGraphicsLineItem类。这里我们使用了C++(www.cppentry.com)的多继承机制。这个机制是很危险的,很容易发生错误,但是这里我们的Shape并没有继承其他的类,只要函数没有重名,一般而言是没有问题的。如果不希望出现不推荐的多继承(不管怎么说,多继承虽然危险,但它是符合面向对象理论的),那就就想办法使用组合机制。我们之所以使用多继承,目的是让Line类同时具有 Shape和QGraphicsLineItem的性质,从而既可以直接添加到QGraphicsScene中,又可以调用startDraw()等函数。

    同样的还有Rect这个类:

    rect.h

    #ifndef RECT_H

    #define RECT_H

    #include <QGraphicsRectItem>

    #include "shape.h"

    class Rect : public Shape, public QGraphicsRectItem

    {

    public:

    Rect();

    void startDraw(QGraphicsSceneMouseEvent * event);

    void drawing(QGraphicsSceneMouseEvent * event);

    };

    #endif // RECT_H

    rect.cpp

    #include "rect.h"

    Rect::Rect()

    {

    }

    void Rect::startDraw(QGraphicsSceneMouseEvent * event)

    {

    setRect(QRectF(event->scenePos(), QSizeF(0, 0)));

    }

    void Rect::drawing(QGraphicsSceneMouseEvent * event)

    {

    QRectF r(rect()。topLeft(), QSizeF(event->scenePos()。x() - rect()。topLeft()。x(), event->scenePos()。y() - rect()。topLeft()。y()));

    setRect(r);

    }

    Line和Rect类的逻辑都比较清楚,和前面的基本类似。所不同的是,Qt并没有使用我们前面定义的两个Qpoint对象记录数据,而是在 QGraphicsLineItem中使用QLineF,在QGraphicsRectItem中使用QRectF记录数据。这显然比我们的两个点的数据记录高级得多。其实,我们也完全可以使用这样的数据结构去重定义前面那些Line之类。

    这样,我们的程序就修改完毕了。运行一下你会发现,几乎和前面的实现没有区别。这里说"几乎",是在第一个点画下的时候,scene会移动一段距离。这是因为scene是自动居中的,由于我们把Line的第一个点设置为(0, 0),因此当我们把鼠标移动后会有一个偏移。

    看到这里或许并没有显示出Graphics View的优势。不过,建议在Line或者Rect的构造函数里面加上下面的语句,

    setFlag(QGraphicsItem::ItemIsMovable, true);

    setFlag(QGraphicsItem::ItemIsSelectable, true);

    此时,你的Line和Rect就已经支持选中和拖放了!值得试一试哦!不过,需要注意的是,我们重写了scene的鼠标控制函数,所以这里的拖动会很粗糙,甚至说是不正确,你需要动动脑筋重新设计我们的类啦!

      

首页 上一页 1 2 3 4 5 6 下一页 尾页 3/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇JNA 调用 so 库 下一篇C/C++下使用SQLite轻量级数据库

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: