Can't emit signal in QML custom Item - qt

I created my own Item with signal clicked, that contatins MouseArea. I want to emit signal clicked, when MouseArea is clicked. But nothing works.
Here is my .qml code:
import QtQuick 2.4
Item {
id: baseButton
property alias text: txt.text
width: txt.width
height: txt.height
signal clicked
onClicked : console.log("Clicked!")
Text {
id: txt
color: "white"
font.pointSize: 8
anchors.centerIn: parent
}
MouseArea {
id: mousearea
anchors.fill: parent
hoverEnabled: true
onEntered: {
txt.color = "yellow"
txt.font.pointSize = 15
}
onExited: {
txt.color = "white"
txt.font.pointSize = 8
}
onClicked: baseButton.clicked
}
}
I'll be very grateful for your help!

Functions (which signals are) are first class objects in JS, so it is not an error to refer to them without parentheses. But you need them in order to execute the function (i.e. emit the signal).
So just change this line:
onClicked: baseButton.clicked()

Related

MouseArea does not pass click to CheckBox

Take a look at this QML snipped:
import QtQuick 2.4
import QtQuick.Controls 2.4
Rectangle {
color: "blue"
width: 50
height: 50
CheckBox {
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
}
}
}
I want to add MouseArea over CheckBox so I can handle doubleclick. However no matter how and what I do CheckBox stops working (clicking it won't show checked mark) as soon as there is MouseArea over it.
What's wrong here?
You can programmatically toggle Qt Quick 2 CheckBox with AbstractButton.toggle(). Also, MouseArea propagateComposedEvents property works only with other MouseAreas and not with Qt Quick Controls QML types.
I don't know your use case so I add few possibilities below.
Signal connect() method
Easiest way to achieve toggling through MouseArea is to create signal chain by connecting MouseArea clicked to CheckBox clicked.
Rectangle {
anchors.centerIn: parent
color: "blue"
width: 50
height: 50
CheckBox {
id: checkBox
onClicked: toggle()
MouseArea {
id: mouseArea
anchors.fill: parent
}
Component.onCompleted: mouseArea.clicked.connect(clicked)
}
}
Note that double click always starts with a single click. If you want to catch double clicks with MouseArea you can e.g. use a Timer for preventing propagating clicks to CheckBox.
Rectangle {
anchors.centerIn: parent
color: "blue"
width: 50
height: 50
CheckBox {
id: checkBox
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
if (timer.running) {
return
}
checkBox.toggle()
timer.start()
}
Timer {
id: timer
interval: 250
repeat: false
}
}
}
}
If you want to support CheckBox's pressed visualization and/or if you want to use bigger MouseArea than the size of the CheckBox you can take a look into this answer of the question Can't click button below a MouseArea.

Make BusyIndicator run when click on button (signal to C++)

