How I can use different delegates in qml for the ListView. For example I have QList<SomeObject*> list, SomeObject has two fields: type (circle, rectangle, etc), and someValue. I created QListModel for this list. I have different qml elements (circle.qml, rectangle.qml, etc). How can I view delegate for an item by type, and view field someValue in this delegate. And can I position these delegates without a table/list, I wanted to position them by coordinates(x, y).
To have different delegates based on a role (it can also be done depending on the row or column), you should use DelegateChooser with DelegateChoice:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import Qt.labs.qmlmodels 1.0
Window {
width: 640
height: 480
visible: true
ListModel {
id: shapeModel
ListElement {
type: "circle"
value: 100
x: 10
y: 10
}
ListElement {
type: "rectangle"
value: 30
x: 100
y: 100
}
ListElement {
type: "circle"
value: 30
x: 300
y: 450
}
ListElement {
type: "rectangle"
value: 20
x: 500
y: 200
}
ListElement {
type: "circle"
value: 25
x: 650
y: 100
}
ListElement {
type: "rectangle"
value: 60
x: 600
y: 200
}
}
Flickable {
anchors.fill: parent
contentWidth: contentItem.childrenRect.width
contentHeight: contentItem.childrenRect.height
Repeater {
model: shapeModel
delegate: DelegateChooser {
role: "type"
DelegateChoice {
roleValue: "rectangle"
Rectangle {
x: model.x
y: model.y
height: model.value
width: model.value
border.color: "orange"
border.width: 1
}
}
DelegateChoice {
roleValue: "circle"
Rectangle {
x: model.x
y: model.y
height: model.value
width: model.value
radius: model.value/2
border.color: "blue"
border.width: 1
}
}
}
}
}
}
It avoids the additional indirection of a Loader.
You can try a concept of qml Loader that may match your requirements. Basically, you cannot define multiple delegates for a single view. So setting your Top Delegate as the loader and loading the items based on the type will help you with this case.
Positioning is also possible you can have x & y pos defined with your model data. So that you can align your view items accordingly. I have used Scrollview + Repeater in this case
Shared a minimal sample here. For sake of simplicity, I have kept my data with Qml ListModel. The same can be done with objects from the c++ model as well.
Here provided the source as a Qt solution project too.
// ShapeModel.qml //
import QtQuick 2.15
import QtQml.Models 2.15
ListModel {
ListElement {
type: "circle"
val: "100"
xpos: 10
ypos: 10
}
ListElement {
type: "rectangle"
val: "30"
xpos: 100
ypos: 100
}
ListElement {
type: "circle"
val: "150"
xpos: 300
ypos: 450
}
ListElement {
type: "rectangle"
val: "20"
xpos: 500
ypos: 200
}
ListElement {
type: "circle"
val: "25"
xpos: 650
ypos: 100
}
ListElement {
type: "rectangle"
val: "60"
xpos: 600
ypos: 200
}
}
// main.qml //
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Qt MVC")
Component {
id: componentCircleId
Rectangle {
border.color: "blue"
border.width: 1
height: value
width: value
radius: value/2
}
}
Component {
id: componentRectangleId
Rectangle {
border.color: "orange"
border.width: 1
height: value
width: value
}
}
Component {
id: componentLoaderId
Loader {
property real value: val
x: xpos
y: ypos
sourceComponent: type === "circle" ?
componentCircleId : componentRectangleId
}
}
ScrollView {
id: scrollviewId
anchors.fill: parent
Repeater {
anchors.fill: parent
model: ShapeModel{}
delegate: componentLoaderId
onItemAdded: {
// scroll area computation
// Better solutions may be available
if(item.x+item.width > scrollviewId.contentWidth)
scrollviewId.contentWidth = item.x+item.width
if(item.y+item.height > scrollviewId.contentHeight)
scrollviewId.contentHeight = item.y+item.height
}
}
}
}
Related
I'm trying to find a means of creating dynamic chartviews using QML. The general idea is for a user to click an element on a ComboBox and click a button to either add a chartview to the screen or remove it.
Currently I'm able to display my charts statically in a RowLayout like so:
import QtQuick 2.15
import QtCharts 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.12
Item{ property int firstModel : 1
property int secondModel : 2
property int maxVal : 8
property int minVal: -8
RowLayout {
anchors.fill: parent
spacing: 10
ChartView{
id: firstModelChartView
animationOptions: ChartView.NoAnimation
theme: ChartView.ChartThemeLight
antialiasing: true
Layout.fillHeight: true
Layout.fillWidth: true
DateTimeAxis{
id:axisXValues
min:new Date(SensorManager.audioStream[firstModel].xMin)
max:new Date(SensorManager.audioStream[firstModel].xMax)
format: "hh:mm:ss:zzz"
}
ValueAxis {
id: axisYValues
min: gLinkMin
max: gLinkMax
}
LineSeries {
id: innerChamberLineSeries
useOpenGL: true
name: "Inner Chamber"
axisX: axisXValues
axisY: axisYValues
}
VXYModelMapper {
id: innerChamberLineSeriesModelMapper
model: SensorManager.audioStream[firstModel]
series: innerChamberLineSeries
firstRow: 1
xColumn: 0
yColumn: 1
}
}
/// SEcond Model ...... Etc You get the gist
}
}
The model is a QAbstractModel Type from C++ and I'm able to display this in realtime. My problem is I've got 20 of these data streams and I don't want to show them all at once. so I want to give the user the ability to be able to add or remove multiple of these chartviews from the screen.
I'm trying to use a GridView and basically adapt this snippet from this post but I'm not really getting far with it.
My Idea is to initially create a chartview and then attach a lineseries to that chartview, here's what I've got so far but I'm stumped on how to attach a specific lineseries model to a chartview and how you remove a specific chartview from the Gridview.
import QtQuick 2.15
import QtCharts 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.12
Item{ property int firstModel : 1
property int secondModel : 2
property int maxVal : 8
property int minVal: -8
ListModel{
id: chartsModel
Component.onCompleted: {
chartsModel.append({"name": qsTr("Inner Vinration Chamber"), value: firstModel })
chartsModel.append({"name": qsTr("Extruder Vibration Y"), value: secondModel })
///////
}
}
ListModel{
id:chartViewModel
}
Row {
id: chartControlsRow
anchors.fill: parent
anchors{
leftMargin: 10
topMargin: 10
}
spacing:0
CustomComboBox {
id: chartSelector
model: chartsModel
textRole: "name"
onCurrentIndexChanged: {
var model = SensorManager.audioStream[chartsModel.get(currentIndex).value]
}
}
CustomButton
{
id: addChartButton
text: "Add Chart"
onClicked: {
modelId.append({'mColor': 'blue'})
var chartViewModel = Qt.createQmlObject('import QtQuick 2.14; ListModel { }',
Qt.application, 'dynamicChartViewModel');
}
}
CustomButton
{
id:removeChartButton
text: "Remove Chart"
onClicked: {
console.log(chartSelector.currentIndex)
chartViewModel.remove(chartSelector.currentIndex);
}
}
}
Rectangle{
anchors.fill: parent
anchors.topMargin: 55
color: "black"
GridView {
id: mGridViewId
anchors.fill: parent
cellWidth: 400; cellHeight: 400
model: chartViewModel
delegate: Rectangle {
width: mGridViewId.cellWidth;
height: mGridViewId.cellHeight
color: mColor
ChartView {
id: mChartView
width: parent.width;
height: parent.height
DateTimeAxis{
id:axisXValues
format: "hh:mm:ss:zzz"
}
ValuesAxis{
id:axisYValues
min:gLinkMin
max:gLinkMax
}
LineSeries {
name: "Lineseries"
id: mLineSeries
useOpenGL: true
axisX: axisXValues
axisY: axisYValues
}
VXYModelMapper{
id: modelMapper
series: mLineSeries
firstRow: 1
xColumn: 0
yColumn: 1
}
}
}
function getDelegateInstanceAt(index) {
return contentItem.children[index];
}
}
}
}
How do I Achieve this? Any tips or pointers would be helpful
I have two questions which are somehow related.
I have created a route on an open street map and I want to extract a list of points that correspond to the way points of the generated route (not just the start and the finish). How can this be achieved? For example I want to extract way points for the generated red route from the image bellow (of course I do not want to extract all the points from a route but from 10 in 10 meters).
How do I erase the generated route with red, and have the original map (without the red route)
I have tried many function on the map item but non of them worked. For example I have tried the code below but the red route remains.
function clearMapDataForSession()
{
mapview.clearData();
routeModel.update()
}
You can get a list of coordinates from the Route by using the properties path or segments. The path property directly gives you a list of coordinates on the given Route, the segments property on the other hand gives you a list of RouteSegments which in turn contain a list of coordinates given by its path property.
Print the list of Route coordinates via segments:
var segments = routeModel.get(0).segments
for (var i = 0; i < segments.length; i++) {
var path = segments[i].path
for (var j = 0; j < path.length; j++)
console.log(path[j])
}
Print the list of Route coordinates via path:
var path = routeModel.get(0).path
for (var i = 0; i < path.length; i++) {
console.log(path[i])
}
If you compare the list of coordinates given by the two options, they are the same. The benefit of RouteSegments is that you get the distance of the segment as a property. So if you want to generate a list of coordinates/points on the Route at the same distance, this would help you in writing some sort of an algorithm.
In order to erase a generated Route you need to call reset() on the RouteModel. If you want to also clear the waypoints of a RouteQuery you should call clearWaypoints() as well.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtLocation 5.15
import QtPositioning 5.15
ApplicationWindow {
id: window
width: 800
height: 600
visible: true
title: qsTr("Map")
header: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
text: qsTr("Reset")
onClicked: {
routeQuery.clearWaypoints()
routeModel.reset()
}
}
}
}
Plugin {
id: mapPlugin
name: "osm"
}
RouteQuery {
id: routeQuery
}
RouteModel {
id: routeModel
plugin: mapPlugin
query: routeQuery
autoUpdate: false
}
Map {
id: map
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 14
MapItemView {
model: routeModel
delegate: MapRoute {
route: routeData
line.color: "blue"
line.width: 5
smooth: true
opacity: 0.8
}
}
MapItemView {
model: routeModel.status == RouteModel.Ready ? routeModel.get(0).path : null
delegate: MapQuickItem {
anchorPoint.x: pathMarker.width / 2
anchorPoint.y: pathMarker.height / 2
coordinate: modelData
sourceItem: Rectangle {
id: pathMarker
width: 8
height: 8
radius: 8
border.width: 1
border.color: "black"
color: "yellow"
}
}
}
MapItemView {
model: routeQuery.waypoints
delegate: MapQuickItem {
anchorPoint.x: waypointMarker.width / 2
anchorPoint.y: waypointMarker.height / 2
coordinate: modelData
sourceItem: Rectangle {
id: waypointMarker
width: 10
height: 10
radius: 10
border.width: 1
border.color: "black"
color: "red"
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
routeQuery.addWaypoint(map.toCoordinate(Qt.point(mouse.x,mouse.y)))
routeModel.update()
}
}
}
}
I'm Trying to make a Circular ListView with List Items arranged on Half Circle. it should look something like this:
I'm using Qt open source license and i cannot find a controller similar in QtControls.
Please any idea or suggestion ?
Thanks in advance
Here is a solution based on the link that folibis shared in the comments above using PathView to layout the items of a model along a PathArc.
import QtQuick
import QtQuick.Window
import QtQuick.Shapes
Window {
visible: true
width: 400
height: 400
Shape {
ShapePath {
strokeWidth: 2
strokeColor: "black"
fillColor: "lightgrey"
startX: 0
startY: 0
PathArc {
x: 0
y: 400
radiusX: 400
radiusY: 400
}
}
}
Shape {
x: 100
ShapePath {
strokeWidth: 2
strokeColor: "grey"
startX: 0
startY: 0
PathArc {
x: 0
y: 400
radiusX: 400
radiusY: 400
}
}
}
PathView {
x: 100
model: ["Apple", "Banana", "Cherry", "Dragonfruit", "Grapefruit", "Orange", "Papaya"]
delegate: Item {
width: 50
height: 50
Rectangle {
height: 50
width: 260
radius: 25
color: "lightgrey"
}
Rectangle {
id: circle
width: 50
height: 50
radius: 25
color: "darkgrey"
}
Text {
anchors.leftMargin: 10
anchors.left: circle.right
anchors.verticalCenter: parent.verticalCenter
text: modelData
font.pixelSize: 24
}
}
path: Path {
// Those 2 coordinates are a bit of hack to push down the first item on the actual arc
// so it won't stick out the top. There might be a better way of doing that
startX: 18
startY: 35
PathArc {
x: 0
y: 400
radiusX: 400
radiusY: 400
}
}
}
}
I have a question about different shift behavior with QtVirtualKeyboard on text and password fields.
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.VirtualKeyboard 2.1
import QtQuick.Window 2.0
Window {
id: window
width: 800
height: 480
color: "#F6F6F6"
MouseArea {
id: content
width: window.width
Column {
id: textEditors
spacing: 15
x: 12
y: 12
width: parent.width - 26
Label {
color: "#565758"
text: "Tap fields to enter text"
anchors.horizontalCenter: parent.horizontalCenter
}
TextField {
width: parent.width
placeholderText: "One line field"
inputMethodHints: Qt.ImhPreferLowercase
}
TextField {
id: passwordField
width: parent.width
echoMode: TextField.Password
placeholderText: "Password field"
// changes do not work
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhPreferLowercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
}
}
}
InputPanel {
id: inputPanel
z: 2
y: window.height
width: window.width
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: window.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
If I click on shift basic TextField, only first letter is a capital one. This is the right behavior. But on the echoMode: textInput.Password shift has a caps-lock functionality, which I don't want.
Is it possible to override it somehow, or is it a QT feature, which is not possible to change?
Thank you
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.VirtualKeyboard 2.1
import QtQuick.Window 2.0
Window {
id: window
width: 800
height: 480
color: "#F6F6F6"
visible: true
MouseArea {
id: content
width: window.width
Column {
id: textEditors
spacing: 15
x: 12
y: 12
width: parent.width - 26
Label {
color: "#565758"
text: "Tap fields to enter text"
anchors.horizontalCenter: parent.horizontalCenter
}
TextField {
width: parent.width
placeholderText: "One line field"
inputMethodHints: Qt.ImhPreferLowercase
}
TextField {
id: passwordField
width: parent.width
echoMode: TextField.Password
placeholderText: "Password field"
// changes do not work
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhPreferLowercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
onTextChanged: console.log(text)
}
}
}
InputPanel {
id: inputPanel
z: 2
y: window.height
width: window.width
property bool shiftActive: (InputContext == null) ? null : InputContext.shiftActive
property variant shiftKey: null
function findKey(parent) {
if (parent === null) {
return null
}
var children = parent.children
if (children === undefined || children === null) {
return null
}
var obj = null
for (var i = 0; i < children.length; i++) {
obj = children[i]
if (obj instanceof ShiftKey) {
return obj
}
obj = findKey(obj)
if (obj === null) {
continue
}
if (obj instanceof ShiftKey) {
return obj
}
}
return null
}
Timer {
id: timer
interval: 0
repeat: false
running: false
onTriggered: {
inputPanel.shiftKey.clicked()
}
}
Connections {
target: (InputContext.inputItem != null && InputContext.inputItem.echoMode === TextField.Password) ? InputContext.inputItem : null
onTextChanged: {
if (inputPanel.shiftActive) {
if (inputPanel.shiftKey == null) {
inputPanel.shiftKey = inputPanel.findKey(inputPanel.keyboard)
}
timer.start()
}
}
}
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: window.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
I've founded one drastic way. Now it won't go into caps-lock state, and works like shift. But there isn't an option to enable it somehow.
import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.VirtualKeyboard 2.1
import QtQuick.Window 2.0
Window {
id: window
width: 800
height: 480
color: "#F6F6F6"
MouseArea {
id: content
width: window.width
Column {
id: textEditors
spacing: 15
x: 12
y: 12
width: parent.width - 26
Label {
color: "#565758"
text: "Tap fields to enter text"
anchors.horizontalCenter: parent.horizontalCenter
}
TextField {
width: parent.width
placeholderText: "One line field"
}
TextField {
id: passwordField
width: parent.width
echoMode: TextField.PasswordEchoOnEdit
placeholderText: "Password field"
inputMethodHints: Qt.ImhAutoUppercase | Qt.ImhPreferLowercase | Qt.ImhSensitiveData | Qt.ImhNoPredictiveText
onTextChanged: {
InputContext.capsLock = false
}
}
}
}
InputPanel {
id: inputPanel
z: 2
y: window.height
width: window.width
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: window.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
The main problem that, that I wanted to the same behavior on the all inputs, not only in this example.
I want to create a chart like this in qt. I already searched and can not find a way to do it.
I also can not find a way to customize Barchart and look like this in Widget Based Applications
Easy in QML!
import QtQuick 2.0
import QtQuick.Layouts 1.1
Rectangle
{
width: 600
height: 300
ListModel
{
id: dataModel
ListElement { label: "C.A"; value: 37 }
ListElement { label: "C.B"; value: 58 }
ListElement { label: "C.C"; value: 16 }
ListElement { label: "C.D"; value: 5 }
ListElement { label: "C.E"; value: 95 }
ListElement { label: "C.F"; value: 10 }
ListElement { label: "C.G"; value: 27 }
ListElement { label: "C.H"; value: 2 }
}
Rectangle
{
height: 4
width: layout.width
anchors.top: layout.bottom
anchors.horizontalCenter: layout.horizontalCenter
color: "#bbbdbe"
}
RowLayout
{
id: layout
width: 400
height: 200
spacing: 0
anchors.centerIn: parent
Repeater
{
id: rpt
property int barWidth: layout.width / count
model: dataModel
delegate:
Rectangle
{
width: rpt.barWidth
height: layout.height
color: "transparent"
Rectangle
{
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: 3
height: (parent.height * value) / 100
color: "#448bbe"
Rectangle
{
color: "#448bbe"
radius: width / 2
width: 8
height: 8
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
}
}
Text
{
y: parent.height + 3
anchors.horizontalCenter: parent.horizontalCenter
text: label
}
}
}
}
}
Screenshot
Since the OP didn't specify he wants a solution for a Qt widgets based application [he did after editing the question], the answer is:
Create your own QWidget class. Override the paintEvent and paint in it with a QPainter. I think there's plenty of examples if you google it.
Like this: http://doc.qt.io/qt-5/qtwidgets-painting-basicdrawing-example.html
Or this: http://programmingexamples.wikidot.com/qt-qpainter-example
Or this: Draw on QWidget
You can go for Custom QQuickPaintedItem here you can find an Example.