Load images from folder to exact coordinates - qt

I have images, which names are the X and Y coordinates (i.e. "100_100.jpg", "200_100.jpg", "100_200.jpg", "200_200.jpg") in one folder ("img"). All images have the same width and height.
So the question is: How I can load the images automatically to coordinates that they have in their names without creating thousand lines of repeating code? (I will have much more than only those 4 images to load so the automation is a must)
Code:
main.qml:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 1000
height: 800
visible: true
title: qsTr("Tiled Photos")
Rectangle {
id: rectangle
anchors.fill: parent
anchors.margins: 20
color: "#cdecf9"
// THIS PART I WANT TO BE AUTOMATED:
Image {
id: img_100_100
width: 100
height: 100
source: "img/100_100.png"
asynchronous: true
x: 100
y: 100
}
Image {
id: img_100_200
width: 100
height: 100
source: "img/100_200.png"
asynchronous: true
x: 100
y: 200
}
Image {
id: img_200_100
width: 100
height: 100
source: "img/200_100.png"
asynchronous: true
x: 200
y: 100
}
Image {
id: img_200_200
width: 100
height: 100
source: "img/200_200.png"
asynchronous: true
x: 200
y: 200
}
// END
}
}

Your question doesn't mention how the app knows what files there are. Maybe it's supposed to search a directory for these files, or maybe they're just a hardcoded list, etc. So let's assume you have a list of filenames that comes from...somewhere.
property var filenames: [
"100_100.png",
"100_200.png",
"200_100.png",
"200_200.png"
]
This is going to be the model for our Repeater. For readability, let's add a function that takes one of those filenames and extracts the x and y coordinates from it:
function getXY(filename) {
// First chop off the extension by looking for the '.'
var noExt = filename.split('.');
// Then split the string up by the '_' character
return noExt[0].split('_');
}
And finally, this is how we use it.
Repeater {
model: filenames
Image {
width: 100
height: 100
asynchronous: true
// Use the filename from the model
source: "img/" + modelData
// Store the result of our function call to get coordinates
property var coordinates: getXY(modelData)
x: coordinates[0]
y: coordinates[1]
}
}

Related

QML ApplicationWindow zooms in unintentionally and doesn't show all components

