Qt - 如何将 QGraphicsScene 缩放到 QGraphicsView

Posted

技术标签:

【中文标题】Qt - 如何将 QGraphicsScene 缩放到 QGraphicsView【英文标题】:Qt - How to scale QGraphicsScene into QGraphicsView 【发布时间】:2018-02-04 03:29:03 【问题描述】:

我有一个场景,我想制作它的缩影,所以我想调整它的大小以适应特定的比例。我有这样的事情:

void makeMiniature(QGraphicsScene * scene)

     QGraphicsView * gv = new GraphicsView(scene, this);
     gv->setMaximumHeight(150);
     gv->setMaximumWidth(150);
     gv->setSceneRect(0, 0, gv->frameSize().width(), gv->frameSize().height());

     layout->addWidget(gv);

其中 layout 是 QVBoxLayout 类型。目前我只是在 150x150 窗口中获得我的场景的一部分,但我想调整所有的大小以便它可以适合那里。有什么办法吗?或者我可以以某种方式将 QGraphicsScene 转换为 QGraphicsView 元素吗?

【问题讨论】:

你可以展示一张图片来说明你想要什么。 它应该能够从最大 800x600 像素缩放每个图像 你已经把我的评论弄糊涂了,你可以放一张图片来避免混淆。并更详细地说明您想要的内容。 你想缩放QGraphicsScene还是缩放QGraphicsScene中的一个项目(QPixmapGraphicsItem)? 我的意思是我有一个小型图形编辑器,所有更改都保存到 QGraphicsScene。然后我希望能够展示它的缩影,但我不知道如何。 【参考方案1】:

问题的要求是QGraphicsView,它会自动缩放视图以适应整个场景。

我已经扫描了文档。 QGraphicsView 如果没有这样的功能可用。要么我没找到,要么没有。

但是,有一些方法可以用很少的代码“DIY”这个功能:

QGraphicsScene::sceneRect() ... 返回图形场景的边界矩形 QGraphicsView::fitInView() ... 调整图形视图中的视图转换,使某个矩形的区域可见。

我创建了一个MCVE 来证明这一点:testQGraphicsViewDual.cc

#include <QtWidgets>

enum  Width = 256, Height = 256 ;
enum  MinWidth = 8, MinHeight = 8 ;
enum  MaxRects = 8 ;

// returns a random floating point number in [min, max).
qreal randF(qreal min, qreal max)

  return (qreal)qrand() / RAND_MAX * (max - min) + min;


// returns a random integer number in [min, max).
int rand(int min, int max)

  return qrand() % (max - min) + min;


/* adds a random rectangle to a Qt graphics scene.
 * The scene is cleared if the decremented clear counter reaches 0.
 */
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()); // update scene rect.


// class for widget to demostrate auto-fit-in-view
class Canvas: public QGraphicsView 
  // variables:
  private:
    // flag: true ... auto-scaling enabled to fit whole scene in view
    bool _autoScale;

  // methods:
  public: 
    // constructor.
    Canvas(bool autoScale = false);
    // destructor.
    virtual ~Canvas() = default;
    // disabled:
    Canvas(const Canvas&) = delete;
    Canvas& operator=(const Canvas&) = delete;

    // returns current state of autoScale.
    bool autoScale() const  return _autoScale; 
    // sets 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);


// main function
int main(int argc, char **argv)

  qDebug() << "Version:" << QT_VERSION_STR;
  // main application
  QApplication app(argc, argv);
  // setup graphics scene
  QGraphicsScene qGScene;
  // setup GUI
  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();
  // install timer for continuous population of qGScene
  QTimer qTimer; unsigned clear = 0;
  qTimer.setInterval(1000 /* ms */);
  QObject::connect(&qTimer, &QTimer::timeout,
    [&qGScene, &clear]()  populate(qGScene, clear); );
  qTimer.start();
  // do runtime loop
  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

注意事项:

    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。

【讨论】:

以上是关于Qt - 如何将 QGraphicsScene 缩放到 QGraphicsView的主要内容,如果未能解决你的问题,请参考以下文章

qt - 在 QGraphicsScene 中如何捕捉特定项目

Qt 5.10 QGraphicsView 无法将 QGraphicsScene 缩放到全屏

QT 在 QGraphicsScene 上方显示一个 QWidget

添加到 QGraphicsScene 时如何更新 OpenGL 绘图(QOpenGLWidget)?

QT:QGraphicsView QGraphicsScene QGraphicsItem理解

子类 QGraphicsScene 出错