PDA

View Full Version : foreground problem



Alundra
30th March 2014, 05:21
Hi all,
I have a QGraphicsView inherited class who has a background, mouse overrided to allow pan on right click.
The Drag mode is set to RubberDrag to allow selection using rectangle on left click.
Mouse wheel is overrided to allow zoom who is just a change of Transform.
The problem is on the foreground, when I paint a text the size change because of zoom.
Another problem is this foreground is painted multiple time, normally it should just be painted on the bottom right of the window.
For example, the problem is visible when a selection rectangle is added using left click.
Here the code of the foreground :

void CEditorNodeView::drawForeground( QPainter* painter, const QRectF& rect )
{
QFont Font( "Arial", 20 );
QFontMetrics FontMetrics( Font );
painter->setPen( QColor( 128, 128, 128, 128 ) );
painter->setFont( Font );
const int X = rect.right();
const int Y = rect.bottom();
const int Width = FontMetrics.width( m_Name );
const int Height = FontMetrics.height();
painter->drawText( X - Width - 5, Y - Height, Width, Height, Qt::AlignCenter, m_Name );
}
If I add update() at the end of the function the problem is not visible but that mean that need update all the viewport always.
To use this hack NoViewportUpdate need to be set too to not update multiple time.
One solution for this problem ?
Thanks for the help

wysota
30th March 2014, 09:56
What exactly is the problem? If you mean that the text size changes, this is because the foreground is drawn is scene coordinates. If you want the size to remain fixed then either invert the world transformation of the view or simply reimplement paintEvent for the view, call the base class implementation and then do your own drawing in widget coordinates.

Alundra
30th March 2014, 15:47
Using :
painter->setTransform( painter->transform() * transform().inverted() );
The scaling is the same but translation is bad then when zoom, surely that end to identity matrix.
About the prob of paint multiple time, here a screenshot who shows the problem (using drawForeground) :
http://uppix.com/f-ForegroundProble533820680015e692.png
Adding update() at the end of drawForeground only the bottom-right text is visible who is the correct result.

wysota
30th March 2014, 16:27
I still fail to understand what is the problem you are having. If you have only one view on the scene then an answer could be to add another item to the scene with ItemIgnoresTransformations flag set.

Alundra
30th March 2014, 17:51
The problem is only this one is wanted :
http://uppix.com/f-ForegroundProble53383cd00015e6e0.png
drawForeground should paint only one I guess, so have 2 painted is bad behavior.

wysota
30th March 2014, 22:05
And what is the code responsible for painting that? Could you post a minimal compilable example reproducing the problem?

Alundra
31st March 2014, 04:05
The text is painted on the bottom-right, use left click to have the selection rectangle.
You will see the the text is painted on the bottom of the rectangle and stay at the end.
The same problem is visible when you move the cursor on QGraphicsItem of the scene.
Add update() at the end of drawForeground and you will see the problem will go away.


#include <QApplication>
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>

class CustomGraphicsView : public QGraphicsView
{
public:

CustomGraphicsView(QGraphicsScene* Scene, QWidget* Parent = 0) :
QGraphicsView(Scene, Parent)
{
setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
setRenderHint( QPainter::Antialiasing );
setDragMode( DragMode::RubberBandDrag );
}

void drawForeground( QPainter* painter, const QRectF& rect )
{
QFont Font( "Arial", 20 );
QFontMetrics FontMetrics( Font );
painter->setPen( QColor( 128, 128, 128, 128 ) );
painter->setFont( Font );
const int X = rect.right();
const int Y = rect.bottom();
const int Width = FontMetrics.width( "Name" );
const int Height = FontMetrics.height();
painter->drawText( X - Width - 5, Y - Height, Width, Height, Qt::AlignCenter, "Name" );
}
};

