如何在 Qt C++ 中围绕不同的锚点正确旋转 QGraphicsItem

问题描述 投票:0回答:2

我正在处理具有两个锚点的自定义 QGraphicsItem,我希望能够在用户与这些锚点交互时围绕这些锚点旋转项目。我已经实现了

mousePressEvent
mouseMoveEvent
来检测用户点击了哪个锚点,设置旋转锚点,并计算旋转角度。

这是我的代码的简化版本:

MyView.h

static constexpr float ANCHOR_RADIUS = 10;

class MyView : public QGraphicsItem {
public:
    MyView(float xPos, float yPos, float width, float height, QGraphicsItem *parent = nullptr)
        : _width(width), _height(height), _viewState(VIEW) {
        setPos(xPos, yPos);

        setFlag(ItemIsMovable);

        auto diameter = 2 * ANCHOR_RADIUS;
        _anchor1.setRect(0, 0, diameter, diameter);
        _anchor2.setRect(width - diameter, 0, diameter, diameter);
    }

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override {
        auto diameter = 2 * ANCHOR_RADIUS;
        _anchor1.setRect(0, 0, diameter, diameter);
        _anchor2.setRect(_width - diameter, 0, diameter, diameter);

        // Pin 1 and 2 coordinate
        auto c1 = _anchor1.center();
        auto c2 = _anchor2.center();

        painter->drawLine(static_cast<int> (c1.x()), static_cast<int>(c1.y()),
                          static_cast<int>(c2.x()), static_cast<int>(c2.y()));

        painter->drawRect(boundingRect());

        painter->drawEllipse(_anchor1);
        painter->drawEllipse(_anchor2);
    }

    [[nodiscard]] QRectF boundingRect() const override {
        return {0, 0, static_cast<qreal>(_width), static_cast<qreal>(_height)};
    }

    enum ViewState {
        ANCHOR1, ANCHOR2, VIEW
    };

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
        _tapPoint = event->pos();

        auto cp1 = _anchor1.center(); // get center point of anchor 1
        auto cp2 = _anchor2.center(); // get center point of anchor 2

        // Anchor 1 clicked
        if (_anchor1.contains(_tapPoint)) {
            setTransformOriginPoint(cp2.x(), cp2.y()); // set rotation anchor to anchor 2
            _viewState = ANCHOR1;
        }
            // Anchor 2 clicked
        else if (_anchor2.contains(_tapPoint)) {
            setTransformOriginPoint(cp1.x(), cp1.y()); // set rotation anchor to anchor 1
            _viewState = ANCHOR2;
        }
            // View clicked
        else {
            QGraphicsItem::mousePressEvent(event);
            _viewState = VIEW;
        }
    }
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override {
        auto p = event->pos();

        switch (_viewState) {
            case ANCHOR1: {

                // calculate the angle of the rotation based on the mouse touch
                auto angle = qRadiansToDegrees(qAtan2(p.y() - _anchor2.y(), _width));
                setRotation(rotation() - angle); // rotate the item around anchor 2

                break;
            }
            case ANCHOR2: {
                // calculate the angle of the rotation based on the mouse touch
                auto angle = qRadiansToDegrees(qAtan2(p.y() - _anchor1.y(), _width));
                setRotation(rotation() + angle); // rotate the item around anchor 1
                break;
            }
            default:
                QGraphicsItem::mouseMoveEvent(event); // move the item normally
        }
    }

private:
    float _width, _height;
    QRectF _anchor1, _anchor2;
    QPointF _tapPoint;
    ViewState _viewState;
};

main.cpp

static constexpr int WIDTH = 500;
static constexpr int HEIGHT = 500;

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    QGraphicsScene scene;
    scene.setSceneRect(QRectF(0, 0, WIDTH, HEIGHT));

    QGraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing);

    QVBoxLayout layout;
    layout.addWidget(&view);

    QWidget widget;
    widget.setLayout(&layout);

    MyView myView(100, 100, 200, 20);
    scene.addItem(&myView);

    widget.show();

    return QApplication::exec();
}

但是,当我尝试从一个锚点(围绕另一个锚点)旋转项目然后从另一个锚点再次旋转它时,它会跳回初始位置!我不确定为什么会这样。

正如您在此视频中所见,当我第一次旋转视图时它起作用了,但是当我尝试从另一个锚点旋转它时,它的位置跳到另一个位置!

这就是我想要实现的(使用 GeoGebra 工具创建):

解决方案需要适用于在

MyView::paint()
函数中绘制的任何形状,而不是仅限于
line
。虽然here有在线解决方案,但它只适用于
line
,同样,@kenash0625的解决方案也只适用于线路

问题:是什么导致了这个问题,我该如何修改我的代码以实现围绕不同锚点平滑旋转的预期行为?

c++ qt rotation qgraphicsview qgraphicsitem
2个回答
1
投票

这个应该适用于在 MyView::paint() 函数中绘制的任何形状

我对您的代码做了 2 处更改

  1. 添加呼叫

    QGraphicsItem::setTransformations(const QList<QGraphicsTransform *> &transformations)

  2. 改变

auto angle = qRadiansToDegrees(qAtan2(p.y()- _anchor2.y() , _width));

