QSceneView中拖线时刷新报错
Refresh error when dragging a line in QSceneView
我需要在 QSceneView
中的两个项目之间绘制一个连接器。我已将 QGraphicsObject
用于创建此行(它不是 QGraphicsLineItem
因为我需要稍后更新其外观)。这是 class:
#ifndef TRANSITIONDRAGLINE_HPP_
#define TRANSITIONDRAGLINE_HPP_
#include "Style/CanvasTransitionDragLine.hpp"
#include <QGraphicsObject>
#include <QPointF>
#include <string>
class TransitionDragLine : public QGraphicsObject {
Q_OBJECT
public:
TransitionDragLine(const Style::CanvasTransitionDragLine& style, QGraphicsItem* parent = nullptr);
virtual ~TransitionDragLine();
void setStartPoint(const QPointF& startPoint);
void setEndPoint(const QPointF& endPoint);
public:
QRectF boundingRect() const override;
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
private:
Style::CanvasTransitionDragLine m_style;
QPointF m_startPoint;
QPointF m_endPoint;
};
#endif // !TRANSITIONDRAGLINE_HPP_
#include "TransitionDragLine.hpp"
#include <QPainter>
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION //
///////////////////////////////////////////////////////////////////////////////
TransitionDragLine::TransitionDragLine(const Style::CanvasTransitionDragLine& style, QGraphicsItem* parent) :
QGraphicsObject(parent),
m_style(style) {
}
TransitionDragLine::~TransitionDragLine() {
}
void TransitionDragLine::setStartPoint(const QPointF& startPoint) {
m_startPoint = startPoint;
}
void TransitionDragLine::setEndPoint(const QPointF& endPoint) {
m_endPoint = endPoint;
update();
}
///////////////////////////////////////////////////////////////////////////////
// VIRTUAL PUBLIC SECTION //
///////////////////////////////////////////////////////////////////////////////
QRectF TransitionDragLine::boundingRect() const {
qreal dx = m_endPoint.x() - m_startPoint.x();
qreal dy = m_endPoint.y() - m_startPoint.y();
qreal x{ 0.0 };
qreal y{ 0.0 };
qreal w{ 0.0 };
qreal h{ 0.0 };
qreal penHalfWidth{ m_style.getPen().widthF() * 0.5 };
if (dx >= 0.0 && dy >= 0.0) {
x = 0.0 - penHalfWidth;
y = 0.0 - penHalfWidth;
w = dx + penHalfWidth;
h = dy + penHalfWidth;
}
else if (dx >= 0.0 && dy < 0.0) {
x = 0.0 - penHalfWidth;
y = dy - penHalfWidth;
w = dx - penHalfWidth;
h = -dy - penHalfWidth;
}
else if (dx < 0.0 && dy >= 0.0) {
x = dx - penHalfWidth;
y = 0 - penHalfWidth;
w = -dx - penHalfWidth;
h = dy - penHalfWidth;
}
else {
x = dx - penHalfWidth;
y = dy - penHalfWidth;
w = -dx - penHalfWidth;
h = -dy - penHalfWidth;
}
return QRectF(x, y, w, h);
}
void TransitionDragLine::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
qreal px = m_endPoint.x() - m_startPoint.x();
qreal py = m_endPoint.y() - m_startPoint.y();
painter->setPen(m_style.getPen());
painter->drawLine(0.0, 0.0, px, py);
}
当我想使用它时,我在 Canvas
class 中设置了一个标志,它继承了 QGraphicsView
,然后更新它。当我按下鼠标按钮时,我设置起点然后设置终点(并调用 update()
),每次我移动鼠标直到按下左键。基本上我在轮询中调用 startTransitionLineDrag
直到按下鼠标,当我释放鼠标按钮时,我在我的 Canvas 中调用 endTransitionLineDrag
(QGraphicsView
的子 class ):
void Canvas::mouseMoveEvent(QMouseEvent* event) {
switch (m_currentState) {
case CurrentState::PressedOnTransition: {
if (event->buttons() == Qt::LeftButton) {
startTransitionLineDrag(event);
}
} break;
}
QGraphicsView::mouseMoveEvent(event);
}
void Canvas::mouseReleaseEvent(QMouseEvent* event) {
if (m_currentState == CurrentState::PressedOnTransition) {
if (true /* business logic here not useful for the problem */) {
endTransitionLineDrag(event);
}
}
QGraphicsView::mouseReleaseEvent(event);
}
void Canvas::startTransitionLineDrag(QMouseEvent* event) {
if (m_transitionDragLine == nullptr) {
m_transitionDragLine = new TransitionDragLine(m_style.getTransitionDragLineStyle());
m_transitionDragLine->setStartPoint(mapToScene(event->pos()));
m_scene->addItem(m_transitionDragLine);
m_transitionDragLine->setPos(mapToScene(event->pos()));
}
m_transitionDragLine->setEndPoint(mapToScene(event->pos()));
//repaint();
}
void Canvas::endTransitionLineDrag(QMouseEvent* event) {
/* other business logic */
deleteDragTransitionLine();
}
void Canvas::deleteDragTransitionLine() {
if (m_transitionDragLine) {
m_scene->removeItem(m_transitionDragLine);
delete m_transitionDragLine;
m_transitionDragLine = nullptr;
}
}
逻辑有效:当我激活拖动时,我可以看到该线并且它会更新,直到按下鼠标按钮。但是你可以在附图中看到我有一个渲染问题:
线条渲染不正常;我有该行过去图像的踪迹,例如 QGraphicsView
未正确更新。
我在 TransitionDragLine::setEndPoint
中感觉到代码味道 我需要在设置线的终点后调用 update()
(否则线不显示)但我找不到方法解决问题。
我做错了什么?
编辑:
我在这里看到了一个解决方案:
Artifacts showing when modifying a custom QGraphicsItem
每次我更新终点时,我都尝试调用 prepareGeometryChange()
,但工件仍然存在:
void TransitionDragLine::setEndPoint(const QPointF& endPoint) {
m_endPoint = endPoint;
prepareGeometryChange();
update();
}
您对 QRectF TransitionDragLine::boundingRect() const
的实施似乎不正确。
由于涉及多个坐标系,因此正确设置可能有点棘手。
正确的解决方案是正确设置边界框,但对于小场景,通常设置 QGraphicsView::updateMode
to QGraphicsView::FullViewportUpdate
就足够了。
也就是说,QGraphicsView
将重绘整个视口,而不仅仅是边界框区域。
我需要在 QSceneView
中的两个项目之间绘制一个连接器。我已将 QGraphicsObject
用于创建此行(它不是 QGraphicsLineItem
因为我需要稍后更新其外观)。这是 class:
#ifndef TRANSITIONDRAGLINE_HPP_
#define TRANSITIONDRAGLINE_HPP_
#include "Style/CanvasTransitionDragLine.hpp"
#include <QGraphicsObject>
#include <QPointF>
#include <string>
class TransitionDragLine : public QGraphicsObject {
Q_OBJECT
public:
TransitionDragLine(const Style::CanvasTransitionDragLine& style, QGraphicsItem* parent = nullptr);
virtual ~TransitionDragLine();
void setStartPoint(const QPointF& startPoint);
void setEndPoint(const QPointF& endPoint);
public:
QRectF boundingRect() const override;
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
private:
Style::CanvasTransitionDragLine m_style;
QPointF m_startPoint;
QPointF m_endPoint;
};
#endif // !TRANSITIONDRAGLINE_HPP_
#include "TransitionDragLine.hpp"
#include <QPainter>
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION //
///////////////////////////////////////////////////////////////////////////////
TransitionDragLine::TransitionDragLine(const Style::CanvasTransitionDragLine& style, QGraphicsItem* parent) :
QGraphicsObject(parent),
m_style(style) {
}
TransitionDragLine::~TransitionDragLine() {
}
void TransitionDragLine::setStartPoint(const QPointF& startPoint) {
m_startPoint = startPoint;
}
void TransitionDragLine::setEndPoint(const QPointF& endPoint) {
m_endPoint = endPoint;
update();
}
///////////////////////////////////////////////////////////////////////////////
// VIRTUAL PUBLIC SECTION //
///////////////////////////////////////////////////////////////////////////////
QRectF TransitionDragLine::boundingRect() const {
qreal dx = m_endPoint.x() - m_startPoint.x();
qreal dy = m_endPoint.y() - m_startPoint.y();
qreal x{ 0.0 };
qreal y{ 0.0 };
qreal w{ 0.0 };
qreal h{ 0.0 };
qreal penHalfWidth{ m_style.getPen().widthF() * 0.5 };
if (dx >= 0.0 && dy >= 0.0) {
x = 0.0 - penHalfWidth;
y = 0.0 - penHalfWidth;
w = dx + penHalfWidth;
h = dy + penHalfWidth;
}
else if (dx >= 0.0 && dy < 0.0) {
x = 0.0 - penHalfWidth;
y = dy - penHalfWidth;
w = dx - penHalfWidth;
h = -dy - penHalfWidth;
}
else if (dx < 0.0 && dy >= 0.0) {
x = dx - penHalfWidth;
y = 0 - penHalfWidth;
w = -dx - penHalfWidth;
h = dy - penHalfWidth;
}
else {
x = dx - penHalfWidth;
y = dy - penHalfWidth;
w = -dx - penHalfWidth;
h = -dy - penHalfWidth;
}
return QRectF(x, y, w, h);
}
void TransitionDragLine::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
qreal px = m_endPoint.x() - m_startPoint.x();
qreal py = m_endPoint.y() - m_startPoint.y();
painter->setPen(m_style.getPen());
painter->drawLine(0.0, 0.0, px, py);
}
当我想使用它时,我在 Canvas
class 中设置了一个标志,它继承了 QGraphicsView
,然后更新它。当我按下鼠标按钮时,我设置起点然后设置终点(并调用 update()
),每次我移动鼠标直到按下左键。基本上我在轮询中调用 startTransitionLineDrag
直到按下鼠标,当我释放鼠标按钮时,我在我的 Canvas 中调用 endTransitionLineDrag
(QGraphicsView
的子 class ):
void Canvas::mouseMoveEvent(QMouseEvent* event) {
switch (m_currentState) {
case CurrentState::PressedOnTransition: {
if (event->buttons() == Qt::LeftButton) {
startTransitionLineDrag(event);
}
} break;
}
QGraphicsView::mouseMoveEvent(event);
}
void Canvas::mouseReleaseEvent(QMouseEvent* event) {
if (m_currentState == CurrentState::PressedOnTransition) {
if (true /* business logic here not useful for the problem */) {
endTransitionLineDrag(event);
}
}
QGraphicsView::mouseReleaseEvent(event);
}
void Canvas::startTransitionLineDrag(QMouseEvent* event) {
if (m_transitionDragLine == nullptr) {
m_transitionDragLine = new TransitionDragLine(m_style.getTransitionDragLineStyle());
m_transitionDragLine->setStartPoint(mapToScene(event->pos()));
m_scene->addItem(m_transitionDragLine);
m_transitionDragLine->setPos(mapToScene(event->pos()));
}
m_transitionDragLine->setEndPoint(mapToScene(event->pos()));
//repaint();
}
void Canvas::endTransitionLineDrag(QMouseEvent* event) {
/* other business logic */
deleteDragTransitionLine();
}
void Canvas::deleteDragTransitionLine() {
if (m_transitionDragLine) {
m_scene->removeItem(m_transitionDragLine);
delete m_transitionDragLine;
m_transitionDragLine = nullptr;
}
}
逻辑有效:当我激活拖动时,我可以看到该线并且它会更新,直到按下鼠标按钮。但是你可以在附图中看到我有一个渲染问题:
线条渲染不正常;我有该行过去图像的踪迹,例如 QGraphicsView
未正确更新。
我在 TransitionDragLine::setEndPoint
中感觉到代码味道 我需要在设置线的终点后调用 update()
(否则线不显示)但我找不到方法解决问题。
我做错了什么?
编辑:
我在这里看到了一个解决方案:
Artifacts showing when modifying a custom QGraphicsItem
每次我更新终点时,我都尝试调用 prepareGeometryChange()
,但工件仍然存在:
void TransitionDragLine::setEndPoint(const QPointF& endPoint) {
m_endPoint = endPoint;
prepareGeometryChange();
update();
}
您对 QRectF TransitionDragLine::boundingRect() const
的实施似乎不正确。
由于涉及多个坐标系,因此正确设置可能有点棘手。
正确的解决方案是正确设置边界框,但对于小场景,通常设置 QGraphicsView::updateMode
to QGraphicsView::FullViewportUpdate
就足够了。
也就是说,QGraphicsView
将重绘整个视口,而不仅仅是边界框区域。