Propagating onPositionChanged events to background items in QtQuick 1.1 - c++

Background.qml
import QtQuick 1.1
Item {
MouseArea {
id: backgroundMouseArea
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
console.log("Background")
}
}
}
Foreground.qml
import QtQuick 1.1
Item {
Background {
width: 1920
height: 1080
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
console.log("Foreground")
[mouse.accepted = false] - Not working (as the docs say)
[backgroundMouseArea.onPositionChanged(mouse)] - Not working
}
}
}
I need to execute onPositionChanged event on both background and foreground items.
F.ex. for onPressed I would do it by setting mouse.accepted = false in the foreground item.
Can I call onPositionChanged of the background item manually? If yes, how do I do it?

I am not completely sure what you are trying to achieve here.
A MouseArea is meant to grab mouse events from hardware. If you really want to propagate mouse events to background from a different MouseArea, maybe what you actually want to do is give Background a simple property mousePosition instead of the MouseArea, and then set that position from the Foreground onPositionChanged handler.
Also, your Foreground code relies on an internal id parameter inside of Background. This smells really bad. It is often more useful to think about the "Public API" of the Background and Foreground "classes". If what I described above is really what you want to do, this is what it should look like IMHO:
// Background.qml
import QtQuick 1.1
Rectangle {
// an object with just x and y properties
// or the complete mouseevent, whatever you want
// Use variant for QtQuick 1/Qt4, var for QtQuick 2.0 / Qt5
property variant mousePosition
onMousePositionChanged: console.log(
"Background " + mousePosition.x + " " + mousePosition.y
)
}
//Foreground.qml
import QtQuick 1.1
Item {
// use only ids defined in the same file
// else, someone might change it and not know you use it
Background { id: background }
MouseArea {
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
console.log("Foreground")
background.mousePosition = {x: mouse.x, y: mouse.y}
}
}
}
...........

Related

Retrieve center of QML map in C++

I've the following QML that shows a map:
import QtQuick 2.0
import QtQuick.Window 2.14
import QtPositioning 5.15
import QtLocation 5
Item {
width: Qt.platform.os == "android" ? Screen.width : 512
height: Qt.platform.os == "android" ? Screen.height : 512
visible: true
property double center_latitude: 59.91
property double center_longitude: 10.75
Plugin {
id: mapPlugin
name: "osm"
}
Map {
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(center_latitude, center_longitude)
zoomLevel: 14
copyrightsVisible: false
}
}
I set int in a QQuickWidget and everything seems ok:
m_quickWidget->setSource(QUrl(QStringLiteral("qrc:/qml/map.qml")));
I can see the map correctly. Now I want to retrieve the coordinates of the center of the map, that should be retrieved with the map center property. The problem is that I don't know how to access to that property with C++.
I've tried
auto mp = m_quickWidget->rootObject()->findChild<QQuickItem*>("Map");
auto items = m_quickWidget->rootObject()->childItems();
auto center = mp->property("center").value<QGeoCoordinate>();
extent.setCenter(LLA{ center.latitude(), center. Longitude(), 0.0 });
center = m_quickWidget->rootObject()->property("center").value<QGeoCoordinate>();
but mp gives me a nullptr, while items is a vector of only one element, and I don't know how to handle it. The last row gives me a QGeoCoordiante with NaN, so it's not correct.
How can I retrieve the center from the QQuickWidget?
findChild() is searching for items via the objectName. So you need to add an objectName to your Map.
Map {
objectName: "testmap"
center: QtPositioning.coordinate(59.91, 10.75)
...
}
and then you can search for it.
auto map = view->rootObject()->findChild<QQuickItem *>("testmap");
if (map) {
auto center = map->property("center").value<QGeoCoordinate>();
qDebug() << map << center;
}
The above code returns the following.
QDeclarativeGeoMap(0x55fc0d7de000, name="testmap", parent=0x55fc0d746940, geometry=0,0 800x600)
QGeoCoordinate(59.91, 10.75)

Black screen after existing FullScreen mode

