PDA

View Full Version : Getting size of a QGraphicsScene



sedi
29th April 2012, 04:05
Hi, this is a "spin-off" question of the thread here (http://www.qtcentre.org/threads/48695-Preserving-border-antialiasing-QWidget-on-a-QGraphicsProxyWidget?p=218981#post218981).

To keep control over available screen space I want to know the visible part of a QGraphicsScene - in scene coordinates.

To verify that my information is correct, I want to draw a black rectangle all over the visble scene area. Of course, the rectangle is just for testing purposes :-)

I've tried the following approach:

MainWindow::MainWindow()
{

this->showMaximized();

scene=new QGraphicScene(this);
view = new QGraphicsView(scene);
QWidget *widget = new QWidget;
QHBoxLayout *layout = new QHBoxLayout;


//widget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy:: Expanding);
setCentralWidget(widget);
widget->setLayout(layout);
layout->addWidget(view);

this->scene->setSceneRect(this->view->rect());

QGraphicsRectItem* rectItem = new QGraphicsRectItem();
rectItem->setRect(this->view->mapToScene(this->view->rect()).boundingRect());

scene->addItem(rectItem);
rectItem->setZValue(1000);
rectItem->setBrush(QBrush(Qt::black,Qt::SolidPattern));


view->setRenderHints(QPainter::HighQualityAntialiasing|Q Painter::TextAntialiasing);
view->setDragMode(QGraphicsView::ScrollHandDrag);
view->setInteractive(true);
view->setMouseTracking(true);



[...put other stuff onto scene...]

}

I'd love to see a black screen now - but the RectItem is much smaller than expected - thus I have quite obviously made a mistake in getting the size of the viewable area...

Only the code seems logical and correct to me.

Desired result: 7652

Actual result: 7653

What am I doing wrong?

amleto
29th April 2012, 13:24
You haven't tried to get the viewable area anywhere, though. You are setting the scene to be the same size as the view widget.

When you set the scene rect to the rect of the view, you expect that http://qt-project.org/doc/qt-4.8/qgraphicsview.html#transform is identity. Have you checked it?



edit:

see my sig as well.
scene=new QGraphicScene(this);
that will give compiler error. Please only cut/paste code.



You also have the problem of not updating your rects on size change of the view...
Having done a quick test, this is your problem

I added some code like this


void
Widget::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);

m_view->scene()->clear();

m_view->scene()->setSceneRect(m_view->rect());

QTransform t = m_view->transform();

QGraphicsRectItem* rectItem = new QGraphicsRectItem();
rectItem->setRect(m_view->mapToScene(m_view->rect()).boundingRect());
rectItem->setBrush(QBrush(Qt::black,Qt::CrossPattern));

m_view->scene()->addItem(rectItem);
}

and the rectangle always fills the widget.

sedi
29th April 2012, 14:38
Thank you amleto, nice to see you over here, too! Well, I don't know how to get the area - that's kind of my problem ;-). I thought the viewable size might be exactly that of the view. I've changed "view" to view->viewport():

rectItem->setRect(this->view->mapToScene(this->view->viewport()->rect()).boundingRect()); But it didn't change the result.

I have never set any translation on scene or view, thus I hadn't checked that and I'm not quite sure if I did it correctly now:

qDebug()<<this->view->transform(); yields this:
QTransform(type=TxNone, 11=1 12=0 13=0 21=0 22=1 23=0 31=0 32=0 33=1) Because of "type=TxNone" I think this should be a non-transforming matrix. Thinking about it... it's a 3x3 matrix with diagonal "1"s, that's identity, of course. Long time since I last thought about that stuff... :-)

As for the compilable code: it's a >6000 lines project by now, so I have built a new project here that is compilable and seems the smallest size possible to reproduce my problem:
main.cpp:

#include <QtGui>
#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWin;
mainWin.show();
return app.exec();
}
mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QGraphicsScene>
#include <QMainWindow>

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow();
~MainWindow();
private:
QGraphicsView *view;
QGraphicsScene *scene;
};


#endif // MAINWINDOW_H
mainwindow.cpp:


#include <QtGui>
#include "mainwindow.h"
#include <qdebug.h>

MainWindow::MainWindow()
{
this->showMaximized();
scene=new QGraphicsScene(this);
view = new QGraphicsView(scene);
QHBoxLayout *layout = new QHBoxLayout;
QWidget *widget = new QWidget;

setCentralWidget(widget);
widget->setLayout(layout);

layout->addWidget(view);
this->scene->setSceneRect(this->view->viewport()->rect());

QGraphicsRectItem* rectItem = new QGraphicsRectItem();
rectItem->setRect(this->view->mapToScene(this->view->viewport()->rect()).boundingRect());
qDebug()<<this->view->transform();

scene->addItem(rectItem);
rectItem->setBrush(QBrush(Qt::black,Qt::SolidPattern));
}
MainWindow::~MainWindow()
{
delete scene;
}

