I am trying to display the following number, 40.0 by using this code:
Text{
id: txt
property int number: 40.0
text: number
x:94
y:213
font.family: "Helvetica"
font.bold:true
font.pointSize: 60
color:"#fff"
}
For some reason, only 40 is displaying, and no decimal. These are the other things I've tried so far:
1.
str = QString::number(flat, 'f', 40.0);
2.
var german = Qt.locale("de_DE");
var d;
d = Number.fromLocaleString(german, "40.0") // d == 40.0
3.
Text {
Number(40.0).toLocaleString(Qt.locale("de_DE"))
}
Since you are working with real numbers, type should be real.
In QML you can use JS's toFixed() to format a number using fixed-point notation.
To bind property value to a function you should do as follows:
Text {
id: txt
property real number: 40.1
text: {
return txt.number.toFixed(1)
}
}
Related
I am Learning QML newly and am trying to resolve the following warning:
[warning] main.qml:392:25: Unable to assign [undefined] to double
main.qml
Rectangle{
id: rect
...
readonly property real scale0: (rotateRepeater.yPointM - rotateRepeater.yPointT) / height
readonly property real scale1: (rotateRepeater.yPointB - rotateRepeater.yPointM) / height
readonly property real yScale: [scale0, scale1][index] // Warning is in this line
}
The error is being show for property yScale.
Method 1 that I tried was -
readonly property real yScale: Binding {
when: onScale0Changed && onScale1Changed
yScale: [scale0, scale1][index]
}
and got the following error :
"cannot assign to non-existent property "yScale"
I tried Googling and found out two possible answers
-https://stackoverflow.com/questions/52290153/qml-unable-to-assign-undefined-to
-https://stackoverflow.com/questions/73306793/qml-failure-accessing-properties-with-error-unable-to-assign-undefined-to
but, I was unable to solve the warning.
Any help here is much appreciated.
Thanks in Advance.
Some further explorative testing is required for you to narrow down the cause of the undefined values. Here's one way how you can achieve further debugging:
Rectangle{
id: rect
...
readonly property real scale0: (rotateRepeater.yPointM - rotateRepeater.yPointT) / height
readonly property real scale1: (rotateRepeater.yPointB - rotateRepeater.yPointM) / height
readonly property real yScale: getYScale(scale0, scale1, index)
function getYScale(scale0, scale1, index) {
console.log("scale0: ", scale0, "scale1: ", scale1, "index: ", index);
return [scale0, scale1][index];
}
}
I made an assumption that I believe one of your inputs has an undefined value, and that undefined value was, possibly momentary. Doing the above code will not only help you see which parameter it is but, that function may get triggered multiple times so you can see it transition from an undefined state to a defined state. Then, you can build error handling in your function, e.g.
function getYScale(scale0, scale1, index) {
console.log("scale0: ", scale0, "scale1: ", scale1, "index: ", index);
if (scale0 === undefined) scale0 = 0;
if (scale1 === undefined) scale1 = 0;
if (index === undefined) index = 0;
let result = [scale0, scale1][index];
if (result === undefined) result = 0;
return result;
}
Doing the above actually has a lot of paranoid edge case handling, but, it should give you a deeper understanding of the intermediate values used in your property binding and help tailor a fix. Once you get the problem solved, you can discard the above function and incorporate the result back into a 1 line solution, perhaps something like:
Rectangle{
id: rect
...
readonly property real scale0: (rotateRepeater.yPointM - rotateRepeater.yPointT) / height
readonly property real scale1: (rotateRepeater.yPointB - rotateRepeater.yPointM) / height
readonly property real yScale: [scale0 ?? 0, scale1 ?? 0][index ?? 0] ?? 0
}
As I said above, you probably set incorrect index that is out of range. Check this code:
Item {
id: item
property int index: 0
property double value: [1.0, 2.0, 3.0][index]
onValueChanged: {
console.log(value)
}
Timer {
interval: 1000;
running: true;
repeat: true
onTriggered: item.index = Math.round(Math.random() * 3)
}
}
The output is possible will be:
qml: 1
qml: 3
qml: 2
main.qml:18:9: Unable to assign [undefined] to double
qml: 3
qml: 2
qml: 3
main.qml:18:9: Unable to assign [undefined] to double
Do this:
Rectangle{
id: rect
...
readonly property real scales: [(rotateRepeater.yPointM - rotateRepeater.yPointT) / height ,
(rotateRepeater.yPointB - rotateRepeater.yPointM) / height]
readonly property real yScale: scales[index] // Warning is in this line
}
Property evaluations are arbitrarily ordered, so the eval of yScale may happen when either scale0 or scale1 are not evaluated yet. There is also a possibility that the index value is out of range, as mentioned in other answers.
Quote from the Qt documentation:
Syntactically, bindings are allowed to be of arbitrary complexity. However, if a binding is overly complex - such as involving multiple lines, or imperative loops - it could indicate that the binding is being used for more than describing property relationships. Complex bindings can reduce code performance, readability, and maintainability. It may be a good idea to redesign components that have complex bindings, or at least factor the binding out into a separate function. As a general rule, users should not rely on the evaluation order of bindings.
I have some QML below where I want to always make sure that values up to the hundreth decimal place is always displayed. Currently, when you click on the big red button the number at the top will go up by 1. If the button is pressed and held, there is a timer that will help repeatedly increment the number while the button is held.
I am working with a float data type, and every time there is some sort of increment called, the trailing 0s will disappear. However, I need those 0s to be displayed. So I tried to insert some Javascript into the onClicked and onPressAndHold signals, but the line I have commented out displayVal.text = result will cause the numbers to stop incrementing. Is my logic faulty, or is there a better way to add trailing 0s? I might as well also mention that just appending ".00" to the end of everything would not work because in the future, I will be dealing with decimal numbers that aren't whole, like 10.20 or 11.89. I am not too familiar with Javascript either so my current code snippet to try to add 0's came from another forum post. I know it partially works at least because of the print statement.
If possible, I would like to avoid as much complex Javascript as possible; complex as in needing functions or something. If this could even be handled in the Python code that would be even better.
main.qml:
import QtQuick 2.14
import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.15
Item {
visible: true
width: 600; height: 400
id: itemView
signal startIncrement()
signal stopIncrement()
signal incrementOnce()
ColumnLayout{
Repeater{
model: modelManager.model
ColumnLayout{
Text {
id: displayVal
text: model.display
font.pixelSize: 30
}
Rectangle{
id: incrementButton
color: "red"
width: 250
height: 100
MouseArea {
anchors.fill: parent
onClicked: {
incrementOnce()
var num = parseFloat(displayVal.text)
var result = num.toFixed(Math.max(2, (num.toString().split('.')[1] || []).length));
// displayVal.text = result
console.log("OnClick Formatted #: " + result + " DisplayVal: " + displayVal.text)
}
onPressAndHold:{
startIncrement()
var num = parseFloat(displayVal.text)
var result = num.toFixed(Math.max(2, (num.toString().split('.')[1] || []).length));
// displayVal.text = result
console.log("Started increment")
}
onReleased:{
stopIncrement()
console.log("Stopped increment")
}
}
}
}
}
}
}
main.py:
import os
import sys
from pathlib import Path
sys.path.append(os.path.join(os.path.dirname(sys.path[0]), ".."))
from PySide6.QtCore import QUrl, Qt, QCoreApplication, QTimer, QObject, Property
from PySide6.QtGui import QGuiApplication, QStandardItemModel, QStandardItem
from PySide6.QtQuick import QQuickView
CURRENT_DIRECTORY = Path(__file__).resolve().parent
DisplayRole = Qt.UserRole
class ModelManager(QObject):
def __init__(self):
super().__init__()
self._model = QStandardItemModel()
self._model.setItemRoleNames({DisplayRole: b"display"})
item = QStandardItem()
item.setData("10.00", DisplayRole)
self._model.appendRow(item)
self.timer = QTimer()
self.timer.setInterval(100)
#Property(QObject, constant=True)
def model(self):
return self._model
def start_increment(self):
self.timer.timeout.connect(self.increment)
self.timer.start()
def increment(self):
presentVal = float(self._model.item(0).data(DisplayRole)) +1.0
self._model.item(0).setData(presentVal, DisplayRole)
print(presentVal)
def stop_increment(self):
if self.timer.isActive():
self.timer.stop()
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
url = QUrl.fromLocalFile(os.fspath(CURRENT_DIRECTORY / "main.qml"))
def handle_status_changed(status):
if status == QQuickView.Error:
QCoreApplication.exit(-1)
modelManager = ModelManager()
view.rootContext().setContextProperty("modelManager", modelManager)
view.statusChanged.connect(handle_status_changed, Qt.ConnectionType.QueuedConnection)
view.setSource(url)
root = view.rootObject()
root.startIncrement.connect(modelManager.start_increment)
root.stopIncrement.connect(modelManager.stop_increment)
root.incrementOnce.connect(modelManager.increment)
view.show()
sys.exit(app.exec())
The problem is that when you do displayVal.text = result you stop the ability to automatically update the displayed text from the model. The result is that, while the model is updated, the display text is not, as you set a static value for that.
The solution is to use toFixed directly in the text definition:
Text {
id: displayVal
text: model.display.toFixed(2)
font.pixelSize: 30
}
And set the value of the item as a number, not a string:
item.setData(10., DisplayRole)
This obviously means that you must remove all the computation you're trying to achieve after incrementOnce and startIncrement, since it's unnecessary any more.
Note that:
you shouldn't overwrite the existing display role, and you should probably use another name if absolutely necessary; in reality, you can avoid setting the role names and use the standard Qt.DisplayRole to set the number (again, not as a string);
connecting the timer in the start_increment function is an error, as it will result in calling increment everytime you connect it: if you press the button twice, the increment will be done twice, if you press it three times, it will increment by 3, etc. Move the connection in the __init__;
So what if I have a the following property:
property string someString: qsTr("Text")
and I have the following function:
function isEqual(toThisString) {
if (someString === toThisString) {
return true
} else
return false
}
}
My question is what if my application has a translation to some other language other than English. Will the function still compare to "Text" or will it compare to the translated string?
An example with Qt:
const QString english = "Hello world");
const QString spanish = QObject::tr(english); // Hola mundo
Is equal?
qDebug() << "Is equal? " << (english == spanish);
qDebug() << "Is equal? " << (QObject::tr(english) == spanish);
Result:
Is equal? false
Is equal? true
You have to translate every string, and should be the same string exactly. It's dificult to manage, looks like a recipe for disaster.
You have to use an alternative data field to compare values, avoid strings. Example for a QCombobox:
enum Countries {EEUU = 0, Spain, France};
QCombobox* combo = new QCombobox;
combo->addItem(QObject::tr("EEUU"), EEUU);
combo->addItem(QObject::tr("Spain"), Spain);
combo->addItem(QObject::tr("France"), France);
To get the selected country or compare with a field in the combo model, you could use currentData() function or something like this.
I can assign values to a variable property but not to an array here? ... values aren't kept.
Column {
id: table
property variant aa: [false, false] //issue... later
property variant bb: false //this works
...
Button {
anchors.top: parent.top
anchors.topMargin: 0//40
anchors.right: parent.right
anchors.rightMargin: 0//50
corpFarmAware: true
text: tr_NOOP("Next")
onClicked: {
table.aa[0] = false;
table.aa[1] = true;
cb0.checked = table.aa[0];//issue of arrays ??
cb1.checked = table.aa[1];
table.bb = true;
cb2.checked = table.bb;//WORKS
}
}
According to the documentation on variant in QML:
While this is a convenient way to store array and map-type values, you must be aware that the items and attributes properties above are not QML objects (and certainly not JavaScript object either) and the key-value pairs in attributes are not QML properties. Rather, the items property holds an array of values, and attributes holds a set of key-value pairs. Since they are stored as a set of values, instead of as an object, their contents cannot be modified individually
I think you should use var to store your properties.
qt gives me an error when I try to put a wildcard in my select query. I need to compare a variable with a query result but putting in wildcards doesnt seem to work.please help!
Text{
id: searchresults
text: ""
font.pixelSize: 45
color: "black"
opacity: 1
x: 10
function findHerb(value) {
var db = getDB();
db.transaction(
function(tx) {
var rec = tx.executeSql('SELECT name FROM Herbs WHERE name LIKE "?%"', [value]);
var r = "";
for(var i=0; i<rec.rows.length; i++) {
r+= rec.rows.item(i).name + "\n"
}
text = r;
}
)
}
}
This is kind of a hacky suggestion as it obviously doesn't address why your exact approach isn't working correctly. But my guess is that the % is somehow causing an issue when it expands the parameters into the statement.
Try something like this...
var likeVal = value + "%";
var rec = tx.executeSql('SELECT name FROM Herbs WHERE name LIKE "?"', [likeVal]);
Also, my other guess is that the args are parameterized for you with quotes already and you are making it double quote. Here is another possibility:
var rec = tx.executeSql('SELECT name FROM Herbs WHERE name LIKE ?%', [likeVal]);