How to run SequentialAnimation in the background? - qt

I'm designing Qt5.7/QML application. The main idea is that pressing a button will trigger shell command which takes long time. The results of this command should be reflected in widgets.
In the meantime, I need SequentialAnimation to run, to show the user that something is happening in the background. So I start the animation and then call the process which sends the shell command.
But the animation seems to be freezed as the entire GUI until the shell command returns.
Any suggestions?
QML Code:
CircularGauge {
id: speedometer_wr
x: 199
y: 158
value: valueSource.bps
maximumValue: 400
width: height
height: container.height * 0.5
stepSize: 0
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: 46
}
style: DashboardGaugeStyle {}
NumberAnimation {
id: animation_wr
running: false
target: speedometer_wr
property: "value"
easing.type: Easing.InOutSine
from: 0
to: 300
duration: 15000
}
}
Button {
id: button_benchmark
x: 168
y: 26
width: 114
height: 47
text: qsTr("Benchmark")
tooltip: "Execute the performance test"
checkable: true
function handle_becnhmark() {
speedometer_wr.value = 0;
// Uncheck the button
button_benchmark.checked = false;
// Start the animation
animation_wr.start();
// Run the benchmark
var res = backend.run_benchmark();
// Stop the animation
animation_wr.stop();
speedometer_wr.value = parseFloat(res.split(",")[0]);
}
onClicked: {handle_becnhmark()}
}
Cpp Code:
#include <QProcess>
#include <QtDebug>
#include "backend.h"
#include <QRegExp>
BackEnd::BackEnd(QObject *parent) : QObject(parent)
{
}
QString BackEnd::run_benchmark() {
qDebug() << "in run_benchmark";
QProcess proc;
// Execute shell command
proc.start(<LONG_SHELL_COMMAND>);
if (!proc.waitForStarted())
return "";
if (!proc.waitForFinished())
return "";
// Get the results, parse and return values
QString result(proc.readAll());
return result;
}

