Repeater {
model: myModel.buttonParameters
delegate: Button
{
width: 47
height: 47
contentItem: Text {
id: content
text: modelData.name
font.family: MyStyle.fontFamily
fontSizeMode: Text.Fit
font.pixelSize: 30
font.styleName: "Bold"
topPadding: height / 6
color: modelData.visibility ? MyStyle.colorFromSeriesName(this.text) : MyStyle.dividerColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle
{
anchors.fill: parent
radius: 4
color: MyStyle.backgroundColor
border.color:MyStyle.dividerColor
border.width: 2
}
onClicked: {
cntModel.visibilityOfChartChanged(modelData.name, "plot");
}
}
}
On the C++ side.
myModel.buttonParameters is a QList<MyModel*> , where MyModel is a class inherited from QObject.
Q_PROPERTY(QVariant buttonParameters READ buttonParametersList NOTIFY buttonParametersChanged)
QVariant buttonParametersList()
{
return QVariant::fromValue(m_buttonParametersList );
}
The problem is that with a certain change in the model
(the signal buttonParametersChanged is sent), namely,
if the number of objects was equal to one and after the update there
is also one object, but with different characteristics, no redrawing
takes place, the old button remains. Moreover, it somehow depends on
the runtime. Also, if I remove the line with color, the model will update.
The issue is that buttonParametersChanged is a signal that triggers on the assignment of a new container (QList<>) to buttonParameters. It doesn't trigger on changes to the contents of an existing QList<> assigned to that property.
However, you can always manually trigger buttonParametersChanged when you know you've modified the contents of the QList<> which should give you the effect you want.
Note, a QML ListModel or C++ equivalent would likely be more appropriate for this use case. Many QML components are designed to specifically integrate with them and handle the cases of container contents changing.
Related
Good evening,
We are programming a DaVinci DM8168 board with Qt5.7.1 and QtQuick2.7
we would like to pass a list of type:
QList from C ++ to QML
to be able to draw it asynchronously and above all efficiently. The list is modified by a thread on C ++.
We cannot state the update frequence of this list at all: it can be just one time per second or dozens! It depends by the target enviroment.
our solution (partially working) is the following:
On the C++ side we are using QPROPERTIES:
an extract of our code:
MyClass.h
class MyClass: public Observer
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> getFailuresHistory READ getFailuresHistory NOTIFY failuresHistoryListChanged)
QList<QObject*>getFailuresHistory(){return failuresHistory_;}
signals:
void failuresHistoryListChanged();
}
then my .cpp we emit the signal every time the list changes ( not true at all: if more elements of the list changes at the same time we emit the signal only once in order to optimize the application)
emit failuresHistoryListChanged();
Then on QML "side" we do the following:
Item
{
// ... other code
//DELAGATE
Component
{
id: failuresDelegateHistory
Item
{
id: listHistoryItem
width: listHistory.width
height: 10
anchors.leftMargin: 5
Row
{
Text{
id: failureIdHistory
width: 30
text: qsTr(modelData.codeFailure) + mainSettingsHandler.emptyString
color: mainWindow.currentStyleColor.colorRed
font.pixelSize: 16
horizontalAlignment: Text.AlignHCenter
}
Text {
id: descriptionFailIdHistory
width: (listHistoryItem.width-failureIdHistory.width)*0.5
color:mainWindow.currentStyleColor.colorWhite
maximumLineCount: 1
text: qsTr(modelData.descriptionFail) + mainSettingsHandler.emptyString
wrapMode: Text.WrapAnywhere
horizontalAlignment: Text.AlignJustify
font.pixelSize: 12//16
}
Text
{
id: columnTimeStampFailureHistory
width: (listHistoryItem.width-descriptionFailIdHistory.width-failureIdHistory.width- 2)
maximumLineCount: 1
text: modelData.dateFail + mainSettingsHandler.emptyString
wrapMode: Text.WrapAnywhere
color:mainWindow.currentStyleColor.colorWhite
font.pixelSize: 12
horizontalAlignment: Text.AlignJustify
}
}
}
}
//LIST
ListView
{
id: list1
y: labelHistory.y + labelHistory.height+5
width: (FileConfiguration.widthMenuArea - space - 5)*0.5-1-space
height: (270 - space) *itemsPerPage
model: myClass.getFailuresHistory//failuresHistoryTable.getFailuresHisory
delegate: failuresDelegateHistory
spacing: 10
enabled: false
}
// ... other code
}
OK;
as I said before this solution is partially working because is very, very slow!
When the:
emit failuresHistoryListChanged();
on c++ side is called, the list is drawn but the software is "slow". We have keepalive message that shall be set every 300 ms and that is not sent anymore for a couple of seconds.
We need a more efficent way to pass the list to the QML.
if we comment the emit the software works fine within its deadlines.
So our purpose is: we would like to pass in a very efficent way this list to QML. Do you have solutions that involve a modification of the code that we have shown you or new architectures? do you have any examples? we are not experts in qml.
If you need more information ask us.
We thank you in advance, we have been trying to solve the problem for a long time.
thanks
I'm looking for a simple way to make widgets for a touch-screen that will allow users to set the time and IP address on the computer running the code and provide a simple (uppercase Latin-alphabetic) name.
This question is not about how to actually set the system time or IP address; I'm just looking for information about how to make the graphical widgets themselves.
What I want is for each editable property (time, address, and name) to be divided into "scrollable" fields, where the fields for "time" are hours, minutes, possibly seconds, and AM/PM/24-hr, and the fields for address/name are the individual characters. Each field would have an arrow above and below it, and touching on an arrow would scroll through the valid values for that field.
I think this is a pretty common UX pattern, especially in meatspace (e.g. on alarm clocks), but just in case it's not clear what I'm trying to describe, here's an example with a user editing the "name" property:
^^^
BN
vvv
User presses "down" below the "N":
^^^
BO
vvv
User presses "down" below the empty space:
^^^^
BOA
vvvv
...and again on the same down-arrow:
^^^^
BOB
vvvv
I'm writing this using C++14 with Qt 5. (If worst comes to worst, I'd be open to writing a separate app using a different language and/or framework, but I'm not asking for framework suggestions here; if you have one, let me know and I'll open a corresponding question on Software Recommendations SE.)
I don't see anything in the Qt 5 widget library like this; most of the input widgets are text fields. QSpinBox looks somewhat promising, but the arrows are probably too small for my touchscreen, and using a separate spinbox for each letter would probably be confusing and ugly.
I don't really know enough about Qt or GUI-programming in general to feel confident trying to write my own widgets from scratch, but this interface looks simple enough that I would expect a couple lines of QML would get me well on my way.
ListView as well as PathView can produce the desired result with slightly different behaviors and slightly different performances. Differently from ListView, PathView is circular, i.e. elements can be iterated continuously by using just one of the selection controls. It is also easier to fully customize the behavior of the path in PathView via the PathAttribute type. Anyhow path customization seems not to be a required feature, according to the question.
If you implement the solution via a ListView you should ensure that just one element is shown and that any model is processed.
Component {
id: spinnnnnnnner
Column {
width: 100
height: 110
property alias model: list.model
property string textRole: ''
spacing: 10
Item {
width: 100
height: 25
Text { anchors.centerIn: parent; text: "-"; font.pixelSize: 25; font.bold: true }
MouseArea {anchors.fill: parent; onClicked: list.decrementCurrentIndex() }
}
ListView {
id: list
clip: true
width: 100
height: 55
enabled: false // <--- remove to activate mouse/touch grab
highlightRangeMode: ListView.StrictlyEnforceRange // <--- ensures that ListView shows current item
delegate: Text {
width: ListView.view.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 50
font.bold: true
text: textRole === "" ? modelData :
((list.model.constructor === Array ? modelData[textRole] : model[textRole]) || "")
}
}
Item {
width: 100
height: 25
Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 25; font.bold: true }
MouseArea {anchors.fill: parent; onClicked: list.incrementCurrentIndex() }
}
}
}
The checks over the model ensure that any type of model can be passed to the component. Here is an example using three very different models:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
ApplicationWindow {
visible: true
width: 400
height: 300
ListModel {
id: mod
ListElement {texty: "it1"}
ListElement {texty: "it2"}
ListElement {texty: "it3"}
}
Row {
Repeater {
id: rep
model: 3
delegate: spinnnnnnnner
Component.onCompleted: {
rep.itemAt(0).model = mod // listmodel
rep.itemAt(0).textRole = "texty"
rep.itemAt(1).model = 10 // number model
//
rep.itemAt(2).model = ["foo", "bar", "baz"] // array model
}
}
}
}
PathView implementation is not so different from the ListView one. In this case it is sufficient to define a vertical path and specify that just one one element is visible at a time via pathItemCount. Finally, setting preferredHighlightBegin/preferredHighlightEnd ensures that the visible element is centered in the view. The revisited component is the following:
Component {
id: spinnnnnnnner
Column {
width: 100
height: 110
property alias model: list.model
property string textRole: ''
spacing: 10
Item {
width: 100
height: 25
Text { anchors.centerIn: parent; text: "-"; font.pixelSize: 25; font.bold: true }
MouseArea {anchors.fill: parent; onClicked: list.decrementCurrentIndex() }
}
PathView {
id: list
clip: true
width: 100
height: 55
enabled: false // <--- remove to activate mouse/touch grab
pathItemCount: 1
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
path: Path {
startX: list.width / 2; startY: 0
PathLine { x: list.width / 2; y: list.height }
}
delegate: Text {
width: PathView.view.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 50
font.bold: true
text: textRole === "" ? modelData :
((list.model.constructor === Array ? modelData[textRole] : model[textRole]) || "")
}
}
Item {
width: 100
height: 25
Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 25; font.bold: true }
MouseArea {anchors.fill: parent; onClicked: list.incrementCurrentIndex() }
}
}
}
I have a TreeView
TreeView {
id: dndView
rowDelegate: Item {
height: 30
}
itemDelegate: dndDelegate
model: myModel
TableViewColumn {
title: "Name"
resizable: true
}
}
and its delegate which does work
Rectangle {
id: dragRect
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: 'gray'
width: dndView.width - 20
height: 30
Image {
id: menuItemImage
anchors.verticalCenter:parent.verticalCenter
source:model.CommandIcon
}
Text {
anchors.left:menuItemImage.right
anchors.verticalCenter:parent.verticalCenter
text:model.CommandTitle
font.pixelSize: 14
}
Roles in C++ file:
QHash<int, QByteArray> MenuTreeModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[TitleRole] = "CommandTitle";
roles[IconRole] = "CommandIcon";
return roles;
}
It displays text and icon correctly but nevertheless when I expand the items or close the application I have warnings:
qrc:/DraggableRectangle.qml:15: TypeError: Cannot read property 'CommandIcon' of null qrc:/DraggableRectangle.qml:20: TypeError: Cannot read property 'CommandTitle' of null*
What's wrong with it?
If the warnings only appear on specific events, it may mean the model is destroyed or not yet created when your QML view tries to use it.
E.g. if the model is destroyed before the view when you close the application, the view still tries to use a destroyed object for a short time (before it's destroyed too).
Try this :
text: model ? model.CommandTitle : ""
Its the anchor in the root of your delegate.
anchors.horizontalCenter: parent.horizontalCenter.
You can't anchor the left hand side of a TreeView's delegate. Remove that line and I bet it will work.
I have a QML textInput element like this:
TextBox.qml
FocusScope {
id: focusScope
property int fontSize: focusScope.height -30
property int textBoxWidth: parent.width * 0.8
property int textBoxHeight: 45
property string placeHolder: 'Type something...'
property bool isUserInTheMiddleOfEntringText: false
width: textBoxWidth
height: textBoxHeight
Rectangle {
width: parent.width
height: parent.height
border.color:'blue'
border.width: 3
radius: 0
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
}
Text {
id: typeSomething
anchors.fill: parent; anchors.rightMargin: 8
verticalAlignment: Text.AlignVCenter
text: placeHolder
color: 'red'
font.italic: true
font.pointSize: fontSize
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
TextInput {
id: textInput
anchors {
right: parent.right
rightMargin: 8
left: clear.right
leftMargin: 8
verticalCenter: parent.verticalCenter
}
focus: true
selectByMouse: true
font.pointSize: fontSize
}
Text {
id: clear
text: '\u2717'
color: 'yellow'
font.pointSize: 25
opacity: 0
visible: readOnlyTextBox ? false : true
anchors {
left: parent.left
leftMargin: 8
verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
onClicked: {
textInput.text = ''
focusScope.focus = true;
textInput.openSoftwareInputPanel()
}
}
}
states: State {
name: 'hasText'; when: textInput.text != ''
PropertyChanges {
target: typeSomething
opacity: 0
}
PropertyChanges {
target: clear
opacity: 0.5
}
}
transitions: [
Transition {
from: ''; to: 'hasText'
NumberAnimation {
exclude: typeSomething
properties: 'opacity'
}
},
Transition {
from: 'hasText'; to: ''
NumberAnimation {
properties: 'opacity'
}
}
]
}
I want to add autocomplete and suggestions like google search to this text box. Autocomple get data from database and database return a list of dictionaries by a pyside SLOT.(or c++ slot)
How I can do this work?
Take a look at this code: https://github.com/jturcotte/liquid/blob/master/qml/content/SuggestionBox.qml
I bet it will do the job.
Edit:
Code that linked above is somewhat complicated and requires C++ backend, so I simplified it and made pure Qml example application, that you can play with, edit a little and apply to your needs. Sources can be found here. Most important things there are:
This implementation of SuggestionBox that uses some sort of model as it's source for completing/suggesting something
Its signal itemSelected(item) will be emitted every time user clicks on item
Main component of application that binds its LineEdit component to SuggestionBox
Note that code is quite rough and written for a sake of example.
I was looking for something very similar: a QML autocomplete component built around QML TextField, rather than the lower-level, more flexible but also more work intensive TextInput as in the question.
Since I could not find that, I implemented it. If anyone wants to use it: it's licensed under MIT and available as part of an application I am developing. You find the component in src/qml/AutoComplete.qml, and the application may serve as usage example. Features:
highlighting of autocompleted characters in bold, as in Google Search
Key bindings (navigating with arrow keys, Return / Enter, Esc to close completion box, Esc Esc to unfocus)
uses a simple QStringList as model for now, with the application showing how to update the model with live SQL database queries when the next key is pressed
heavily documented code, so it should be easy enough to adapt
Let me know if this is useful, I might then package it as a Qt QPM package or even try to make it mature enough to be added to the QML UI library KDE Kirigami.
I want to use qml with master-detail interface, but i don't know how to pass current item to detail view right way. The ListView in master view uses C++ model (add-on of QSQLTableModel, it's work fine) and I see two ways to pass item:
Create C++ classes with fields with static name like QSqlRecord field names and pass it to qml with w->rootContext()->setContextProperty() (w is QDeclarativeView *), but now i don't use any classes like this and can change my database and qml views without changing c++ code, I would like to save it
Create a lot of properties in any detail qml like
Rectangle {
id: mainRect
property alias myFieldName: txt_nominal.text
Column {
width: parent.width
Text {
id: txt_nominal
font.bold: true
}
}
}
and set this properties from c++ code by setting w->rootContext()->setContextProperty(record.fieldName(i),record.field(i).value()); (record - QSqlRecort at current row)
Is there any easier way to solve my problem?
PS The code I wrote above is not checked for accuracy, and is written to make it more clear what I mean
UPD
Maybe it will be useful for somebody, I discovered 3-rd way, rather, the modification of second - you can wrap fields into QVariantMap and pass only one object to qml. This is exactly what I wanted
in cpp:
QVariantMap testObject;
testObject["testField"]="first string from cpp";
testObject["testField2"]="second string from cpp";
rootContext()->setContextProperty("testObject",testObject);
in qml:
Text {
id: simpleTextt
text: testObject.testField
anchors.centerIn: parent
}
You could use the isCurrentItem property of the delegate to pass the data from ListView delegate to your details qml. That way you could get away without have to add additional c++ code. This is basically your second approach but without c++. You also do not need to add many properties as long as each of your QML elements that you want to change have an id.
If you have a number of different QML for different details views you would also have to use the Loader to load the appropriate details QML.
Just a toy example assuming that you have only one details template for all of your elements in the list (as mentioned above if that is not the case than you can use loader instead of detailsRect):
Rectangle {
width: 300; height: 400
Rectangle {
id: detailsRect
anchors.right: parent.right
width: 100
height: 500
color: "blue"
Text {
id: detailsText
text: ""
}
}
ListView {
id: list
anchors.fill: parent
model: 20
delegate: Rectangle {
color: ListView.isCurrentItem ? "red" : "green"
width: 40
height: 40
Text {
text: index
}
ListView.onIsCurrentItemChanged: {
if(ListView.isCurrentItem)
{
detailsRect.color = "yellow"
detailsText.text = index
}
}
MouseArea {
anchors.fill: parent
onClicked: {
list.currentIndex = index
}
}
}
}
}