Code execution while mousearea pressed qml - qt

I checked the different mouseArea events in the documentation and there is no chance to execute a js function while a mouseArea is being pressed.
As I am not finding a direct way, so I went for this workaround.
I set a Timer to update my panel in case the added bool property isPressed of my mousearea is set to true.
Mouse area:
MouseArea {
id: myMouseArea
//pos and size removed. Not relevant
property bool isPressed: false
onPressed: {
isPressed = true;
}
onReleased: {
isPressed = false;
}
}
Timer:
Timer {
id: updatePanelTimer
interval: 100
repeat: true
running: true
triggeredOnStart: true
onTriggered: updatePanel()
}
Function:
function updatePanel() {
if (myMouseArea.isPressed) {
//run code
}
}
Is there a more direct way to have a code executed while a mouseArea is being pressed? I would like to know if there is a way to do this whithin the mouseArea itself, mainly if its possible without the need of the timer.

If you want to run code only while mouse is in pressed state :
MouseArea {
id: myMouseArea
//pos and size removed. Not relevant
property bool isPressed: false
onPressed: {
updatePanelTimer.start()
}
onReleased: {
updatePanelTimer.stop()
}
}

Related

QML: Bind loop detected without double assignment

As far as I know the bind loop happens when I try to assign two properties each other. Example:
CheckBox {
checked: Settings.someSetting
onCheckedChanged: {
Settings.someSetting = checked;
}
}
but in my scenario I can't see such a "double assignment". I report here the full code:
import QtQuick 2.7
import QtQuick.Window 2.3
Window {
visible: true;
width: 500
height: 500
Rectangle {
id: main
anchors.fill: parent
color: "black"
property bool spinning: true
property bool stopping: false
Rectangle {
x: 0.5 * parent.width
y: 0.5 * parent.height
width: 10
height: 200
radius: 5
color: 'red'
transformOrigin: Item.Top
rotation: {
if (main.stopping)
{
main.spinning = false;
main.stopping = false;
}
return timer.angle
}
}
Timer {
id: timer
interval: 5
repeat: true
running: true
onTriggered: {
if (main.spinning) angle += 1;
}
property real angle
}
MouseArea {
id: control
anchors.fill: parent
onClicked: {
main.stopping = true;
}
}
}
}
When you click with the mouse you will get the warning:
qrc:/main.qml:17:9: QML Rectangle: Binding loop detected for property "rotation"
I don't see my mistake. I'm using flags (bool variables) to control the execution of my code. I know in this case I can just stopping the timer directly, but the actual program is more complex than this example.
The binding is in the following lines:
rotation: {
if (main.stopping)
{
main.spinning = false;
main.stopping = false;
}
return timer.angle
}
The change of rotation is triggered by the change of main.stopping: let's say that change main.stopping is given by the mouseArea, then it will be called a rotation, but inside this there is an if, and in this you are changing back to main.stopping , where he will call rotation back.
If a property in QML changes all the elements that depend on it will change

Qt Quick QML MouseArea autorepeat on press

Hopefully I'm not missing something obvious here.
I am writing an app and have made a zoom in button with an Image and a MouseArea. I need the button to repeat a method call after, say, every second to zoom in while holding the mouse button down. It isn't entirely obvious how to make this repeat. Right now I have:
Rectangle {
id:zoomInBtn
Image {
id: zoomInImg
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
source: zoomIn.pressed ? ":/img/zoom_in_sel" : ":/img/zoom_in_unsel"
}
MouseArea {
id: zoomIn
anchors.fill: parent
onPressed: { cameraController.zoomIn(0.5); }
}
I have also tried with
onPressAndHold: { cameraController.zoomIn(0.5); }
which does basically the same, although with a small delay as expected, but I need to repeat this action every second while the mouse button is held.
To perform the task you need you must use a Timer. the timer must remain active while the containsMouse is active. you must also enable triggeredOnStart to run immediately if the timer is activated.
Rectangle {
id:zoomInBtn
Image {
id: zoomInImg
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
source: zoomIn.pressed ? ":/img/zoom_in_sel" : ":/img/zoom_in_unsel"
}
MouseArea {
id: zoomIn
anchors.fill: parent
}
Timer {
id: timer
interval: 1000
repeat: true
triggeredOnStart: true
running: zoomIn.containsMouse
onTriggered: cameraController.zoomIn(0.5) //task
}
}
}

