Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Create QGraphicsItems on top of QGraphicsView from QMainWindow



  • Hi,
    I recently made my own QGraphicsView and I added a photo from the MainWindow. I would like to create/draw multiple items on top of it. These can vary in amounts and in item sort.
    I would like to draw from the mainwindow, but i'm not sure if that's the right way to go.
    Should I draw the figures in the MainWindow or in the custom QGraphicsView?
    Drawing is done by checking the mousepress event and I would like to draw there where the mouse event takes place. Also I would like to zoom in and out (which I already got working).

    Any tips?

    This is my current code. It loads an image and lets you zoom in and out.

    MainWindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "graphicsview.h"
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        createActions();
        createMenus();
    
        scene = new QGraphicsScene();
        ui->graphicsView->setScene(scene);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::createActions()
    {
        openAct = new QAction(tr("&Open..."), this);
        openAct->setShortcuts(QKeySequence::Open);
        connect(openAct, SIGNAL(triggered()), this, SLOT(openPhoto()));
    
        exitAct = new QAction(tr("&Exit"), this);
        exitAct->setShortcuts(QKeySequence::Quit);
        connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
        
        drawAct = new QAction (tr("&Draw"), this);
    }
    
    void MainWindow::createMenus()
    {
    
    }
    
    void MainWindow::openPhoto()
    {
        QString fileName;
        fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath());
        QImage image(fileName);
        item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
        scene->addItem(item);
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        openPhoto();
    }
    

    this is my graphicsView.cpp:

    #include "graphicsview.h"
    
    graphicsView::graphicsView(QWidget *parent) : QGraphicsView(parent)
    {
    
    }
    
    void graphicsView::wheelEvent(QWheelEvent *event)
    {
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        double scaleFactor = 1.2;
    
        if(event->delta() > 0)
        {
            scale(scaleFactor, scaleFactor);
        }
        else
        {
            scale(1/scaleFactor, 1/scaleFactor);
        }
    }
    


  • @hobbyProgrammer
    You should really read https://doc.qt.io/qt-5/graphicsview.html# and for this case especially the section "The Graphics View Coordinate System".

    You get your coordinates in view coordinates, but add an item in scene coordinates. The scene auto-sizes to the items you already have in there, unless you override the sceneRect (see QGraphicsScene::sceneRect() for a description of that behavior). So depending on the size of your pixmap, the scene may be zoomed out or zoomed in. It's like watching a soccer game on a TV, and measuring the distance to the goal using a ruler on the TV set.

    If you want the code inside the QGraphicsView, you'll have to use QGraphicsView::mapToScene


  • Qt Champions 2017

    My inputs. Since you are using the GraphicsView, i suggest you to use GraphicsItem & draw your shapes.



  • @dheerendra yes, but should I do that in the graphicsview or in the mainwindow (like i did the photo)?



  • @hobbyProgrammer said in Create QGraphicsItems on top of QGraphicsView from QMainWindow:

    @dheerendra yes, but should I do that in the graphicsview or in the mainwindow (like i did the photo)?

    These are two questions in one:

    • How are the object instances related to each other?
    • Where do I put my code?

    The photo is a QGraphicsPixmap item inside the scene (if I read your other thread correctly), so the same should be the case for any other items you create.
    However, that does not answer the question of where to put your code.

    If I hack a quick and dirty project, I usually put the code inside the main window. If it should be a little more polished, I create a class that manages the scene for such things. I create that class in the main window, and it returns a readily-configured scene for the main window to use.



  • @Asperamanca Hi,
    Thank you so much for your answer.
    To get a better understandig of what I need to do I would like an answer to the following question:
    What's actually the difference between the scene and the view?


  • Lifetime Qt Champion

    @hobbyProgrammer said in Create QGraphicsItems on top of QGraphicsView from QMainWindow:

    What's actually the difference between the scene and the view?

    See documentation: https://doc.qt.io/qt-5/qgraphicsview.html
    "QGraphicsView visualizes the contents of a QGraphicsScene in a scrollable viewport."

    And https://doc.qt.io/qt-5/qgraphicsscene.html
    "The class serves as a container for QGraphicsItems."



  • @jsulm okay so I should add all the items to the scene and then set the view's scene to the scene I put in all the items and it will draw those items for me?



  • I've rewritten my code to be able to load the image inside of the QGraphicsView, but the canvas stays empty.
    Can someone help me find the mistake?

    mainWindow.cpp:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "graphicsview.h"
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        createActions();
        createMenus();
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::createActions()
    {
        openAct = new QAction(tr("&Open..."), this);
        openAct->setShortcuts(QKeySequence::Open);
        connect(openAct, SIGNAL(triggered()), this, SLOT(openPhoto()));
    
        exitAct = new QAction(tr("&Exit"), this);
        exitAct->setShortcuts(QKeySequence::Quit);
        connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
    
        drawAct = new QAction (tr("&Draw"), this);
    }
    
    
    
    void MainWindow::createMenus()
    {
    
    }
    
    void MainWindow::openPhoto()
    {
        QString fileName;
        fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath());
    
        graphicsView *gv = new graphicsView();
        gv->openImage(fileName);
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        openPhoto();
    }
    

    graphicsView.cpp

    #include "graphicsview.h"
    
    graphicsView::graphicsView(QWidget *parent) : QGraphicsView(parent)
    {
        scene = new QGraphicsScene();
        this->setScene(scene);
    }
    
    void graphicsView::wheelEvent(QWheelEvent *event)
    {
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        double scaleFactor = 1.2;
    
        if(event->delta() > 0)
        {
            scale(scaleFactor, scaleFactor);
        }
        else
        {
            scale(1/scaleFactor, 1/scaleFactor);
        }
    }
    
    void graphicsView::mousePressEvent(QMouseEvent *event)
    {
        if(event->button() == Qt::LeftButton)
        {
        }
        else if(event->button() == Qt::RightButton)
        {
    
        }
    
    }
    
    void graphicsView::openImage(QString filename)
    {
        QImage image = QImage(filename);
        item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
        scene->addItem(item);
    }
    
    


  • @hobbyProgrammer said in Create QGraphicsItems on top of QGraphicsView from QMainWindow:

    graphicsView *gv = new graphicsView();
    gv->openImage(fileName);
    

    You create a new view, but you do nothing with it.
    Do you want to pop up a new window every time you open an image? Or do you want to have the GraphicsView as part of the main window?



  • @Asperamanca since I promoted my widget in the designer, I changed the code to this:

        ui->graphicsView->openImage(fileName);
    

    now it works perfectly! Thank you so much!



  • Just added this:

    void graphicsView::mousePressEvent(QMouseEvent *event)
    {
        if(event->button() == Qt::LeftButton)
        {
            QGraphicsEllipseItem *newEllipse = new QGraphicsEllipseItem();
    
            int x,y;
            x = event->pos().x();
            y = event->pos().y();
    
            newEllipse->setRect(QRectF(x,y,5,5));
            scene->addItem(newEllipse);
        }
    }
    

    but it results into this:

    70949c9e-f4b0-43f3-9cee-b6a3286b55bd-image.png

    I would really like it if the item showed up on the exact same position where I pressed the mouse.
    I have no clue why it doesn't do that.
    Any ideas?



  • it seems to be the problem where it wants to draw from the middle instead of from the top left corner. I know that this problem is solvable, but I can't find the solution anywhere.



  • @hobbyProgrammer
    You should really read https://doc.qt.io/qt-5/graphicsview.html# and for this case especially the section "The Graphics View Coordinate System".

    You get your coordinates in view coordinates, but add an item in scene coordinates. The scene auto-sizes to the items you already have in there, unless you override the sceneRect (see QGraphicsScene::sceneRect() for a description of that behavior). So depending on the size of your pixmap, the scene may be zoomed out or zoomed in. It's like watching a soccer game on a TV, and measuring the distance to the goal using a ruler on the TV set.

    If you want the code inside the QGraphicsView, you'll have to use QGraphicsView::mapToScene



  • This post is deleted!


  • @Asperamanca
    Hi,
    Thank you so much for the explaination.
    It made me feel like an absolute fool, but that's okay. It's really good to learn and now what everything stands for and you clarified that so much for me using that example.



  • @hobbyProgrammer
    Not sure whether your last question is still open. You talk about mapToScene, but you don't use it in your code.

    You don't sound like a fool to me...to quote John Cleese: "One of the big problems of the world is that the fools are so sure of themselves, and the wise people so full of doubt".
    If you were a fool, you wouldn't be asking questions, but instead insisting that Qt is stupid, and you didn't need it anyway :-)



  • @Asperamanca No it's solved (that's why I removed it)

    "If you were a fool, you wouldn't be asking questions, but instead insisting that Qt is stupid, and you didn't need it anyway :-)"
    this is litteraly every one of my co-students. They all hate Qt so much, but it needs a lot of time and patience to get right in. I'm gonna keep trying, because I think you can create a lot of different things with it.


Log in to reply