Qt / SVG - 获取元素的准确坐标
Qt / SVG - getting accurate coordinates of element
我正在使用 Qt,试图制作一个“可点击的 SVG”小部件的原型。我正在尝试从 QSvgWidget
.
获取 SVG 中特定元素的准确坐标
为了弄清楚如何做到这一点,我有以下非常粗略的代码。我想要做的是以编程方式让“foo”QLabel 与显示的 SVG 中的“foo”矩形具有相同的位置。
from PySide2.QtWidgets import QApplication, QWidget, QLabel, QMainWindow
from PySide2.QtSvg import QSvgWidget
from PySide2.QtCore import QTimer
import sys
app = QApplication(sys.argv)
window = QMainWindow()
central = QSvgWidget("test_svg.svg")
foo = QLabel("foo", parent=central)
window.setCentralWidget(central)
bound1 = central.renderer().boundsOnElement("foo")
bound2 = central.renderer().transformForElement("foo").mapRect(bound1)
tl = bound2.topLeft()
foo.move(tl.x(), tl.y())
window.show()
app.exec_()
这是 SVG:
<svg height="400" width="450" viewbox="0 0 450 400" xmlns="http://www.w3.org/2000/svg">
<circle stroke="red" fill="none" cx="50" cy="50" r="30" />
<rect id="foo" stroke="blue" fill="none" x="100" y="300" width="20" height="5" />
</svg>
这是我在屏幕上看到的内容:
当我调试发生的事情时,我看到 bound1.topLeft()
和 bound2.topLeft()
相同,并且等于 PySide2.QtCore.QPointF(99.500000, 299.500000)
。这几乎是“foo”元素在 SVG 坐标中的位置。
我不确定正确的术语是什么,但让我将 SVG 坐标系 定义为 SVG 文件中使用的坐标系和 小部件坐标系 作为对应于实际像素位置的坐标系,其中某些内容相对于相关小部件的左上角以像素呈现。
所以 QSvgWidget
似乎给我 SVG 坐标而不是“小部件坐标”。
有谁知道我是否有一种规范的方法让 Qt 为我提供针对这些目的“准确”的坐标?我可能可以通过使用小部件大小来计算比例因子等来解决这个问题,但 Qt 似乎有比这更好的方法。毕竟,在某些时候 Qt 必须知道实际缩放坐标才能进行渲染。
所以我们转到 QSvgTinyDocument 的渲染实现:
https://github.com/qt/qtsvg/blob/5.15.2/src/svg/qsvgtinydocument.cpp#L311
然后进行相关方法:
https://github.com/qt/qtsvg/blob/5.15.2/src/svg/qsvgtinydocument.cpp#L462
...
if (m_implicitViewBox || !preserveAspectRatio()) {
...
QTransform transform;
transform.scale(target.width() / source.width(),
target.height() / source.height());
QRectF c2 = transform.mapRect(source);
...
因此 target 是像素坐标中要绘制的目标表面的矩形,source 是 SVG 坐标驱动的 SVG 源。
在我看来,您首先猜测计算从 SVG 坐标到绝对像素坐标的系数正是 Qt 在内部所做的。
相关 QPainter 存储有关从 SVG 坐标到像素的转换信息,然后用于从 SVG 绘制实际像素,但据我所知你不能不应该访问像这样的深层内部框架状态。猜猜你首先假设计算比例系数是你应该做的。
请注意,在 Qt::KeepAspectRatio 模式的情况下,事情可能会变得更加麻烦,SVG 将居中并在小部件目标表面允许的范围内缩放,同时保持纵横比相同。
看起来这种情况在 Qt 5.15 中是可能的:
https://github.com/qt/qtsvg/blob/52d3788c7b0116ea3db232dccca5f1e3f1e229ac/src/svg/qsvgtinydocument.cpp#L474
https://doc.qt.io/qt-5/qsvgrenderer.html#aspectRatioMode-prop
我已将您的示例修改(非常快速地修改)为:
- 重新计算标签位置 window resize
- 使用系数从 SVG 映射到像素坐标
https://gist.github.com/mosolovsa/1bcbc2a87dc27869618266bea62a4d76
我正在使用 Qt,试图制作一个“可点击的 SVG”小部件的原型。我正在尝试从 QSvgWidget
.
为了弄清楚如何做到这一点,我有以下非常粗略的代码。我想要做的是以编程方式让“foo”QLabel 与显示的 SVG 中的“foo”矩形具有相同的位置。
from PySide2.QtWidgets import QApplication, QWidget, QLabel, QMainWindow
from PySide2.QtSvg import QSvgWidget
from PySide2.QtCore import QTimer
import sys
app = QApplication(sys.argv)
window = QMainWindow()
central = QSvgWidget("test_svg.svg")
foo = QLabel("foo", parent=central)
window.setCentralWidget(central)
bound1 = central.renderer().boundsOnElement("foo")
bound2 = central.renderer().transformForElement("foo").mapRect(bound1)
tl = bound2.topLeft()
foo.move(tl.x(), tl.y())
window.show()
app.exec_()
这是 SVG:
<svg height="400" width="450" viewbox="0 0 450 400" xmlns="http://www.w3.org/2000/svg">
<circle stroke="red" fill="none" cx="50" cy="50" r="30" />
<rect id="foo" stroke="blue" fill="none" x="100" y="300" width="20" height="5" />
</svg>
这是我在屏幕上看到的内容:
当我调试发生的事情时,我看到 bound1.topLeft()
和 bound2.topLeft()
相同,并且等于 PySide2.QtCore.QPointF(99.500000, 299.500000)
。这几乎是“foo”元素在 SVG 坐标中的位置。
我不确定正确的术语是什么,但让我将 SVG 坐标系 定义为 SVG 文件中使用的坐标系和 小部件坐标系 作为对应于实际像素位置的坐标系,其中某些内容相对于相关小部件的左上角以像素呈现。
所以 QSvgWidget
似乎给我 SVG 坐标而不是“小部件坐标”。
有谁知道我是否有一种规范的方法让 Qt 为我提供针对这些目的“准确”的坐标?我可能可以通过使用小部件大小来计算比例因子等来解决这个问题,但 Qt 似乎有比这更好的方法。毕竟,在某些时候 Qt 必须知道实际缩放坐标才能进行渲染。
所以我们转到 QSvgTinyDocument 的渲染实现: https://github.com/qt/qtsvg/blob/5.15.2/src/svg/qsvgtinydocument.cpp#L311
然后进行相关方法: https://github.com/qt/qtsvg/blob/5.15.2/src/svg/qsvgtinydocument.cpp#L462
...
if (m_implicitViewBox || !preserveAspectRatio()) {
...
QTransform transform;
transform.scale(target.width() / source.width(),
target.height() / source.height());
QRectF c2 = transform.mapRect(source);
...
因此 target 是像素坐标中要绘制的目标表面的矩形,source 是 SVG 坐标驱动的 SVG 源。 在我看来,您首先猜测计算从 SVG 坐标到绝对像素坐标的系数正是 Qt 在内部所做的。
相关 QPainter 存储有关从 SVG 坐标到像素的转换信息,然后用于从 SVG 绘制实际像素,但据我所知你不能不应该访问像这样的深层内部框架状态。猜猜你首先假设计算比例系数是你应该做的。
请注意,在 Qt::KeepAspectRatio 模式的情况下,事情可能会变得更加麻烦,SVG 将居中并在小部件目标表面允许的范围内缩放,同时保持纵横比相同。 看起来这种情况在 Qt 5.15 中是可能的: https://github.com/qt/qtsvg/blob/52d3788c7b0116ea3db232dccca5f1e3f1e229ac/src/svg/qsvgtinydocument.cpp#L474
https://doc.qt.io/qt-5/qsvgrenderer.html#aspectRatioMode-prop
我已将您的示例修改(非常快速地修改)为:
- 重新计算标签位置 window resize
- 使用系数从 SVG 映射到像素坐标 https://gist.github.com/mosolovsa/1bcbc2a87dc27869618266bea62a4d76