2
votes

Im doing a little Plotting program with a group of students , we are using Qt's QGraphicsScene on a QGraphicsView to let the user plot custom Points on Specific Positions (x , y) , each point has to have a text on top of it.

Here is the function responsible for adding Points to the Scene :

void MainWindow::AddPoint(float x, float y, QString name)
{
    y = y * -1; // To Flip Y-Axis
    float Radius = 1; // Point's (Eclipse) Radius
    QGraphicsItem *Point = p_Scene->addEllipse(x , y , Radius , Radius , QPen(QColor(Qt::red)) , QBrush(QColor(Qt::red))); // Creates a Red Colored Point on the given Coordinates (x , y) 
    /*
    QGraphicsTextItem *Text = p_Scene->addText(name); // Creates a Text
    Text->setDefaultTextColor(QColor(Qt::red)); // Sets Text's Color to Red
    Text->setFont(QFont("Courier New" , 4)); // Sets Text's Font Size
    Text->setPos(x , y - 10); // Set Text's Position (On top of the Point)
    ui->graphicsView->setScene(p_Scene); // Adds Text to the Scene
    */
}

so the Implementation would be like :

AddPoint(0 , 0 , "P1"); // Point 1
AddPoint(50 , 100 , "P2"); // Point 2
AddPoint(100 , 0 , "P3"); // Point 3 

This will results in :

An Example of Plotting

We are using :

ui->graphicsView->fitInView(ui->graphicsView->scene()->sceneRect() , Qt::KeepAspectRatio);

to make sure that QGraphicsView shows only whats visible (pretty important).

so the problem here is , if we were to make the drawing larger , say for example :

AddPoint(0 , 0 , "P1");
AddPoint(0 , 1000 , "P2"); // y = 1000

This will draw a very long line which will make the Points + Text we created so small that it cant even be seen :

Example of Plotting using Large numbers

So what we need here is to somehow calculate the SceneRect (i think) and find out the radius value + font size that we should use for both the Point and the Text so they stay the same size regardless of the Scene's Size.

EDIT : This is the NEW code (according to vcloarec's solution) :

GraphicsWindow.h (QGraphicsView Subclass) :

#ifndef GRAPHICSVIEW_H
#define GRAPHICSVIEW_H

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QDebug>

class GraphicsView : public QGraphicsView
{
    Q_OBJECT

public:
    explicit GraphicsView(QWidget *parent = 0);
    void AddPoint(float x , float y , QString name = "");
    void resize();

private:
    QGraphicsScene *p_Scene;
    int p_SizeInView;
};

#endif // GRAPHICSVIEW_H

GraphicsWindow.cpp :

#include "GraphicsView.h"

GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent)
{
    p_PointRadius = 0.0;
    p_PointsLastN = 0;
    p_SizeInView = 5;
    p_Scene = new QGraphicsScene(this);

    this->setScene(p_Scene);
}

void GraphicsView::AddPoint(float x, float y, QString name)
{
    y = y * -1;

    QGraphicsItem *_Point = p_Scene->addEllipse(x , y , 1 , 1 , QPen(QColor(Qt::red)) , QBrush(QColor(Qt::red)));

    this->fitInView(scene()->sceneRect() , Qt::KeepAspectRatio);
    resize();
}

void GraphicsView::resize()
{
    qreal scale = p_SizeInView / this->transform().m11();

    for(int i = 0; i < this->scene()->items().count(); i++)
    {
        this->scene()->items().at(i)->setScale(scale);
    }
}

MainWindow.cpp :

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->toolBar->addWidget(ui->ZoomUp_Button);
    ui->toolBar->addWidget(ui->ZoomDown_Button);

    setCentralWidget(ui->graphicsView);

    ui->graphicsView->AddPoint(0 , 0);
    ui->graphicsView->AddPoint(1000 , 0);
    ui->graphicsView->AddPoint(1000 , 50);
    ui->graphicsView->AddPoint(0 , 50);
}

MainWindow::~MainWindow()
{
    delete ui;
}

This code scales the Points according to a fixed Scale but still results in Scrollbars which is something we have to solve. Somehow it ignores fitInView() , OR it does actually fit it but when the Points are resized it resizes the SceneRect or something

Here is the result :

enter image description here

PS : We subclassed QGraphicsView because we will need MouseEvents and other things later.