I created an interface has a ListView and two Buttons. When click on Scan button it will call to C++ and make change to the model of ListView. After that C++ will emit signal to inform model is changed therefore ListView in QML will update with new model. I want to make BusyIndicator running during that process. How can i do that ?.
I saw a few solutions on stackoverflow like this one: BusyIndicator does not show up but none of them worked for my case.
Can anyone help me ? Thanks.
Here is my qml code:
import QtQuick 2.5
import QtQuick.Layouts 1.3
import Qt.labs.controls 1.0
Rectangle
{
objectName: "bluetoothPage"
anchors.fill: parent
property var bluetoothDataModel: messageFromApp.bluetoothData
onBluetoothDataModelChanged: listView.update()
signal qmlScanButtonSignal()
signal qmlDisconnectButtonSignal()
ColumnLayout
{
anchors.fill: parent
spacing: 6
RowLayout
{
Layout.fillWidth: true
Text
{
Layout.fillWidth: true
text: "Connect with ECU"
font.bold: true
font.pixelSize: 20
}
BusyIndicator
{
id: busyIndicator
Layout.preferredWidth: 30
Layout.preferredHeight: 30
running: false
visible: false
}
}
GroupBox
{
Layout.fillHeight: true
Layout.fillWidth: true
title: qsTr("Available device:")
ListView
{
id: listView
anchors.fill: parent
model: bluetoothDataModel
delegate: Component
{
Item
{
width: parent.width
height: 40
Column
{
Text { text: "Name:" + model.modelData.name }
Text { text: "Number:" + model.modelData.macAddress }
}
MouseArea
{
anchors.fill: parent
onClicked: listView.currentIndex = index
}
}
}
highlight: Rectangle
{
color: "blue"
}
}
}
RowLayout
{
Layout.fillWidth: true
Layout.preferredHeight: 10
Button
{
Layout.fillHeight: true
Layout.fillWidth: true
text: "Scan"
onClicked: qmlScanButtonSignal()
}
Button
{
Layout.fillHeight: true
Layout.fillWidth: true
text: "Disconnect"
onClicked: qmlDisconnectButtonSignal()
}
}
}
}
Only this solution worked for me in my case. However, like everybody said using QCoreApplication::processEvents()
is really bad practice. I also try to using QThread but it got crash when emitted signal inside thread. If you guy have any futher solutions, please let me now. I'm really appreciate. Thanks.
QML
BusyIndicator {
running: CPPModule.busy
}
CPP
void CPPModule::setBusy(const bool &busy)
{
m_busy = busy;
emit busyChanged();
}
void CPPModule::InsertIntoDB()
{
setBusy(true);
QThread::msleep(50);
QCoreApplication::processEvents();
/*
very Long Operation
*/
setBusy(false);
}
Another solution is this:
Timer {
id: myTimer
interval: 1
onTriggered: {
app.someLongRunningFunction();
myActivityIndicator.visible = false;
}
}
Butoon{
....
onClicked: {
myActivityIndicator.visible=true;
myTimer.start();
}
}

in Qt How to enable ListView and its item all receive MouseArea events?

I'm using Qt 5.6
I want ListView and its items all receive MouseArea onEntered, onClicked signals.
I tried the examples and changed:
ListView {
anchors.fill: parent
model: searchModel
delegate: Component {
Row {
spacing: 5
Marker { height: parent.height }
Column {
Text { text: title; font.bold: true
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: console.log("eeee");
}
}
Text { text: place.location.address.text }
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: console.log("entered");
}
}
Only ListView can accept onEntered signal, there is no response from its items.
How to enable items receive MouseArea events ?
To propagate clicked events, you should set propagateComposedEvent to true to the outermost MouseArea.
Guess if the same applies to the entered event.

How to make double MouseArea take effect?

Here is my QML code :
Rectangle
{
.....
Rectangle
{
....height and width is smaller than parent
MouseArea
{
id: mouseArea2
anchors.fill: parent
hoverEnabled: true
onEntered:
{
console.log("enter 2")
}
}
}
MouseArea
{
id: mouseArea1
anchors.fill: parent
hoverEnabled: true
onEntered:
{
console.log("enter 1")
}
}
}
Only mouseArea1 takes effect. If I remove mouseArea1 then mouseArea2 takes effect. So I think the mouse event must be handled by mouseArea1 and let it couldn't be passed to mouseArea2.
I search the document to find out which attr can prevent such behavior but nothing found. So how to let the mouseArea1 and mouseArea2 take effect at the same time?
For "composed" mouse events -- clicked, doubleClicked and pressAndHold -- you can achieve this using the propagateComposedEvents property. But that won't work here because hover events are not composed events.
So what you need to do instead is to change the order in which the MouseAreas are evaluated.
One simple trick is to swap the order of the two MouseAreas in the QML source itself. By placing the smaller one after the larger one, the smaller one takes precedence:
Rectangle{
//.....
MouseArea{
id: mouseArea1
anchors.fill: parent
hoverEnabled: true
onEntered:{
console.log("enter 1")
}
}
Rectangle{
//....height and width is smaller than parent
MouseArea{
id: mouseArea2
anchors.fill: parent
hoverEnabled: true
onEntered:{
console.log("enter 2")
}
}
}
}
A second method that achieves the same thing is to add a z index to the topmost MouseArea that's greater than the lower one. By default every element has a z index of 0, so just adding z: 1 to the smaller MouseArea will do the trick:
Rectangle{
//.....
Rectangle{
//....height and width is smaller than parent
MouseArea{
z: 1 // <-----------------
id: mouseArea2
anchors.fill: parent
hoverEnabled: true
onEntered:{
console.log("enter 2")
}
}
}
MouseArea{
id: mouseArea1
anchors.fill: parent
hoverEnabled: true
onEntered:{
console.log("enter 1")
}
}
}
I have found the solution in the documentation. Take for instance the following QML code:
import QtQuick 2.0
Rectangle {
color: "yellow"
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: console.log("clicked yellow")
}
Rectangle {
color: "blue"
width: 50; height: 50
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onClicked: {
console.log("clicked blue")
mouse.accepted = false
}
}
}
}
Here the yellow Rectangle contains a blue Rectangle. The latter is the top-most item in the hierarchy of the visual stacking order; it will visually rendered above the former.
Since the blue Rectangle sets propagateComposedEvents to true, and also sets MouseEvent::accepted to false for all received clicked events, any clicked events it receives are propagated to the MouseArea of the yellow rectangle beneath it.
Clicking on the blue Rectangle will cause the onClicked handler of its child MouseArea to be invoked; the event will then be propagated to the MouseArea of the yellow Rectangle, causing its own onClicked handler to be invoked.

