【问题标题】:Arc in QGraphicsSceneQGraphicsScene中的弧线
【发布时间】:2014-11-13 04:48:41
【问题描述】:

我想在 QGraphicsScene 中实现 arc。我希望在单击三个点时绘制我的弧,以便在单击三个点时绘制弧,其中第一个点将是弧的起点,第二个将是弧上的任何点,第三个将是弧的终点。我曾尝试研究 drawArc 函数,但对 startangle 和 spanangle 感到困惑。我无法动态设置它们。请给我建议一些方法。


我尝试将解决方案嵌入到我的项目中,但出现以下错误:

error: cannot allocate an object of abstract type 'arc'
                 arcItem = new arc(++id, startP, midP, endP);

你能帮我解决这个问题吗?我正在为我的项目提供部分代码。 在 cadgraphicsscene 的 mousepress 事件中,我做了以下事情。

cadgraphicsscene.cpp

    void CadGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
    {
        // mousePressEvent in the graphicsScene
        if(mouseEvent->button() == Qt::LeftButton)
        {
            switch (entityMode)
            {

            case ArcMode:
                if (mFirstClick)
                {
                    startP = mouseEvent->scenePos();
                    mFirstClick = false;
                    mSecondClick = true;
                }

                else if (!mFirstClick && mSecondClick)
                {
                    midP = mouseEvent->scenePos();
                    mFirstClick = false;
                    mSecondClick = false;
                    mThirdClick = true;
                }

                else if (!mSecondClick && mThirdClick)
                {
                    endP = mouseEvent->scenePos();
                    mThirdClick = false;
                    mPaintFlag = true;
                }

                if (mPaintFlag)
                {
                    arcItem = new arc(++id, startP, midP, endP);
                    itemList.append(arcItem);
                    mUndoStack->push(new CadCommandAdd(this, arcItem));
                    setFlags();
                }
            }
        }
   } 

arc.cpp

#include "arc.h"

arc::arc(int i, QPointF point1, QPointF point2, QPointF point3)
{
    // assigns id
    id = i;
    p1 = point1;
    p2 = point2;
    p3 = point3;

    lineBC(point2, point3);
    lineAC(point1, point3);
    lineBA(point2, point1);

    rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));

    bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
    bisectorBC.setAngle(lineBC.normalVector().angle());

    bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
    bisectorBA.setAngle(lineBA.normalVector().angle());


    bisectorBA.intersect(bisectorBC, &center);

    ellipse = new QGraphicsEllipseItem(center.x() - rad, center.y() - rad, rad*2, rad*2);

    lineOA(center, point1);
    lineOC(center, point3);
}

arc::arc(int i, QLineF start, QLineF end)
{
    // assigns id
    id = i;

    lineOA.angle() = start;
    lineOC.angle() - lineOA.angle() = end;
}

int arc::type() const
{
    // Enable the use of qgraphicsitem_cast with arc item.
    return Type;
}

void arc::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                QWidget *widget)
{
    QPen paintpen;
    painter->setRenderHint(QPainter::Antialiasing);
    paintpen.setWidth(1);

    if (isSelected())
    {
        // sets brush for end points
        painter->setBrush(Qt::SolidPattern);
        paintpen.setColor(Qt::red);
        painter->setPen(paintpen);

        paintpen.setStyle(Qt::DashLine);
        paintpen.setColor(Qt::black);
        painter->setPen(paintpen);
        painter->drawArc(ellipse->boundingRect(),lineOA.angle(),lineOC.angle() - lineOA.angle());
    }
    else
    {
        painter->setBrush(Qt::SolidPattern);
        paintpen.setColor(Qt::black);
        painter->setPen(paintpen);
        painter->drawArc(ellipse->boundingRect(),lineOA.angle(),lineOC.angle() - lineOA.angle());

    }
}

arc.h

include <QGraphicsItem>