So I'm trying to create an application in QML, and doing so by using an ApplicationWindow. The following code is in main.qml, where some of the components are defined in other files (they are not essential for this problem):
import QtQuick 2.12
import QtQuick.Extras 1.4
import QtQuick.Controls 2.5
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.3
import QtDataVisualization 1.3
import Qt3D.Core 2.9
import Qt3D.Render 2.9
import QtCharts 2.3
ApplicationWindow {
id: window
width: 1280
height: 720
//visibility: ApplicationWindow.FullScreen
visible: true
color: "#444444"
Speedometer {
id: speedometer
x: 49
y: 144
width: 306
height: 320
minValue: 0
maxValue: 600
visible: true
}
Thermometer {
id: thermometer
x: 1170
y: 233
scale: 2
minValue: 0
maxValue: 50
visible: true
}
Slider {
id: slider
from: 0
to: 100
x: 28
y: 594
width: 1224
height: 98
font.pointSize: 14
hoverEnabled: false
enabled: false
live: true
snapMode: Slider.NoSnap
value: 0
visible: true
Behavior on value {
NumberAnimation {
duration: 200
}
}
background: Rectangle {
x: slider.leftPadding
y: slider.topPadding + slider.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: 4
width: slider.availableWidth
height: implicitHeight
radius: 2
color: "#999999"
}
handle: Rectangle {
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
y: slider.topPadding + slider.availableHeight / 2 - height / 2
implicitWidth: 26
implicitHeight: 26
radius: 13
color: "#0099ff"
}
}
Timer {
interval: 200
running: true
repeat: true
property var distance: Math.random
onTriggered: update()
}
function update(){
var distance = (Math.random() * 0.03) + 0.1;
var speed = (distance * 50) / 0.02;
slider.value = slider.value + distance;
speedometer.value = speed;
thermometer.value = Math.random() * 25 + 25;
}
DataManager {
id: manager
onNewVelocity: {
lineSeries.append(velocity.x, velocity.y)
chartView.title = name
}
}
Timer {
id: timer
repeat: true
interval: 1
onTriggered: {
manager.dummyData();
}
}
Chart {
id: chart
height: 250
width: 250
x: 1000
}
Text {
id: labelText
color: "white"
width: 300
height: 100
}
Button {
id: startThread
text: "Start"
onClicked: {
timer.start()
}
}
Button {
id: stopThread
text: "Stop"
x: 200
onClicked: {
timer.stop()
}
}
Button {
id: clearGraph
text: "Clear"
x: 100
onClicked: {
lineSeries.clear()
}
}
}
The content itself isn't that important, but the thing that is, is the fact that when I personally run the project, the application window doesn't show what it is supposed to. And I'd like to emphasize: I'm collaborating with others on this exact project, and when they run the exact same code as me, they get different results.
The first image is what I get when running the code:
The second image is what they get, and what is actually supposed to show when running the code:
So I've been trying to search Google for every possible solution, but have found nothing. I've reinstalled Qt and QtCreator, but to no prevail. We are running the exact same thing, but I'm the only one that don't see all components show up. The first image is way more zoomed in than the latter, and I'd really like if anyone knew how to fix this. I've struggled to find anything that could help so far
(Extra note: the code I ran didn't include the graph as it wasn't up to date with current version on git, but the other things remain the same. So the bar below and the thermometer component on the right SHOULD be visible, but they're not).
I eventually found a solution to what seems to be a bug from Qt's end. I had to launch QtCreator via a qtcreator.bat file in the same directory as qtcreator.exe with the following content:
#echo off
set QT_SCALE_FACTOR_ROUNDING_POLICY=Round
set QT_AUTO_SCREEN_SCALE_FACTOR=0
qtcreator.exe
When I now run the project, it shows the correct scale :)

QML how to clip image & extract images from single image

I am having a image which contains numbers in following order :--
0 1 2 3 4 5 6 7 8 9 ... i have to show meter reading .. so now how to create sub image of individual numbers from this image so that i can display each number separately .. means to say i want to extract images of number from this image so that i can display them separately on Meter reading .. please suggest.
Full image size is 108x16 .
I tried using column then using the repeater i replicated the image, but how to change x,y of image inside the repeater so that image inside the repeater is clipped .. so that i extract the numbers from image.. ?
I created column's of the images... using repeater.. but not able to clip the real image.. so that X,Y coordinate changes... and each column contains Digit from the image.
Window {
visible: true
width: 400
height: 300
color: "grey"
Item {
id: container
anchors.centerIn: parent
width: 12
height: 16
clip: true
property int position: 0
Column {
id: image
y: -container.position * 16
Repeater {
model: 10
delegate: Rectangle {
width: 12
height: 16
Image {
anchors.fill: parent
source: "files/REE Demo images/odo_font_orange.png"
}
}
}
}
}
Timer {
interval: 1000
repeat: true
running: true
onTriggered: {
if(container.position == 9)
container.position = 0;
else
container.position ++
}
}
You may want to use Sprite and SpriteSequence. Obviously, you have to set running to true so that you can manually set your desired frame(number) by using jumpTo method.
As your image is not 120 pixels wide, it means that your sprites are not the same size. But I recommend you to choose 12 pixels width frames within your original image. SpriteSequence will resize your sprite to its own size if it has a different size. It means that you have to manually set frameX of each Sprite object to get a good visual appearance.
SpriteSequence {
id: digit
width: 12
height: 16
interpolate: false
goalSprite: ""
running: true
property var sourceImage: 'files/REE Demo images/odo_font_orange.png'
Sprite{
name: "1"
source: digit.sourceImage
frameCount: 1
frameWidth: 12
frameHeight: 16
frameX: 0
frameY: 0
}
Sprite{
name: "2"
source: digit.sourceImage
frameCount: 1
frameWidth: 12
frameHeight: 16
frameX: 12
frameY: 0
}
//other digits with their topleft and width/height
//in original image should be repeated with their own name
function setNumber(n){
digit.jumpTo(n);
}
}
You should place your image inside a container with clip:true and play with x/y of the content item, for example:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 400
height: 300
Item {
id: container
anchors.centerIn: parent
width: 30
height: 30
clip: true
property int position: 0
Column {
id: image
y: -container.position * 30
Repeater {
model: 10
delegate: Rectangle {
width: 30
height: 30
Text {
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: index
font.pixelSize: 20
color: "orange"
}
}
}
}
}
Timer {
interval: 1000
repeat: true
running: true
onTriggered: {
if(container.position == 9)
container.position = 0;
else
container.position ++
}
}
}
here I replace Image with Column item to just illustrate the idea. You maybe want to add some move animation etc.
Updated:
taking you image into account:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 400
height: 300
Item {
id: container
anchors.centerIn: parent
width: 12
height: 16
clip: true
property int position: 1
Image {
id: image
x: -container.position * 12
source: "http://i.stack.imgur.com/jnbEZ.png"
}
}
Timer {
interval: 1000
repeat: true
running: true
onTriggered: {
if(container.position == 9)
container.position = 0;
else
container.position ++
}
}
}
but since your images are not aligned right the result is a bit rough. You need 10 cells with the same size each one.

