Can a RowLayout item have signals in QML? - qt

I'm having a little problem with some custom Items I'm using im my QML code. I created a set of Items consisting in a RowLayout containing a label and an input control (e.g. SpinBox or ComboBox). In order to "bring the value changed signal outside" i added to the external RowLayout a signal that should be emitted onValueChanged. Here's the code:
import QtQuick 2.10
import QtQuick.Controls 2.2
import QtQuick.Window 2.2
import QtQuick.Controls.Material 2.3
import QtQuick.Layouts 1.0
RowLayout {
property alias label: innerLabel.text
property alias value: innerNum.value
property alias editable: innerNum.editable
property alias min: innerNum.from
property alias max: innerNum.to
property string myPage: ""
property string myMeasure: ""
property int fontSize: 16
property bool signaling: true
property var colorAccent: Material.accent
property var colorPrimary: Material.primary
signal fabValueChanged(var message)
Label{
id: innerLabel
Layout.fillHeight: filling
Layout.fillWidth: filling
Material.foreground: colorAccent
font.pixelSize: fontSize
verticalAlignment: Text.AlignVCenter
property bool filling: true
onTextChanged: function(){
if (text == ""){
filling = false;
width = 0;
}
else{
filling = true;
}
}
}
SpinBox{
id: innerNum
Layout.fillHeight: true
Layout.fillWidth: true
Material.foreground: colorAccent
editable: true
to: 100000
from: 1
value: 1
onValueChanged: function(){
if(parent.signaling){
var toSend = {"name":parent.objectName,"value":value,"measure":parent.myMeasure};
fabValueChanged(toSend);
}
}
}
}
When I connect the fabValueChanged to a simple slot that just prints the message I got nothing, even if the onValueChanged signal is correctly emitted.
Can it be that RowLayout cannot emit signals? If that's the case, How can I modify my object in order to avoid this problem?
If my problem is not the layout, wht is going on?

I'm really rossy for waistin other people's time on this issue. In the end the problem was with the function I used to dynamically create these objects. So the object as it is can emit signal without any problem.
Sorry again

Related

OnClicked property sends data back to Go, but slot isn't invoked

So, I am trying to write a simple login page in Qml, the user writes data in the two text fields, and once the login button is pressed the Go Slot will send some data over to another function for authentication.
The only problem is, when the login button is pressed, the slot isn't invoked, the code continues outside the main loop that creates the UI and exits the program with a SIGSEV.
Basically going past:
// Execute app
gui.QGuiApplication_Exec()
and exiting MakeUI()
With this error:
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x265a014]
Here's my Go Code:
package UI
import (
"fmt"
_ "github.com/heyuan110/gorepertory/logger"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/qml"
"github.com/therecipe/qt/quickcontrols2"
"os"
)
type QmlBridge struct {
core.QObject
//_ func(username string,password string) bool `signal:sendToQml`
_ func(username string,password string )bool `slot:sendToGo`
}
func MakeUI() {
//var QmlBridgeVar *QmlBridge
var QmlBridgeVar = NewQmlBridge(nil)
// Create application
app := gui.NewQGuiApplication(len(os.Args), os.Args)
// Enable high DPI scaling
app.SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
// Use the material style for qml
quickcontrols2.QQuickStyle_SetStyle("material")
// Create a QML application engine
engine := qml.NewQQmlApplicationEngine(nil)
engine.RootContext().SetContextProperty("QmlBridgeVar", QmlBridgeVar)
//QmlBridgeVar.ConnectSendToQml(func(username string,password string){
//
//})
QmlBridgeVar.ConnectSendToGo(func(username string, password string){
fmt.Println(username,password)
})
// Load the main qml file
engine.Load(core.NewQUrl3("qml/main.qml", 0))
// Execute app
gui.QGuiApplication_Exec()
}
And here's my Corresponding Qml:
import QtQuick 2.0
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
title: "Bouncer"
property int margin: 11
minimumWidth: 600
minimumHeight: 450
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.margins: margin
GroupBox {
id: rowBox
title: "Login"
Layout.fillWidth: true
RowLayout {
id: rowLayout
anchors.fill: parent
TextField {
id: usernameField
placeholderText: "Username"
Layout.fillWidth: true
}
TextField {
id: passwrdField
placeholderText: "Password"
Layout.fillWidth: true
}
Button {
id: loginButton
text: "Login"
MouseArea{
id: loginMouseArea
anchors.fill: parent
onClicked: QmlBridgeVar.sendToGo(usernameField.text, passwrdField.text)
}
}
}
}
}
}
I am prone to believe I might have some problems with the Qt bindings for go? Though I am not entirely sure. If anybody can give me valuable feedback I'd highly appreciate it.

