I have a vector graphics file (here a svg) that I would like to display in a window and be able to zoom in and out of at runtime (using CTRL +/-/0) without loosing quality. Unfortunately Qt seems to be loading the image with fixed size and therefore not vectorized. When zooming in, the image gets pixelized.
I am using an Image, a Flickable and resizeContent() + returnToBounds(). I could not find any work around online, the only solution I found was to set the sourceSize to zoom in/out but this does not work at runtime.
Here is my code :
import QtQuick
import QtQuick.Layouts
import QtQuick.Pdf
Window {
id:root
visibility:Window.Maximized
visible: true
title: qsTr("XXX")
property bool ctrlPressed : false
property real maxScale : 5
property real minScale:0.95
RowLayout{
anchors.fill:parent
anchors.leftMargin: 10
anchors.rightMargin : 10
spacing:0
Rectangle{
id:menucolumn
color:"transparent"
width:340
height:50
}
Item{
id:container
Layout.alignment: Qt.AlignTop
Layout.fillHeight: true
Layout.preferredWidth: root.width - menucolumn.width - bunkerButton.width
focus:true
Flickable{
id:flickArea
anchors.fill:parent
clip:true
contentWidth: container.width
contentHeight: container.height
flickableDirection:Flickable.HorizontalAndVerticalFlick
property real zoom : 0.95
onZoomChanged: {
text1.text = "Zoom : " + Math.round(flickArea.zoom*100) + "%"
var zoomPoint = Qt.point(flickArea.width/2 + flickArea.contentX,
flickArea.height/2+flickArea.contentY);
flickArea.resizeContent((vueEnsembleImg.width * flickArea.zoom), (vueEnsembleImg.height * flickArea.zoom), zoomPoint);
//vueEnsembleImg.sourceSize.height =container.height*flickArea.zoom
//vueEnsembleImg.sourceSize.width=container.width*flickArea.zoom
flickArea.returnToBounds();
}
Image{
id:vueEnsembleImg
source: "image.svg"
anchors.centerIn:parent
fillMode: Image.PreserveAspectFit
scale:flickArea.zoom
smooth:false
antialiasing:false
transform: Scale {
id: scaleID ;
origin.x: flickArea.contentX + flickArea.width * flickArea.visibleArea.widthRatio / 2
origin.y: flickArea.contentY + flickArea.height * flickArea.visibleArea.heightRatio / 2
}
}
}
Text{
id:text1
visible:false
anchors.top:parent.top
anchors.left:parent.left
text:"Zoom : " + Math.round(flickArea.zoom*100) + "%"
}
Keys.onPressed: (event)=> {
if(event.key === Qt.Key_Control){
root.ctrlPressed = true
text1.visible = true
}
if((event.key === Qt.Key_Plus) & (root.ctrlPressed)){
if(flickArea.zoom < root.maxScale){//if(vueEnsembleImg.scale < root.maxScale){
flickArea.zoom += 0.05
}
}
else if((event.key === Qt.Key_Minus) & (root.ctrlPressed)){
if(flickArea.zoom > root.minScale){//if(vueEnsembleImg.scale > root.minScale){
flickArea.zoom -= 0.05
}
}
else if ((event.key === Qt.Key_0) & (root.ctrlPressed)){
flickArea.zoom = 0.95
}
}
Keys.onReleased: (event)=>{
if(event.key === Qt.Key_Control){
text1.visible = false
root.ctrlPressed = false
}
}
}
Rectangle{
id:bunkerButton
color:"transparent"
width:110
height:50
}
}
}
I also tried using a pdfPageImage but since it inherits from Image it behaves the same for this. I am using Qt 6.7.1. Unfortunately I really have to be able to do this as it is a request from my client. Is there a way to achieve this ? Perhaps with other object types ?
To implement Image.sourceSize
it's best to property bind to width and height, so, as the SVG scales, the sourceSize
will recalculate automatically:
Image {
sourceSize: Qt.size(width, height)
}
However, I prefer setting icon.source
, icon.width
, icon.height
and icon.color
for scaling and recoloring my SVG. Various components have the icon
property, e.g. Button
:
import QtQuick
import QtQuick.Controls
Page {
Slider {
id: slider
from: 100
to: 500
value: 256
}
IconButton {
id: iconButton
y: 100
width: slider.value
height: slider.value
icon.width: slider.value
icon.height: slider.value
icon.source: "globe.svg"
icon.color: pressed ? "red" : "blue"
onClicked: PropertyAnimation {
target: iconButton
property: "rotation"
from: 0
to: 360
}
}
}
// IconButton.qml
import QtQuick
import QtQuick.Controls
Item {
id: iconButton
width: 32
height: 32
property alias icon: button.icon
property alias pressed: button.pressed
signal clicked
Button {
id: button
anchors.centerIn: parent
background: Item { }
icon.width: iconButton.width
icon.height: iconButton.height
onClicked: iconButton.clicked()
}
}
// globe.svg : https://raw.githubusercontent.com/Esri/calcite-ui-icons/master/icons/globe-32.svg
You can Try it Online!