How to set pressed=false for mouse area QML

I've faced with next situation:
I have an Item with MouseArea. This is my button on the screen - keyboard.
I persorm long press for button on keyboard.
I don't release this button (holding it).
In onPressed{} signal handler I'm opening another screen.
My buton from keyboard doesn't receive anymore Release signal.
When I come back to the previous keyboard - my button still pressed.
I've tried to set "pressed"=false, but this is readonly property.
I've tried to emit released() signal, but it doesn't clear the "pressed' flag.
But I think that I did it in wrong way. Function release takes parameter- mause - mouseEvent. The mouse parameter provides information about the click, including the x and y position of the release of the click, and whether the click was held. And I didn't find how to set it correct.
I've tried to update MouseArea, it doesn't help.
I don't know what else I have to try to unsed the "pressed" flag.
My mouse area is simple:
MouseArea {
id: mouseArea
property bool haveToRelease: false
onHaveToReleaseChanged: {
if(haveToRelease)
{
console.log("BaseButton.qml: call canceled()")
released()
haveToRelease = false
}
}
anchors.fill: parent
hoverEnabled: true
onReleased: {
console.log("BaseButton.qml: onReleased")
}
onPressedChanged: {
console.log("BaseButton.qml: onPressedChanged, pressed = ", pressed)
}
}
In my button I have the next handler:
onVisibleChanged: {
if(config.isToyota && !visible && pressed) {
console.log("Key.qml :: config.isToyota && !visible && pressed")
releaseButton = true
}
}
I will be vary glad if somebody help me to solve this issue!
Thanks a lot!
as for me that works as expected:
Window {
visible: true
width: 640
height: 480
Rectangle {
width: 100
height: 100
anchors.centerIn: parent
color: "orange"
MouseArea {
anchors.fill: parent
onPressed: {
console.log("pressed");
wnd.show();
released(mouse);
}
onReleased: {
console.log("released");
}
onPressedChanged: console.log("pressed: " + pressed );
}
}
Window {
id: wnd
width: 200
height: 200
}
}
The output:
qml: pressed
qml: released
qml: pressed: true
qml: pressed: false

BusyIndicator does not show up

I want to show a BusyIndicator while a long process is going on. The problem is it does not show up when I make it run and shows afterwards when the process is completed. According to the docs
The busy indicator should be used to indicate activity while content is being loaded or the UI is blocked waiting for a resource to become available.
I have created a minimal code that based upon the original code
Window {
id: win
width: 300
height: 300
property bool run : false
Rectangle {
anchors.fill: parent
BusyIndicator {
anchors.centerIn: parent
running: run
}
MouseArea {
anchors.fill: parent
onClicked: {
run = true
for(var a=0;a<1000000;a++) { console.log(a) }
run = false
}
}
}
}
So when the Rectangle is clicked I want to display the BusyIndicator for the time till the calculations gets completed.
For example purpose I have used the for loop here. In actual scenario I call a function (which inserts some 1000 rows into the Database) through the ContextProperty. But in that case too the BusyIndicator is not displayed.
Am I doing it the right way? Or what would be the best way to do it?
You cannot view your BusyIndicator just because long operation in onClicked handler blocks application GUI and indicator does not update. You should run such operation in a different thread to avoid freezing of GUI. Simple example:
QML
Window {
id: win
width: 300
height: 300
property bool run : false
Rectangle {
anchors.fill: parent
BusyIndicator {
id: busy
anchors.centerIn: parent
running: win.run
}
MouseArea {
anchors.fill: parent
onClicked: {
win.run = true
thread.sendMessage({run : true});
}
}
WorkerScript {
id: thread
source: "handler.js"
onMessage: {
win.run = messageObject.run;
}
}
}
}
handle.js
WorkerScript.onMessage = function(message) {
if(message.run === true) {
for(var a=0;a<1000000;a++) { console.log(a) }
}
WorkerScript.sendMessage({run : false});
}
There is a way to do this using QQuickWindow's afterSynchronizing signal:
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
width: 400
height: 400
visible: true
Component.onCompleted: print(Qt.formatDateTime(new Date(), "mm:ss:zzz"), "QML loaded")
onAfterSynchronizing: {
print(Qt.formatDateTime(new Date(), "mm:ss:zzz"), "Window content rendered")
if (!loader.item) {
loader.active = true
}
}
Item {
anchors.fill: parent
BusyIndicator {
running: !loader.item
anchors.centerIn: parent
}
Loader {
id: loader
active: false
anchors.fill: parent
sourceComponent: Text {
wrapMode: Text.Wrap
Component.onCompleted: {
for (var i = 0; i < 500; ++i) {
text += "Hello, ";
}
}
}
}
}
}
The idea is to use a Loader to have control over when the expensive operation happens. You could also use a dynamically loaded component via Qt.createQmlObject(), or Qt.createComponent() to dynamically load a component in a separate file.
If you run the example, you'll see that you get the following output:
qml: 58:12:356 QML loaded
qml: 58:12:608 Window content rendered
We use QQuickWindow's afterSynchronizing signal to know when the content of the window has been displayed, and only act on it the first time (via if (!loader.item)).
When the signal is initially emitted, we can be sure that the BusyIndicator has started its animation, so the user will actually see a spinning icon.
Once the Loader has finished loading the text, its item property will become non-null and the BusyIndicator will disappear.
Run into the same problem today! I will assume you are controlling your BusyIndicator from a C++ property called busy. And you are setting busy to true just before your calculations and to false just after. Doing this solved it for me. It's not a very elegant solution but it works:
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);
}

