Search code examples
c++qtscaleqgraphicsviewqgraphicsscene

Is there QLabel::setScaledContents equivalent function for QGraphicsScene in Qt?


I'm using QGraphicsView and QGraphicsScene to display an uploaded image and then show some drawing on it. I'm uploading and image like so:

void MeasuresWidget::on_openAction_triggered()
{
    QString fileName = QFileDialog::getOpenFileName(this,tr("Open File"), QDir::currentPath());
    if (!fileName.isEmpty())
    {
        QImage image(fileName);
        if (image.isNull())
        {
            QMessageBox::information(this, tr("Measures Application"), tr("Cannot load %1.").arg(fileName));
            return;
        }
        scene->clear();
        scene->addPixmap(QPixmap::fromImage(image).scaledToWidth(w, Qt::SmoothTransformation));
    }
}

The problem i'm facing is that if i upload an image that is smaller than the one that was uploaded before, there appears to be empty space, i.e. scene maintains the size of previous image(the bigger one) and is bigger than current one. I tried maintaining original size of scene in individual variable and using setSceneRect() in each upload action:

//in constructor
    originalRect = ui->graphicsView->rect();
//in upload action
    scene->setSceneRect(originalRect);

but result is that size of scene always stays the same and, if it's bigger than the actual image, cuts it. I used QLabel to display an image before and i used QLabel::setScaledContents() function and it worked fine for me. So, my question is can i achieve the same behaviour with QGraphicsScene?

Update 1: Application behaves the way i want if i create new scene every upload action. The code now looks like:

void MeasuresWidget::on_openAction_triggered()
{
    scene = new QGraphicsScene(this);
    ui->graphicsView->setScene(scene);
    QString fileName = QFileDialog::getOpenFileName(this,tr("Open File"), QDir::currentPath());
    if (!fileName.isEmpty())
    {
        QImage image(fileName);
        if (image.isNull())
        {
            QMessageBox::information(this, tr("Image Viewer"), tr("Cannot load %1.").arg(fileName));
            return;
        }
        scene->clear();
        scene->addPixmap(QPixmap::fromImage(image).scaledToWidth(w, Qt::SmoothTransformation));
    }

}

Is this ok? Can i achieve the behaviour i want without a need to create new scene every upload action?


Solution

  • You just have to resize the scene when you insert your pixmap based on its size.

    If you define a new class inheriting from QGraphicsScene, you can handle it easily:

    class GraphicsScene: public QGraphicsScene
    {
    public:
        GraphicsScene(QRect const& rect, QObject* parent=nullptr): QGraphicsScene(rect, parent),
            background(nullptr)
        {}
    
        QGraphicsPixmapItem *addPixmap(const QPixmap &pixmap)
        {
            // We already have a background. Remove it
            if (background)
            {
                removeItem(background);
                delete background;
            }
            background = QGraphicsScene::addPixmap(pixmap);
            // Move the pixmap
            background->setPos(0, 0);
            // Change the scene rect based on the size of the pixmap
            setSceneRect(background->boundingRect());
            return background;
        }
    private:
        QGraphicsPixmapItem* background;
    };
    
        GraphicsScene* scene = new GraphicsScene(QRect());
        QGraphicsView* view = new QGraphicsView();
        view->setScene(scene);
        view->show();
    
        QPixmap pix1(QSize(2000, 2000));
        pix1.fill(Qt::red);
    
    
        QPixmap pix2(QSize(100, 300));
        pix2.fill(Qt::green);
    
        // The scene will be 2000x2000
        QTimer::singleShot(1000, [=]() { scene->addPixmap(pix1); });
        // The scene will be 100x300
        QTimer::singleShot(10000, [=]() { scene->addPixmap(pix2); });