auto angle = qRadiansToDegrees(qAtan2( _anchor2.y() -p.y(), _width));

这里是编辑代码:


#include<QGraphicsItem>
#include<QPainter>
#include<QGraphicsSceneMouseEvent>
#include<QGraphicsScene>
#include<QGraphicsView>
#include <QGraphicsRotation>
#include<qmath.h>
static constexpr float ANCHOR_RADIUS = 10;

class MyView : public QGraphicsItem {
public:
    MyView(float xPos, float yPos, float width, float height, QGraphicsItem* parent = nullptr)
        : _width(width), _height(height), _viewState(VIEW) {
        setPos(xPos, yPos);

        setFlag(ItemIsMovable);

        auto diameter = 2 * ANCHOR_RADIUS;
        _anchor1.setRect(0, 0, diameter, diameter);
        _anchor2.setRect(width - diameter, 0, diameter, diameter);
    }

    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override {
        auto diameter = 2 * ANCHOR_RADIUS;
        _anchor1.setRect(0, 0, diameter, diameter);
        _anchor2.setRect(_width - diameter, 0, diameter, diameter);

        // Pin 1 and 2 coordinate
        auto c1 = _anchor1.center();
        auto c2 = _anchor2.center();

        painter->drawLine(static_cast<int> (c1.x()), static_cast<int>(c1.y()),
            static_cast<int>(c2.x()), static_cast<int>(c2.y()));

        painter->drawRect(boundingRect());

        painter->drawEllipse(_anchor1);
        painter->drawEllipse(_anchor2);
    }

    [[nodiscard]] QRectF boundingRect() const override {
        return { 0, 0, static_cast<qreal>(_width), static_cast<qreal>(_height) };
    }

    enum ViewState {
        ANCHOR1, ANCHOR2, VIEW
    };

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent* event) override {
        _tapPoint = event->pos();

        // Anchor 1 clicked
        if (_anchor1.contains(_tapPoint)) {
            _viewState = ANCHOR1;
        }
        // Anchor 2 clicked
        else if (_anchor2.contains(_tapPoint)) {
            _viewState = ANCHOR2;
        }
        // View clicked
        else {
            QGraphicsItem::mousePressEvent(event);
            _viewState = VIEW;
        }
    }
    void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override {
        auto p = event->pos();

        auto cp1 = _anchor1.center(); // get center point of anchor 1
        auto cp2 = _anchor2.center(); // get center point of anchor 2

        switch (_viewState) {
        case ANCHOR1: {
            // calculate the angle of the rotation based on the mouse touch
            auto angle = qRadiansToDegrees(qAtan2( _anchor2.y() -p.y(), _width));
            QGraphicsRotation* rot = new QGraphicsRotation;
            rot->setOrigin(QVector3D(cp2.x(), cp2.y(), 0));
            rot->setAxis(Qt::ZAxis);
            rot->setAngle(angle);
            _trans.push_back(rot);
            setTransformations(_trans);
            break;
        }
        case ANCHOR2: {
            // calculate the angle of the rotation based on the mouse touch
            auto angle = qRadiansToDegrees(qAtan2(p.y() - _anchor1.y(), _width));
            QGraphicsRotation* rot = new QGraphicsRotation;
            rot->setOrigin(QVector3D(cp1.x(), cp1.y(), 0));
            rot->setAxis(Qt::ZAxis);
            rot->setAngle(angle);
            _trans.push_back(rot);
            setTransformations(_trans);
            break;
        }
        default:
            QGraphicsItem::mouseMoveEvent(event); // move the item normally
        }
    }

private:
    float _width, _height;
    QRectF _anchor1, _anchor2;
    QPointF _tapPoint;
    ViewState _viewState;
    QList<QGraphicsTransform*> _trans;
};

static constexpr int WIDTH = 500;
static constexpr int HEIGHT = 500;

int main7(int argc, char* argv[]) {
    QApplication a(argc, argv);

    QGraphicsScene scene;
    scene.setSceneRect(QRectF(0, 0, WIDTH, HEIGHT));

    QGraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing);

    QVBoxLayout layout;
    layout.addWidget(&view);

    QWidget widget;
    widget.setLayout(&layout);

    MyView myView(100, 100, 200, 20);
    scene.addItem(&myView);

    widget.show();

    return QApplication::exec();
}
#include"FileName.moc"

0
投票

无法添加评论所以我发布这个作为答案,它可能帮助

移动完成后,这些项目的位置似乎设置不正确,所以您可以尝试手动设置它;

使用

mouseReleaseEvent()
知道定位何时完成。

使用

scenePos()
获取物品的位置。

并确保它们停留在用户设置的位置,使用

setPos()
功能。


编辑:

示例用法(注意这只是伪代码):

//We will be here when user releases the mouse
void MyView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
    
    //Lets call first item _anchor1.
    //Set position of _anchor1 with setPos(),
    //giving the argument of _anchor1's current position using scenePos().
    _anchor1.setPos(_anchor1.scenePos());

    //lets call second item _anchor2
    //Set position of _anchor1 with setPos(),
    //giving the argument of _anchor1's current position using scenePos().
    _anchor2.setPos(_anchor2.scenePos());

}

这可能有效也可能无效。

© www.soinside.com 2019 - 2024. All rights reserved.