'Qt / SVG - getting accurate coordinates of element
I'm working with Qt, trying to prototype a "clickable SVG" widget. I'm trying to get accurate coordinates for a particular element within an SVG from a QSvgWidget
.
I have the following very rough code for the purposes of working out how to do this. What I'm trying to do is to programmatically get the "foo" QLabel to have the same position as the "foo" rectangle in the displayed SVG.
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_()
Here's the 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>
Here's what I see on-screen:
When I debug what's going on, I see that bound1.topLeft()
and bound2.topLeft()
are the same, and are equal to PySide2.QtCore.QPointF(99.500000, 299.500000)
. which is almost where the "foo" element is in SVG coordinates.
I'm not sure exactly what the correct terminology is, but let me define the SVG coordinate system as the one used in the SVG file and the widget coordinate system as the one which corresponds to the actual pixel position where something is rendered in pixels relative to the top-left corner of the widget in question.
So the QSvgWidget
appears to be giving me SVG coordinates rather than "widget coordinates".
Does anyone know if there is a canonical way for me to get Qt to give me coordinates which are "accurate" for these purposes? I can probably work it out by using the widget size to work out a scaling factor etc., but it seems likely that Qt has a better way than this. After all, at some point Qt must know the actual scaled coordinates in order to do the rendering.
Solution 1:[1]
Have a look at: https://github.com/qt/qtsvg/blob/52d3788c7b0116ea3db232dccca5f1e3f1e229ac/src/svg/qsvgrenderer.cpp#L418
renderer is: https://github.com/qt/qtsvg/blob/52d3788c7b0116ea3db232dccca5f1e3f1e229ac/src/svg/qsvgrenderer.cpp#L120
So we go to the render implementation of QSvgTinyDocument: https://github.com/qt/qtsvg/blob/5.15.2/src/svg/qsvgtinydocument.cpp#L311
and then proceed to the related method: 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);
...
So target is the rectangle of target surface to paint on in pixel coordinates, source is the SVG coordinates driven SVG source. It seems to me that you first guess about calculating the coefficients to go from SVG coordinates to absolute pixel coordinates is exactly what Qt does internally.
Related QPainter stores info about tranform from SVG coords to pixels and later used to paint actual pixels from SVG, but as far as I believe you can't shouldn't access deep internal framework state like this. Guess you first assumption to calculate coefficients of scaling is what you should do.
Please take notion that things can go even cumbersome in case of Qt::KeepAspectRatio mode, the SVG would be centered and scaled as far as widget target surface permit with keeping aspect ratio the same. Looks like that case is possible from 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
I've modified (very quick'n'dirty) your example to:
- recalculate label position on window resize
- use coefficients to map from SVG to pixel coordinates https://gist.github.com/mosolovsa/1bcbc2a87dc27869618266bea62a4d76
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |