我正在处理具有两个锚点的自定义 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();
}
但是,当我尝试从一个锚点(围绕另一个锚点)旋转项目然后从另一个锚点再次旋转它时,它会跳回初始位置!我不确定为什么会这样。
正如您在此视频中所见,当我第一次旋转视图时它起作用了,但是当我尝试从另一个锚点旋转它时,它的位置跳到另一个位置!
解决方案需要适用于在
MyView::paint()
函数中绘制的任何形状,而不是仅限于line
。虽然here有在线解决方案,但它只适用于line
,同样,@kenash0625的解决方案也只适用于线路
问题:是什么导致了这个问题,我该如何修改我的代码以实现围绕不同锚点平滑旋转的预期行为?
这个应该适用于在 MyView::paint() 函数中绘制的任何形状
我对您的代码做了 2 处更改
添加呼叫
QGraphicsItem::setTransformations(const QList<QGraphicsTransform *> &transformations)
从
改变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"
移动完成后,这些项目的位置似乎设置不正确,所以您可以尝试手动设置它;
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());
}
这可能有效也可能无效。