I have the score defined as below to be printed on screen
property int score: 0
Text {
id: score1
text: "Score: "+ score
anchors.bottom: grid.top
font.pointSize: 20
color: "red"
}
and this list model to access and change square colors and text values in the grid
property variant colorArray: ["cyan","red","white"]
ListModel {
id: dummyModel
ListElement {
squareColor: "white"
txt: 0
}
ListElement {
squareColor: "white"
txt: 0
}
ListElement {
squareColor: "white"
txt: 0
}
ListElement {
squareColor: "white"
txt: 0
}
}
Timer {
// to change square colors randomly
id: alfa
interval: 2000; running: true; repeat: true
onTriggered: {
for (var i=0;i<dummyModel.count;i++) {
dummyModel.setProperty(i,"squareColor",colorArray[Math.floor(Math.random()*3)])
}
}
}
Timer {
// to change score values in grid element randomly
id: beta
interval: 2000; running: true; repeat: true
onTriggered: {
for (var i=0;i<dummyModel.count;i++) {
var sc = Math.floor(Math.random()*20) // random value from 0 to 20
dummyModel.setProperty(i,"txt",sc) // score in each square
}
}
}
and this grid contains colored squares, when a square is clicked and color is cyan score should be updated by the number in the text field tttt
Grid{
id: grid
rows: 2
columns:2
spacing: 5
anchors.centerIn: parent
Repeater{
id: gridRect
model: dummyModel // from a list element model
Rectangle{
id: rect
width: 50
height: width
color: "cyan"
radius: 10
Text {
id: tttt
anchors.centerIn: parent
color: "lightBlue"
text : txt
}
MouseArea{
anchors.fill: parent
onClicked: {
if (rect.color == "cyan")
score = score + tttt.text
else if (rect.color == "red")
score = score - tttt.text
}
}
}
}
}
but score can't be updated on click instead I get this error/warning:
<Unknown File>: Can't assign to existing role 'txt' of different type [Number -> String]
why is that?
The Text component can receive any type of data: string, number, etc. and try to convert it to string, in your case when you use txt.text you are getting that converted string. So there are 2 solutions convert the string to number, or better not use the value of the Text component but the property of the model:
// ...
Rectangle{
id: rect
width: 50
height: width
color: model.squareColor
radius: 10
Text {
id: tttt
anchors.centerIn: parent
color: "lightBlue"
text : model.txt
}
MouseArea{
anchors.fill: parent
onClicked: {
if (model.squareColor === "cyan")
score = score + model.txt
else if (model.squareColor === "red")
score = score - model.txt
}
}
}
// ...
Related
I need scale and rotate a Rectangle around mouse point. When the Rectangle is not rotated my solution works fine, but if I apply Rotation transform I face the problem - my Rectangle move to an unexpected point. In my solution I use a MouseArea for drag the Rectangle, Scale and Rotation transforms for scale and rotate.
import QtQuick 2.0
Rectangle {
id: root
color: "gray"
Rectangle {
color: "black"
width: 360
height: 200
opacity: 0.5
x: 500
y: 350
}
Rectangle {
id: sample
color: "green"
width: 360
height: 200
opacity: 0.5
x: 500
y: 350
property real currX: 0
property real currY: 0
property real currZoom: 1
property real maxZoom: 5
property real minZoom: 0.5
transform: [
Scale {
id: scaler
origin.x: sample.currX
origin.y: sample.currY
xScale: sample.currZoom
yScale: sample.currZoom
},
Rotation{
id: rotation
origin.x: 180
origin.y: 100
angle: 30
}
]
MouseArea{
id: mouseArea
anchors.fill: parent
drag.target: sample
onClicked: mouse => {
zoom(true, mouse.x, mouse.y)
}
onWheel: (wheel) => {
var isIn = wheel.angleDelta.y > 0
zoom(isIn, wheel.x, wheel.y)
}
function zoom(isIn, x, y) {
var prevZoom = sample.currZoom
var prevX = sample.currX
var prevY = sample.currY
sample.currX = x
sample.currY = y
sample.currZoom = calculateZoom(isIn, prevZoom)
sample.x = sample.x + (prevX - sample.currX) * (1 - prevZoom)
sample.y = sample.y + (prevY - sample.currY) * (1 - prevZoom)
printSamplePostion()
}
function calculateZoom(isIn, prevZoom) {
var newZoom = isIn ? prevZoom + 0.1 : prevZoom - 0.1
if (newZoom > mouseArea.maxZoom)
newZoom = mouseArea.maxZoom
if (newZoom < mouseArea.minZoom)
newZoom = mouseArea.minZoom
return newZoom
}
function printSamplePostion() {
console.log("== x: 500 y: 350 ======")
console.log("-- x: " + sample.x)
console.log("-- y: " + sample.y)
console.log("=======================")
}
}
}
}
Thanks in advance
I am trying to reduce the size of qml file by calling same rectangle component and changing only the required fields and keep the rest same.
The part shown below is working but want to reduce the size.
Basically, I dont want to make moisture rectangle. I want to use temperature rectangle and modify say "x " value and inside connections modify only "path". Is that possible if yes than how ? thank you !!!
Rectangle {
id: landingScreen
x: 0
y: 0
width: 800
height: 350
color: "#E4E4E4"
visible: true
property string path: ""
property string val: ""
Rectangle {
id: temperature
x: 8
y: 11
width: 351
height: 329
color: "#ffffff"
radius: 10
Text{
id: textFieldtemp
text :qsTr("")
y:50
font.family: "Helvetica"
font.pointSize: 24
anchors.horizontalCenter: parent.horizontalCenter
}
Connections
{
target: myModel
onSensorValueChanged:{
path = "/root/temp"
val = value
if (addr === path)
{
textFieldtemp.text = "Temperature " + val + "*C"
}
}
}
}
Rectangle {
id: moisture
x: 369
y: 13
width: 209
height: 157
color: "#ffffff"
radius: 10
Text{
id: textFieldmoist
text :qsTr("")
y:50
font.family: "Helvetica"
font.pointSize: 24
anchors.horizontalCenter: parent.horizontalCenter
}
Connections
{
target: myModel
onSensorValueChanged:{
path = "/root/moist"
val = value
if (addr === path)
{
textFieldmoist.text = "Moisture " + val + "*C"
}
}
}
}
}
This sounds like you should just create a new QML file and give it some properties which you can set from the landingScreen. I named it SensorRectangle.qml
Rectangle {
id: sensor
color: "#ffffff"
radius: 10
property string address
property string title
property string unit
Text{
id: textField
property var value
text: sensor.title + " " + value + " " + sensor.unit
y:50
font.family: "Helvetica"
font.pointSize: 24
anchors.horizontalCenter: parent.horizontalCenter
}
Connections
{
target: myModel
onSensorValueChanged:{
if (addr === sensor.address)
{
textField.value = value
}
}
}
}
And then your landing screen becomes:
Rectangle {
id: landingScreen
x: 0
y: 0
width: 800
height: 350
color: "#E4E4E4"
visible: true
property string path: ""
property string val: ""
SensorRectangle {
id: temperature
x: 8
y: 11
width: 351
height: 329
title: "Temperature"
unit: "°C"
address: "/root/temp"
}
SensorRectangle {
id: moisture
x: 369
y: 13
width: 209
height: 157
title: "Moisture"
unit: "°C"
address: "/root/moist"
}
}
I'm trying to reorder Repeater items using drag & drop. I'm using a Repeater due to dynamically loaded data. Below is what I have so far using a simple sqlite database with sample data added. What I'm hoping for is to get the re-order set up so when the user releases the dragged object, the database is updated to reflect the new order. The "Change" button reorders the elements as I want, I just can't get it to work property with drag and drop.
The loadData() function simply creates the table and inserts sample data. The "Change" button won't be in the final code, I just wanted to use it to get the re-ordering code to work.
import QtQuick.Controls 2.2 as QC2
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.LocalStorage 2.0
Window {
id: root
width: 320
height: 480
property var db
property int rowHeight: 90
Component.onCompleted: {
db = LocalStorage.openDatabaseSync("test_db", "1.0", "Database", 100000)
loadData()
}
property var sql_data: []
function loadData() {
var sql, rs, len, i
db.transaction(function(tx) {
tx.executeSql("DROP TABLE IF EXISTS tbl")
tx.executeSql("CREATE TABLE IF NOT EXISTS tbl (
rowId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, listOrder INTEGER NOT NULL)")
len = 5
for (i = 0; i < len; i++)
tx.executeSql("INSERT INTO tbl (name, listOrder) VALUES ('Item " + i + "', " + i + ")")
rs = tx.executeSql("SELECT name, listOrder FROM tbl ORDER BY listOrder ASC")
})
len = rs.rows.length
sql_data = new Array(len)
for (i = 0; i < len; i++) {
sql_data[i] = {"name": "", "order": ""}
sql_data[i]["name"] = rs.rows.item(i).name
sql_data[i]["order"] = rs.rows.item(i).listOrder
}
repeater.model = sql_data
}
Flickable {
id: mainFlick
anchors.fill: parent
contentHeight: mainColumn.height
Column {
id: mainColumn
width: parent.width
QC2.Button {
text: "Change"
onClicked: {
var p1, p2
var d1 = new Date().getTime()
var movedEle = 3
var newPos = 1
var ele1 = repeater.itemAt(movedEle)
var ele2 = repeater.itemAt(newPos)
ele1.y = ele2.y
root.sql_data[movedEle]["order"] = root.sql_data[newPos]["order"]
if (newPos < movedEle) {
p1 = newPos
p2 = movedEle
} else {
p1 = movedEle
p2 = newPos
}
root.db.transaction(function(tx) {
var sql = "UPDATE tbl SET listOrder = " + root.sql_data[p1]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[p2]["order"])
for (var i = p1; i < p2; i++) {
if (i !== movedEle) {
repeater.itemAt(i).y = repeater.itemAt(i).y + rowHeight
root.sql_data[i]["order"] += 1
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
}
}
})
sql_data = sql_data.sort(function(a,b) {
return a["order"] - b["order"]
})
repeater.model = sql_data
var d2 = new Date().getTime()
console.debug("Seconds: " + (d2 - d1) / 1000)
}
}
Repeater {
id: repeater
width: parent.width
model: []
delegate: Column {
id: repeaterItem
width: parent.width - 20
height: rowHeight
anchors.horizontalCenter: parent.horizontalCenter
property int pos: index
DropArea {
id: dragTarget
width: parent.width
height: 20
Rectangle {
id: dropRect
anchors.fill: parent
color: "green"
states: [
State {
when: dragTarget.containsDrag
PropertyChanges {
target: dropRect
color: "red"
}
}
]
}
}
Row {
id: contentRow
width: parent.width
height: parent.height
z: 1
Column {
id: itemColumn
width: parent.width - 70
Text {
text: "Name: " + modelData["name"]
}
Text {
text: "Order: " + modelData["order"]
}
Text {
text: "Repeater Index: " + index
}
} // itemColumn
MouseArea {
id: dragArea
width: 40
height: itemColumn.height
drag.axis: Drag.YAxis
drag.target: dragRect
onReleased: parent = ((dragRect.Drag.target !== null) ? dragRect.Drag.target : root)
Rectangle {
Component.onCompleted: console.debug(dragRect.Drag.target)
id: dragRect
width: 40
height: itemColumn.height
color: "grey"
Drag.active: dragArea.drag.active
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
states : State {
when: dragArea.drag.active
ParentChange { target: dragRect; parent: root }
AnchorChanges {
target: dragRect
anchors.verticalCenter: undefined
anchors.horizontalCenter: undefined
}
}
}
}
} // contentRow
Behavior on y {
PropertyAnimation {
duration: 200
easing.type: Easing.Linear
}
}
} // repeaterItem
} // repeater
} // mainColumn
} // mainFlick
} // id
Much of the drag and drop code came from the Qt Examples site.
Here's what my problems are:
The MouseArea used for dragging the rectangles doesn't seem to change locations. Once a rectangle is actually moved, it stays in its new location but if I want to move it again, I have to click and drag where it was originally when the app loads.
If I switch the target of the Drag Area from the child rectangle to the entire row (repeaterItem), everything moves properly but will no longer interact with the Drop areas.
I think I can get the index of the new location after a row has been dragged simply by getting the y-value of the Drop Area. Would there be a better way to do this?
Since the "Change" button already re-orders the row elements, I only need help getting the rows to properly interact with the Drop Areas and then get the y-position of the Drop Area when the dragged item is released.
It may not be the most efficient out there but it's a good start for anybody looking to do something similar.
import QtQuick.Controls 2.2 as QC2
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.LocalStorage 2.0
Window {
id: root
width: 320
height: 580
property var db
property int rowHeight: 90
property int len
Component.onCompleted: {
db = LocalStorage.openDatabaseSync("test_db", "1.0", "Database", 100000)
loadData()
}
property var sql_data: []
function loadData() {
var sql, rs, i
db.transaction(function(tx) {
tx.executeSql("DROP TABLE IF EXISTS tbl")
tx.executeSql("CREATE TABLE IF NOT EXISTS tbl (
rowId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, listOrder INTEGER NOT NULL)")
len = 15
for (i = 0; i < len; i++)
tx.executeSql("INSERT INTO tbl (name, listOrder) VALUES ('Item " + i + "', " + i + ")")
rs = tx.executeSql("SELECT name, listOrder FROM tbl ORDER BY listOrder ASC")
})
len = rs.rows.length
sql_data = new Array(len)
for (i = 0; i < len; i++) {
sql_data[i] = {"name": "", "order": ""}
sql_data[i]["name"] = rs.rows.item(i).name
sql_data[i]["order"] = rs.rows.item(i).listOrder
}
repeater.model = sql_data
}
Flickable {
id: mainFlick
anchors.fill: parent
contentHeight: mainColumn.height
rebound: Transition {
NumberAnimation {
properties: "y"
duration: 500
easing.type: Easing.OutCirc
}
}
Column {
id: mainColumn
width: parent.width
function moveEle(start, end, f) {
if (f === false) {
var p1, p2, ele1, ele2
var c = false
var d1 = new Date().getTime()
ele1 = repeater.itemAt(start)
console.debug(f)
ele2 = repeater.itemAt(end)
root.sql_data[start]["order"] = root.sql_data[end]["order"]
if (end < start) {
p1 = end
p2 = start
} else {
p1 = start
p2 = end
c = true
}
root.db.transaction(function(tx) {
var sql = "UPDATE tbl SET listOrder = " + root.sql_data[p1]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[p2]["order"])
for (var i = p1; i < p2; i++) {
if (c === false) {
if (i !== start) {
root.sql_data[i]["order"]++
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
}
} else {
root.sql_data[i]["order"]--
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
}
}
})
} else if (f === true) {
c = false
d1 = new Date().getTime()
ele1 = repeater.itemAt(start)
console.debug(f)
end--
ele2 = repeater.itemAt(end)
root.sql_data[start]["order"] = root.sql_data[end]["order"]
p1 = start
p2 = end
c = true
root.db.transaction(function(tx) {
var sql = "UPDATE tbl SET listOrder = " + root.sql_data[p1]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[p2]["order"])
tx.executeSql(sql)
for (var i = p1; i <= p2; i++) {
if (i !== start) {
root.sql_data[i]["order"]--
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
}
}
})
}
sql_data = sql_data.sort(function(a,b) {
return a["order"] - b["order"]
})
repeater.model = sql_data
var d2 = new Date().getTime()
console.debug("Seconds: " + (d2 - d1) / 1000)
}
Repeater {
id: repeater
width: parent.width
model: []
delegate: Column {
id: repeaterItem
width: parent.width
height: rowHeight
anchors.horizontalCenter: parent.horizontalCenter
z: dragArea.drag.active ? 2 : 1
property int pos: index
DropArea {
id: dragTarget
width: parent.width
height: 15
property int ind: index
onEntered: {
drag.source.ind = index
drag.source.f = false
if (drag.source.ind !== drag.source.oldInd && drag.source.ind !== drag.source.oldInd + 1)
drag.source.caught = true
}
onExited: drag.source.caught = false
Rectangle {
id: dropRect
anchors.fill: parent
z: 0
states: [
State {
when: dragTarget.containsDrag
PropertyChanges {
target: dropRect
color: "grey"
}
PropertyChanges {
target: dropRectLine
visible: false
}
}
]
Rectangle {
id: dropRectLine
width: parent.width
height: 1
anchors.verticalCenter: parent.verticalCenter
color: "black"
}
}
}
Row {
id: contentRow
width: parent.width
height: parent.height
Drag.active: dragArea.drag.active
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
Column {
id: itemColumn
width: parent.width - 70
Text {
text: "Name: " + modelData["name"]
}
Text {
text: "Order: " + modelData["order"]
}
Text {
text: "Repeater Index: " + index
}
} // itemColumn
MouseArea {
id: dragArea
width: 40 //repeater.width
height: itemColumn.height
drag.axis: Drag.YAxis
drag.target: contentRow
property point beginDrag
property point dropTarget
property bool caught: false
property int ind
property int oldInd: index
property bool f
onPressed: {
dragArea.beginDrag = Qt.point(contentRow.x, contentRow.y);
}
onReleased: {
if (dragArea.caught) {
dropRectFinal.color = "white"
dropRectLineFinal.visible = true
mainColumn.moveEle(index,dragArea.ind, dragArea.f)
} else {
backAnimY.from = contentRow.y;
backAnimY.to = beginDrag.y;
backAnim.start()
}
}
Rectangle {
id: dragRect
width: 40
height: itemColumn.height
color: "grey"
}
} // contentRow
} // dragArea
ParallelAnimation {
id: backAnim
SpringAnimation { id: backAnimY; target: contentRow; property: "y"; duration: 300; spring: 2; damping: 0.2 }
}
} // repeaterItem
} // repeater
DropArea {
id: dragTargetFinal
width: parent.width
height: 15
property int ind: root.len
onEntered: {
drag.source.ind = ind
drag.source.f = true
if (drag.source.ind !== drag.source.oldInd && drag.source.ind !== drag.source.oldInd + 1)
drag.source.caught = true
}
onExited: drag.source.caught = false
Rectangle {
id: dropRectFinal
anchors.fill: parent
states: [
State {
when: dragTargetFinal.containsDrag
PropertyChanges {
target: dropRectFinal
color: "grey"
}
PropertyChanges {
target: dropRectLineFinal
visible: false
}
}
]
Rectangle {
id: dropRectLineFinal
width: parent.width
height: 1
anchors.verticalCenter: parent.verticalCenter
color: "black"
}
}
}
} // mainColumn
QC2.ScrollBar.vertical: QC2.ScrollBar { active: scrollAnim.running ? true : false }
} // mainFlick
DropArea {
id: scrollUp
width: parent.width
height: 50
anchors.top: parent.top
visible: {
var visible = false
if (mainFlick.contentY > 0)
visible = true
visible
}
onEntered: {
scrollAnim.from = mainFlick.contentY
scrollAnim.to = 0
scrollAnim.start()
}
onExited: scrollAnim.stop()
}
DropArea {
id: scrollDown
width: parent.width
height: 50
anchors.bottom: parent.bottom
visible: {
var visible = false
if (mainFlick.contentY < mainColumn.height - Window.height)
visible = true
visible
}
onEntered: {
scrollAnim.from = mainFlick.contentY
scrollAnim.to = mainColumn.height - Window.height
scrollAnim.start()
}
onExited: scrollAnim.stop()
}
SmoothedAnimation {
id: scrollAnim
target: mainFlick
property: "contentY"
velocity: 400
loops: 1
maximumEasingTime: 10
}
} // root
I have a Canvas with the image, that I load via filedialog, how can I get the pixel array of this image?
I need convert it to grayscale by converting every pixel using formula and load it back to the Canvas.
Here the code:
import QtQuick 2.0
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.2
import QtQuick.Dialogs 1.0
ApplicationWindow {
id: window
visible: true
width: 1000
height: 750
Material.theme: Material.Dark
Material.background: "#2C303A"
Material.accent: "#65B486"
Material.foreground: "#efefef"
GridLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 9
columns: 4
rows: 3
rowSpacing: 10
columnSpacing: 10
Canvas {
id: canvas
height: window.height - 15
Layout.columnSpan: 4
Layout.fillWidth: true
property bool loaded: false
property var filepath: ''
onDrawChanged: requestPaint()
onFilepathChanged: {
loadImage(filepath)
}
onImageLoaded: {
loaded = true
requestPaint()
}
onPaint: {
if (loaded) {
var ctx = getContext("2d");
ctx.drawImage(filepath, 0, 0, width, height)
}
if (to_grayscale) {
var ctx = getContext("2d");
var ar = ctx.getImageData(0, 0, width, height).data
for(var i in ar){
print(i)
}
}
}
}
FileDialog {
id: fileDialog
title: "Please choose a file"
nameFilters: ["Image files (*.jpg *.png *.jpeg)"]
onAccepted: {
console.log("You chose: " + fileDialog.fileUrls)
canvas.filepath = fileDialog.fileUrls
canvas.requestPaint()
}
onRejected: {
console.log("Canceled")
}
}
Drawer {
id: drawer
visible: true
modal: false
width: 0.33 * window.width
height: window.height
GridLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 9
columns: 2
rows: 3
rowSpacing: 10
columnSpacing: 10
Button {
text: 'Load image'
onClicked: fileDialog.visible = true
}
Button {
text: 'RGB to Grayscale'
onClicked: canvas.draw = true
}
}
}
}
}
I'm trying to get ImageData, but here's empty
I read that Canvas contain PixelArray, but I don't know how to get it.
Thank you.
To access the rgba values
var ar = ctx.getImageData(0, 0, width, height);
for( var x=0; x < ar.data.length; x=x+4 )
{
// To read RGBA values
var red = ar.data[x];
var green = ar.data[x + 1];
var blue = ar.data[x + 2];
var alpha = ar.data[x + 3];
console.log(red + ", " + green + ", " + blue + ", " + alpha );
// To convert to grey scale, modify rgba according to your formula
ar.data[x] = 0.2126 *ar.data[x] + 0.7152* ar.data[x+1] + 0.0722 *ar.data[x+2];
ar.data[x+1] = 0.2126 *ar.data[x] + 0.7152* ar.data[x+1] + 0.0722 *ar.data[x+2];
ar.data[x+2] = 0.2126 *ar.data[x] + 0.7152* ar.data[x+1] + 0.0722 *ar.data[x+2];
ar.data[x+3] = 255;
}
// update the canvas with new data
ctx.drawImage(ar.data, 0, 0);
You have to requestPaint() in onClicked slot of Button
I want to create a reusable component where I could pass a model i.e.
["red", "green", "blue", "black", "orange", "pink", "gray", "navy", "magenta"]
And it would fill Grid with rectangles of model data. And if there are more than let's say 6 items in model it would then fill other "page".
That's how it should look like:
Currently I use StackLayout have 2 Grid items and Repeater inside of them and I divided my model into 2:
model: ["red", "green", "blue", "black", "orange", "pink"]
model: ["gray", "navy", "magenta"]
To fill each "page" with rectangles.
Writing logic to dynamically separate model into separate parts for each page seems overly complicated.
I have tried GridView but I couldn't find important properties like in Grid:
topPadding: 10
bottomPadding: 10
leftPadding: 20
rightPadding: 20
spacing: 10
columns: 2
Source of my example:
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: mainArea
width: 400
height: 400
color: "beige"
StackLayout {
id: stackLayout
anchors.fill: parent
currentIndex: 0
Grid {
anchors.fill: parent
topPadding: 10
bottomPadding: 10
leftPadding: 20
rightPadding: 20
spacing: 10
columns: 2
property int maxRows: 3
Repeater {
model: ["red", "green", "blue", "black", "orange", "pink"]
Rectangle {
width: (parent.width - parent.leftPadding - parent.rightPadding - parent.spacing) / parent.columns
height: (parent.height - parent.topPadding - parent.bottomPadding - (parent.maxRows - 1) * parent.spacing) / parent.maxRows
color: modelData
}
}
}
Grid {
anchors.fill: parent
topPadding: 10
bottomPadding: 10
leftPadding: 20
rightPadding: 20
spacing: 10
columns: 2
property int maxRows: 3
Repeater {
model: ["gray", "navy", "magenta"]
Rectangle {
width: (parent.width - parent.leftPadding - parent.rightPadding - parent.spacing) / parent.columns
height: (parent.height - parent.topPadding - parent.bottomPadding - (parent.maxRows - 1) * parent.spacing) / parent.maxRows
color: modelData
}
}
}
}
}
Button {
anchors.bottom: mainArea.verticalCenter
anchors.bottomMargin: 5
anchors.left: mainArea.right
text: "<"
onClicked: stackLayout.currentIndex = 0
}
Button {
anchors.top: mainArea.verticalCenter
anchors.topMargin: 5
anchors.left: mainArea.right
text: ">"
onClicked: stackLayout.currentIndex = 1
}
}
For a simple array, you can use the method array.slice(from, to) to create models for each page.
property int page: 0
Button {
text: "up"
onClicked: page++
}
Grid {
y: 100
rows: 2
columns: 2
Repeater {
model: ["red", "green", "blue", "black", "orange", "pink", "gray", "navy", "magenta", "yellow", "cyan", "brown", "lightblue", "darkred"].slice(page * 4, (page + 1) * 4)
Rectangle {
width: 100
height: 100
color: modelData
}
}
}
For QAbstractItemModel-descendents, you can use the method explained here if you want to have a QML-only solution.
Otherwise you might implement a faster filter model in C++ utilizing the QSortFilterProxyModel or maybe the QIdentityProxyModel
See this implementation by GrecKo for a possible way, how to get the SortFilterProxyModel working in QML.
You could try filtering the model to show only specific indices.
Or even simpler, you can simply set the delegate visibility depending on the index and items per page:
ApplicationWindow {
id: main
visible: true
width: 640
height: 480
color: "darkgray"
property int maxRows: 3
property int page: 0
property int iperp: 2 * maxRows
Grid {
anchors.fill: parent
topPadding: 10
bottomPadding: 50
leftPadding: 20
rightPadding: 20
spacing: 10
columns: 2
Repeater {
id: rep
model: ["red", "green", "blue", "black", "orange", "pink", "gray", "navy", "magenta", "yellow", "cyan", "brown", "lightblue", "darkred"]
Rectangle {
width: (parent.width - parent.leftPadding - parent.rightPadding - parent.spacing) / parent.columns
height: (parent.height - parent.topPadding - parent.bottomPadding - (maxRows - 1) * parent.spacing) / maxRows
color: modelData
visible: {
var i = page * iperp
return index >= i && index < i + iperp
}
Text {
anchors.centerIn: parent
text: index
}
}
}
}
Row {
anchors.horizontalCenter: main.contentItem.horizontalCenter
anchors.bottom: main.contentItem.bottom
Button {
text: "<<"
enabled: page
onClicked: --page
}
Button {
text: ">>"
enabled: page < rep.count / iperp - 1
onClicked: ++page
}
}
}