I have a listview who's delegate has a repeater in it which is supposed to be populated by text. If the repeater's model property is hard coded this way:
model: ['String 1', 'String 2', 'String 3'];
It works perfectly by showing 3 items in the repeater's region.
However, I want to send such a list using a ListElement and this is what I tried:
ListElement{
shape: "Circle"; colors: ['Red', 'Blue'];
}
Unfortunately this approach doesn't work and throws an error:
ListElement: cannot use script for property value
How many I achieve this? TIA
You can't:
Values must be simple constants; either strings (quoted and optionally within a call to QT_TR_NOOP), boolean values (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
The most powerful way to expose data to views is by creating a C++ model.
However, if you really don't want to go to C++, you could store the colours in a comma-separated string, and then split them:
import QtQuick 2.6
import QtQuick.Window 2.0
Window {
visible: true
width: 200
height: 200
ListView {
width: 32
height: 64
anchors.centerIn: parent
model: ListModel {
ListElement{
shape: "Circle"
colors: "red, blue"
}
ListElement{
shape: "Square"
colors: "green,yellow"
}
}
delegate: Rectangle {
width: 32
height: 32
radius: shape === "Circle" ? width / 2 : 0
property var colorArray: colors.split(",")
gradient: Gradient {
GradientStop {
position: 0
color: colorArray[0]
}
GradientStop {
position: 1
color: colorArray[1]
}
}
}
}
}
An alternative to #Mitch's answer would be to ditch the ListModel and use a plain Javascript array of objects as the model.
With that solution, you'll lose the dynamic features of the ListModel (adding, removing, inserting, ...). You also won't be able to use sections in your view or use a proxymodel on this model.
import QtQuick 2.6
import QtQuick.Window 2.0
Window {
visible: true
width: 200
height: 200
ListView {
width: 32
height: 64
anchors.centerIn: parent
model: [
{
shape: "Circle",
colors: ["red", "blue"]
},
{
shape: "Square",
colors: ["green", "yellow"]
}
]
delegate: Rectangle {
width: 32
height: 32
radius: modelData.shape === "Circle" ? width / 2 : 0
gradient: Gradient {
GradientStop {
position: 0
color: modelData.colors[0]
}
GradientStop {
position: 1
color: modelData.colors[1]
}
}
}
}
}
Related
I have an issue with setting coordinates inside Repeater:
import QtQuick
Window {
id: mainWindow
property int wi: 640
property int he: 500
width: wi
height: he
visible: true
title: qsTr("Game")
Rectangle {
id: gameWindow
width: wi/1.6
height: he
anchors.right: parent.right
visible: true
color: "black"
clip: true
Grid {
id: gameGrid
columns: 25
spacing: 0
rows: 32
anchors.fill: parent
Repeater {
model: 600
Rectangle {
width: wi/40
height: 20
border.width: 2
color: "grey"
}
}
}
Grid {
id: sGrid
columns: gameGrid.columns
spacing: gameGrid.spacing
rows: gameGrid.rows
anchors.fill: gameGrid
Repeater {
model: 5
Rectangle {
// anchors.horizontalCenter: sGrid.horizontalCenter
// anchors.verticalCenter: sGrid.verticalCenter
// x: (wi/2) + (index * (wi/40) )
// y: he/2
width: wi/40
height: 20
border.width: 1
color: "red"
}
}
}
}
}
Whole code above, but my question is about the second Repeater with 5 Rectangles.
I have tried to solve that with many ways. Most obvious seemed to me placing coordinates inside Repeater, but now I know it does not work like this - I have to place coordinates somehow inside Rectangle. I have commented code, where are the ways I have tried to solve this.
Anchors work very well - it places the first element exactly where I am expecting.
Problem appears with the next elements. They are placing inside the same element of Grid. I do not understand why the coordinates does not working. Documentation shows I could use "index", don't know, maybe the point is that's "read only" property? I have tried to set Rectangle with prefix "delegate:" with the same result as well.
In your question, you mention you have Grid + Repeater + Rectangle. I am not sure what you want to achieve, but, it feels like you may have better luck by going GridView + Rectangle because GridView supports a model.
Since you want coordinate control of your Rectangles, it is possible to do this alone with Repeater + Rectangle. No need for Grid since the Grid will impact the coordinate system of your Rectangle.
Below illustrates how you can use a simple ListModel to control the placement of your Rectangles:
import QtQuick
import QtQuick.Controls
Page {
Repeater {
model: ListModel {
ListElement { p:100; q:100; w:50; h:50; c:"red"; o:0 }
ListElement { p:200; q:100; w:50; h:50; c:"green"; o:15 }
ListElement { p:300; q:200; w:50; h:50; c:"blue"; o:30 }
ListElement { p:300; q:300; w:50; h:50; c:"orange"; o:45 }
ListElement { p:200; q:400; w:50; h:50; c:"purple"; o:60 }
}
delegate: Rectangle {
x: p
y: q
width: w
height: h
color: c
rotation: o
}
}
}
You can Try it Online!
[EDIT]
With the following, it shows how you could use index in 10 delegates to place your rectangles using a formula:
import QtQuick
import QtQuick.Controls
Page {
Repeater {
model: 10
delegate: Rectangle {
x: 100 + (index % 4) * 100
y: 100 + Math.floor(index / 4) * 100
width: 50
height: 50
color: ["green","red","orange","blue","purple"][index%5]
rotation: index * 10
Text {
anchors.centerIn: parent
text: index
color: "white"
}
}
}
}
You can Try it Online!
I'm following the code pattern of my unavailable predecessor - using nested ListView's to implement a TreeView-like interface. I'd prefer not to use a TreeView, purely to keep the same patterns throughout the code, rather than introducing more patterns. But I can't get the basics to work with a very simple piece of code!
I've tried looking for a definitive QML/QtQuick reference book but my searches mostly point to people asking why there isn't one. I find my self fighting against QML rather than working WITH it, a sure sign that I need to hit a book. So Question 2 is what should I read?
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 1.4
Window {
id: theWindow
visible: true
width: 640
height: 480
ListModel {
id: outerModel
ListElement{ display: "One" }
ListElement{ display: "Two" }
ListElement{ display: "Three" }
}
ListModel {
id: innerModel
ListElement{ display: "A" }
ListElement{ display: "B" }
}
Column{
ListView {
model: outerModel
height: 100
width: 200
delegate: Column {
Text {
text: display
}
ListView {
model: innerModel
height: 100
width: 200
delegate: Text {
text: display
}
} // ListView inner
} //delegate Column
} // ListView outer
} // Column
} // Window
I'd expect a result similar to:
One
A
B
Two
A
B
Three
A
B
But I only get
One
A
B
The problem is the fixed height of 100px on both of your ListViews.
Remove the first wrapping Column and set a dyamic height on the inner ListView based on content:
ListView {
model: outerModel
height: parent.height // <====
width: 200
delegate: Column {
Text {
text: display
}
ListView {
model: innerModel
height: contentHeight // <====
width: 200
delegate: Text {
text: display
}
}
}
}
(or implement an actual TreeView :))
I want to move a qml Item out of the left side of the app window.
While this task works perfectly for the right side of the window by defining a state like this
states: State {
name: "hidden"
when: is_hidden == true
AnchorChanges {
target: right_item_to_move
anchors.right: undefined
}
PropertyChanges {
target: right_item_to_move
x: main_window.width
}
}
and defining the appropriate Transition, I can't get it to work on the left side of the main window because negative x coordinates are not allowed.
I.e. this does not work:
states: State {
name: "hidden"
when: is_hidden == true
AnchorChanges {
target: left_item_to_move
anchors.left: undefined
}
PropertyChanges {
target: left_item_to_move
x: -left_item_to_move.width
}
}
How can I achieve this task? I'm using Qt 5.8 and QtQuick 2.0.
In my opinion, one should strive to stay true to one way of positioning, so you should either use anchors or x/y-coordinates.
Here you can find an overview how to make the right choice.
In short: When in doubt, use anchors. When the positioning is only relative to the parent (static) use x and y and if not possible otherwise do so even when not relative to the parent.
As you have chosen anchors, in my opinion you should stick to that - meaning: change the anchoring, so that instead of the left anchor line of the object, the right anchor line will be anchored to the window's left.
This would look like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Rectangle {
anchors.centerIn: parent
width: 300
height: 600
color: 'green'
Button {
id: but
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
state = (state === 'left' ? '' : 'left')
}
states: [
State {
name: 'left'
AnchorChanges {
target: but
anchors.left: undefined
anchors.right: parent.left
}
}
]
transitions: [
Transition {
AnchorAnimation {
duration: 200
}
}
]
}
}
}
An example, how it might look, if you choose to modify the x value, it might look like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Rectangle {
anchors.centerIn: parent
width: 300
height: 600
color: 'green'
Button {
id: but
property bool shown: true
anchors {
verticalCenter: parent.verticalCenter
}
onClicked: {
shown = !shown
}
x: (shown ? 0 : -width)
Behavior on x {
XAnimator {
duration: 200
}
}
}
}
}
I wonder if it is possible to somehow use an ObjectModel as the model for multiple DelegateModel to use it's ability to create Groups.
For example, I might have an ObjectModel containing multiple Rectangle as this
ObjectModel {
id: rootModel
Rectangle { width: 50; height: 20; color: 'red' }
Rectangle { width: 80; height: 50; color: 'blue' }
Rectangle { width: 50; height: 20; color: 'green' }
Rectangle { width: 80; height: 50; color: 'orchid' }
Rectangle { width: 50; height: 20; color: 'black' }
}
My goal might be to display all Rectangle with the width: 50 in one View, while those with the width: 80 in another view.
So I create a DelegateModel
DelegateModel {
id: delMod1
model: rootModel
groups: [
DelegateModelGroup { name: 'width50' },
DelegateModelGroup { name: 'width80' }
]
}
As far as I understood the documentation I need to attach the property
item.DelegateModel.inWidth50: ....width === 50
item.DelegateModel.inWidth80: ....width === 80
or something like this, to add my elements to the groups. But this is where I fail as I don't know where and how to do this.
Though there are tons of workarounds (such as disassembling the Objects in the ObjectModel and using a ListModel, recreating the Objects in the DelegateModel delgate, which robbs a lot of the versatility as the Objects and it's children would need to have a predictable structure...) I think it would be wonderful, if it is possible to make this idea run.
Takk fyrir,
-m-
It would seem that in the design of QML user reparent was not really "envisioned", because even though it is possible, it involves creating and changing states, which is just not convenient to add to each and every item.
import QtQuick 1.0
Item {
width: 200; height: 100
Rectangle {
id: redRect
width: 100; height: 100
color: "red"
}
Rectangle {
id: blueRect
x: redRect.width
width: 50; height: 50
color: "blue"
states: State {
name: "reparented"
ParentChange { target: blueRect; parent: redRect; x: 10; y: 10 }
}
MouseArea { anchors.fill: parent; onClicked: blueRect.state = "reparented" }
}
}
I was wondering if there is a more elegant way to reparent items without polluting items with unnecessary states?
not certain if you need to use QtQuick 1.0, but with 2.0 this also works and is imo more straight forward.
import QtQuick 2.0
Item {
width: 200; height: 100
Rectangle {
id: redRect
width: 100; height: 100
color: "red"
}
Rectangle {
id: blueRect
x: redRect.width
width: 50; height: 50
color: "blue"
MouseArea { anchors.fill: parent; onClicked:
{ blueRect.parent = redRect; blueRect.x = 10; blueRect.y = 10 }
}
}
}