I want to have an QtObject accessible through a property of another QtObject.
In this concrete case, a QtObject serves as a model for various Items.
TestObj.qml
QtObject {
id: root
property real position: 0
readonly property NumberAnimation animation: NumberAnimation { // <-*1
target: root
property: 'position' //<-*2
from: 0
to: 1
duration: 10000 * (1 - position)
onRunningChanged: console.log('running', running)
}
}
main.qml
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
width: 700
height: 700
visible: true
Button {
x: 600 * model.position
y: 400 * model.position
onClicked: model.animation.running = true
}
TestObj {
id: model
}
}
It runs flawless, as expected. However the QtCreator finds some "Build Problems" at the two marked lines (<-*) in TestObj.qml.
*1: It expects the token: ','
*2: It expects the token: '}'
What's up with the QtCreator (4.0.2, Windows)? Is it right or wrong?
When I remove the readonly, the problems disappear, but I want it to be readonly.
For me it would be sufficient, to have the running-property exposed, but as QtObjects can't have (visual) children for it has no default property, I can't see any other way to do this.
I don't see this with Creator 4.1.0, so it could be that it is a bug that has been fixed.
Related
I'm using debian 11, with a Xorg server running and Qt5.15.2 installed.
Very basic question that I don't know how to tackle: let's say we have a simple QML file with a InputPanel and a Textfield:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.VirtualKeyboard 2.15
import QtQuick.Controls 1.4
Window {
id: window
width: Screen.width
height: Screen.height
visible: true
title: qsTr("Hello World")
InputPanel {
id: inputPanel
z: 99
x: 0
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
}
}
}
}
TextField {
placeholderText: qsTr("Enter name")
}
}
I've already done in the main.cpp :
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
And already inserted this line in the .pro file of my project:
QT += quick virtualkeyboard
The issue I found is: VirtualKeyboard does not type anything if I use the touch screen (it correctly appears but no key is prompted and warning "input method is not set" is raised), untill I click with my USB mouse. Then everything is fine, the virtual keyboard prompts and all is fine.
My question is: why? Is it something with X11, debian or something else(like xinput?)?
I tried some configuration of the Xserver for input handling, and adding focus to some QML elements but none works
I've created a QML UI that has a dial and a custom control. The custom control is basically a progress bar with a MouseArea to allow the user to set the value by clicking it. As Qt's property binding docs point out, as soon as I assign to the custom control's value from Javascript in the MouseArea click handler, I lose the declarative binding between it and the dial.
Is it possible to make this binding bidirectional, or even better, to link the values of both controls to a single value above both of them in the QML hierarchy? And is it possible to do this with declarative syntax so I don't have complex event handler code in every control?
import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.3
import QtQuick.Window 2.2
import QtQuick.Shapes 1.0
Window {
id: window
visible: true
width: 800
height: 200
readonly property int range: 10
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
spacing: 5
Dial {
id: dial1
live: true
from: 0
to: window.range
stepSize: 1
snapMode: Dial.SnapAlways
}
Control {
id: dut
implicitWidth: 200
implicitHeight: 50
property int range: window.range
property int value: dial1.value
onValueChanged: {
console.log("New value: " + value);
}
Rectangle {
width: parent.width
height: parent.height
color: Qt.rgba(0,0,0,0)
border.color: Qt.rgba(0,0,0,1)
border.width: 1
}
Rectangle {
width: parent.width * dut.value/dut.range
height: parent.height
color: Qt.rgba(0,0,0,1)
}
MouseArea {
anchors.fill: parent
onClicked: {
dut.value = Math.round(mouseX/width * dut.range);
}
}
}
}
}
Note that if I reverse the relationship ie. have dial1.value: dut.value, then the binding isn't broken (although it's not quite bidirectional).
I realise that this example basically reinvents the scrollbar, but I'm trying to work my way up to more complex controls, for which declarative relationships between values would make life much easier.
Elaboration from a comment: What I don't understand, but want to, is how it's done for other QML components. For example, with a Dial I can set its value property to be bound to some other component's property, and clicking on the dial doesn't remove that binding. I don't have to hook into its mouse events to do that. Despite looking through the source for how this is done, I'm not really any closer to understanding it.
There are other questions about bidirectional property bindings in QML, but I haven't been able to apply them to my problem because (a) I really, really want something declarative, and (b) the MouseArea properties and events don't seem to work well with Binding objects (as in, I can't figure out how to integrate the two things).
I would have done this:
import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.3
import QtQuick.Window 2.2
import QtQuick.Shapes 1.0
Window {
id: window
visible: true
width: 800
height: 200
readonly property int range: 10
property int commonValue
RowLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
spacing: 5
Dial {
id: dial1
live: true
from: 0
to: window.range
stepSize: 1
snapMode: Dial.SnapAlways
onValueChanged: {
commonValue = dial1.value
console.log("New value: " + value);
}
}
Rectangle {
width: 200
height: 50
color: Qt.rgba(0,0,0,0)
border.color: Qt.rgba(0,0,0,1)
border.width: 1
MouseArea {
anchors.fill: parent
onClicked: {
commonValue = Math.round(mouseX/width * window.range)
dial1.value = commonValue
}
}
Rectangle {
width: parent.width * window.commonValue/window.range
height: parent.height
color: Qt.rgba(0,0,0,1)
}
}
}
}
Use a Binding QML Type:
MouseArea {
id: mouseArea
anchors.fill: dut
}
Binding {
target: dut
property: 'value'
value: Math.round(mouseArea.mouseX/mouseArea.width * dut.range);
when: mouseArea.pressed && mouseArea.containsMouse
}
Note that the when property on the Binding means it's only active as a binding when those conditions are fulfilled ie. the mouse is over the area and one of the "accepted buttons" is pressed.
This does not mean that the value reverts when the conditions aren't met, just that the value stops updating when they're not met. However, if you have another binding active somewhere else, that one may cause the the value to "snap back" because it will "take over" when this Binding ceases to apply.
Depending on the other components you use, this might not even be enough, and you might need to implement your properties in C++ to get them to work as you expect.
I'm getting back into Qt lately after a hiatus of several years, and it looks like QML is the "new hotness" these days. In the past, I've managed to get widget-based examples from Qt's documentation to work with relative ease, but... now that I'm trying to learn QML, I'm having trouble closing the gaps in the example code.
Specifically, the docs for Qt.QmlStateMachine say:
The following snippet shows a state machine that will finish when a button is clicked:
import QtQuick 2.0
import QtQml.StateMachine 1.0 as DSM
Rectangle {
Button {
anchors.fill: parent
id: button
text: "Finish state"
DSM.StateMachine {
id: stateMachine
initialState: state
running: true
DSM.State {
id: state
DSM.SignalTransition {
targetState: finalState
signal: button.clicked
}
}
DSM.FinalState {
id: finalState
}
onFinished: Qt.quit()
}
}
}
Perhaps I'm completely naive, but I thought I could just create a new Qt Quick application in QtCreator and paste the above snippet into main.qml. When I do this, though, I'm immediately confronted with an error saying:
QQmlApplicationEngine failed to load component
qrc:/main.qml:19 Button is not a type
So... I look at the docs for the QML Button type and notice that it says near the top:
Import Statement: import QtQuick.Controls 1.4
So, I add that to the top of main.qml and try to run again. And it 'works', but... there's no main window—or any other visual content whatsoever. Hmm. I guess I can see where that (maybe) makes sense, perhaps I shouldn't have replaced the entire contents of main.qml? So I decide to try retaining the Window component from the original QML supplied by QtCreator, changing my main.qml file to look like this:
import QtQuick 2.8
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQml.StateMachine 1.0 as DSM
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
Button {
anchors.fill: parent
id: button
text: "Finish state"
DSM.StateMachine {
id: stateMachine
initialState: state
running: true
DSM.State {
id: state1
DSM.SignalTransition {
targetState: finalState
signal: button.clicked
}
}
DSM.FinalState {
id: finalState
}
onFinished: Qt.quit()
}
}
}
}
After doing this, I see a main window when I run, but it is empty. Um... shouldn't there at least be a button in there somewhere?
Anyway, I wasn't smart enough to figure this out after almost 90 minutes of fiddling around. It seems that Qt's documentation authors are assuming a basic level of QML knowledge that I simply don't possess, so I'm unable to 'fill in the blanks'. Which is a real shame, because QML looks awesome. And I'm particularly excited to see what I can do with the declarative state machine framework! Can anybody tell me what I'm doing wrong with this particular example?
(In case it matters, I'm using Qt 5.9.2 with QtCreator 4.4.1...)
UPDATE: In his answer, #eyllanesc pointed out a small typo in the second code snippet I posted above. Where I wrote id: state1, it should have been id: state.
The documentation assumes some basic knowledge of the previous topics and in the initial paragraph: http://doc.qt.io/qt-5/qtqml-index.html gives you a list of topics that you should read and learn.
And like all language one must read the errors of the code and analyze its logic.
...main.qml:17:13: QML StateMachine: No initial state set for StateMachine
QStateMachine::start: No initial state set for machine. Refusing to start.
.../main.qml:19: ReferenceError: state is not defined
This error clearly indicates that the initial state is not recognized, and this can be caused by 2 reasons, the first is that you have not established it or the second is that you have established an inappropriate state, and in your case it is the second reason.
you have established the initial state:
initialState: state
but state does not exist, I think you wanted to place state1
initialState: state1
The button is not shown because you have established that its size is the same as that of the parent: anchors.fill: parent, and Button's parent is Rectangle, and if Rectangle is not set a size will have a size of 0, causing the son to have it too. A possible solution is to establish Rectangle the size of the parent:
import QtQuick 2.8
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQml.StateMachine 1.0 as DSM
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
anchors.fill: parent
Button {
anchors.fill: parent
id: button
text: "Finish state"
DSM.StateMachine {
id: stateMachine
initialState: state1
running: true
DSM.State {
id: state1
DSM.SignalTransition {
targetState: finalState
signal: button.clicked
}
}
DSM.FinalState {
id: finalState
}
onFinished: Qt.quit()
}
}
}
}
or not use Rectangle:
import QtQuick 2.8
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQml.StateMachine 1.0 as DSM
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Button {
anchors.fill: parent
id: button
text: "Finish state"
DSM.StateMachine {
id: stateMachine
initialState: state1
running: true
DSM.State {
id: state1
DSM.SignalTransition {
targetState: finalState
signal: button.clicked
}
}
DSM.FinalState {
id: finalState
}
onFinished: Qt.quit()
}
}
}
Here is the code of the window I wanna be opened in file PopUpFreeCoins.qml:
import QtQuick 2.0
import QtQuick.Controls 2.1
Item {
property int t
property int c
ListModel{
id:ff
ListElement {
name: "ByFollow"
s: "Images/follow.png"
}
ListElement {
name: "ByLike"
s: "Images/care.png"
}
ListElement {
name: "ByComment"
s: "Images/chat.png"
}
}
ListView{
width:t-t/10
height: c/5
layoutDirection:Qt.LeftToRight
orientation: ListView.Horizontal
model: ff
spacing:50
delegate: Button{
contentItem: Image{
source: s
}}
}
}
property t is set equal to window width in main file and property c is set to window height. This is code of my Button.qml:
Button{//Below Right
width:profilePicture.width/2
height:profilePicture.width/2
x:profilePicture.x+profilePicture.width
y:profilePicture.y+profilePicture.height
contentItem: Image {
source: "Images/freecoins.png"
anchors.fill: parent
}
onClicked: PopUp{height:100;width:300;PopUpFreeCoins{t:a;c:b;}}
}
property a is window width and b is window height.
this line onClicked: PopUp{height:100;width:300;PopUpFreeCoins{t:a;c:b;}} has an error I don't know how to handle!
Here is the error:
Cannot assign object type PopUpFreeCoins_QMLTYPE_0 with no default
method
You need to create the Object somehow. You have multiple ways for dynamically create Objects. One way is to use Component.createObject(parent) which requires you to have a Component instantiated in your file.
Here you can also pass a Object ({property0 : value, property1:value ... }) as second argument, to set the properties of the Component to be instantiated. You should not set the parent to null as it might happen, that the JS-garbage collector is too aggressive once again.
Alternatively you can use the Loader to load it from either a source (QML-file) or sourceComponent. Here you won't have problems with the garbage collector.
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
width: 1024
height: 800
visible: true
Button {
text: 'create'
onClicked: test.createObject(this)
}
Button {
x: 200
text: 'load'
onClicked: loader.active = !loader.active
}
Loader {
id: loader
source: 'TestObj.qml'
active: false
}
Component {
id: test
TestObj {}
}
}
TestObj.qml includes the Window to be opened.
Alternatively you can have the Window created from the beginning, and just change the visible to true or false.
Im beginner in qml. I have set KeyNavigation.up (in item up) to an id of another item (down).
Why i could't retrieve KeyNavigation.up like this in qml ?!
var x = down.KeyNavigation.up
UPDATE:
This is an example. why i couldn't get a.KeyNavigation.up ?
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
TextField {
id: up
KeyNavigation.down: down
}
TextField {
id: down
KeyNavigation.up: up
}
function fun(){
var x = up.KeyNavigation.down
}
}
Your code actually works.
I don't see where you're calling fun(), but if you for example add the line onActiveFocusItemChanged: fun() after visible: true you will see the x variable is OK.
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
visible: true
width: 500; height: 500
onActiveFocusItemChanged: fun()
function fun(){
var x = up.KeyNavigation.down
print("x = up.KeyNavigation.down: " + x.text)
var y = x.KeyNavigation.up
print("y = x.KeyNavigation.up: " + y.text)
}
TextField {
id: up
y: 50
KeyNavigation.down: down
}
TextField {
id: down
y: 100
KeyNavigation.up: up
}
}
In qml, items have properties. And you should declare properties. Without your whole code i cannot really give you a more detailed answer. Just have a look at this
In QML, ids must begin with a lowercase letter. If you're using Qt Creator, you'd see this problem as soon as you typed it. When you run the program, you'd see this error:
IDs cannot start with an uppercase letter
So, either you typed your code in manually for this question (bad idea) and you mistyped the ids, or you didn't run it, which would be strange.
Using a lowercase id works:
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
Component.onCompleted: print(a.KeyNavigation.up)
Item {
id: A
KeyNavigation.up: b
}
Item {
id: b
}
}
Output:
qml: QQuickItem(0x596dcc9180)