Combine 2 MouseAreas on GridView

I have code like this:
GridView {
// ... declarations ...
model: theModel
delegate: MouseArea {
id: cellMouseArea
onClicked: // open the cell
}
MouseArea {
id: gridViewMouseArea
// here process horizontal mouse press/release actions
}
}
with a MouseArea defined in each delegate and an overall MouseArea covering my GridView. In the cellMouseArea I want to perform an open item action whereas in the gridViewMouseArea I want to implement mouseX handle to open/close a sidebar. However, the two MouseAreas do not work together. How can I carry it out?
You can exploit propagateComposedEvents:
If propagateComposedEvents is set to true, then composed events will
be automatically propagated to other MouseAreas in the same location
in the scene. Each event is propagated to the next enabled MouseArea
beneath it in the stacking order, propagating down this visual
hierarchy until a MouseArea accepts the event. Unlike pressed events,
composed events will not be automatically accepted if no handler is
present.
You can set the property to true on the GridView MouseArea. In this way click events are propagated to the MouseAreas in the delegates whereas the outer MouseArea can implement other behaviours such as drag or hoven.
Here is an example in which outer MouseArea defines drag property to slide in/out a Rectangle ( simulating your sidebar) and thanks to the propagateComposedEvents clicks are managed by the single delegates.
import QtQuick 2.4
import QtQuick.Window 2.2
ApplicationWindow {
width: 300; height: 400
color: "white"
Component {
id: appDelegate
Item {
width: 100; height: 100
Text {
anchors.centerIn: parent
text: index
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
console.info("Index clicked: " + index)
}
}
}
}
Component {
id: appHighlight
Rectangle { width: 80; height: 80; color: "lightsteelblue" }
}
GridView {
anchors.fill: parent
cellWidth: 100; cellHeight: 100
highlight: appHighlight
focus: true
model: 12
delegate: appDelegate
MouseArea {
anchors.fill: parent
z:1
propagateComposedEvents: true // the key property!
drag.target: dragged
drag.axis: Drag.XAxis
drag.minimumX: - parent.width
drag.maximumX: parent.width / 2
onMouseXChanged: console.info(mouseX)
}
}
Rectangle{
id: dragged
width: parent.width
height: parent.height
color: "steelblue"
x: -parent.width
}
}

Resources