Holding common objects as a list in QML

I am having an architectural problem in the app I am developing in QML. Please consider the following figure:
Some facts about the application:
I need to store an array of elements names, here it is Orange, Apple and Banana.
The amount of elements is fixed and will not change at runtime.
Although there are only 1 array of elements, it should be possible to present the in different graphical forms at the same time. In the example, the elements are once represented as yellow squares, and other time as green triangles. They do not necessarily have to be shown in the same order. But the order also doesn't change at runtime.
I want to avoid unnecessary code copying, thus, wanted to use only 1 list with different graphical representations. I am having problems implementing this however.
I don't quite understand what OP wants to archive, but I guess that model is what you need.
This is a simple example of reusable model:
import QtQuick 2.11
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 600
height: 400
title: qsTr("Model example")
ListModel {
id: myModel;
ListElement { name: "Apple" }
ListElement { name: "Orange" }
ListElement { name: "Banana" }
}
Repeater {
model: myModel
delegate: type1
}
Repeater {
model: myModel
delegate: type2
}
ListView {
model: myModel
delegate: Text { text: name; height: 30; }
width: 100
height: 200
}
ComboBox {
width: 100
y: 200
model: myModel
}
Component {
id: type1
Canvas {
x: 100 + Math.round(Math.random() * 400)
y: Math.round(Math.random() * 100)
rotation: Math.round(Math.random() * 360)
antialiasing: true
width: 100
height: 100
onPaint: {
var ctx = getContext("2d");
ctx.fillStyle = "#00DD00";
ctx.beginPath();
ctx.moveTo(50, 0);
ctx.lineTo(100, 100);
ctx.lineTo(0, 100);
ctx.fill();
}
Text {
anchors.centerIn: parent
text: name
}
}
}
Component {
id: type2
Rectangle {
x: 100 + Math.round(Math.random() * 400)
y: 200 + Math.round(Math.random() * 100)
rotation: Math.round(Math.random() * 360)
antialiasing: true
width: 100
height: 100
color: "orange"
Text {
anchors.centerIn: parent
text: name
}
}
}
}
Welp, can't comment until I have enough reputation, but can't you just use QAbstractListModel for this? Then you could use two path views that determine where the objects go. Here's an example, but you would have your own QAbstractListModel instead of the examples ListModel. The delegate would determine the shape of the items.
The reason to use QAbstractListModel over QML's ListModel is because ListModel is created runtime.