I am trying to make a such thing:
I have a main window with a single button.
After pressing this button two semi transparent windows appear on all screens. They are in a FullScreen mode.
After 4 seconds screens dissapear.
Everything is ok. But when I cklick one of the screens, during process of disappearing, it becomes totaly black. How can I fix it?
// main.qml
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.2
Window {
id: main
visible: true
width: 100
height: 50
title: "Hello Splash World"
Button {
anchors.fill: parent
text: "Show splash"
onClicked: {
for (var i = 0; i < Qt.application.screens.length; ++i) {
var component = Qt.createComponent("SplashScreen.qml");
var window = component.createObject(main, {screen: Qt.application.screens[i]});
window.height = Qt.application.screens[i].height
window.width = Qt.application.screens[i].width
window.showSplash()
}
}
}
}
// SplashScreen.qml
import QtQuick 2.10
import QtQuick.Controls 2.2
ApplicationWindow {
id: splash
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground
color: "transparent"
Timer {
running: true
interval: 4000
onTriggered: hideSplash()
}
function showSplash() {
appearAnimation.start()
}
function hideSplash() {
disappearAnumation.start()
}
background: Rectangle {
id: bg
color: "black"
opacity: 0.8
}
SequentialAnimation {
id: appearAnimation
PropertyAction { target: splash; property: "visibility"; value: ApplicationWindow.FullScreen }
NumberAnimation { target: bg; property: "opacity"; duration: 1000; to: 0.8 }
}
SequentialAnimation {
id: disappearAnumation
NumberAnimation { target: bg; property: "opacity"; duration: 2000; to: 0 }
PropertyAction { target: splash; property: "visibility"; value: ApplicationWindow.Hidden }
}
}
I've come across some strange problems with repainting during further development of my program. For example, changing the size of the main form led to black form to. The solution I've found is to use OpenGL for rendering. You can do it by inserting this code:
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
Denis Popov answer is correct, but in this mode my application was a bit laggy. Problem wasn't occuring if mode was set to:
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
From the other hand this time I was getting following warning in the output everytime I was creating window:
DXGI WARNING: IDXGIFactory::CreateSwapChain: Blt-model swap effects (DXGI_SWAP_EFFECT_DISCARD and DXGI_SWAP_EFFECT_SEQUENTIAL) are legacy swap effects that are predominantly superceded by their flip-model counterparts (DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL and DXGI_SWAP_EFFECT_FLIP_DISCARD). Please consider updating your application to leverage flip-model swap effects to benefit from modern presentation enhancements. More information is available at http://aka.ms/dxgiflipmodel. [ MISCELLANEOUS WARNING #294: ]
Best solution I come up with so far is to run application in debug mode with one flag and in release/deployment with another:
#ifdef QT_DEBUG
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
#else
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif

Event filter in QML

I have to send 48 Bytes of Data to controller via Qtcpsocket. I have represented each Bit in a Byte as a Button in QML. So whenever the user clicks the button, I have to set the Corresponding Bit to true/false and immediately send the entire 48 Bytes of data.
I have so many buttons(Bits) in so many QML files. How to detect which button has been pressed and immediately set the corresponding Bit? How to get the Object in qml ?
What i have done is emitting a signal in the Bit when button is pressed. Now confused how to pass it to the backend c++ on to the sockets because then i would have so many signals. I feel like it’s not a proper way to do. Any smart / better solution or similar example would be really helpful.Thanks
I don't exactly understand from you code what are you trying to do but may be this example can help:
Byte.qml
import QtQuick 2.4
import QtQuick.Controls 1.2
Row {
id: block
property int number: 0
signal dataChanged()
Repeater {
model: 8
Button {
width: 20
height: 20
property int buttonIndex: index
text: buttonIndex
checkable: true
checked: false
onClicked: {
block.number = block.number ^ (1 << buttonIndex);
dataChanged();
}
}
}
}
main.qml
import QtQuick 2.4
import QtQuick.Window 2.0
Window {
id: win
width: 800
height: 600
Column {
id: numbers
property var bytes: []
anchors.centerIn: parent
Repeater {
model: 6
Byte {
property int byteIndex: index
Component.onCompleted: numbers.bytes[byteIndex] = 0;
onDataChanged: {
numbers.bytes[byteIndex] = number;
console.log(numbers.bytes);
}
}
}
}
}
I use here 6 bytes to just bring up the idea. All you need is just to send it to C++ instead of printing it out to console. Intergrating QML and c++ is widely described in the Internet. You can start from here