How can I switch the focus for the pop-up window?

I encounter a problem which is that the pop-up window cannot get the focus when it is shown. I tried to use the activefocus function in main window, but it doesn't work. It is supposed that if I press the enter key, the pop-window will be closed. How can I get the focus for the pop-up window? Thanks.
...
GridView {
id:grid_main
anchors.fill: parent
focus: true
currentIndex: 0
model: FileModel{
id: myModel
folder: "c:\\folder"
nameFilters: ["*.mp4","*.jpg"]
}
highlight: Rectangle { width: 80; height: 80; color: "lightsteelblue" }
delegate: Item {
width: 100; height: 100
Text {
anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }
text: fileName
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
}
}
}
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show();
subWindow.forceActiveFocus();
event.accepted = true;
grid_main.focus = false;
}
}
}
Window {
id: subWindow
Keys.onPressed: {
if (event.key == 16777220) {//press enter
subWindow.close();
}
}
}
...
Let's start with some basics:
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show()
...
event.accepted = true
}
}
Not to mention how error-prone it is, just for the sake of readability, please don't hard-code enum values like 16777220. Qt provides Qt.Key_Return and Qt.Key_Enter (typically located on the keypad) and more conveniently, Keys.returnPressed and Keys.enterPressed signal handlers. These convenience handlers even automatically set event.accepted = true, so you can replace the signal handler with a lot simpler version:
Keys.onReturnPressed: {
subWindow.show()
...
}
Now, the next thing is to find the correct methods to call. First of all, the QML Window type does not have such method as forceActiveFocus(). If you pay some attention to the application output, you should see:
TypeError: Property 'forceActiveFocus' of object QQuickWindowQmlImpl(0x1a6253d9c50) is not a function
The documentation contains a list of available methods: Window QML type. You might want to try a combination of show() and requestActivate().
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
Then, you want to handle keys in the sub-window. Currently, you're trying to attach QML Keys to the Window. Again, if you pay attention to the application output, you should see:
Could not attach Keys property to: QQuickWindowQmlImpl(0x1ddb75d7fe0) is not an Item
Maybe it's just the simplified test-case, but you need to get these things right when you give a testcase, to avoid people focusing on wrong errors. Anyway, what you want to do is to create an item, request focus, and handle keys on it:
Window {
id: subWindow
Item {
focus: true
Keys.onReturnPressed: subWindow.close()
}
}
Finally, to put the pieces together, a working minimal testcase would look something like:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: window
width: 300
height: 300
visible: true
GridView {
focus: true
anchors.fill: parent
// ...
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
}
Window {
id: subWindow
Item {
focus: true
anchors.fill: parent
Keys.onReturnPressed: subWindow.close()
}
}
}
PS. Key events rely on focus being in where you expect it to be. This may not always be true, if the user tab-navigates focus elsewhere, for example. Consider using the Shortcut QML type for a more reliable way to close the popup.

Resources