PDA

View Full Version : Getting a specific list element



schu4647
11th May 2017, 21:21
I am new to programming but I volunteered to make a GUI for my job. I have a nice GUI going that triggers GPIO on the PI using QT Quick. There is one more feature I really want. I have a model with List Elements being displayed using pathview. I want specific details about the item in the current index. In this case its a recipe system so when you scroll through all the recipes in pathview, I want the recipe for the item that is highlighted to show up at the bottom of the screen. My problem is if index 3 is selected I don't know how to get ingredient 1, ingredient 2, etc for index 3. If I called the pathview id: view, is there some sort of view, element 3, ingredient 1 lookup I can use?

import QtQuick 2.0

In Recipes.qml
ListModel {
id: recipe
ListElement { name: "Beef Rissotto"
icon: "file:/Beef_Risott.jpg" }
ListElement { name: "Southwestern Alfredo"
icon: "file:/Southwestern_Alfredo.jpg" }
ListElement { name: "Chef's chocolate salty balls"
icon: "file:/Beef_Risott.jpg" }
ListElement { name: "Other good stuff"
icon: "file:/Southwestern_Alfredo.jpg" }

}


In main.qml


Component {
id: appDelegate
Item {
width: 400; height: 400; z:1;
scale: PathView.iconScale

Image {
id: myIcon
y: 0; anchors.horizontalCenter: parent.horizontalCenter
source: icon

}
Text {
anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }

text: name
font.pixelSize: 40
}

MouseArea {
anchors.fill: parent
onClicked: view.currentIndex = index



}
}
}

Component {
id: appHighlight
Rectangle { width: 400; height: 400; color: "yellow" }
}

