How to control caching of HTTP resources in QML? - qt

Consider the following piece of code, which fetches an RSS feed and then displays the images within as a slideshow which loops forever:
import QtQuick 2.2
import QtQuick.XmlListModel 2.0
Rectangle {
id: window
color: "black"
width: 800
height: 480
PathView {
anchors.fill: parent
highlightRangeMode: PathView.StrictlyEnforceRange
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
highlightMoveDuration: 500
snapMode: PathView.SnapOneItem
pathItemCount: 3 // only show previous, current, next
path: Path { // horizontal
startX: -width; startY: height/2
PathLine{x: width*2; y: height/2}
}
model: XmlListModel {
source: "http://feeds.bbci.co.uk/news/business/rss.xml"
query: "/rss/channel/item"
namespaceDeclarations: "declare namespace media = 'http://search.yahoo.com/mrss/';"
XmlRole { name: "image"; query: "media:thumbnail/#url/string()" }
}
delegate: Image {
width: PathView.view.width
height: PathView.view.height
source: image
fillMode: Image.PreserveAspectCrop
}
Timer { // automatically loop through the images
interval: 1000; running: true; repeat: true;
onTriggered: {
parent.incrementCurrentIndex()
}
}
Timer {
interval: 600000; running: true; repeat: true;
onTriggered: parent.model.reload()
}
}
}
This code loads the images from the web as it needs them. However, once an image is no longer displayed it discards the data. The next time the slideshow loops around the image will be reloaded from the web. As a result, the code hits the remote image server once per second for as long as it is running, downloading 50-300KB each time.
The code runs on an embedded system with not much RAM, so caching the decoded image data by keeping the delegate when it is not on the path is not an option.
Instead the caching should be done at the HTTP level, storing the original downloaded files. It should therefore obey the HTTP cache control headers.
The caching should be done in memory only as the system has only a small flash disk.
How can I implement this in Qt? I assume it will involve C++ code, that is fine.

To control the caching behaviour when QML fetches a network resource you would subclass QQmlNetworkAccessManagerFactory and have it create QNetworkAccessManagers with a cache attached. Then you attach the factory to your QQmlEngine:
class MyNAMFactory : public QQmlNetworkAccessManagerFactory
{
public:
virtual QNetworkAccessManager *create(QObject *parent);
};
QNetworkAccessManager *MyNAMFactory::create(QObject *parent)
{
QNetworkAccessManager *nam = new QNetworkAccessManager(parent);
nam->setCache(new QNetworkDiskCache(parent));
return nam;
}
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QQuickView view;
view.engine()->setNetworkAccessManagerFactory(new MyNAMFactory);
view.setSource(QUrl("qrc:///main.qml"));
view.show();
return app.exec();
}
Caches must implement the QAbstractNetworkCache interface. Qt has one built in cache type, QNetworkDiskCache which, as the name implies, saves the cache to disk. There is no built-in class for in-memory caching, but it would be fairly easy to implement one by using a QHash to store the URLs, data, and metadata.

Related

How to run SequentialAnimation in the background?

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]);
}
{
}

Updating multiple QImages on QML::Image view displays only the last one sent

I have Image qml component to display QImages. Following is the QML code for it.
Code:
Item {
Image {
id: my_qimage_viewer
anchors.fill: parent
}
Connections {
target: qimage_selector_cpp_backend
onQImageDisplayRequested: {
my_qimage_viewer.source = next_qimage_source
console.log("New qimage sent for display is : " + my_qimage_viewer.source)
}
}
}
What is working:
I have a C++ class using QQuickImageProvider to supply QImages with different IDs every time.
This technique basically works if I update single QImages on selection of some button by user. I can generate a QImage on the fly & update it on my_qimage_viewer. Also able to display multiple different images on demand from user. This proves that my technique using QQuickImageProvider is basically working.
What is not working
But, here is where the problem arises. when I send many QImages subsequently, like 50-60 of them all in a for loop, then it does not display all but only displays the last one sent for update !
My objective here is to try play 50-60 QImages with some millis gap in between to make them appear like a video/animation. Is it that the Image component is not made for such kind of use? Or is it that I am doing some mistake?
Question:
May be I should wait for each QImage to be display complete before updating the next? How can I do that if that is whats missing?
Is there an example of an app using Image to display multiple QImages making it appear like a video or animation?
if you use a for-loop you do not give time to show and update the images, you should give a little time between each image.
Assuming that the acquisition time of the image is less than a certain value, for example less than 1/60 seconds we could use a NumberAnimation setting in an appropriate step:
Window {
visible: true
width: 640
height: 480
Image {
property int number
id: name
source: "image://numbers/"+number
NumberAnimation on number {
from:0
to: 60
duration: 1000
}
}
}
imageprovider.h
#ifndef IMAGEPROVIDER_H
#define IMAGEPROVIDER_H
#include <QQuickImageProvider>
#include <QPainter>
#include <QTime>
class ImageProvider : public QQuickImageProvider
{
public:
ImageProvider():QQuickImageProvider(QQuickImageProvider::Image){
qsrand(QTime::currentTime().msec());
}
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize){
int width = 100;
int height = 50;
if (size)
*size = QSize(width, height);
QImage img(requestedSize.width() > 0 ? requestedSize.width() : width,
requestedSize.height() > 0 ? requestedSize.height() : height, QImage::Format_RGB32);
img.fill(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
QPainter painter(&img);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.drawText(QRectF(QPointF(0, 0), img.size()), Qt::AlignCenter,
QTime::currentTime().toString("hh:mm:ss.z")+" "+id);
painter.end();
return img;
}
};
#endif // IMAGEPROVIDER_H
A complete example can be found in the following link

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.'
}
}

