QML: Buble event to parent - qt

How can component inform parents when certain action happens? I mean something like event.buble in JavaScript. https://www.w3docs.com/learn-javascript/bubbling-and-capturing.html
For example some elements in dialog can send a "Ok" or "Cancel" action.
Parent item does not know all the child items items in advance. I would add something like:
Widget {
signal cancel
signal ok
...
}
ParentItem {
id: myParentItem
onCancel { ... }
onOk { ... }
Widget {
id: first
}
Widget {
id: second
}
// no connection section needed. Auto-connect signals by name.
}
}
Edit:
Note: adding separate Widget and then connection is a bit impractical. Some one can forget to add one or other, moreover when deleting and renaming one can remove only one part or rename one part incorrectly.
Calling parent.functionName is impractival too because then such Widget can be used only in parents having functionName.

One idea is to search through all the children and check their type. If they are the Widget type, then connect their signals to some ParentItem function. I haven't tested this code, but something like it should work.
ParentItem {
id: myParentItem
function doCancel() { ... }
function doOk() { ... }
Component.onCompleted: {
for (var i = 0; i < children.length; i++) {
if (children[i] instanceOf Widget) {
children[i].onOk.connect(doOk);
children[i].onCancel.connect(doCancel);
}
}
}
Widget {
id: first
}
Widget {
id: second
}
}

Related

QML signal and slot between 2 different QML