Instead of the scene being filled entirely by the black QGraphicsRectItem (as expected) the result of this "naked" example is this one:
7654
I've tried to follow this thread (http://www.qtcentre.org/threads/16541-How-to-get-the-visible-rect-of-a-QGraphicsView), but my result doesn't change, when I do the correspondend changes in my mainwindow.cpp:

QMatrix const matrix = view->matrix().inverted();
QRect visibleRect = matrix.mapRect(view->viewport()->rect());
// when the viewport is scrolled, then the top left point of the visible rectangle needs to be moved
// according to the scroll bar positions
visibleRect.moveTopLeft(matrix.map(QPoint(view->horizontalScrollBar()->value(),
view->verticalScrollBar()->value())));

QGraphicsRectItem* rectItem = new QGraphicsRectItem();
rectItem->setRect(visibleRect);
I am quite desperate now. It seemed to work for shad0w, why not for me?
Thank you for your time!

amleto
29th April 2012, 17:53
sorry, I edited my post a few times and took some questions out as I found the actual problem I think you are facing - see the end of my previous post.

sedi
29th April 2012, 23:02
Hi Amleto, you've made my week. Thinking about it, it's just logical. I had thought, "this->showMaximized();" would have put me onto the sunny side of a maximized window already - but, of course, the "mainWin.show();" is yet to come after all the stuff I did in the constructor. Actually I took a different approach in the details, but you are right, the real problem is: i have to wait for the resize. I've tried it out quickly with a single shot timer - BINGO.


QPointF tl(view->horizontalScrollBar()->value(), view->verticalScrollBar()->value());
QPointF br = tl + view->viewport()->rect().bottomRight();
QMatrix mat = view->matrix().inverted();
QRectF visibleRect=mat.mapRect(QRectF(tl,br));
rectItem->setPos(view->mapToScene(view->viewport()->pos()));
visibleRect.setRect(visibleRect.x(),visibleRect.y( ),visibleRect.width()-1,visibleRect.height()-1); //the "-1" is just for being able to see the red pen
rectItem->setPen(QPen(Qt::red));
rectItem->setRect(visibleRect);


Edit:
To make it work in the initialisation of my real program it was not enough to hook into the resizeEvent, I also had to call the calculation from QEvent::show, as done here:

void CentralWidget::resizeEvent(QResizeEvent *)
{
emit this->centralWidgetWasResized();
}
bool CentralWidget::event(QEvent * event)
{
if (event->type()==QEvent::Show) emit this->centralWidgetWasResized();
event->ignore();
QWidget::event(event);
return true;
}

Still, though, I'll have to keep in mind that the correct size is provided only after nearly all my constructors' work has been done.
Thank you very much for all the effort you've put into this!

Spitfire
30th April 2012, 10:19
To make it work in the initialisation of my real program it was not enough to hook into the resizeEvent, I also had to call the calculation from QEvent::show, as done here:
That is incorrect. If you had to do that - you did something wrong.

Take a look (as compile at try) at this example code:


// mainwindow.h
class QGraphicsView;
class QGraphicsRectItem;

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow( QWidget* parent = 0) ;

protected:
void resizeEvent( QResizeEvent* event );

private:
QGraphicsView* view;
QGraphicsRectItem* item;
};

//mainwindow.cpp
MainWindow::MainWindow( QWidget* parent )
:
QMainWindow( parent ),
view( new QGraphicsView( this ) ),
item( NULL )
{
QGraphicsScene* scene = new QGraphicsScene( this );
this->item = scene->addRect( 0,0,0,0, QPen(), Qt::blue );

this->view->setScene( scene );
this->setCentralWidget( view );
}

void MainWindow::resizeEvent( QResizeEvent* e )
{
this->view->scene()->setSceneRect( this->view->viewport()->rect() );
this->item->setRect( this->view->viewport()->rect() );
}

// main
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.showMaximized(); // here you show the window, not in the constructor

return a.exec();
}

Resize event always has the new size of the window and is delivered after the show event. The is no reason for you to check size of the window in the show event.
Also, you should not show widget from within the widget, you've got main() for that.


Still, though, I'll have to keep in mind that the correct size is provided only after nearly all my constructors' work has been done.
Again not correct - window size is set after calling show (for first time or resize or any other method of changing window geometry) but only after execution flow goes back to the event loop.
So not only window size isn't set when you constructor work is entirely finished, it will not be set untill the system takes over (in this case when you call a.exec() ).

sedi
1st May 2012, 00:35
Thank you, I'll look int that!!