It is problem of singlethread applications. You need to run your execution of shell command in the another thread. Just look at QThread example of using. Main thing is that you need a signal-slot based communication between you GUI and backend (if you are calling some "hard" functions in backend).
Working with threads is make sence in many areas like working with hard drive (reading/writing to file), requests to server or database. All of this can be done synchrony or asynchrony. Base meaning for you - would it be freezes main process or not (answer - asynchronous methods wouldn't).
In your app are using QProcess to call some external command. QProcess start() method will execute this command asynchronously by itself. It means you no need to use QThreads. But it make sence if you are using waitForFinished(). If you will read QProcess reference you will see that waitForStarted() and waitForFinished() methods is a synchronous. That means they will freeze main process and GUI indeed.
So what you need is
declare pointer to QProcess in class header (it would be better to use QSharedPointer<QProcess>)
class BackEnd : public QObject {
...
private slots:
void handleProcFinished(int exitCode, QProcess::ExitStatus exitStatus);
signals:
void executionEnds(const QString &res);
private:
QProcess* proc;
}
In source you need initialize proc and connect QProcess finished() signal to slot that will push the output of executed by proc command with another signal in BackEnd.
BackEnd::BackEnd(QObject *parent) : QObject(parent) {
proc = new QProcess();
connect(proc, &QProcess::finish, this, &BackEnd::handleProcFinished);
}
void handleProcFinished(int exitCode, QProcess::ExitStatus exitStatus) {
//check code
...
QString result("");
if (all is fine) {
result = proc->readAll();
} else {
//debug or something else
}
emit executionEnds(result);
}
In QML side it will be something like this:
Button {
onClicked: {
speedometer_wr.value = 0;
// Uncheck the button
button_benchmark.checked = false;
// Start the animation
animation_wr.start();
// Run the benchmark
var res = backend.run_benchmark();
}
}
Connections {
target: backend //[name of some contextProperty which will be emiting the signals]
onExecutionEnds: {
animation_wr.stop();
if (res != "") {
speedometer_wr.value = parseFloat(res.split(",")[0]);
}
{
}

Related

How to trigger a .sh file or a bash command on a Qt4 button click?

Right now, all I have is my QML file, with the button.
/*PlasmaComponents.*/ToolButton {
id: shutdownButton
text: i18n("Shutdown")
iconSource: "system-shutdown"
enabled: power.canShutdown
onClicked: doTheThing();
}
From reading about QML, it seems I'll need to add a C++ process. Is this possible with QML4? If not, could QProcess work? What files would need changing if so?
you can write a process executor class like this :
#include <QProcess>
#include <QVariant>
class Process : public QProcess {
Q_OBJECT
public:
Process(QObject *parent = 0) : QProcess(parent) { }
Q_INVOKABLE void start(const QString &program, const QVariantList &arguments) {
QStringList args;
// convert QVariantList from QML to QStringList for QProcess
for (int i = 0; i < arguments.length(); i++)
args << arguments[i].toString();
QProcess::start(program, args);
}
Q_INVOKABLE QByteArray readAll() {
return QProcess::readAll();
}
};
and register them :
#include <QtQml>
#include "process.h"
qmlRegisterType<Process>("Process", 1, 0, "Process");
and finally run your command from QML :
import QtQuick 2.4
import QtQuick.Controls 1.3
import Process 1.0
ApplicationWindow {
width: 800
height: 480
visible: true
Text {
id: text
}
Process {
id: process
onReadyRead: text.text = readAll();
}
Timer {
interval: 1000
repeat: true
triggeredOnStart: true
running: true
onTriggered: process.start("poweroff", [ "-f" ]);
}
-f make forced to power off immediately.

How to refresh a combo-box in qml after sending a signal

Basically, I have a combo-box in qml that I populate using a QStringList. However, I'm not able to refresh the combo-box (reload) to show the list has changed. I looked into doing that using the Loader but I couldn't figure it out. Can someone guide me in how to do it.
network.qml
Popup{
contentItem: Rectangle{
LabelValueList {
id: list1
row1: LabelValue {
id: row1
row2: LabelValue {
id: row2
value: ComboBox {
id: combobox
model: ListModel {
id: comboModel
Component.onCompleted: {
//..
}
}
}
}
}
}
}
}
network.h
class Network : public QObject{
Q_OBJECT
Q_PROPERTY(QStringList listOfNetworks READ m_listOfNetworks NOTIFY updateNetworks)
private:
QStringList m_listOfNetworks;
public:
explicit Network(QObject *parent = 0);
QStringList listOfNetworks() const;
public slots:
void slot_scanNetworks();
signals:
void updateNetworks();
};
network.cpp
Network::Network(QObject *parent) : QObject (parent){
}
void Network::slot_scanNetworks(){
QFile SSIDsFile("/home/root/networking/listOfWifiNetworks.txt");
m_listOfNetworks.clear();
if (!SSIDsFile.open(QIODevice::ReadOnly | QIODevice::Text)){
//
}
else{
QTextStream scanNetworkStream(&SSIDsFile);
while (!scanNetworkStream.atEnd()){
QString line = scanNetworkStream.readLine();
if (line.size() != 0){
QStringList lineSplit = line.split(' ');
m_listOfNetworks.append(lineSplit[1]);
}
}
}
SSIDsFile.close();
emit updateNetworks();
}
How do I reload the combo-box of row2 to refresh the list? It only get's the list at the beginning but i want to update the drop-down (combo-box) when I emit the signal updateNetworks(). I tried using the Loader and setting the source.Component of it to the id of row2 but I kept getting the error "Error: Cannot assign QObject* to QQmlComponent". Any help?
I am not pro but maybe help u this post .
you can use a timer on ur application and set 1s to refresh this and make a variable and when call signal call this variable
example :
// make a variable type of bool by value = false
property bool refreshMyBoxNow : false
//add this timer to ur project
Timer
{
id: timeToRefreshBox
//interval = time to tick . (1000) = 1sec
interval: 1; running: true; repeat: true
onTriggered:
{
//your code here for refresh box
}
}

How can I reset a timer every time I receive a touch event from a qml page

import QtQuick 2.6;
import QtQuick.Controls 2.1 ;
import QtQuick.Layouts 1.3 ;
Page{
id: page
width: 800
height: 1024
background: Rectangle {
color: "black" ;
anchors.fill:parent ;
}
Rectangle {
id:rect1
x: 0
y:10
width: 100
height: 100
color : "red"
MouseArea {
anchors.fill: parent
onClicked: tmr.restart()
}
}
Rectangle {
id:rect2
x: 0
y:110
width: 100
height: 100
color : "blue"
MouseArea {
anchors.fill: parent
onClicked: tmr.restart()
}
}
Timer {
id : tmr
interval : 30000
repeat : true
running: true
onTriggered: {
console.log ("hello world ")
}
}
}
I develop a software for embedded imx6 freescale device using qt framework.
Basically I just want to restart my timer every time I click and every time I get a touch event on my screen whether the click/touch happen inside the mouse area of my rectangles or outside of them.
The idea is similar to a screensaver.
There are multiple ways, and the right way depends on your requirements.
If you don't need to guarantee that the timer triggers during a input you can just layer a MouseArea on top of everything. In this MouseArea you handle the pressed-signals, but dont accept them.
This allows you to handle the mouse input in the lower layers later. However you only realize whenever a new press happens, and the Timer might trigger e.g. during a half-an-hour finger-move input.
The second way is to have all MouseAreas report uppon their handled signals, that the signal happend, to reset the Timer. For all unhandled signals, you layer a MouseArea beneath everything else, handle all signals there to catch what has been falling through.
Resorting to C++ you might create a Item at the root of your Item-tree, and override the childMouseEventFitler
See my answer here for more on this.
In this case you should add a MouseArea right inside this Item, so it has something to filter at any place.
Note! This method will be triggered for each MouseArea that might be under your click. But in your scenario, this would be fine, I guess.
Thanks to GrecKo I looked into the general eventFilter again, and indeed it is really easy.
you create a simple QObject following the singleton pattern, in which you reimplement the eventFilter-method, so that it will emit a signal
mouseeventspy.h
#pragma once
#include <QObject>
#include <QtQml>
#include <QQmlEngine>
#include <QJSEngine>
class MouseEventSpy : public QObject
{
Q_OBJECT
public:
explicit MouseEventSpy(QObject *parent = 0);
static MouseEventSpy* instance();
static QObject* singletonProvider(QQmlEngine* engine, QJSEngine* script);
protected:
bool eventFilter(QObject* watched, QEvent* event);
signals:
void mouseEventDetected(/*Pass meaningfull information to QML?*/);
};
mouseeventspy.cpp
#include "mouseeventspy.h"
#include <QQmlEngine>
#include <QJSEngine>
#include <QEvent>
MouseEventSpy::MouseEventSpy(QObject *parent) : QObject(parent)
{
qDebug() << "created Instance";
}
// This implements the SINGLETON PATTERN (*usually evil*)
// so you can get the instance in C++
MouseEventSpy* MouseEventSpy::instance()
{
static MouseEventSpy* inst;
if (inst == nullptr)
{
// If no instance has been created yet, creat a new and install it as event filter.
// Uppon first use of the instance, it will automatically
// install itself in the QGuiApplication
inst = new MouseEventSpy();
QGuiApplication* app = qGuiApp;
app->installEventFilter(inst);
}
return inst;
}
// This is the method to fullfill the signature required by
// qmlRegisterSingletonType.
QObject* MouseEventSpy::singletonProvider(QQmlEngine *, QJSEngine *)
{
return MouseEventSpy::instance();
}
// This is the method is necessary for 'installEventFilter'
bool MouseEventSpy::eventFilter(QObject* watched, QEvent* event)
{
QEvent::Type t = event->type();
if ((t == QEvent::MouseButtonDblClick
|| t == QEvent::MouseButtonPress
|| t == QEvent::MouseButtonRelease
|| t == QEvent::MouseMove)
&& event->spontaneous() // Take only mouse events from outside of Qt
)
emit mouseEventDetected();
return QObject::eventFilter(watched, event);
}
Than you register it as singleton type to QML like this:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "mouseeventspy.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterSingletonType<MouseEventSpy>("MouseEventSpy", 1, 0, "MouseEventSpy", MouseEventSpy::singletonProvider);
// We do this now uppon creation of the first instance.
// app.installEventFilter(MouseEventSpy::instance());
engine.load(QUrl(QStringLiteral("main.qml")));
return app.exec();
}
Now in QML you can import the instance of the singleton in the necessary files and use the signal, e.g. to reset a Timer
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import MouseEventSpy 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Connections {
target: MouseEventSpy
onMouseEventDetected: myTimer.restart()
}
Timer {
id: myTimer
interval: 1000
onTriggered: console.log('It has been 1 seconds since the last mouse event')
}
Text {
anchors.center: parent
text: myTimer.running ? 'Timer is Running\nMove the mouse to reset'
: 'Move the Mouse to make the timer run again.'
}
}

Loading multiple images on a page for Blackberry 10

I am working on BB10 cascades. I am trying to load multiple images from network. I have referred following example and I have modified it to load a single image. Sample code is as follows:
main.qml
import bb.cascades 1.2
Page {
Container {
id: outer
Container {
preferredHeight: 500
preferredWidth: 768
layout: DockLayout {}
onCreationCompleted: {}
// The ActivityIndicator that is only active and visible while the image is loading
ActivityIndicator {
id: activity
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
preferredHeight: 300
visible: _loader.loading
running: _loader.loading
}
// The ImageView that shows the loaded image after loading has finished without error
ImageView {
id: image
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
image: _loader.image
visible: !_loader.loading && _loader.label == ""
}
// The Label that shows a possible error message after loading has finished
Label {
id: lable
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
preferredWidth: 500
visible: !_loader.loading && !_loader.label == ""
text: _loader.label
multiline: true
}
}
Button {
text: "load Image"
onClicked: {
_loader.load();
console.log("loading:::"+_loader.loading);
}
}
}
}
applicatioui.hpp
#ifndef ApplicationUI_HPP_
#define ApplicationUI_HPP_
#include "imageloader.hpp"
#include
namespace bb
{
namespace cascades
{
class LocaleHandler;
}
}
class QTranslator;
class ApplicationUI : public QObject
{
Q_OBJECT
public:
ApplicationUI();
virtual ~ApplicationUI() {}
Q_INVOKABLE void prepareImage();
Q_INVOKABLE void loadImage();
Q_INVOKABLE ImageLoader* getImageloadderInstance();
private slots:
void onSystemLanguageChanged();
private:
ImageLoader* image;
QTranslator* m_pTranslator;
bb::cascades::LocaleHandler* m_pLocaleHandler;
};
#endif /* ApplicationUI_HPP_ */
applicatioui.cpp
#include "applicationui.hpp"
#include
#include
#include
#include
using namespace bb::cascades;
ApplicationUI::ApplicationUI() :
QObject()
{
// prepare the localization
m_pTranslator = new QTranslator(this);
m_pLocaleHandler = new LocaleHandler(this);
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
bool res = QObject::connect(m_pLocaleHandler, SIGNAL(systemLanguageChanged()), this, SLOT(onSystemLanguageChanged()));
// This is only available in Debug builds
Q_ASSERT(res);
// Since the variable is not used in the app, this is added to avoid a
// compiler warning
Q_UNUSED(res);
// initial load
onSystemLanguageChanged();
// Create scene document from main.qml asset, the parent is set
// to ensure the document gets destroyed properly at shut down.
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
qml->setContextProperty("_loader",this);
// Create root object for the UI
AbstractPane *root = qml->createRootObject();
if(root)
{
}
// Set created root object as the application scene
Application::instance()->setScene(root);
}
void ApplicationUI::onSystemLanguageChanged()
{
QCoreApplication::instance()->removeTranslator(m_pTranslator);
// Initiate, load and install the application translation files.
QString locale_string = QLocale().name();
QString file_name = QString("loading_Image_%1").arg(locale_string);
if (m_pTranslator->load(file_name, "app/native/qm")) {
QCoreApplication::instance()->installTranslator(m_pTranslator);
}
}
ImageLoader* ApplicationUI::getImageloadderInstance()
{
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
return (image);
}
void ApplicationUI::prepareImage()
{
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
}
void ApplicationUI::loadImage()
{
image->load();
}
Now I want to load multiple images. I tried to create QList<QObject*>* image;
and add instances of the ImageLoader class, but I don't know how to access it in main.qml.
Any ideas how to do so?
https://github.com/RileyGB/BlackBerry10-Samples/tree/master/WebImageViewSample
follow this link there is a control called "WebImageView". you just have to set the url of image and its done. int main.qml coder have shown user friendly UI.
thanks to #Bojan Kogoj

Assigning Keyboard Shortcuts to QML Components

I am deep into building a Desktop Application with QML and Qt Creator and I am currently researching keyboard handling and how it works with QML elements. I am already aware of the lack of proper QML replacements for Desktop Widgets.
My current problem is that I wish to assign some global keyboard shortcuts to some particular QML components (like assigning keyboard shortcuts to buttons on the GUI) which should activate them. The best I could manage is to use FocusScopes and Key Navigation to be able to just navigate the GUI via keyboards, but this isn't the same thing.
Can anyone suggest what to do in this scenario? Is there any such feature coming in with Qt 5? I couldn't find any information on this on the Internet.
Answering my own question as the Shortcuts are now possible to implement in Qt 5.1.1.
Shortcuts can be easily bound to QtQuick controls like Button, ToolButtons and MenuItem using the QML Action item. e.g. :
ApplicationWindow {
...
ToolButton { action: openAction } // Add a tool button in a ToolBar
...
Action {
id: openAction
text: "&Open"
shortcut: "Ctrl+O"
onTriggered: // Do some action
tooltip: "Open an image"
}
}
Pressing Ctrl+O will execute the action specified in the onTriggered section.
Refer to Qt Quick Controls Gallery example
Starting from Qt 5.9 the desired behavior is even included:
import QtQuick 2.9
Item {
Shortcut {
context: Qt.ApplicationShortcut
sequences: [StandardKey.Close, "Ctrl+W"]
onActivated: {
container.clicked()
console.log("JS: Shortcut activated.")
}
}
}
If you omit the context, it will only work for currently active windows, otherwise for the entire application, see the documentation.
You can totally use shortcut in QML by using EventFilter in C++(Qt).
You can do by below steps:
1. Create a Shortcut class by C++.
2. Register QML Type for Shortcut class
3. Import Shortcut to QML file and handle it.
#ifndef SHORTCUT_H
#define SHORTCUT_H
#include <QDeclarativeItem>
class Shortcut : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant key READ key WRITE setKey NOTIFY keyChanged)
public:
explicit Shortcut(QObject *parent = 0);
void setKey(QVariant key);
QVariant key() { return m_keySequence; }
bool eventFilter(QObject *obj, QEvent *e);
signals:
void keyChanged();
void activated();
void pressedAndHold();
public slots:
private:
QKeySequence m_keySequence;
bool m_keypressAlreadySend;
};
#endif // SHORTCUT_H
#include "shortcut.h"
#include <QKeyEvent>
#include <QCoreApplication>
#include <QDebug>
#include <QLineEdit>
#include <QGraphicsScene>
Shortcut::Shortcut(QObject *parent)
: QObject(parent)
, m_keySequence()
, m_keypressAlreadySend(false)
{
qApp->installEventFilter(this);
}
void Shortcut::setKey(QVariant key)
{
QKeySequence newKey = key.value<QKeySequence>();
if(m_keySequence != newKey) {
m_keySequence = key.value<QKeySequence>();
emit keyChanged();
}
}
bool Shortcut::eventFilter(QObject *obj, QEvent *e)
{
if(e->type() == QEvent::KeyPress && !m_keySequence.isEmpty()) {
//If you want some Key event was not filtered, add conditions to here
if ((dynamic_cast<QGraphicsScene*>(obj)) || (obj->objectName() == "blockShortcut") || (dynamic_cast<QLineEdit*>(obj)) ){
return QObject::eventFilter(obj, e);
}
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(e);
// Just mod keys is not enough for a shortcut, block them just by returning.
if (keyEvent->key() >= Qt::Key_Shift && keyEvent->key() <= Qt::Key_Alt) {
return QObject::eventFilter(obj, e);
}
int keyInt = keyEvent->modifiers() + keyEvent->key();
if(!m_keypressAlreadySend && QKeySequence(keyInt) == m_keySequence) {
m_keypressAlreadySend = true;
emit activated();
}
}
else if(e->type() == QEvent::KeyRelease) {
m_keypressAlreadySend = false;
}
return QObject::eventFilter(obj, e);
}
qmlRegisterType<Shortcut>("Project", 0, 1, "Shortcut");
import Project 0.1
Rectangle {
.................
.................
Shortcut {
key: "Ctrl+C"
onActivated: {
container.clicked()
console.log("JS: " + key + " pressed.")
}
}
}
So assuming you are calling a function on that button click event like this,
Button {
...
MouseArea {
anchor.fill: parent
onClicked: callThisFunction();
}
}
Then you can assign assign global keyboard shortcuts in this way. But the limitation is the Global QML element (a parent element which holds all other QML elements) should have the focus. Ex. :
Rectangle {
id: parentWindow
...
...
Button {
...
MouseArea {
anchor.fill: parent
onClicked: callThisFunction();
}
}
Keys.onSelectPressed: callThisFunction()
}
This is not exactly what you want but it may help.

Resources