【发布时间】:2017-07-25 21:00:44
【问题描述】:
我在 Windows 上使用 Qt 4.7。
我的qgraphicsview/qgraphicsscene 中有一个错误,在缩放视图后,某些东西会导致“挂起”,这似乎是事件循环中的递归/无限循环。
对于没有简单、完整的代码示例,我深表歉意;我将尽我所能描述症状和情况,我希望有人能提出一两个我可以尝试的想法:
我已将QGraphicsView、QGraphicsScene 和QGraphicsItem 子类化。例如,QGraphicsView 中的 wheelEvent 已被子类化以执行以下操作:
scaleView(pow((double)2, -event->delta() / 240.0));
在哪里
void MyGraphicsView::scaleView(qreal scaleFactor)
{
qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
if (factor < 0.07 || factor > 100)
return;
scale(scaleFactor, scaleFactor);
}
我的graphicsview子类的构造函数如下:
MyGraphicsView(QWidget *parent) :
QGraphicsView(parent)
{
setRenderHint(QPainter::Antialiasing);
setTransformationAnchor(AnchorUnderMouse);
setCacheMode(QGraphicsView::CacheNone);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setDragMode(QGraphicsView::ScrollHandDrag);
}
我的QGraphicsItem构造函数子类如下:
MyGraphicsItem(QGraphicsObject *parent) :
QGraphicsObject(parent),
_colour(qrand() % 256, qrand() % 256, qrand() % 256),
_collapsed(false)
{
setFlag(ItemIsMovable);
setFlag(ItemSendsGeometryChanges);
setCacheMode(DeviceCoordinateCache);
setZValue(-1);
expandedPosition(this->pos());
}
我可以将问题隔离为对wheelEvent 的调用,或其他调用QGraphicsView::scaleView 的事件。
如果有一个MyGraphicsItem 可见,那么尝试绘制(或由绘制引起的东西)会导致整个系统挂起。我认为这是事件循环中的一个循环,但要摆脱它(ctrl+alt+del 来调出“开始任务Manager”,然后 esc 取消)我失去了我所在的循环。
循环似乎是对QGraphicsView::focusInEvent 和focusOutEvent 的重复调用。我已经覆盖了这些,并且无论我对这些函数做什么,似乎都没有太多运气来停止循环。
编辑:这似乎是一条红鲱鱼,是在调试器内切换到断点的结果。
如果视图没有显示MyGraphicsItem,则没有问题。
另外,我可以在缩放后平移和重新绘制视图,但尝试调整窗口大小或更改焦点会导致问题。
同样,如果没有打到scaleView,我也没有问题。
也许我太含糊了;但请回答我能回答的任何问题以帮助澄清。任何帮助表示赞赏! 更新:
所以我似乎把它归结为与我的 QGraphicsItem 子类的子类有关。
我从弹性节点示例中提取了一个节点,并将其放入我的场景中。我可以使用我的 sceven 和 view 放大和缩小,而且不会挂起。
我什至可以让 Node 成为 MyGraphicsItem 的子类,而不是 QGraphicsItem,同样没有手。
只要我将 MyGraphicsItem 的子类放入场景中,我就会发现问题。 我希望越来越近!
【问题讨论】:
-
尝试升级你的 Qt - 你有一个古老的版本。此类错误通常会在后续版本中修复。
-
我不能随心所欲地更改 Qt 版本;这是在生产环境中,更改 Qt 版本非常重要:-)
-
最好尝试找出实际发生的情况(通过 printf() 风格的调试,如果没有别的),但如果所有其他方法都失败了,您可能想尝试将 scaleView() 调用移动到一个单独的上下文,例如通过调用 QTimer::singleShot(0, this, SLOT(myScaleViewSlot())) 而不是直接从处理程序调用 scaleView() 。这样,如果故障是由于调用 scaleView() 的事件循环中的位置引起的,则可以通过从事件循环的不同部分调用它来避免故障。您需要一种方法来传递 scale 参数;也许通过将其存储在成员变量中。
-
我应该更清楚; scale 函数执行良好并正常返回事件循环。但是在它执行之后,一些事件就会导致问题。例如,我可以平移视图(导致绘制事件)没问题,但我在尝试调整窗口大小或将焦点放在另一个窗口时遇到了问题