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 中调用 endTransitionLineDragQGraphicsView 的子 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 将重绘整个视口,而不仅仅是边界框区域。