I need to perform some actions in let's say main.qml according to button press in button.qml. Button inside of the button QML is inside of some custom object. Let's give it a name customObject. So customObject in button.qml looks like this:
customObject {
id:trialObject
signal backPagePressed()
Button {
id:button1
MultitouchArea {
onPressed:
{
trialObject.backPagePressed()
}
Now when I press the button, it emits backPagePressed(). My question is: How can I make a slot for this signal in main QML? I'm familiar to signal and slot mechanism in C++, but that does not work for QML. I made something like this in main.qml:
Loader
{
id:pageLoader
onBackPagePressed:
{
pageLoader.source =""
}
}
That part needs to delete the Loader's source so that it will go back to page before. However, I'm not sure about onBackPagePressed: ... How can I connect my signal backPagePressed, to the related part in my loader?
You should use a Connections object (documented here) together with the item property of the Loader:
Loader {
id: pageLoader
source: "CustomObject.qml"
}
Connections {
target: pageLoader.item
//Qt < 5.15
onBackPagePressed: pageLoader.source = ""
//Qt >= 5.15
function onBackPagePressed()
{
pageLoader.source = ""
}
}

how to trigger a c++ function in qml

I have one more question in connection with my QVariantMap. Currently I trigger the filling directly at startup (c++):
StyleConfiguration::StyleConfiguration(QObject *parent) : QObject(parent)
{
restore("q_button");
}
However, I would like to have the map filled directly at runtime in qlm, but don't know how to manage that.
Somethings like this:
xxxx: _styleconfiguration.restore("q_button")
Similar to a repeater:
Repeater{
model: _styleconfiguration.restore("q_button")
}
what should I use for xxx? Would be happy if someone can help me further.
addition:
mainWin.qml:
Rectangle {
id: dashboard
...
Row {
...
//----------------Button-------------//
Grid {
....
Component.onCompleted: _styleconfiguration.readValue("q_button") // **<- Call to read out the json**
Repeater {
id: dashboardButtons
model: _configuration.buttonMapping
Q_Button{ // **<- use read out values from the json**
id: dashButton
text: dashboard.functionsDashboard[modelData].text
...
}
}
}
}
//----------------Longpress Menu-------------//
Popup {
...
contentItem: Rectangle {
...
Column {
....
Component.onCompleted: _styleconfiguration.readValue("l_button")
Repeater {
model: dashboard.functionsDashboard
L_Button {
text: modelData.text.replace('\n', ' ')
....
}
}
}
}
}
}
}
Q_Button.qml:
Rectangle {
id: button
width: _styleconfiguration.styleMapping["width"]
height: _styleconfiguration.styleMapping["height"]
...
}
I hope the addition helps for understanding
I'm not sure I understand your question completely...
You have to mark your method declaration as Q_INVOKABLE in order to call it from QML:
Q_INVOKABLE void restore(const QString& s);
Then you can call it at creation of your QML Component.
Component.onCompleted: _styleconfiguration.restore("q_button")

Items inside TabView/Tab are not accessible

Suppose we have a QML file like the following:
Window {
Component.onCompleted: rect.color ="green"
TabView {
Tab {
Rectangle {
id: rect
color: "white"
}
}
}
}
When I run this code, for some reason, it gives me this error:
ReferenceError: rect is not defined
Somebody can say it's a scope problem but the following code works fine:
Window {
Component.onCompleted: rect.color ="green"
Item {
Item {
Rectangle {
id: rect
color: "white"
}
}
}
}
In my case I have a big form with tabs and controls inside it and I pass all the controls to several functions to validate the form, i.e. some code like this:
function onClose() {
validate(control1);
validate(control2);
// etc
}
but while accessing controls by id I get the above error.
How about just binding the rectangle to a color parameter instead of a hardcoded value?
This wil separate your Model and View code further to make it more readable elsewhere in your project as well...try:
Window {
property string myColor: "White"
Component.onCompleted: myColor = "Green"
TabView {
Tab {
Rectangle {
id: rect
color: myColor
}
}
}
}
To view the QML object tree, you need to start your project in debug mode. Then split your code window so that the "Locals and Expressions" view is showing (checkbox ticked on the right side). You will see your root item and all the other controls nested in a tree and now you can directly edit the values of their properties, and the changes will render immediately. There is a youtube video tutorial on debugging: https://youtu.be/mPXn6L2Wftc?t=19m55s
I'm not sure if the tree would give you access to the controls at runtime, but it might help you with debugging.
Ok, since Tab item cannot be accessed from outside I think it can be done in this way:
TabView {
id: tabView
Tab {
title: "tab1"
function validate() { /* validate all the controls related to tab1 only */ }
Item { id: item1 }
Item { id: item2 }
}
Tab {
title: "tab2"
function validate() { /* validate all the controls related to tab2 only */ }
Item { id: item3 }
Item { id: item4 }
}
function validateTabs() {
for(var i = 0; i < tabView.count;i ++) {
var tab = tabView.getTab(i);
if(tab && tab.active && tab.item.validate) {
if(!tab.item.validate())
return false;
}
}
return true;
}
}
The good point that if some Tab wasn't opened and so not changed it will not be validated.

How to pass “this” from a QML element to a JS Function

Similar to the this keyword in C++, I’d like to either have a QML element to pass itself into a JS function, or have it set a property on another element to itself. Is this possible?
For example:
Rectangle{
id:theParent
property var theElement
SomeElement{
id:theChild
MouseArea {
anchors.fill:parent
onClicked: {
someJsFunction(*whatGoesHere*)
parent.theElement=*whatGoesHere*
}
}
Or, Consider this:
Rectangle{
id:theParent
property var theElement
SomeElement{
id:theChild
}
Then, in SomeElement.qml:
Rectangle{
MouseArea {
anchors.fill:parent
onClicked: {
someJsFunction(*whatGoesHere*)
parent.theElement=*whatGoesHere*
}
}
}
In this case, the *whatGoesHere* would be the instance of SomeElement where these are being called from.
Is this possible in QML? I would think the id property would make sense, but according to the docs, you cannot query the value of the id field, and anyway the id wouldn't be available if my SomeElement was described in a separate file, and the whatGoesHere handling above appeared in that separate file rather than in a particular instance.
I have two complementary proposals :
First, for a single usage, pass the ID as it's basically a pointer to the item :
MouseArea {
id: myClicker;
onClicked: { callFunc (myClicker); }
}
Then if you need multiple items to share this behavior, that means you're using MVC so the ID will work exactly the same :
Repeater {
model: 100;
delegate: MouseArea {
id: myClicker;
onClicked: { callFunc (myClicker); }
}
}
That is the classical part.
But to todo even better if you create your own components, keep in mind to create a 'self' helper property that does the 'this' job properly :
MouseArea { // component root definition
id: base;
property var self : base; // bind self on the I
}
Then use it like this :
Repeater {
model: 100;
delegate: MyComponent {
onClicked: { callFunc (self); }
}
}
Use this as often as you want !
Instance of your SomeElement is its id property value i.e. theChild. You can use it as this. No other built-in way exists as far as I can tell. But you can try to add your own QML item hierarchy with property which will return this.
Another way is to get children of some parent and use them. So you get children, locate the child you need and use that particular instance
If you define your element in a separate file, then you can simply assign an id and use it. It will be valid just within the context of that instance:
SomeElement.qml
Rectangle{
id: thisElement
MouseArea {
anchors.fill: parent
onClicked: {
someJsFunction(thisElement);
parent.theElement = thisElement;
}
}
}

Can Repeater delegate in Qml be made to behave in a generic way to the given Items?

{New to Qml (quick 2.0 using Qt 5.1 beta) and learning}.
I wanted to know if such an idiom would be possible in Qml:
Below I have objLeftColumn which expects its children to expose a boolean m_bIsSelected and a MouseArea alias m_mouseProperty and uses them to make the collection of such children mutually exclusive, ie., only one of them can be in selected state. The followin works fine but I need to repeat it every time I want and specially if I wanted it for Row etc.
Column {
id: objLeftColumn
property int m_iLastButtonClicked: -1
property int m_iCurrentButtonClicked: -1
onM_iCurrentButtonClickedChanged: {
if(m_iLastButtonClicked != -1) {
objLeftColumn.children[m_iLastButtonClicked].m_bIsSelected = false
}
m_iLastButtonClicked = m_iCurrentButtonClicked
}
Repeater {
id: objLeftColumnRepeater
model: 5
delegate: ABCD {
id: objABCD
m_mouseProperty.onClicked: {
if(m_bIsSelected) {
objLeftColumn.m_iCurrentButtonClicked = index
}
else {
objLeftColumn.m_iLastButtonClicked = -1
objLeftColumn.m_iCurrentButtonClicked = -1
}
}
}
}
}
Can I write a generic objLeftColumn (in a separate qml file) that could arrange the given Items in Column while aslo dealing with exclusivity of their selection?
The idea is instead of giving the component to the delegate right there an then, I'll give it later and for each instantiation of the component (depending on numeric value of model above and below) the delegate: in Repeater should behave similarly.
eg., in psedo code:
in Exclusive.qml:
Column {
id: objLeftColumn
property int m_iLastButtonClicked: -1
property int m_iCurrentButtonClicked: -1
property alias m_delegate: objLeftColumnRepeater.delegate
onM_iCurrentButtonClickedChanged: {
if(m_iLastButtonClicked != -1) {
objLeftColumn.children[m_iLastButtonClicked].m_bIsSelected = false
}
m_iLastButtonClicked = m_iCurrentButtonClicked
}
Repeater {
id: objLeftColumnRepeater
model: 5
onItemAdded: {
//state of item can be manipulated but want to
//add behaviour to the item eg:
/*item {
m_mouseProperty.onClicked: {
//do something
}
}*/
}
}
}
in SomeOther.qml:
Exclusive {
model: 5
delegate: ABCD
}
Exclusive {
model: 9
delegate: DEFG
}
etc..So this way Column in Exclusive is more generic and can be called with any Item assigned to its delegate and will behave similarly. Is this possible in qml
This needs a bit of trickery to be solved, I can think of two ways:
Use the JS connect() function to manually create the connections. Something like this:
Repeater {
id: objLeftColumnRepeater
model: 5
onItemAdded: {
item.m_mouseProperty.onClicked.connect(function() {
console.log("Clicked!");
});
}
}
Wrap the delegate into an Item by using a Loader, and use aConnections element for the connection. Something like this:
property Component delegate
Repeater {
id: objLeftColumnRepeater
model: 5
delegate: Item {
Loader {
id: loader
sourceComponent: delegate
}
Connections {
target: loader.item.m_mouseProperty
onClicked: console.log("Clicked")
}
}

Resources