#include "qmath.h"
class arc : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    arc(int, QPointF, QPointF, QPointF);
    arc(int, QLineF, QLineF);


    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                       QWidget *widget);
    enum { Type = UserType + 6 };
    int type() const;
    int id;

    QPointF startP, midP, endP, p1, p2, p3,center;
    QLineF lineBC;
    QLineF lineAC;
    QLineF lineBA;
    QLineF lineOA;
    QLineF lineOC;
    QLineF bisectorBC;
    QLineF bisectorBA;
    QGraphicsEllipseItem *ellipse;
    qreal rad;

private:
    QVector<QPointF> stuff;

};

#endif // ARC_H

请帮我解决错误。

【问题讨论】:

    标签: c++ qt qgraphicsview qgraphicsscene


    【解决方案1】:

    这听起来可以用一些相对简单的数学来解决:

    https://www.google.com/search?q=define%20circle%20three%20points

    https://math.stackexchange.com/a/213678

    https://www.khanacademy.org/math/geometry/triangle-properties/perpendicular_bisectors/v/three-points-defining-a-circle

    这是我将数学翻译成 Qt 的优点

    // m_points is a QList<QPointF>
    // use math to define the circle
    QLineF lineBC(m_points.at(1), m_points.at(2));
    QLineF lineAC(m_points.at(0), m_points.at(2));
    QLineF lineBA(m_points.at(1), m_points.at(0));
    qreal rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
    
    QLineF bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
    bisectorBC.setAngle(lineBC.normalVector().angle());
    
    QLineF bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
    bisectorBA.setAngle(lineBA.normalVector().angle());
    
    QPointF center;
    bisectorBA.intersect(bisectorBC, &center);
    
    qDebug() << rad << center;
    

    QT QGraphicsScene Drawing Arc

    QPainterPath* path = new QPainterPath();
    path->arcMoveTo(0,0,50,50,20);
    path->arcTo(0,0,50,50,20, 90);
    scene.addPath(*path);
    

    将所有这些放在一个不错的小项目中变成这样:

    https://github.com/peteristhegreat/ThreePointsCircle

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    

    主窗口.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
        ~MainWindow();
    };
    
    #endif // MAINWINDOW_H
    

    主窗口.cpp

    #include "mainwindow.h"
    #include <QGraphicsView>
    #include <QGraphicsScene>
    #include "graphicsscene.h"
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        QGraphicsView * view = new QGraphicsView;
    
        GraphicsScene * scene = new GraphicsScene();
        view->setScene(scene);
    
        view->setSceneRect(-300,-300, 300, 300);
        this->resize(600, 600);
    
        this->setCentralWidget(view);
    }
    
    MainWindow::~MainWindow()
    {
    
    }
    

    图形场景.h

    #ifndef GRAPHICSSCENE_H
    #define GRAPHICSSCENE_H
    
    #include <QGraphicsScene>
    #include <QGraphicsSceneMouseEvent>
    #include <QPointF>
    #include <QList>
    
    class GraphicsScene : public QGraphicsScene
    {
        Q_OBJECT
    public:
        explicit GraphicsScene(QObject *parent = 0);
        virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent);
        virtual void mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent);
        virtual void mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent);
        virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent * mouseEvent);
    signals:
    
    public slots:
    
    private:
        QList <QPointF> m_points;
    };
    
    #endif // GRAPHICSSCENE_H
    

    图形场景.cpp

    #include "graphicsscene.h"
    #include <QDebug>
    #include <QGraphicsEllipseItem>
    #include <QGraphicsPathItem>
    #include <QPainterPath>
    #include "qmath.h"
    
    GraphicsScene::GraphicsScene(QObject *parent) :
        QGraphicsScene(parent)
    {
        this->setBackgroundBrush(Qt::gray);
    }
    
    void GraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent)
    {
        qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
        QGraphicsScene::mouseDoubleClickEvent(mouseEvent);
    }
    
    void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent)
    {
        qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
        QGraphicsScene::mouseMoveEvent(mouseEvent);
    }
    
    void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)
    {
        qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
        QGraphicsScene::mousePressEvent(mouseEvent);
    }
    
    void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent * me)
    {
        qDebug() << Q_FUNC_INFO << me->scenePos();
        int radius = 20;
        QGraphicsEllipseItem * ellipse = this->addEllipse(me->scenePos().x() - radius, me->scenePos().y() - radius, radius*2, radius*2);
    
        ellipse->setBrush(Qt::white);
        m_points.append(me->scenePos());
        if(m_points.size() == 3)
        {
            // use math to define the circle
            QLineF lineBC(m_points.at(1), m_points.at(2));
            QLineF lineAC(m_points.at(0), m_points.at(2));
            QLineF lineBA(m_points.at(1), m_points.at(0));
            qreal rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
    
            QLineF bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
            bisectorBC.setAngle(lineBC.normalVector().angle());
    
            QLineF bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
            bisectorBA.setAngle(lineBA.normalVector().angle());
    
            QPointF center;
            bisectorBA.intersect(bisectorBC, &center);
    
            qDebug() << rad << center;
    
            bool drawCircle = true;
    
            QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem(center.x() - rad, center.y() - rad, rad*2, rad*2);
            if(drawCircle)
                this->addItem(ellipse);
    
            // add arc
            // this->addItem(path);
            QPainterPath path;
            QLineF lineOA(center, m_points.at(0));
            QLineF lineOC(center, m_points.at(2));
            path.arcMoveTo(ellipse->boundingRect(),lineOA.angle());
            path.arcTo(ellipse->boundingRect(), lineOA.angle(), lineOC.angle() - lineOA.angle());
            QGraphicsPathItem * pathItem = new QGraphicsPathItem(path);
            pathItem->setPen(QPen(Qt::red,10));
            this->addItem(pathItem);
    
            if(!drawCircle)
                delete ellipse;
            m_points.clear();
        }
    
        QGraphicsScene::mouseReleaseEvent(me);
    }
    

    【讨论】:

    • 当我尝试将此代码嵌入到我的项目中时,我遇到了一些错误。请帮我解决错误。我已经用我的文件更新了代码。
    • 看起来您的paintEvent 不在正确的位置。尝试在 google 上搜索如何将 qgraphicsitem 子类化。
    • 我在google上搜索了一下,发现构造函数有问题。不知道为什么它没有选择弧的类型,尽管我在弧的构造函数中传递了同样的东西。请帮帮我。
    【解决方案2】:

    QGraphicsItem 的子类,它取 3 个点,并用圆弧与这三个点相交。第二点总是在中间。 (可选性和其他属性尚未完全实现或测试)。

    注意:Qt Creator 包括更高级的子类 QGraphicsItems 示例,例如 Colliding Mice,以及 40,000 个芯片示例。

    http://qt-project.org/doc/qt-5/examples-graphicsview.html

    还要从QGraphicsItem 启用QObject 信号、槽和属性,您应该使用QGraphicsObject

    注意:添加到github上here

    arcgraphicsitem.h

    #ifndef ARCGRAPHICSITEM_H
    #define ARCGRAPHICSITEM_H
    
    #include <QGraphicsItem>
    #include <QLineF>
    #include <QPointF>
    #include <QPainter>
    #include <QStyleOptionGraphicsItem>
    #include <QWidget>
    
    class ArcGraphicsItem : public QGraphicsItem
    {
    
    public:
        ArcGraphicsItem();
        ArcGraphicsItem(int i, QPointF point0, QPointF point1, QPointF point2);
        ~ArcGraphicsItem();
    
        QRectF boundingRect() const;
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    
        int type() const { return Type;}
        int id() {return m_id;}
    
        QPainterPath shape() const;
    protected:
    
    private:
        void init();
    
        enum { Type = UserType + 6 };
        int m_id;
    
        QPointF startP, midP, endP, p1, p2, p3, center;
        QLineF lineBC;
        QLineF lineAC;
        QLineF lineBA;
        QLineF lineOA;
        QLineF lineOB;
        QLineF lineOC;
        QLineF bisectorBC;
        QLineF bisectorBA;
        qreal startAngle;
        qreal spanAngle;
    
        QRectF circle;
        QRectF boundingRectTemp;
        qreal rad;
    };
    
    #endif // ARCGRAPHICSITEM_H
    

    arcgraphicsitem.cpp

    #include "arcgraphicsitem.h"
    #include "qmath.h"
    #include <QPen>
    #include <QDebug>
    #include <QPainterPath>
    
    ArcGraphicsItem::ArcGraphicsItem(int i,
                                     QPointF point1,
                                     QPointF point2,
                                     QPointF point3)
        : m_id(i), p1(point1), p2(point2), p3(point3)
    
    {
        init();
    }
    
    
    ArcGraphicsItem::ArcGraphicsItem()
    {
        p1 = QPointF(0,0);
        p2 = QPointF(0,1);
        p3 = QPointF(1,1);
        m_id = -1;
        init();
    }
    
    ArcGraphicsItem::~ArcGraphicsItem()
    {
    
    }
    
    void ArcGraphicsItem::init()
    {
        lineBC = QLineF(p2, p3);
        lineAC = QLineF(p1, p3);
        lineBA = QLineF(p2, p1);
    
        rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
    
        bisectorBC = QLineF(lineBC.pointAt(0.5), lineBC.p2());
        bisectorBC.setAngle(lineBC.normalVector().angle());
    
        bisectorBA = QLineF(lineBA.pointAt(0.5), lineBA.p2());
        bisectorBA.setAngle(lineBA.normalVector().angle());
        bisectorBA.intersect(bisectorBC, &center);
    
        circle = QRectF(center.x() - rad, center.y() - rad, rad*2, rad*2);
    
        lineOA = QLineF(center, p1);
        lineOB = QLineF(center, p2);
        lineOC = QLineF(center, p3);
    
    
        startAngle = lineOA.angle();
        spanAngle = lineOA.angleTo(lineOC);
        // Make sure that the span angle covers all three points with the second point in the middle
        if(qAbs(spanAngle) < qAbs(lineOA.angleTo(lineOB)) || qAbs(spanAngle) < qAbs(lineOB.angleTo(lineOC)))
        {
            // swap the end point and invert the spanAngle
            startAngle = lineOC.angle();
            spanAngle = 360 - spanAngle;
        }
    
        int w = 10;
        boundingRectTemp = circle.adjusted(-w, -w, w, w);
    }
    
    QRectF ArcGraphicsItem::boundingRect() const
    {
        // outer most edges
        return boundingRectTemp;
    }
    
    void ArcGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        QPen paintpen;
        painter->setRenderHint(QPainter::Antialiasing);
        paintpen.setWidth(1);
        // Draw arc
    
        if(isSelected())
        {
            painter->setBrush(Qt::SolidPattern);
            paintpen.setColor(Qt::black);
            painter->setPen(paintpen);
        }
        else
        {
            paintpen.setStyle(Qt::DashLine);
            paintpen.setColor(Qt::black);
            painter->setPen(paintpen);
        }
    
        QPainterPath path;
        path.arcMoveTo(circle,startAngle);
        path.arcTo(circle, startAngle, spanAngle);
    
        // Draw points
    
        if (isSelected())
        {
            // sets brush for end points
            painter->setBrush(Qt::SolidPattern);
            paintpen.setColor(Qt::red);
            painter->setPen(paintpen);
    
            qreal ptRad = 10;
            painter->drawEllipse(p1, ptRad, ptRad);
            painter->drawEllipse(p2, ptRad, ptRad);
            painter->drawEllipse(p3, ptRad, ptRad);
        }
    
        painter->setBrush(Qt::NoBrush);
        painter->drawPath(path);
    }
    
    QPainterPath ArcGraphicsItem::shape() const
    {
        QPainterPath path;
        path.arcMoveTo(circle,startAngle);
        path.arcTo(circle, startAngle, spanAngle);
        return path;
    }
    

    希望有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 2020-09-24
      • 2020-09-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多