EDIT : Solved by vcloarec : The solution was to insert the Points at (-0.5 , -0.5) and than setPose(x , y) which will set the Position to the x , y we pass to the AddPoint(x , y). The Points now keep the same size regardless of the Scene's size , and it will show all the Points created at once without any scrollbars or anything.

Thank You !

1

1 Answers

1
votes

The dimensions of the points end the text is define in the scene coordinate, not in the viewport (the windows) coordinate .

If you want the points and text keep their dimension on the display, you have to update the dimension depending of the "zoom" of your viewport.

Edit :

I try an analogy :

  • QGraphicsView is a camera
  • QGraphicScene is the real life
  • QGraphicsItems are people ant things

If you want to see a particular part of the scene, you use QGraphicsView::setSceneRect(const QRectF & rect) to "zoom" on the part define by rect. When you "zoom" or "unzoom" with the camera on objects, this objects don't change their size in the real life, but in the screen the size change. It is the same behaviour with QGraphicsView.

If you want a fix size of the representation of your object, you have to adapt the size of your object with the scale of your scene. In your example with addPoint(0 , 0 , "P1"); addPoint(0 , 1000 , "P2"), the two points are far away from each other, and the points and texts are very small in comparison of this distance.

The solution of your problem depends of the type of the representation you want (dynamic, static,...)

Maybe you can use the matrix returned by QTransform QGraphicsView::transform() const and their diagonal elements to find the scale to use.

Look at this :

    class MyView:public QGraphicsView
{
public:
    MyView(QWidget *parent):QGraphicsView(parent),sizeInView(5) {}

    void resize();

protected:
    void wheelEvent(QWheelEvent *event);

private:
    int sizeInView;

};

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void putPoint();

private:

    Ui::MainWindow *ui;
    QGraphicsScene *scene;
    MyView *view;
};

And

   MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
   ui->setupUi(this);
   view=new MyView(this);
   centralWidget()->setLayout(new QHBoxLayout());
   centralWidget()->layout()->addWidget(view);

   scene=new QGraphicsScene(this);
   view->setScene(scene);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::putPoint()
{
    QGraphicsEllipseItem *point1= new QGraphicsEllipseItem(-0.5,-0.5,1,1);
    point1->setPos(0,0);
    QGraphicsEllipseItem *point2= new QGraphicsEllipseItem(-0.5,-0.5,1,1);
    point2->setPos(0,100);
    QGraphicsEllipseItem *point3= new QGraphicsEllipseItem(-0.5,-0.5,1,1);
    point3->setPos(0,1000);
    QGraphicsEllipseItem *point4= new QGraphicsEllipseItem(-0.5,-0.5,1,1);
    point4->setPos(100,0);
    QGraphicsEllipseItem *point5= new QGraphicsEllipseItem(-0.5,-0.5,1,1);
    point5->setPos(100,100);
    QGraphicsEllipseItem *point6= new QGraphicsEllipseItem(-0.5,-0.5,1,1);
    point6->setPos(100,1000);

    scene->addItem(point1);
    scene->addItem(point2);
    scene->addItem(point3);
    scene->addItem(point4);
    scene->addItem(point5);
    scene->addItem(point6);

    view->fitInView(scene->sceneRect(),Qt::KeepAspectRatio);
    view->resize();
}


void MyView::resize()
{
    qreal scale=sizeInView/transform().m11();
    for (int i=0;i<scene()->items().count();++i)
        scene()->items().at(i)->setScale(scale);
}

void MyView::wheelEvent(QWheelEvent *event)
{
    float fact=1.5;
    if (event->delta()>=120)
    {
        setTransform(transform()*fact);
        resize();
    }

    if (event->delta()<=-120)
    {
       setTransform(transform()/fact);
       resize();
    }
}

Be careful, the insertion point of your QGraphicsItem in the scene is define by QGraphicsItem::setPos. The (x,y), you use when you create the point, is the position in the local coordinate system, not in the scene coordinate systeme and it is not the center on you point but the topleft rectangle containing the ellipse.

So if the center of your point is not on the point of insertion, when you resize, the point move ... That's why i place the point at (-0.5,-0.5) in local coordinate with a height and width equal to 1. Then, I place the point with setPos in the scene coordinate.

If you want to disable the scrollbar :

setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff )