3
votes

I have a scene and I want to make a miniature of it so I want to resize it so it can fit into specific scale. I have something like this:

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

Where layout is QVBoxLayout type. Currently I'm just getting a piece of my scene in 150x150 window but I want to resize all of it so it can fit there. Is there any way to do it? Or can I somehow convert a QGraphicsScene into QGraphicsView element?

1
You could show an image that illustrates what you want.eyllanesc
It should be able to scale every image from max 800x600 pxszakal
You already confused me with that comment, you could place an image to avoid confusion. And be more detailed in what you want.eyllanesc
Do you want to scale the QGraphicsScene or do you want to scale an item (QPixmapGraphicsItem) within the QGraphicsScene?eyllanesc
I mean I have a small graphic editor and all changes are saved to QGraphicsScene. And then I want to be able to show miniature of it but I don't know how.szakal

1 Answers

3
votes

The requirement of the question is a QGraphicsView which automatically scales view to fit-in the whole scene.

I've scanned the doc. of QGraphicsView if there isn't such a function already available. Either I didn't find or there isn't.

However, there are some methods which can be used to "DIY" this feature with very few code:

I made an MCVE to demonstrate this: 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();
}

The QMake script testQGraphicsViewDual.pro

SOURCES = testQGraphicsViewDual.cc

QT += widgets

Compiled and tested in bash (on 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

Notes:

  1. The class Canvas is derived from Qt class QGraphicsView to add the auto-fit-in feature. For this, the paintEvent() is overloaded. It just calls QGraphicsView::fitInView() before "falling back" to QGraphicsView::paintEvent().

  2. The left and right view are instances of Canvas – the left with autoScale disabled, the right with autoScale enabled.

  3. Both views share the same QGraphicsScene instance qGScene.

  4. The populate() functions is used to fill the the graphics scene. Thereby, the coordinates/sizes for contents are choosen that resulting geometry will very probably not fit into the view.

  5. The snapshot shows that the left (not scaled) view shows only part of the scene (visualized by the scrollbars). The right view shows the whole scene (minified to fit into view) i.e. no scrollbars.

  6. To prevent accidental flickering of scrollbars (due to rounding issues), the activation of autoScroll de-activates the scrollbars explicitly.

  7. After watching the sample code a while, I realized that the scene rectangle was never shrinking (even though the scene is periodically cleared). This caused me some head-ache. I tried QGraphicsScene::itemsBoundingRect but considered it as sub-optimal solution as the doc. explicitly remarks:

    This function works by iterating over all items, and because of this, it can be slow for large scenes.

    Finally, I changed populate() so that QGraphicsScene::sceneRect is set explicitly ((only) when its contents is modified).

Regarding the last issue, I found a similar Q/A which I find worth to mention:
SO: QGraphicsScene::clear doesn't change sceneRect.