Как сделать линейку на границе QGraphicsView - PullRequest
1 голос
/ 13 июня 2019

Я работаю над небольшим .ui проектом и пытаюсь понять, как правильно сделать линейку на QGraphicsView.

Поэтому, когда пользователь видит изображение, оно выглядит следующим образом:

rulerA

Но если пользователю нужно увеличить (или уменьшить) линейку, то она будет двигаться вместе со значением измерений:

rulerB

Спасибо за то, что пролили свет на эту проблему, и предоставили любой потенциальный пример или указали в правильном направлении.

1 Ответ

2 голосов
/ 15 июня 2019

Создайте новый класс, создав подкласс QWidget, чтобы нарисовать свою линейку.Затем установите поле области просмотра в зависимости от размера вашей линейки.

Основная трудность заключается в обработке единиц измерения вашей линейки: в процессе рисования в Qt используются только пиксели.Таким образом, вы должны преобразовать все расстояние в правильные единицы.

Пример линейки (в миллиметрах) для любого QAbstractScrollArea (включая 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();
    }
};

Функции paintEvent() и resizeEvent() могут быть упрощены, если вы хотите нарисовать значения по вертикали, а не по горизонтали (вам не нужно изменять размеры линейки, чтобы отображать все цифры).

Какиспользуйте его:

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;
};

Пиксель преобразования -> миллиметры не очень точен, и вы должны найти другой способ сделать это.

Я не обрабатывал изменение размера сцены, либо.

...