I need to save a QML item as image. I use grabToImage() method (https://doc.qt.io/qt-5/qml-qtquick-item.html#grabToImage-method) and it works well when my item is completely on the screen. But the item is a ScrollView and sometimes it's parts are outside the view. And I want to have full item on image. is it possible?
Here is an example to my problem:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.2
Window {
width: 640
height: 480
visible: true
title: qsTr("Example")
Button {
text: qsTr("Save as image")
onClicked: {
scheme.grabToImage(function(result) {
result.saveToFile("temp.png")
});
}
}
ScrollView {
id: scheme
y: 50
clip: true
width: 400
height: 200
contentWidth: rect.width
contentHeight: rect.height
Rectangle {
id: rect
width: 600
height: 400
color: "gray"
Rectangle {
x: 0
y: 0
width: 100
height: 100
color: "blue"
}
Rectangle {
x: 500
y: 300
width: 100
height: 100
color: "red"
}
}
}
}
Instead of grabbing the ScrollView (which only has size of 400x200), grab the child of ScrollView (which is the full 600x400).
rect.grabToImage(function(result) {
result.saveToFile("temp.png")
});
I am working with QML to try to display an image. I have the following code:
Rectangle {
id: border
anchors {
top: parent.top;
topMargin: vpx(20);
right: parent.right;
}
color: "black"
z: 6
width: 500
height: 750
Image {
id: picture
width: parent.width
height: parent.height
transformOrigin: Item.Center
rotation: (implicitWidth/implicitHeight > 1.3) ? 270 : 0
anchors.fill: border
fillMode: Image.PreserveAspectFit
source: ".../pic.png"
}
If rotation is set to 0, it scales correctly to fill the rectangle. If rotation is set to 270, it doesn't fill the rectangle - it rotates correctly, but it is well within the rectangle in both directions. It should have scaled up more.
Why is this happening?
EDIT: The code above has been edited. All of the above code is within a larger rectangle. The purpose of this code is to rotate images 90 degrees when they have a width > height. When rotation is set to 0 (i.e. height > width and no rotation needed), the picture displays as the first case below. When set to 270 (i.e. width > height, rotation needed), the picture displays as the second case below. I would like it to be comparable to the first picture, which fills the width, as I understand "fillMode: Image.PreserveAspectFit" should work.
black is rectangle, red is the image.
It is kind of tricky, but you can try this way:
Rectangle {
id: border
anchors {
top: parent.top;
right: parent.right;
}
color: "black"
z: 6
width: 500
height: 750
Item {
id: pictureItem
transformOrigin: Item.Center
rotation: (picture.implicitWidth/picture.implicitHeight > 1.3) ? 270 : 0
anchors.centerIn: parent
width: rotation == 270 ? parent.height : parent.width
height: rotation == 270 ? parent.width : parent.height
Image {
id: picture
anchors.fill: parent
fillMode: Image.PreserveAspectFit
source: ".../pic.png"
}
}
}
I have prepared two test images:
And this is what I get with above chunk of code:
How to increase touch area of control points on mobile device with FabricJS? Because on mobile they are too small for smooth interactive with. But not change size of control points ( for nice view ).
Here is my code:
How to increase interactive of control points = view control points size x 2 ?
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
var canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
// test customize control points
var rect = new fabric.Rect({
left: 30,
top: 30,
fill: 'red',
width: 150,
height: 150
});
canvas.add(rect);
rect.set({
transparentCorners: false,
cornerColor: 'white',
cornerStrokeColor: 'rgba(14,19,24,.15)',
borderColor: 'rgba(41,198,203,1)',
selectionLineWidth: 8,
cornerSize: 12,
cornerStyle: 'circle',
});
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
Please show me the way to resolve this problem.
By the way: ( on mobile device ) is there any way to allow drag object only object is selected before?
Thank you very much!
You need to override _setCornerCoords method of object and change the value of cornerHypotenuse.
DEMO
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
var canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
// test customize control points
var rect = new fabric.Rect({
left: 30,
top: 30,
fill: 'red',
width: 150,
height: 150
});
canvas.add(rect);
rect.set({
transparentCorners: false,
cornerColor: 'white',
cornerStrokeColor: 'rgba(14,19,24,.15)',
borderColor: 'rgba(41,198,203,1)',
selectionLineWidth: 8,
cornerSize: 5,
cornerStyle: 'circle'
});
rect._setCornerCoords = function() {
var coords = this.oCoords,
newTheta = fabric.util.degreesToRadians(45 - this.angle),
cornerHypotenuse = this.cornerSize * 2 * 0.707106,
cosHalfOffset = cornerHypotenuse * fabric.util.cos(newTheta),
sinHalfOffset = cornerHypotenuse * fabric.util.sin(newTheta),
x, y;
for (var point in coords) {
x = coords[point].x;
y = coords[point].y;
coords[point].corner = {
tl: {
x: x - sinHalfOffset,
y: y - cosHalfOffset
},
tr: {
x: x + cosHalfOffset,
y: y - sinHalfOffset
},
bl: {
x: x - cosHalfOffset,
y: y + sinHalfOffset
},
br: {
x: x + sinHalfOffset,
y: y + cosHalfOffset
}
};
}
}
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
I'm looking for a solve for my example. I want drag a corner of rectangle and it will change scale but when i drag corner to zoom out rectangle then it error.
Please, give a solve. Thanks very much!
======================================================================================================================================================
My code:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
title: qsTr("Test Crop")
width: 640
height: 480
visible: true
property double photohfake: selComp.height
property double photowfake: selComp.width
property double photowold: selComp.width
property double photohold: selComp.height
Image {
id: image1
anchors.fill: parent
source: "http://dongdomobile.vn/wp-content/uploads/2014/09/Cute-Grey-Seamless-Pattern-For-Website-Background-300x136.jpg"
Rectangle {
x:parent.width / 4
y: parent.height / 4
width: parent.width / 2
height: parent.width / 2
id: selComp
border {
width: 2
color: "steelblue"
}
color: "#354682B4"
Rectangle {
width: 18
height: 18
color: "steelblue"
anchors.verticalCenter:parent.top
anchors.horizontalCenter: parent.left
MouseArea {
anchors.fill: parent
drag{ target: parent; axis: Drag.XAndYAxis }
onPositionChanged: {
console.log("mouseX "+mouseX)
console.log("mouseY "+mouseY)
if(drag.active){
photohfake=photohfake-mouseY //calculator new height
selComp.scale=selComp.scale/photohold*photohfake //calculator new scale
photowfake=photowold*photohfake/photohold //calculator new width
selComp.x+=(photowold-photowfake)/2
selComp.y+=mouseY/2
console.log("photohfake "+photohfake)
console.log("photohold "+photohold)
photohold=photohfake
photowold=photowfake
console.log("selComp.scale "+selComp.scale)
if(selComp.scale<0.5)
selComp.scale=0.5
else if(selComp.scale>4)
selComp.scale=4
}
}
}
}
}
}
}
If by errors you mean the rectangle moving weirdly when resizing, this should fix it:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
title: qsTr("Test Crop")
width: 640
height: 480
visible: true
Rectangle {
id: image1
anchors.fill: parent
color: "lightgrey"
Rectangle {
x:parent.width / 4
y: parent.height / 4
width: parent.width / 2
height: parent.width / 2
id: selComp
border {
width: 2
color: "steelblue"
}
color: "#354682B4"
Rectangle {
width: 18
height: 18
color: "steelblue"
anchors.verticalCenter:parent.top
anchors.horizontalCenter: parent.left
MouseArea {
anchors.fill: parent
drag{ target: parent; axis: Drag.XAndYAxis }
onPositionChanged: {
if(drag.active){
var delta = Math.max(mouseX, mouseY)
var newWidth = selComp.width - delta
var newHeight = selComp.height - delta;
if (newWidth < width || newHeight < height)
return
selComp.width = newWidth
selComp.x = selComp.x + delta
selComp.height = newHeight
selComp.y = selComp.y + delta
}
}
}
}
}
}
}
I've created this QML window but when I grab it's border with a mouse and resize it contents of window are repainted very slow. You can clone my repo and test it yourself. Any ideas how to improve performance?
import QtQuick 2.0
Rectangle {
id: root
width: 600
height: 600
color: "lightgrey"
ListView {
model: mainModel
spacing: 5
width: parent.width
orientation: ListView.Horizontal
delegate: Rectangle {
//width: parent.desiredWidth / mainModel.dataLen()//+ " " + model.sort
width: root.width / mainModel.dataLen() - 10
//width: 200
ListView {
id: lv1
ScrollBar {
flickable: lv1
vertical: true
hideScrollBarsWhenStopped: false
scrollbarWidth: 5
}
model: homm
spacing: 5
width: parent.width
height: 150
orientation: ListView.Vertical
delegate:
Rectangle {
radius: 5
anchors.rightMargin: 5
anchors.leftMargin: 5
width: lv1.width
height: 20
color: "black"
Text { text: model.name
anchors.fill: parent
color: "white"
}
}
}
}
}
}
In this QML I have listView of listViews which visualize ListModel of ListModels. homm is a property name for main model. Inner models' elements have a property named name. You can browser code of these classes here and here.