问题的要求是创建一个自动缩放以适应整个场景的
QGraphicsView
。
我已经查阅了
QGraphicsView
的文档,但没有找到现成的函数。也许是我没有找到,或者确实没有这样的函数。
然而,有一些方法可以用很少的代码“自制”这个功能:
我制作了一个
MCVE来演示这一点:
testQGraphicsViewDual.cc
。
#include <QtWidgets>
enum { Width = 256, Height = 256 };
enum { MinWidth = 8, MinHeight = 8 };
enum { MaxRects = 8 };
qreal randF(qreal min, qreal max)
{
return (qreal)qrand() / RAND_MAX * (max - min) + min;
}
int rand(int min, int max)
{
return qrand() % (max - min) + min;
}
void populate(QGraphicsScene &qGScene, unsigned &clear)
{
if (!clear--) { qGScene.clear(); clear = MaxRects; }
const QPointF pt(randF(-Width, Width - MinWidth), randF(-Height, Height - MinHeight));
const QSizeF size(
randF(MinWidth, 2 * Width - pt.x()), randF(MinHeight, 2 * Height - pt.y()));
qGScene.addRect(
QRectF(pt, size),
QPen(QColor(rand(0, 256), rand(0, 256), rand(0, 256)), 2),
QColor(rand(0, 256), rand(0, 256), rand(0, 256)));
qGScene.setSceneRect(qGScene.itemsBoundingRect());
}
class Canvas: public QGraphicsView {
private:
bool _autoScale;
public:
Canvas(bool autoScale = false);
virtual ~Canvas() = default;
Canvas(const Canvas&) = delete;
Canvas& operator=(const Canvas&) = delete;
bool autoScale() const { return _autoScale; }
void setAutoScale(bool autoScale);
protected:
virtual void paintEvent(QPaintEvent *pQEvent) override;
};
Canvas::Canvas(bool autoScale):
QGraphicsView()
{
setAutoScale(autoScale);
}
void Canvas::setAutoScale(bool autoScale)
{
_autoScale = autoScale;
setHorizontalScrollBarPolicy(_autoScale ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAsNeeded);
setVerticalScrollBarPolicy(_autoScale ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAsNeeded);
}
void Canvas::paintEvent(QPaintEvent *pQEvent)
{
const QGraphicsScene *pQGScene = scene();
if (pQGScene && _autoScale) {
fitInView(pQGScene->sceneRect(), Qt::KeepAspectRatio);
}
QGraphicsView::paintEvent(pQEvent);
}
int main(int argc, char **argv)
{
qDebug() << "Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QGraphicsScene qGScene;
QWidget qWin;
qWin.resize(2 * Width, Height);
QHBoxLayout qHBox;
Canvas qView1(false);
qView1.setScene(&qGScene);
Canvas qView2(true);
qView2.setScene(&qGScene);
qHBox.addWidget(&qView1, 1);
qHBox.addWidget(&qView2, 1);
qWin.setLayout(&qHBox);
qWin.show();
QTimer qTimer; unsigned clear = 0;
qTimer.setInterval(1000 );
QObject::connect(&qTimer, &QTimer::timeout,
[&qGScene, &clear]() { populate(qGScene, clear); });
qTimer.start();
return app.exec();
}
QMake脚本testQGraphicsViewDual.pro
SOURCES = testQGraphicsViewDual.cc
QT += widgets
代码已在bash
中编译和测试(在cygwin / Windows 10上):
$ qmake-qt5
$ make
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQGraphicsViewDual.o testQGraphicsViewDual.cc
g++ -o testQGraphicsViewDual.exe testQGraphicsViewDual.o -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread
$ ./testQGraphicsViewDual
Version: 5.9.2
![snapshot of testQGraphicsViewDual](https://istack.dev59.com/JwuTU.webp)
注释:
class Canvas
类是从Qt的class QGraphicsView
派生出来的,以添加自适应功能。为此,重载了paintEvent()
函数。在“退回”到QGraphicsView::paintEvent()
之前,它只调用QGraphicsView::fitInView()
。
左侧和右侧视图都是Canvas
的实例——左侧禁用了autoScale
,右侧启用了autoScale
。
两个视图共享同一个QGraphicsScene
实例qGScene
。
populate()
函数用于填充图形场景。因此,选择内容的坐标/大小,使得结果几何图形很可能不适合视图。
快照显示左侧(未缩放)视图仅显示场景的一部分(由滚动条可视化)。右侧视图显示整个场景(缩小以适应视图),即没有滚动条。
为防止滚动条意外闪烁(由于舍入问题),激活autoScroll
会显式地禁用滚动条。
观看示例代码一段时间后,我意识到场景矩形从未收缩(即使场景定期清除)。这让我头疼。我尝试了QGraphicsScene::itemsBoundingRect
,但认为它是次优解决方案,因为文档明确指出:
此函数通过迭代所有项来工作,因此对于大型场景可能会很慢。
最后,我改变了populate()
,以便在修改其内容时显式设置QGraphicsScene::sceneRect
。
关于上一个问题,我发现了一个类似的问答,值得一提:
SO: QGraphicsScene::clear doesn't change sceneRect。