I'm using RangeSlider for my project in QML. My range slider code is as follows:
RangeSlider {
id: rangeSLider
first.value: 0.0
second.value: 1.0
anchors.horizontalCenter: parent.horizontalCenter
width: 275
onFirstChanged: console.log("Change")
}
When I run this code, I get an error. The error is:
Cannot assign to non-existent property "onFirstChanged"
I want to change a text in a parent QML file. So I thought I'd use onFirstChange and onSecondChange functions to do that, but it did not work.
How can I do this?
For future reference, please provide a MCVE. It helps other people help you a lot easier. In this case, I'd like to see a QML snippet I could look at using qmlscene (including showing what imports you use, this is important!)
Assuming you are using QtQuickControls 2's RangeSlider, The first and second properties are constant. This means that those values themselves do not change, but rather, the members of those properties (e.g. first.value) change instead. So you want to connect the change signal to the first/second/ instance, rather than on the RangeSlider itself, something like this:
RangeSlider {
from: 1
to: 100
first.value: 30
second.value: 70
Connections {
target: first
onValueChanged: console.log("first.value changed!")
}
}
Here is the syntax for RangeSlider of Qt 5.11 (Qt QuickControls 2.4):
RangeSlider {
from: 0
to: 100
first.value: 25
second.value: 75
first.onValueChanged: console.debug("RangeSlider first value is " + first.value)
second.onValueChanged: console.debug("RangeSlider second value is " + second.value)
}
Source: http://doc.qt.io/qt-5.11/qml-qtquick-controls2-rangeslider.html
Related
I am writing an App and I already managed to resolve some issues, with the latest one being the Screen.Width/Height for adjusting the window size dynamically on different monitors (I use laptop, phone, PC, it's simply convenient).
To write the code efficiently and nicely, I want to obtain that specific information and put it into a single set of 2 "variables", that would then hold this information.
I tried assigning the ApplicationWindow object an id: mainWindow, in order to call upon it from a different QML file, to obtain the property value as:
mainWindow.height, mainWindow.width
I then was told to use another approach, custom QML properties, that are declared like:
property (type) (name): (value)
I then followed the advice and declared those properties in Main.qml (with AppWindow) and it does work. The properties of AppWindow (ApplicationWindow) contain the width and height of the Screen multiplied by a specific coefficient.
Then those variables are accessed by the object itself, drawing the App Window as I want it to be.
The problem is that this approach was meant to solve the issue of sharing code across .QML files, and it doesn't
[go down]
ApplicationWindow {
id: mainWindow
//Wide screen support
//Screen.desktopAvailableWidth / 4
//Screen.desktopAvailableHeight / 6
//The below is monitor cross-compatible (phone, PC, laptop)
property int globalWidth: Screen.width / 2
property int globalHeight: Screen.height / 3
width: globalWidth
height: globalHeight
visible: true
title: qsTr("Redacted")
//setWindowIcon(QIcon(":/path/to/icon.png"));
The piece of code below resides in main.qml. It is called upon from that file. Just ignoring the IDs (IDs supposedly [as stated by users/docs] can't be accessed outside of local scope...), the properties (especially custom properties) should be accessible inside Page1, Page2, Page3 etc.
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page1Form {
}
Page2Form {
}
Page3Form {
}
}
The below is the Page2Form.qml file that is called upon (as class definition) in main.qml.
The properties declared in parent objects in main.qml (imo) should be inherited by child objects (imo).
Page {
id: localPage2
width: globalWidth
height: globalHeight
Rectangle {
id: rectangle
x: (localPage2.width / 2) - (width / 2)
y: (localPage2.height / 4) - (height / 2)
width: localPage2.width / 3
height: localPage2.height / 6
color: "#ffffff"
border.color: "#a45c5c"
border.width: 2
TextInput {
id: textInput
x: 0
y: 0
width: localPage2.width / 3
height: localPage2.height / 6
text: qsTr("Redacted")
font.pixelSize: 12
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.weight: Font.Normal
focus: true
}
}
Ok, so what is the problem?
I can try calling those properties as:
mainWindow.globalHeight
globalHeight
mainWindow.height
etc.
They won't be accessed. The form editor will provide me preview of Page object that has 0 size.
The page does render eventually (when compiled and ran), but there is an issue in passing (accessing) the value of that property.
As you can notice, both IDs and custom properties seem to work just fine locally.
Update:
I haven't fixed that issue, I also tried using aliases (references) and the "foreign" QML file will still fail to be assigned proper size (Page width and height).
I then put another custom property of string type with some text in it, I then managed to access that property in Page2.qml and the property is originally in main.qml.
It's bugged, or I have no idea what it is.
I tried 3 approaches:
ID (it's not global as it turns out)
Custom property (kind of works, just not with Screen size...)
Aliases on object's default properties
(property alias globalWidth: mainWindow.width
property alias globalHeight: mainWindow.height)
I am using a Slider with below properties set for it -
Item {
id: root
signal sliderMoved
property alias value: control.value
QtControls.Slider {
orientation: Qt.Vertical
snapMode: QtControls.Slider.SnapOnRelease
from: 59.0
to: 86.0
stepSize : 1.0
onMoved: root.sliderMoved()
}
}
From user QML where this slider is used thru loader
Connections {
target: sliderLoader.item
onSliderMoved: {
console.warn(sliderLoader.item.value);
}
}
But eachtime when the slider is dragged, it returns the value with decimal points. I am expecting value like 59,60,61, 62.....till 86 not like 59.12, 63.45 etc. How to get rid of these decimal points from the returned slider value when the slider is moved
The reason you are seeing decimals is because your slider snapMode is set to snapOnRelease. This means the slider value will not honor your step size until the user releases the slider. Changing to snapAlways will fix this, but it will cause the slider to appear "choppy" during use. If you would still like to use snapOnRelease; I see two options for you moving forward.
Instead of using onMoved to emit sliderMoved, you may use.
onPressedChanged: {
if (!pressed) {
root.sliderMoved()
}
}
This way the signal will only be emitted once the slider has been released/snapped, and you will log the whole number value the user has decided upon.
If it is necessary to log every change the user makes as you have it written currently, round up or down as desired in your log statement to prevent decimals in the logs.
In referencing an item's properties from outside its parent component, I can get a "Cannot assign to non-existent property" error that seems to depend on some compile time order-of-operations.
I have created a small example app that shows some various ways to assign a color to this property, where direct assignment fails, but similar assignment to default properties works, or even later assignment works.
Here is my main.qml:
import QtQuick 2.7
import QtQuick.Window 2.12
Window {
id: application_window
visible: true
width: 640
height: 480
Thing {
// colors.backgroundColor: "red" // Direct assignment of custom property: Fails
// thing.color: "red" // Direct assignment of default property: Works
// Component.onCompleted: colors.backgroundColor = "red" // Run time assignment of custom property: Works
}
}
and a file called Thing.qml in the same dir:
Item {
id: root
height: 50
width: 50
property alias colors: colors
property alias thing: thing
Rectangle {
id: thing
anchors.fill: root
color: colors.backgroundColor
}
Item {
id: colors
property color backgroundColor: "blue"
}
}
By individually uncommenting the lines in main.qml, you can see that directly assigning colors.backgroundColor does not work, but the other ways of changing the color do work, even assigning colors.backgroundColor at runtime. I have also moved the 'colors' Item to a different file, which allows direct assignment (I guess the backgroundColor becomes considered like a default property in this case). Is there any way to directly assign the colors.background color without a separate file or waiting until runtime?
Is there any way to directly assign the colors.background color without a separate file or waiting until runtime?
No.
When you are declaratively setting (using it in the left hand side of a binding) a sub property (property of a grouped property) like foo.bar, the QML engine can only do so if the type of foo has a bar property.
In your example when doing thing.color, thing's type is Rectangle so it does have a color property.
When doing colors.backgroundColor, I guess the type of colors is Item and not the implicit type defined in Thing.qml.
When you are creating a 'colors' Item in a different file, the QML engine is aware of the explicit type of the object and the binding then works.
One could argue that the engine could use the implicit type for the alias property, but I'm not sure if it's the correct way, you are essentially exposing an inline implementation detail to the outside.
You could alway open a bug about that, at least to clarify things even if the behaviour is not changed.
In my application I have a global system that handles navigation between "screens". In QML I can simply call something like:
appNavigation.show("MyScreen.qml", NavigationType.FADE)
this calls a C++ part of the code which handles the current stack of screens and uses signals to report back to QML to do the actual animation. At the end in QML some Loader will load the input qml ("MyScreen.qml" in this case) and show it as defined.
My issue here is how to inject data into newly loaded screen. Essentially I would like to do something like the following:
function showMyScreen() {
MyScreen screen = appNavigation.show("MyScreen.qml", NavigationType.FADE)
screen.someData = "some data here"
}
but is this possible? Could I somehow return the screen that is loaded by the loader?
I am guessing not so I would satisfy with sending the data with the navigation itself like:
function showMyScreen() {
MyScreen screen = appNavigation.show("MyScreen.qml", NavigationType.FADE, "some data here")
}
I could forward the data to the point where I set source to the loader but still what then? How or where would that specific screen that is going to be loaded get the data. To reduce is this is what I get:
function setNewItemWithData(newItem, data) {
loader.source = newItem
loader.concreteScreen.data = data // Not really doable
}
again I assume this is not doable and I need to forward the data down to loader and use onLoaded event. So what I would do is something like:
onLoaded: {
myLoadedScreen.data = data
}
I assume something like this is possible but how? What am I missing here, how do I get myLoadedScreen and how to access its properties?
What I am currently doing now is dumping the data in C++ part and then collecting it in the loaded QML. So like the following:
appNavigation.injectedData = "some data here"
and then in the newly loaded item:
property data = appNavigation.injectedData
It works but this seems like extremely poor coding. Any of the alternatives would be helpful.
Thank you for your patience.
Since the request for MCVE was made:
This is a general problem and I expect it to have multiple solutions. I would be looking forward to any of them. However the minimal example to produce this is creating a new project and adding a loader with another qml to which some property should be changed:
main:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Loader {
anchors.fill: parent; anchors.margins: 20
source: "MyScreen.qml"
// TODO: make screen green (loadedScreen.color = "green")
}
}
MyScreen:
import QtQuick 2.0
Rectangle {
color: "red"
}
Current result is seeing a red rectangle and desired result is to see a green one. The point being that the main screen needs to tell what color the loaded screen needs to use.
You have to use the item property of the Loader to get the object loaded:
Loader {
id: loader
anchors.fill: parent; anchors.margins: 20
source: "MyScreen.qml"
onLoaded: loader.item.color = "green"
}
To do that, you might as well use Component (If you use it when reacting to an event)
Component {
id: myScreenComponent
MyScreen {
anchors.fill: parent
}
}
function showMyScreen() {
myScreenComponent.createObject(this, {"color: "green"});
}
Alternatively, given your first code, I would recommend you to use StackView.
The push method seems to be similar to your appNavigation.show one.
You can give it an url, some properties, and a transition type (that you can customize).
I am working on an Android App. I need to display a console like type of log, which will be written to by the C++ back end. I have tried doing this combining a TextEdit and ScrollView, but the result is really slow. As soon as my log goes beyond ~50 lines, adding a few lines slows down (locks) the interface for a few seconds.
Trimming down the source code, this is the log view section:
property int logMaxLines: 50
ScrollView {
id: logScrollView
anchors.fill: parent
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
TextEdit {
id: logTextEdit
anchors.fill: parent
readOnly: true
color: "darkgreen"
property int linesTrimmed: 0
}
}
Connections{
target: gate
onNewMessageLineAdded :
{
logTextEdit.append(gate.newMessageLine)
if (logTextEdit.lineCount > logMaxLines) {
while (logTextEdit.lineCount >= logMaxLines) {
logTextEdit.text = logTextEdit.text.slice(logTextEdit.text.indexOf('\n')+2)
logTextEdit.linesTrimmed++
}
logTextEdit.insert(0, "[... trimmed " + logTextEdit.linesTrimmed + " lines ...]\n")
}
}
}
I picked a ScrollView as I'd like to have the vertical scroll bar. Lines are added one at a time by the C++ code, when it emits the newMessageLineAdded signal. This is coming from a class which includes this Q_PROPERTY, used to pass the new line content:
Q_PROPERTY(QString newMessageLine READ newMessageLine NOTIFY newMessageLineAdded)
the signal is declared as:
void newMessageLineAdded();
I have added the small bit of java to trim the log when it grows too long, as the issue is there even when this trimming code is not present.
Am I doing something very clunky here? Should I use another type of object to replace the TextEdit, knowing that it is not used at all to edit text, but only as a display?
Thanks.
I recommend you to use ListView instead of TextEdit. And use QStringListModel as model declared in C++ code and added to QML as context property. Read Embedding C++ Objects into QML with Context Properties. It is recommended for better perfomance to have most of logic in C++ code.