Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you keep QPen pixel width the same when zooming in a QGraphicsView

Tags:

c++

qt5

I wrote a quick and nasty program to help me visualise an aspect of a project I'm working on. Although I've been working with Qt since 4.1.1, I've never really had the need to use the QGraphics* module.

When I started playing round with the program, I was working on an old computer running Windows XP and Qt4.7.0 and Visual Studio 2008. I've now just copied the file across to my main computer running Windows 8. I've decided to give Qt5 a shot so I've installed QtCreator with Qt5.0.2.

When I compile the exact same code as I created on the Qt4.7.0/XP machine, I get a vastly different result.

This is what I see on my Qt4.7.0 compilation:

Zoomed-out view from Qt4.7.0/XP compilation

and this is what I see on my Qt5.0.2 compilation: Zoomed-out view from Qt5.0.2/Win8x64 compilation

Clearly there's different behaviour in the drawing of each rectangle's border. Also, if I zoom in using the mouse wheel, the rectangle border width gets bigger in the Qt5 compilation but stays the same(about 1 pixel wide) in the Qt4.7 compilation.

How do I change the code so that behaviour in Qt5 is the same as in Qt4.7?

Here's the full code:

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QWheelEvent>

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QVariant>
#include <QSqlError>
#include <QMessageBox>

class ItemData : public QSqlRecord{
public:
    ItemData(QSqlRecord const &rec) : QSqlRecord(rec) {}

    qreal left() const { return value(0).toDouble(); }
    qreal top() const { return value(1).toDouble(); }
    qreal width() const { return value(2).toDouble() - left(); }
    qreal height() const { return value(3).toDouble() - top(); }
    QRectF rect() const { return QRectF(left(), top(), width(), height()); }
    QString barcode() const { return value(4).toString(); }
    QString msaName() const { return value(5).toString(); }
    QString msaDescription() const { return value(6).toString(); }
    QString hsaName() const { return value(7).toString(); }
    QString hsaDescription() const { return value(8).toString(); }
};

class DSAItem : public QGraphicsRectItem{
public:
    DSAItem(ItemData const &data, QGraphicsItem *parent = 0)
        :QGraphicsRectItem(parent) {
            setFlags(QGraphicsItem::ItemIsSelectable);
            setRect(data.rect());
            QString tip = "<p><b>%1</b></p><p><b>MLSA</b><br/>%2<br/>%3</p><p><b>HLSA</b><br/>%4<br/>%5</p>";
            setToolTip(tip.arg(data.barcode(), data.msaName(), data.msaDescription(), data.hsaName(), data.hsaDescription()));
            if(data.barcode() == "1010100101" || data.barcode() == "1010100114"){
                colour = QColor(Qt::red);
            } else {
                colour = QColor(Qt::yellow);
            }
            colour.setAlphaF(.5);
            setBrush(QBrush(colour));
    }

    QVariant itemChange(GraphicsItemChange change, QVariant const &value){
        if (change == QGraphicsItem::ItemSelectedHasChanged){
            QColor c = value.toBool() ? QColor(Qt::green) : colour;
            c.setAlphaF(.5);
            setBrush(QBrush(c));
            update(rect());

        }
        return QGraphicsRectItem::itemChange(change, value);
    }

private:
    QColor colour;
};

class View : public QGraphicsView {
public:
    View(QWidget *parent = 0): QGraphicsView(parent){
        populateScene();
        setScene(&scene);
        rotate(-90);
        scale(1.0, -1.0);
        setDragMode(ScrollHandDrag);
    }

protected:
    void wheelEvent(QWheelEvent *e){
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);

        double scaleFactor = 1.15;
        if (e->delta() > 0){
            scale(scaleFactor, scaleFactor);
        } else {
            scale(1 / scaleFactor, 1 / scaleFactor);
        }
    }

private:
    void populateScene(){
        QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
        //db credentials here

        QString errorMessage;
        bool ok = db.open();
        if (ok){
            QSqlQuery query(db);
            QString sql = //query string here

            if (query.exec(sql)){
                while(query.next()){
                    scene.addItem(new DSAItem(query.record()));
                }
            } else {
                errorMessage = query.lastError().text();
            }
        } else {
            errorMessage = db.lastError().text();
        }

        if (!errorMessage.isEmpty()){
            QMessageBox::critical(0, "Database Error", errorMessage);
        }
    }

private:
    QGraphicsScene scene;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    View view;
    view.show();

    return a.exec();
}
like image 727
RobbieE Avatar asked Jun 12 '13 21:06

RobbieE


2 Answers

This is a bit late but might be helpful.

The proposed solution works fine, but limits the pen widht. An alternative is to use the instruction

p.setCosmetic(true);

According to the Qt reference (http://qt-project.org/doc/qt-5/qpen.html#isCosmetic)

Cosmetic pens are used to draw strokes that have a constant width regardless of any transformations applied to the QPainter they are used with.

And it also explains why setting the width to zero works:

A zero width pen is cosmetic by default; pens with a non-zero width are non-cosmetic.

like image 141
rpsml Avatar answered Nov 15 '22 20:11

rpsml


The best answer is given by rpsml below this one. But for historical reasons I'll leave this one here.

Setting the width of the pen to zero will make it a "cosmetic" pen with a width of 1.

QPen p = pen();
p.setWidth(0)
setPen(p);
like image 42
Cory Klein Avatar answered Nov 15 '22 19:11

Cory Klein