How to make a ruler on the border of a QGraphicsView

前端 未结 2 1588
说谎
说谎 2021-01-23 12:02

I am working on a small .ui project and I am was trying to understand how to properly make a ruler on a QGraphicsView.

So when the use sees th

2条回答
  •  不思量自难忘°
    2021-01-23 12:25

    Create a new class by subclassing QWidget to draw your ruler. Then, set a viewport margin based on the size of your ruler.

    The main difficulty is to handle the units of your ruler: The painting process in Qt uses only pixels. So, you have to convert all the distance to the right unit.

    An example of ruler (in millimeters) for any QAbstractScrollArea (including QGraphicsView):

    class Ruler: public QWidget
    {
        Q_OBJECT
    public:
        Ruler(QAbstractScrollArea* parent=nullptr): QWidget(parent),
            offset(0)
        {
            setFixedSize(40, parent->height());
            move(0, 40);
            connect(parent->verticalScrollBar(), &QScrollBar::valueChanged, this, &Ruler::setOffset);
        }
        virtual void paintEvent(QPaintEvent* event)
        {
            QPainter painter(this);
            painter.translate(0, -offset);
            int const heightMM = height() * toMM();
            painter.setFont(font());
            QFontMetrics fm(font());
            for (int position = 0; position < heightMM; ++position)
            {
                int const positionInPix = int(position / toMM());
                if (position % 10 == 0)
                {
                    if (position != 0)
                    {
                        QString const txt = QString::number(position);
                        QRect txtRect = fm.boundingRect(txt).translated(0, positionInPix);
                        txtRect.translate(0, txtRect.height()/2);
                        painter.drawText(txtRect, txt);
                    }
                    painter.drawLine(width() - 15, positionInPix, width(), positionInPix);
                }
                else {
                    painter.drawLine(width() - 10, positionInPix, width(), positionInPix);
                }
            }
        }
    
        virtual void resizeEvent(QResizeEvent* event)
        {
    
            int const maximumMM = event->size().height() * toMM();
            QFontMetrics fm(font());
            int w = fm.width(QString::number(maximumMM)) + 20;
            if (w != event->size().width())
            {
                QSize const newSize(w, event->size().height());
                sizeChanged(newSize);
                return setFixedSize(newSize);
            }
            return QWidget::resizeEvent(event);
        }
    
        void setOffset(int value)
        {
            offset = value;
            update();
        }
    signals:
        void sizeChanged(QSize const&);
    private:
        int offset;
    
        static qreal toMM()
        {
            return 25.4 / qApp->desktop()->logicalDpiY();
        }
    };
    

    The paintEvent() and resizeEvent() functions could be simplified if you want to draw the values at the vertical instead of horizontal (you will not have to resize the ruler to be able to display all digits).

    How to use it:

    class GraphicsView: public QGraphicsView
    {
    public:
        GraphicsView(QWidget* parent=nullptr): QGraphicsView(parent),
            ruler(new Ruler(this))
        {
            connect(ruler, &Ruler::sizeChanged, [this](QSize const& size) { setViewportMargins(size.width(), size.width(), 0, 0); });
        }
    
        void setScene(QGraphicsScene* scene)
        {
            QGraphicsView::setScene(scene);
            if (scene)
                ruler->setFixedHeight(scene->height());
        }
    private:
        Ruler* ruler;
    };
    

    The conversion pixel -> millimeters is not really accurate and you should find another way to make it.

    I didn't handle the resize of the scene, either.

提交回复
热议问题