如何在 QGraphicsView 的边框上制作标尺
How to make a ruler on the border of a QGraphicsView
我正在做一个小 .ui
项目,我试图了解如何在 QGraphicsView
上正确制作尺子。
因此,当用户看到图像时,它看起来如下所示:
但如果用户需要放大(或缩小)标尺会随着测量值相应移动:
感谢您阐明这个问题并提供任何可能的例子或指出正确的方向。
通过子classing QWidget
创建一个新的 class 来绘制您的标尺。然后,根据标尺的大小设置视口边距。
主要的困难是处理你的标尺单位: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;
};
像素->毫米的转换并不准确,您应该另辟蹊径。
我也没有处理场景的大小调整。
创建时,scene->height() 默认为零。如果 ruler->setFixedHeight() 无意中将标尺的高度设置为 0,则不会调用标尺的 paintEvent。
void setScene(QGraphicsScene* scene)
{
QGraphicsView::setScene(scene);
if (scene)
ruler->setFixedHeight(scene->height());
}
我正在做一个小 .ui
项目,我试图了解如何在 QGraphicsView
上正确制作尺子。
因此,当用户看到图像时,它看起来如下所示:
但如果用户需要放大(或缩小)标尺会随着测量值相应移动:
感谢您阐明这个问题并提供任何可能的例子或指出正确的方向。
通过子classing QWidget
创建一个新的 class 来绘制您的标尺。然后,根据标尺的大小设置视口边距。
主要的困难是处理你的标尺单位: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;
};
像素->毫米的转换并不准确,您应该另辟蹊径。
我也没有处理场景的大小调整。
创建时,scene->height() 默认为零。如果 ruler->setFixedHeight() 无意中将标尺的高度设置为 0,则不会调用标尺的 paintEvent。
void setScene(QGraphicsScene* scene)
{
QGraphicsView::setScene(scene);
if (scene)
ruler->setFixedHeight(scene->height());
}