Avoid QQuickView delayed qml loading

Here's my situation. I'm trying to combine qml with a mostly widget based UI. To do this, I'm using QQuickView with QWidget::createWindowContainer. I can't use QQuickWidget, because I need to convert the window into a native window, and QQuickWidget doesn't like that. But back to the issue.
My problem is that the first time the view is displayed, it takes like half a second to load causing a very obvious flicker. After that I can hide/show the view all I want, it displays immediately. It's only the first time the qml loads. And I'm fairly certain it's the loading of the qml that causes the issue. Because I have two different QQuickViews that get the same qml as their source. But after any one of them loads once, the other has no issues displaying instantly.
I tried to call show() on view early to get it to load in time. But this causes the qml to appear for a brief moment before any of the widgets get displayed.
Has anyone encountered a similar issue? How can I get the QQuickView to behave.
Edit: I'm using Qt 5.4.2, and I can't update to a newer version due to various reasons.
I was going to say that you can use the same approach as in this answer, but it seems that even that is too early to being loading the QML. It's hacky, but the only other thing I can think of is using a very short Timer:
main.cpp:
#include <QtWidgets>
#include <QtQuick>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0) :
QMainWindow(parent)
{
QQuickView *view = new QQuickView();
QWidget *container = QWidget::createWindowContainer(view, this);
container->setFocusPolicy(Qt::TabFocus);
view->rootContext()->setContextProperty("window", view);
view->setSource(QUrl("qrc:/main.qml"));
setCentralWidget(container);
resize(400, 400);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
Item {
anchors.fill: parent
// Connections {
// target: window
// onAfterSynchronizing: loader.active = true
// }
Timer {
running: true
repeat: true
interval: 50
onTriggered: {
loader.active = true
}
}
Loader {
id: loader
active: false
sourceComponent: Column {
anchors.fill: parent
Repeater {
model: 30000
delegate: Button {
text: index
}
}
}
}
BusyIndicator {
running: loader.status === Loader.Null
anchors.centerIn: parent
}
}
For brevity, I chucked the "heavy" QML into sourceComponent, but you can also use the source property to point to a URL.
BusyIndicator runs its animation on the render thread, so it can continue to spin while the GUI thread is blocked.

C++ / QML architecture - a way to reproduce C++ structure in QML?

I am currently trying to develop a quite important application (OS-like) with Qt 5.2 and Qt Quick 2 ; what I would like to do is to have all the logic implemented in C++, the UI being declared thanks to QML. At this point, it seems logical and the way to get around. However, I can’t figure how to do it the clean way.. I’ve read a lot of documentation, tutorials and examples but nothing so big…
Let’s explain a little what I would like to put as an architecture ; let’s say we have a Scene object, which could contains an undefined number of Application objects. What I would like is to define the logic in CPP (how I load the applications from XML, what the scene should have as properties, …) and then show the scene with QML. Also, we have to notice that Scene and Application elements should be re-used as component ; so, here is the basic idea : I’d like to define graphical styles that are common to each object with a file in QML (extending the CPP type).
For example, I could create a file with this content :
Application {
Rectangle { ... }
}
Saying that an application should be representated as a Rectangle ; then, when I create a Scene object that have a list of Application (or one unique Application, to begin with), I would like it to be displayed automatically (‘cause this is a property of Scene object). Is it even possible ? How can I do that ?
I thought that if I extend the C++ object and declare some graphical elements for it, it would be automatic.. But actually it doesn’t look like that !
Maybe there is another way around ?
Thanks
I don't like this question too much, as it's not really asking anything in particular. The Qt documentation is very comprehensive, so I often find it strange when people say they've read documentation, tutorials and examples, and still haven't found what they're looking for. However, I think I understand the gist of what you're asking, and think the answer could be useful to some, so I'll try to answer it.
main.cpp
#include <QtGui/QGuiApplication>
#include <QtQml>
#include <QQuickItem>
#include "qtquick2applicationviewer.h"
class ApplicationItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QString title MEMBER mTitle NOTIFY titleChanged)
public:
ApplicationItem(QQuickItem *parent = 0) : QQuickItem(parent) {
}
public slots:
void close() {
emit closed(this);
}
signals:
void titleChanged(QString title);
void closed(ApplicationItem *app);
private:
QString mTitle;
};
class SceneItem : public QQuickItem
{
Q_OBJECT
public:
SceneItem() {
}
public slots:
void startApp(const QString &qmlFile) {
QQmlComponent *component = new QQmlComponent(qmlEngine(this), QUrl(qmlFile));
if (component->isLoading()) {
QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
this, SLOT(componentStatusChanged()));
} else {
// The component was synchronously loaded, but it may have errors.
if (component->isError()) {
qWarning() << "Failed to start application:" << component->errorString();
} else {
addApp(component);
}
}
}
void componentStatusChanged(QQmlComponent::Status status) {
QQmlComponent *component = qobject_cast<QQmlComponent*>(sender());
if (status == QQmlComponent::Ready) {
addApp(component);
} else if (status == QQmlComponent::Error) {
qWarning() << "Failed to start application:" << component->errorString();
}
}
void appClosed(ApplicationItem *app) {
int appIndex = mApplications.indexOf(app);
if (appIndex != -1) {
mApplications.removeAt(appIndex);
app->deleteLater();
}
}
private:
void addApp(QQmlComponent *component) {
ApplicationItem *appItem = qobject_cast<ApplicationItem*>(component->create());
appItem->setParentItem(this);
connect(appItem, SIGNAL(closed(ApplicationItem*)), this, SLOT(appClosed(ApplicationItem*)));
mApplications.append(appItem);
delete component;
}
QList<ApplicationItem*> mApplications;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
qmlRegisterType<ApplicationItem>("Test", 1, 0, "ApplicationItem");
qmlRegisterType<SceneItem>("Test", 1, 0, "SceneItem");
viewer.setMainQmlFile(QStringLiteral("qml/quick/main.qml"));
viewer.showExpanded();
return app.exec();
}
#include "main.moc"
I represented both classes as QQuickItem subclasses. SceneItem is composed of many ApplicationItem instances, which are added to the scene by invoking startApp(). This slot takes a path to a QML file as its argument. This file could be loaded over a network, or from a local file, hence we account for the possibility of both synchronous and asynchronous loading.
The QML file should describe the visual appearance of an application, and the scene expects its root type to be ApplicationItem. As an example, here's MySweetApp.qml:
import QtQuick 2.0
import QtQuick.Controls 1.0
import Test 1.0
ApplicationItem {
id: someAppStyle
title: "My Sweet App"
width: 100
height: 100
MouseArea {
anchors.fill: parent
drag.target: parent
}
Rectangle {
radius: 4
color: "lightblue"
anchors.fill: parent
Text {
anchors.left: parent.left
anchors.right: closeButton.right
anchors.leftMargin: 4
anchors.top: parent.top
anchors.topMargin: 4
text: someAppStyle.title
}
Button {
id: closeButton
anchors.right: parent.right
anchors.rightMargin: 4
anchors.top: parent.top
anchors.topMargin: 2
onClicked: close()
text: "x"
width: 20
height: width
}
}
}
Applications can close themselves by invoking the close() slot declared in ApplicationItem.
Here's main.qml:
import QtQuick 2.0
import QtQuick.Controls 1.0
import Test 1.0
SceneItem {
id: scene
width: 360
height: 360
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
text: "Launch app"
onClicked: scene.startApp("qml/quick/MySweetApp.qml")
}
}
This is where the SceneItem is declared, along with a simple interface to launch several instances of My Sweet App (it's a very useful app).
I believe this is the most appropriate way to do what you're asking. It avoids the hassle of setting up lists of ApplicationItems in C++ which are exposed to QML (it's actually not that hard, but this is one area where the documentation could be more obvious), and allows the users of your OS freedom in how applications appear. If you want to be more strict in what can be styled, I'd suggest looking into how Qt Quick Controls does styling.
I would advise against using C++ for the logic unless you really need to - use casese to use C++ for the logic are if you have high-performance requirements like realtime data that needs to processed like 10x per second.
As most of the use cases do not have this requirement, it is better to use QML also for application logic because it will save up to 90% source code (and time) compared with C++. Especially in the beginning of development, you are way faster to code the logic in QML and get results faster. You can later on still move the logic to C++ if needed.
There are 2 good guides about this topic available that explain this in more detail and come with source code examples:
QML Architecture Tips and why/how to avoid C++ in your Qt app
QML Architecture Best Practices and Examples

Resources