PathView {
id: view
anchors.fill: parent
//highlight: appHighlight
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
focus: true
model: recipe
delegate: appDelegate
path: Path {
startX: 100
startY: 120
PathAttribute { name: "iconScale"; value: 0.2 }
PathLine { x: 400; y: 120; }
PathAttribute { name: "iconScale"; value: 0.4 }
PathLine { x: 700; y: 120;}
PathAttribute { name: "iconScale"; value: 0.2 }

}



Image {
id: image
x: 0
y: 0
width: 800
height: 480
opacity: 1
anchors.bottom: parent.bottom
source: "file:/background.jpg"
}

Rectangle{width: parent.width; height:parent.height/2.5 ; color: "black";anchors.bottom:parent.bottom;

I would like to put the specific ingredients here

}

d_stranz
12th May 2017, 06:41
I think you are confusing the data you want to display with how you want to display it. Most developers would not hard-code the data into lists the way you are doing, they would define a data structure that could be stored in a disk file and updated independently of the GUI. The contents of the disk file are read in at run time and used to build the lists displayed on screen.

Something that would work well for a simple thing like a recipe file would be XML, something like this:



<?xml version="1.0" ?>
<RecipeList>
<Recipe name="Beef Risotto" serves="4" difficulty="moderate">
<IngredientsList>
<Ingredient name="Arborio rice" quantity="200" unit="gm" />
<Ingredient name="Onion" quantity="1" unit="each" />
<Ingredient name="Beef" quantity="300" unit="gm" />
etc.
</IngredientList>
<Directions>
Dice onion.
Slice beef into 1cm cubes.
Put beef broth into a saucepan and heat to simmer over medium-low heat.
Melt butter in a large frying pan.
Add the beef cubes and stir until browned.
Remove the beef cubes.
Add onions and fry until slightly browned.
Add rice and stir to coat with butter.
After 1 - 2 minutes, add the white wine and stir continuously until it is absorbed.
Add the broth in 250 ml portions, stirring continuously after each addition until completely absorbed.
When the rice is nearly cooked (it will be mostly translucent), return the beef to the pan and continue stirring until all the broth is absorbed.
Stir in the cheese, adjust the seasonings, and serve immediately.
</Directions>
</Recipe>
<Recipe name="Southwestern Alfredo" serves="6" difficulty="easy">
etc.
</Recipe>
</RecipeList>


You can use the Qt Quick XmlListModel (https://doc.qt.io/qt-5/qml-qtquick-xmllistmodel-xmllistmodel.html) to read this from the disk and hold it in memory.

After you read in this document, you can retrieve the names of recipes to display in your first list. Say you've named your XmlListModel with the id: recipeModel:



import QtQuick 2.0
import QtQuick.XmlListModel 2.0

XmlListModel {
id: recipeModel
source: "file://recipeList.xml"
query: "/RecipeList/Recipe"

XmlRole { name: "recipeName"; query: "@name/string()" }
XmlRole { name: "feeds"; query: "@serves/string()" }
XmlRole { name: "easeOfPrep"; query: "@difficulty/string()" }
}

ListView {
id: recipesView
width: 180; height: 300
model: recipeModel
delegate: Text { text: recipeName }
}


In a second list view, you will use the index of the currently-selected item in the first list view to pull the names of the ingredients from the selected recipe:



XmlListModel {
id: ingredientsModel
source: "file://recipeList.xml"
query: "/RecipeList/Recipe[" + recipesView.currentIndex + "]/IngredientsList/Ingredient"

XmlRole { name: "ingredientName"; query: "@name/string()" }
}

ListView {
id: ingredientsView
width: 180; height: 300
model: ingredientsModel
delegate: Text { text: ingredientName }
}


I haven't tested this; you will probably have to play with it to make it work in your own GUI. I am not sure if you must specify the file name again for the second XmlListModel, or whether you can set its "source:" to be recipesModel. I can't find details in the QML documentation, which is pretty poor on this topic, as usual. You'll just have to try it.

The important things are:

1 - There is a separation between the data you want to display and the code you use to implement the GUI
2 - The second list view is synchronized to the first using the "currentIndex" of the selected item from the first view

Have fun.

schu4647
12th May 2017, 13:44
Thank you so much for that very thorough answer. I will give it a try. I am struggling to find a goo tutorial. Most are hung up on the Widgets and the ones that do QML just show the basics. Any recommendations? I have tried youtube and Udemy.

d_stranz
12th May 2017, 19:53
Unfortunately, I find that the documentation on Qt Quick and QML to be sadly insufficient. What's there is either so basic as to be useless or so involved that it is impossible to understand. When I was looking at XmlListModel last night, the link takes you to a generic List model page that describes all of the various QML list models. The links on -that- page for more information on XmlListModel take you right back to the same page.

The best I can suggest is to go to the Qt Quick Examples and tutorials (https://doc.qt.io/qt-5/qtquick-codesamples.html) and start studying things that sound like they might be useful.

schu4647
13th May 2017, 04:18
So it turns out I am using QtRpi which uses QT5.7 cross compiled from a Linux machine to a raspberry pi. XML is not a supported module.

I do think I found the answer in the stocqt project. In StockListView.qml they do the following.

First they declare these variables up top

property string currentStockId: ""
property string currentStockName: ""


Then they use a model.get to get the data for the active element.
onCurrentIndexChanged: {
if (currentItem) {
root.currentStockId = model.get(currentIndex).stockId;
root.currentStockName = model.get(currentIndex).name;
}
}

I was playing with it and it pulls the element that is active in currentIndex. I can't try it with my stuff until Monday. My wife will kill me if I mess with this on the weekend.

d_stranz
13th May 2017, 19:56
XML is not a supported module.

Surprising. The Qt source code is available, so you could try building the XML module yourself if you are adventurous. If you are more adventurous, you can install a native compiler toolchain on your RPi and build it there instead of cross-compiling.

In any case, you don't really need XML, it is just a convenient way to store hierarchical data in a readable and easily parseable way. You could also use SQLite to make a recipe database; SQLite is implemented as a single source / header pair and you simply compile it as part of your project. You don't even need the database support from Qt.

A database might contain three tables:

- A Recipes table, with fields for RecipeID, Name, Description, and whatever else
- An Ingredient table, with fields for Ingredient ID, Name (this table is independent of recipe - the same ingredient can appear in more than one recipe)
- A RecipeIngredients table linking RecipeID and IngredientID, with fields for Quantity and Unit

From the list view containing recipes, when the user clicks a recipe line, retrieve the RecipeID by searching the Recipes table for the Name.
From the RecipeIngredients table, retrieve all IngredientID entries where RecipeID matches
From the Ingredients table, retrieve the Name for each matching IngredientID and display them in the ingredients list view.


My wife will kill me if I mess with this on the weekend.

"Honey, I'm trying to improve my skills so I can find a better job and make more money so we can have better vacations... I don't have the time to do this during the week."

high_flyer
14th May 2017, 23:20
in addition to d_stranz's answers:

I would would pack the whole data in to one treemodel.
Each recipe is a child of the root element - and it contains the name of the recipe.
Each recipe element is a parent to ingredient elements.

Also, remember that how data is stored and how it is in memory can vary, and it often does differ.
I'd do what would be the easiest thing to do, for me.
If you are very strong with XML, do it in XML, if you are strong with MySQL, you can do in MySQL, etc
I would do it in a text file, using QSettings.
Its easy, and you can open it with any editor and still know what the data is, and even mend it manually if needed.
Well, I am a C++ guy, so if you have found something else that works for you better with QML, stick with it, here is just another view on things, maybe it can give you some ideas, maybe not.

As I said, in memory I'd use a tree-model and in your recipe list I'd simply show the first level of children (the recipe names).
For each selected recipe I'd get the list of its children and show that in a list of ingredients at the bottom.

Since you have asked about QML documentation here is a good link if you don't know it yet:
https://qmlbook.github.io/
And for your case you probably would like to jump to:
https://qmlbook.github.io/en/ch06/index.html