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

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.

Related

Can a RowLayout item have signals in QML?

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

How to implement single connection between objects in QML?

In my Qt app I have many windows, and sometimes they need a "Back" button. This button is placed on ToolBar component in the header of the ApplicationWindow .
What I want to achieve, is that this Back button, would have only single connection to other objects , i.e. the connection to the last object that called connect method. Right now with every connect I am getting a new connection and when the signal is emitted, it is called multiple times. Unfortunately Qt doesn'thave disconnectAll method, if it would , that would have solve my problem , I would just call disconnectAll before and then connect and that would implement single connection.
So , how are you doing this functionality in Qt , with a simple method?
Here is a minimal reproducible example, click on the tabs many times, then press 'Back' button and you will see lots of console.log messages. And what I need is this message to correspond to the last object that is connected to the Back button.
import QtQuick 2.11
import QtQuick.Controls 2.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Tabs")
signal back_btn_clicked()
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page1Form {
id: page1
function page1_callback() {
console.log("page 1 back button triggered")
}
function install_button() {
enable_back_button(page1_callback)
}
}
Page2Form {
id: page2
function page2_callback() {
console.log("page 2 back button triggered")
}
function install_button() {
enable_back_button(page2_callback)
}
}
function install_back_button(idx) {
if (idx===0) {
page1.install_button()
}
if (idx===1) {
page2.install_button()
}
}
}
Button {
id: btn_back
visible: false
text: "Back Button"
onClicked: back_btn_clicked()
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("Page 1")
onClicked: swipeView.install_back_button(0)
}
TabButton {
text: qsTr("Page 2")
onClicked: swipeView.install_back_button(1)
}
}
function enable_back_button(func_name) {
btn_back.visible=true
back_btn_clicked.connect(func_name)
}
}
PageForm.ui is defined like this
import QtQuick 2.11
import QtQuick.Controls 2.4
Page {
width: 600
height: 400
header: Label {
text: qsTr("Page 1")
font.pixelSize: Qt.application.font.pixelSize * 2
padding: 10
}
Label {
text: qsTr("You are on Page 1.")
anchors.centerIn: parent
}
}
The simplest hack, I think, would be to store the callback in a property, then in enable_back_button(), reference that property in your disconnect() function, and update the property accordingly with the new callback passed as a function argument. (The rationale for this argument being that the disconnect() function must take in an argument: the slot to disconnect. So we'll need to keep track of it some way or another.)
ApplicationWindow {
visible: true
// ... omitted for brevity
property var prevCallback: null
// ... ofb
function enable_back_button(func_name) {
btn_back.visible=true
if (prevCallback)
back_btn_clicked.disconnect(prevCallback) // disconnect previous callback
back_btn_clicked.connect(func_name) // connect new callback
prevCallback = func_name // update property with new callback
}
}
And this could work on multiple connections as well, by simply changing the storage into an array, then iterating through that.

QML progress bar is NOT showing up on UI

I have this QML progress bar:
import QtQuick.Controls 2.0 as QQC20
Item {
QQC20.ProgressBar {
id: progressbar_id
visible: false // even if "true", the progress bar does NOT show up on UI
from: editorScene.progressbarMin
to: editorScene.progressbarMax
value: editorScene.progressbarVal
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
I can confirm that the progress bar value and visibility are changed by the methods onValueChanged and onVisibleChanged.
However, the problem is that the progress bar does NOT show up on the UI! How can I actually show the progress bar on the UI? Can anybody give me a hint?
Right now, all you're doing is creating a QML type which you can use as part of your API. To actually see it, you need to create an instance of it under a ApplicationWindow or Window (or anything else equivalent, e.g. Canvas or Felgo's GameWindow).
There are two ways you can accomplish this. You can
Directly add your item as a child of a window.
Put your item in a separate file, and create an instance of that file under a window.
Lé Code
Method 1: Directly Adding as Child
Directly insert your codeblock as a child of an ApplicationWindow.
// Main.qml
import QtQuick 2.0 // for `Item`
import QtQuick.Window 2.0 // for `ApplicationWindow`
import QtQuick.Controls 2.0 // as QQC20 // no need to label a namespace unless disambiguation is necessary
ApplicationWindow {
width: 480 // set the dimensions of the application window
height: 320
// here's your item
Item {
anchors.centerIn: parent // place in centre of window
ProgressBar {
id: progressbar_id
anchors.horizontalCenter: parent.horizontalCenter // horizontally align the progress bar
from: 0 // don't know what editorScene is
to: 100 // so I'm using raw values
value: 5
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
// side note: I'm not getting any output from this handler
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
// provide user-interaction for changing progress bar's value
MouseArea {
anchors.fill: parent // clicking anywhere on the background
onClicked: progressbar_id.value += 5; // increments the progress bar
// and triggers onValueChanged
}
}
Method 2: Using a Separate File
Save your item into a new qml file.
// MyProgressBar.qml
import QtQuick 2.0 // for `Item`
import QtQuick.Controls 2.0 // for `ProgressBar`
// here is your item, it has grown up to be in a file of its own 🚼
Item {
property alias value: progressbar_id.value // for user-interaction
ProgressBar {
id: progressbar_id
anchors.horizontalCenter: parent.horizontalCenter // centre horizontally
from: 0
to: 100
value: 5
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
Note that you still need the import statements.
Then call it from a window in Main.qml. We'll use an ApplicationWindow here.
// Main.qml
import QtQuick 2.0
import QtQuick.Window 2.0 // for `ApplicationWindow`
// import "relative/path/to/progressbar" // use this if MyProgressBar.qml is not in the same folder as Main.qml
ApplicationWindow {
width: 480
height: 320
MyProgressBar {
id: progressbar_id
}
MouseArea {
anchors.fill: parent
onClicked: progressbar_id.value += 5;
}
}
If your qml files aren't in the same directory, make sure you add an import "relative/path" at the top of the Main.qml file among the other import statements.
For example, if
Your Qml project is in /Users/Lorem/Project,
The full path to your Main.qml is /Users/Lorem/Project/qml/Main.qml, and
The full path to your MyProgressBar.qml is /Users/Lorem/Project/qml/myControls/MyProgressBar.qml...
Then use import "myControls" in Main.qml to import the items from the myControls subdirectory. Remember, you only need to import the directory, not the file itself.
Result
This is what the result resembles when I run it from a macOS.
At startup.
After 3 clicks on the background.
There is also console/debug output after each click:
Progressbar value changed: 10
Progressbar value changed: 15
Progressbar value changed: 20

How to run QML StateMachine example from Qt's documentation?

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()
}
}
}

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.

Resources