How to transform the center of a QQuickItem to a new center

I have a Qt QML application. Following is the complete code of the application:
Code:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: app_main_window
visible: true
width: 800
height: 600
title: qsTr("Hello QML")
Rectangle {
id: my_item_1
x: 100
y: 100
width: 100
height: 100
color: "grey"
visible: true
z: 1
}
Rectangle {
id: my_item_2
x: 400
y: 400
width: 100
height: 100
color: "grey"
visible: true
z: 1
MouseArea {
anchors.fill: parent
onClicked: {
// How can I change the center of my_item_1 to some arbitrary new center. lets say app_main_window's center. this involves another question. how can I get app_main_window's center?
}
}
}
}
Question:
My question is simple. How can change the center of any QQuickitem to a new center? So in above case how can change the center of my_item_1 to a new center (Qt.point(some_x_center, some_y_center)) when my_item_2 gets a click event.
Additionally, is possible to get another item's center? Like app_main_window or my_item_2's center to apply to the target my_item_1?
PS:
I have made the code simple to make the question objective. I have quite a complicated logic in my actual code where I need to realign something like my_item_1 to a new center without usage of anchors as the QQuickitem I am trying to do that with is scaled and panned into a new point.
If anchors can't be used, you have to calculate manually the center and assign it.
Quick example:
Item
{
anchors.fill: parent
Rectangle
{
id: nonParentOrSibling
width: 200
height: 200
x: 350
y: 240
color: "red"
}
}
Rectangle
{
id: rectToMove
width: 100
height: 100
y: 200
color: "blue"
MouseArea
{
anchors.fill: parent
onClicked:
{
var itemCenter = Qt.point(nonParentOrSibling.x + nonParentOrSibling.width / 2, nonParentOrSibling.y + nonParentOrSibling.height / 2)
rectToMove.x = itemCenter.x - rectToMove.width / 2
rectToMove.y = itemCenter.y - rectToMove.height / 2
}
}
}
Note that this is a one time moving only, and the rectangle won't move if the target is moved.
To make it follow the target you have to rebind it with Qt.binding.
If rectToMove is not in the same coordinate system as nonParentOrSibling, you can use Item.mapToItem() and mapFromItem() to adapt the coordinates.

Qt: How to merge 2 QQuickItems into one before saving it into a png