Binding C++ to QML code when using Components

---edited url and changed dynamic part to something compilable----
(Using Qt 5.3)
I tried to create a compact sample, but its still too big to post all the files here separately, so i added a link to "uploaded.to" as i cannot seem to attach a zip file here :-((
(warning, spam links and / or waiting time, any better fileshare site you recommend ?
Here is a link to "bindtest.zip" via uploaded.com, beware of spam/ugly pix:
http://ul.to/lqemy5jx
Okay, i will try to post the essence of the files here anyways:
I tried to create a simple Class in C++ containing a StringList and an index.
I Instantiated two Objects of this Class and exposed them via "setContextProperty"
This should be used in QML to initialize a ListView and to be in sync with it.
So whenever a User changes the index in QML, C++ should be notified AND vice versa.
So when i create two Component qml files using the hardwired names set in "setContextProperty" it seems to work fine.
But for the life of me i cannot create a single component file and pass the DataObject to it as a parameter, i simply do not know how to do it, although i tried.
My "final" target ist to create a QML Object dynamically and pass the DataObject to it, this does not work either :-(
So here it comes, code snippets of my sample Project:
Declaring my oh-so-simple Class (DataObject.h)
#ifndef DATAOBJECT_H
#define DATAOBJECT_H
#include <QObject>
#include <QDebug>
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY( int index MEMBER m_index NOTIFY indexChanged )
public slots:
int count() const { return m_Elements.count(); }
QString at(int idx) const { return m_Elements.at(idx); }
public:
void setIndex(int theInt) { m_index = theInt; }
signals:
void indexChanged(int);
public: // too lazy to write accessors for this sample, so make it public
QStringList m_Elements;
private:
int m_index;
};
#endif // DATAOBJECT_H
Registering it in main.cpp:
qmlRegisterType<DataObject>("bindtestTypes", 1, 0, "DataObject");
Here is the part of "dialog.cpp" that initializes and exposes two DataObects:
//preparing first list
m_firstDO.m_Elements = QStringList() << "A" << "B" << "C" << "D";
m_firstDO.setIndex(0);
//preparing second list
m_secondDO.m_Elements = QStringList() << "a" << "b" << "c" << "d";
m_secondDO.setIndex(3);
//publish the 2 Dataobjects
m_engine.rootContext()->setContextProperty( "cppDataList_1", &m_firstDO);
m_engine.rootContext()->setContextProperty( "cppDataList_2", &m_secondDO);
Here is the QML file "ShowLists.qml" that should simply show the 2 ListVies on Top of each other, i commented the 2 NOT working approaches that i would love to work, especially the dynamic one:
import QtQuick 2.2
import QtQuick.Window 2.1
import bindtestTypes 1.0
Window {
visible: true
width: 200
height: 400
Rectangle{
anchors.fill: parent
//dynamic: does not work :-(
// need to click on it to create it
// Rectangle{
// id:upperList
// anchors.top: parent.top;
// anchors.left: parent.left
// width:200
// height:200
// MouseArea{
// anchors.fill: parent
// onClicked: {
// var component = Qt.createComponent("SimpleList.qml");
// var dyncbb = component.createObject(parent, {"theDO": cppDataList_1});
// }
// }
// }
// Rectangle{
// id:lowerList
// anchors.bottom: parent.bottom;
// anchors.left: parent.left
// width:200
// height:200
// MouseArea{
// anchors.fill: parent
// onClicked: {
// var component = Qt.createComponent("SimpleList.qml");
// var dyncbb = component.createObject(parent, {"theDO": cppDataList_2});
// }
// }
// }
//static: would not be my first choice but isnt working anyways...
// SimpleList {
// id:upperList
// property DataObject theDO: cppDataList_1
// anchors.top: parent.top;
// anchors.left: parent.left
// }
// SimpleList {
// id:lowerList
// property DataObject theDO: cppDataList_2
// anchors.bottom: parent.bottom;
// anchors.left: parent.left
// }
//hardwired works, but its not workable for my rather complex project...
SimpleList1 {
id:upperList
anchors.top: parent.top;
anchors.left: parent.left
}
SimpleList2 {
id:lowerList
anchors.bottom: parent.bottom;
anchors.left: parent.left
}
}
}
Here is the first hardwired SimpleList1.qml that works fine, as well as the second:
import QtQuick 2.2
ListView {
id: list_view
width: 200
height: 200
currentIndex: cppDataList_1.index
model: cppDataList_1.count()
delegate: Rectangle {
height: 20
width: 200
Text { text: cppDataList_1.at(index); color: (list_view.currentIndex === index)?"red":"black" }
MouseArea{ anchors.fill: parent; onClicked: list_view.currentIndex = index }
}
onCurrentIndexChanged: cppDataList_1.index = currentIndex;
}
This is the "SimpleList.qml" that i cannot seem to get to work:
import QtQuick 2.2
import bindtestTypes 1.0
Rectangle {
ListView {
id: list_view
property DataObject theDO
width: 200
height: 200
currentIndex: theDO.index
model: theDO.count()
delegate: Rectangle {
height: 20
width: 200
Text { text: theDO.at(index); color: (list_view.currentIndex === index)?"red":"black" }
MouseArea{ anchors.fill: parent; onClicked: list_view.currentIndex = index }
}
onCurrentIndexChanged: theDO.index = currentIndex
}
}
So, can anyone of you help me to get this solved ??
IF you dare to follow the uploaded link and run my sample you can see one more glitch.
It displays 2 Windows, one QQQuickWIndow and a Widget.
In the Widget i can change the indexes as well as in the QML Window.
At first they are in sync but then the QML Window does not get updated anymore by changing the index in the widget, i hope its a glitch and not another general error i made.
Greetings & thanks for any help !
Nils
Argh, i found the problem, i did a very simple mistake:
The property i want to set in the SimpleList Component has to be in the root Object, so instead of this:
Rectangle {
ListView {
id: list_view
property DataObject theDO
...
It has to be done this way:
Rectangle {
property DataObject theDO
ListView {
id: list_view
...
Wow, thats an easy solution for a (seemingly) complex Problem.
Greetings,
Nils

How to do an action in Qml every time a user touches the screen?

I am writing an application for an embedded linux touchscreen device using Qt and Qml.
I need to implement a lock screen that appears after 30 seconds of inactivity.
To do this I've implemented a timer in C++, that changes the state of the program after timeout.
Is it possible to refresh this timer on a global scale every time the user touches the screen, so that I do not have to call the timers' start() slot in every touchable element in my program?
I would also like to refresh this timer even when the user touches a part of the screen where there are no buttons/interactive elements.
Something like this main.qml:
Rectangle {
id: mainRect
width: 800
height: 480
//something like this (oversimplified) pseudo code:
onGlobalUserTouch {
timers.startLockTimer()
}
//end pseudocode
Loader {
id: mainLoader
anchors.fill: parent
source: "FirstPage.qml"
Behavior on opacity {PropertyAnimation{duration:250}}
onLoaded: secondLoader.source = ""
}
states:[
State {
name:"SecondPage"
when: (mainCppClass.state == PanelStates.SECOND_PAGE)
PropertyChanges {
target: mainLoader
source: "SecondPage.qml"
}
}
]
}
All I was able to find on the web was how to implement this in either iOS or Android.
If mainRect is your root component, you can just start your timer in its MouseArea.
// touch area of your root component
MouseArea {
anchors.fill: parent
onClicked: {
timers.startLockTimer();
}
// child component
Rectangle {
color: "yellow"
x: 50; y : 50
width: 100; height: 100
}
}
This does not override descendant MouseAreas. Thus, if you touch area where there are no buttons/interactive elements, your startLockTimer will be called.