Qt - 如何将QGraphicsScene缩放到QGraphicsView中

3

我有一个场景,想要制作它的缩小版,所以我需要调整它的大小,使其适合特定的比例尺。我的情况类似于这样:

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);
}

布局类型为QVBoxLayout。目前我只能在150x150的窗口中看到我的场景的一部分,但我希望将其全部调整大小以适应窗口。有没有办法做到这一点?或者我是否可以将QGraphicsScene转换为QGraphicsView元素?


你可以展示一张图像来说明你想要的。 - eyllanesc
它应该能够将每个图像从最大800x600像素进行缩放。 - szakal
你已经让我感到困惑了,你可以放一张图片来避免混淆。并且在表达你的需求时更加详细。 - eyllanesc
你想要缩放QGraphicsScene还是你想要缩放QGraphicsScene中的一个项(QPixmapGraphicsItem)? - eyllanesc
我的意思是,我有一个小型图形编辑器,并且所有更改都保存到QGraphicsScene中。然后我想能够显示它的缩略图,但我不知道该怎么做。 - szakal
1个回答

4
问题的要求是创建一个自动缩放以适应整个场景的QGraphicsView
我已经查阅了QGraphicsView的文档,但没有找到现成的函数。也许是我没有找到,或者确实没有这样的函数。
然而,有一些方法可以用很少的代码“自制”这个功能: 我制作了一个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

snapshot of testQGraphicsViewDual

注释:

  1. class Canvas类是从Qt的class QGraphicsView派生出来的,以添加自适应功能。为此,重载了paintEvent()函数。在“退回”到QGraphicsView::paintEvent()之前,它只调用QGraphicsView::fitInView()

  2. 左侧和右侧视图都是Canvas的实例——左侧禁用了autoScale,右侧启用了autoScale

  3. 两个视图共享同一个QGraphicsScene实例qGScene

  4. populate()函数用于填充图形场景。因此,选择内容的坐标/大小,使得结果几何图形很可能不适合视图。

  5. 快照显示左侧(未缩放)视图仅显示场景的一部分(由滚动条可视化)。右侧视图显示整个场景(缩小以适应视图),即没有滚动条。

  6. 为防止滚动条意外闪烁(由于舍入问题),激活autoScroll会显式地禁用滚动条。

  7. 观看示例代码一段时间后,我意识到场景矩形从未收缩(即使场景定期清除)。这让我头疼。我尝试了QGraphicsScene::itemsBoundingRect,但认为它是次优解决方案,因为文档明确指出:

    此函数通过迭代所有项来工作,因此对于大型场景可能会很慢。

    最后,我改变了populate(),以便在修改其内容时显式设置QGraphicsScene::sceneRect

关于上一个问题,我发现了一个类似的问答,值得一提:
SO: QGraphicsScene::clear doesn't change sceneRect

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接