From this discussion on StackOverflow, I am well able to save an image out of a QML item into a file as png/jpeg.
How can I overlay or merge two different qml layers & merge them into one, to save it into a png/jpeg ?
Note: I am able to save a single QQuickItem. Just need to know how to overlay 2 QQuickItems
Just have the two qml objects be children of a root Item and then grab that root item, it will capture all its content.
Just make sure the root item is big enough to enclose the children, and the children are not in negative space, because it will only capture what's inside of the footprint of the root item.
You can also do manual composition, from C++ or even QML.
The problem described in your comment is you can't move stuff around, so what can you do? Instead of having the original QML objects as parents of the same root, you can have two Image elements, then you capture item A and set the capture result to serve as a source of image A, then do the same for item B, and finally, you capture the root item, which will capture the two images together.
OK, here is a quick example, it looks a little complicated, because grabs are async, and you have to wait for the individual grab results to be completed before you can grab the "final" root item, thus the usage of the timer. In this example, different items are laid out in a row, but you can compose them any way that you like:
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
Rectangle {
id: s1
visible: false
width: 200
height: 200
color: "red"
}
Rectangle {
id: s2
visible: false
width: 200
height: 200
color: "blue"
}
Row {
id: joiner
visible: false
Image { id: t1 }
Image { id: t2 }
}
Image {
id: result
y: 200
}
Timer {
id: finish
interval: 10
onTriggered: joiner.grabToImage(function(res) {result.source = res.url})
}
Component.onCompleted: {
s1.grabToImage(function(res) {t1.source = res.url})
s2.grabToImage(function(res) {t2.source = res.url; finish.start() })
}
}
First the two rectangles are captured and used as sources for the images in joiner, then joiner is captured and displayed in the result image, all objects except the final result image are hidden.
Even easier, you can use this nifty little helper to quickly join any number of items in a single image:
Item {
id: joinHelper
visible: false
property Component ic: Image { }
property var cb: null
Row { id: joiner }
Timer {
id: finish
interval: 100
onTriggered: joiner.grabToImage(joinHelper.cb)
}
function join(callback) {
if (arguments.length < 2) return // no items were passed
var i
if (joiner.children.length) { // clean previous captures
for (i = 0; i < joiner.children.length; ++i) {
joiner.children[i].destroy()
}
}
cb = callback // set callback for later
for (i = 1; i < arguments.length; ++i) { // for every item passed
var img = ic.createObject(joiner) // create empty image
// need to capture img by "value" because of JS scoping rules
// otherwise you end up with only one image - the final one
arguments[i].grabToImage(function(temp){ return function(res){temp.source = res.url}}(img))
}
finish.start() // trigger the finishing step
}
}
And you use it like this:
joinHelper.join(function(res) { result.source = res.url }, s1, s2)
It still uses a row, but you can easily tweak it to do your own layouting. It works by passing the final callback and all items you want to capture, internally it creates an image for every item, puts them in the container, and then triggers the finishing timer.
Note that depending on how fast the system is and how complex the items are and what their count is, you may need to up the timer interval, because the final callback needs to be executed only after all captures were completed, the image sources were assigned and the images were resized to give the row its proper dimensions.
I also annotated most things to make it easier to understand.
This question seems to be closely related to this one
The solution is to render the Items in question into an texture of a second item, that you don't need to render to the screen. You can compose this Item as you want by adding multiple ShaderEffectSources as children, position them relatively to each other as you like, and set their sources to the Items you want to grab to Image.
Then you grab the Item to Image.
A generic example, that exposes a function to grab a list of Items to one Image, where each Item is stacked ontop of each other, with an opacity of 0.2 each:
import QtQuick 2.0
Rectangle {
id: root
visible: false
color: 'white'
function grabMultipleToImage(url, objects) {
imgRep.url = url
width = Math.max.apply(root, objects.map(function(e) { return e.width }))
height = Math.max.apply(root, objects.map(function(e) { return e.height }))
imgRep.ready = 0
imgRep.model = objects
}
Repeater {
id: imgRep
onReadyChanged: {
if (ready > 0 && ready === model.length) {
console.log(root.width, root.height, imgRep.url)
root.grabToImage(function (res) { res.saveToFile(imgRep.url); model = null })
}
}
property int ready: 0
property string url
delegate: ShaderEffectSource {
sourceItem: modelData
width: modelData.width
height: modelData.height
opacity: 0.2
live: false
Component.onCompleted: { imgRep.ready++ }
}
}
}
The usage of this would be like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Button {
text: 'grab'
onClicked: {
test.grabMultipleToImage('testimg.jpg', [rect1, rect2, rect3])
}
}
ImageGrabber {
id: test
}
Rectangle {
x: 100
y: 205
id: rect1
color: 'blue'
width: 10
height: 20
}
Rectangle {
x: 250
y: 12
id: rect2
color: 'green'
width: 20
height: 30
}
Rectangle {
x: 100
y: 100
id: rect3
color: 'red'
width: 100
height: 5
}
}
But if you have the need for more complex merging, you can also create this object manually and grab it when ever you want.
Without metering it, according to the note from the documentation
Note: This function will render the item to an offscreen surface and copy that surface from the GPU's memory into the CPU's memory, which can be quite costly. For "live" preview, use layers or ShaderEffectSource.
this solution should be more efficient than using Images with ItemGrabResults as sources for it keeps the stuff in the GPU memory until it is grabed and stored.

Resources