class MainWindow : public QMainWindow
{
public:

MainWindow(QWidget* parent = 0) :
QMainWindow(parent)
{
QGraphicsScene* Scene = new QGraphicsScene(this);
Scene->setSceneRect(-DBL_MAX * 0.5f, -DBL_MAX * 0.5f, +DBL_MAX, +DBL_MAX);
setCentralWidget(new CustomGraphicsView(Scene, this));
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

wysota
1st April 2014, 11:11
This is my code:

#include <QApplication>
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <limits.h>
#include <values.h>

class CustomGraphicsView : public QGraphicsView
{
public:

CustomGraphicsView(QGraphicsScene* Scene, QWidget* Parent = 0) :
QGraphicsView(Scene, Parent)
{
setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
setRenderHint( QPainter::Antialiasing );
setDragMode( QGraphicsView::RubberBandDrag );
}

void drawForeground( QPainter* painter, const QRectF& rect )
{
QFont Font( "Arial", 20 );
QFontMetrics FontMetrics( Font );
painter->setPen( QColor( 128, 128, 128, 128 ) );
painter->setFont( Font );
const int X = rect.right();
const int Y = rect.bottom();
const int Width = FontMetrics.width( "Name" );
const int Height = FontMetrics.height();
painter->drawText( X - Width - 5, Y - Height, Width, Height, Qt::AlignCenter, "Name" );
}
};

class MainWindow : public QMainWindow
{
public:

MainWindow(QWidget* parent = 0) :
QMainWindow(parent)
{
QGraphicsScene* Scene = new QGraphicsScene(this);
Scene->setSceneRect(-DBL_MAX * 0.5f, -DBL_MAX * 0.5f, +DBL_MAX, +DBL_MAX);
setCentralWidget(new CustomGraphicsView(Scene, this));
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

And this is the result I get:

10217

Alundra
1st April 2014, 15:00
Yea the code you use is the same I showed, the problem is when you click left to show the rectangle of selection or cursor on item.
The problem is that will repaint "NAME" on all that, the wanted behavior is to only have it on bottom right.
The problem was solved by J-P Nurmi on Qt BugReports :
But still the problem of scaling of font when zooming.


void drawForeground( QPainter* painter, const QRectF& )
{
QFont Font( "Arial", 20 );
QFontMetrics FontMetrics( Font );
painter->setPen( QColor( 128, 128, 128, 128 ) );
painter->setFont( Font );
QRectF r = rect();
const int X = r.right()- 20;
const int Y = r.bottom() - 20;
const int Width = FontMetrics.width( "Name" );
const int Height = FontMetrics.height();
painter->drawText( QRectF(mapToScene(X - Width - 5, Y - Height), QSizeF(Width, Height)), Qt::AlignCenter, "Name" );
}

wysota
1st April 2014, 15:53
The problem with your code is that you are not even checking where you are supposed to draw. The rect you get is not the whole scene rect but rather the rect that graphics view asks you to redraw. Regarding font scaling, if you manage to demonstrate the problem to us using code, maybe we can provide a fix. Currently the code you have does not take zooming into account, so the larger the zoom, the larger the text you'll get. As I said before, you'd have to invert the transform so that the original matrix is used or reimplement paintEvent and draw in widget coordinates:


class GV : public QGraphicsView {
public:
GV(QWidget *parent = 0) : QGraphicsView(parent) {}
void setName(const QString &n) { m_name = n; update(); }
protected:
void paintEvent(QPaintEvent *pe) {
QGraphicsView::paintEvent(pe);
QPainter p(viewport());
QFont f("Arial", 20);
p.setFont(f);
p.drawText(viewport()->rect(), Qt::AlignRight|Qt::AlignBottom, m_name);
}
private:
QString m_name;
};

Alundra
1st April 2014, 16:23
It's weird that using inverted matrix I don't have the good result but using ResetTransform that works :


void CEditorNodeView::drawForeground( QPainter* painter, const QRectF& )
{
painter->resetTransform();
const QFont Font( "Arial", 20 );
painter->setPen( QColor( 128, 128, 128, 128 ) );
painter->setFont( Font );
painter->drawText(viewport()->rect(), Qt::AlignRight | Qt::AlignBottom, "ANIMATION");
}

That end to the same as paintEvent like that, maybe the paintEvent method is faster because no reset needed ?
I have to do :


const QRectF r = rect();
const int X = r.right() - 4;
const int Y = r.bottom();

I have to offset it to show in good position, maybe a question of border ? A way exist to do that auto ?