How do I have declarative, bidirectional bindings involving QML MouseAreas?

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.

How to setup my button component to open a window

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.

How to put attached properties to child item

Let's assume I have a component like this
RowLayout {
MyItem {
Layout.fillWidth: true
Layout.fillHeight: true
... // other properties
}
MyItem {
Layout.fillWidth: true
Layout.fillHeight: true
... // other properties
}
}
in which MyItem.qml is defined like this
Rectangle {
... // other properties
// Layout.fillWidth: true
// Layout.fillHeight: true
}
Can I put Layout.fillWidth to MyItem, so that I don't need to repeat it in RowLayout ?
Can I put Layout.fillWidth to MyItem, so I don't need to repeat it in RowLayout ?
I think the question has the answer in it: if you don't want to repeat, just use the Repeater type. The documentation states that
Items instantiated by the Repeater are inserted, in order, as children of the Repeater's parent. The insertion starts immediately after the repeater's position in its parent stacking list. This allows a Repeater to be used inside a layout.
The example which follows in the documentation uses Row but the very same approach can be applied to other layouts, e.g. RowLayout. Actually, it works for any type with attached properties as per the Repeater nature ("insert items inside parent").
Here is an example. Assume we have defined an Example type.
import QtQuick 2.5
Rectangle {
property alias text: inner.text
color: "steelblue"
Text {
id: inner
anchors.centerIn: parent
font.pixelSize: 30
}
}
We can add the layout properties to our Example type inside the Repeater, for instance like this:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
Window {
id: window
width: 600
height: 400
visible: true
RowLayout {
id: row
anchors.fill: parent
Repeater {
model: 6
delegate : Example {
text: index
Layout.fillWidth: true // layout options added in the delegate
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter
Layout.maximumWidth: parent.width / model.length
}
}
}
}
The model property of the Repeater can be either an array of strings or another model, as usual.
This approach is flexible enough to combine several Repeaters to create more complex structures. See for instance the following example in which Text is used to fill the screen inside a GridLayout:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
Window {
id: window
width: 600
height: 400
visible: true
GridLayout {
id: grid
anchors.fill: parent
rows: 2
columns: 6
Repeater {
model: grid.columns
Text {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.row: 0
Layout.column: index
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: index + 1 // index of the repeater as text
}
}
Repeater {
model: grid.columns
Text {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.row: 1
Layout.column: index
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: index + 7
}
}
}
}
Yes, you can do that, but it will end in an error whenever you decide to use that component in a context where it has no attached property named Layout.fillWidth or, more in general, whenever you decide not to use it as a top element within a layout.

What is the proper way to use ids in QML

I need to use most of my child qml childrens in parent qml so using aliasing i am doing it now but some how i am not ok that calling login.help.visible too many dots. could some one give me best solution for this.
here is my code
main.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle{
anchors.fill: parent
Widget{
id: login
}
MouseArea{
anchors.fill: parent
onClicked: login.help.visible = false
onEntered: {
login.help.visible = true
login.progress.visible = true
ogin.pwd.visible = true
}
onExited: {
login.help.visible = false
login.progress.visible = false
ogin.pwd.visible = false
}
}
}
}
Widget.qml
import QtQuick 2.0
import QtQuick.Controls 1.2
Item {
id: newid
property alias help: helpid
property alias progress: ploder
property alias username: name
property alias password: pwd
Help{
id: helpid
}
TextCustom{
id: name
}
TextCustom{
id: pwd
}
progressLoader{
id: ploder
}
}
Usually, once I define a new Component, I use to encapsulate its children, logic and properties by hiding everything to the users, while I expose the properties that are freely modifiable (even the ones that belong to the children of my component) by means of its properties (aka the property keyword, correctly bound to the target properties of the component or of its children).
This way, the children of my component are nicely isolated from the users of the component itself.
It follows an example:
// MyComponent.qml
Item {
property alias B: firstChild.B
Item {
id: firstChild
property int A: 0 // this is hidden to the users of MyComponent...
property int B: 0 // ... this is not, indeed
}
}
// AnotherComponent.qml
// the outer Component has the following child
MyComponent {
B: 3 // directly modifies the property B of firstChild in MyComponent
}

Resources