1
0
Fork 0

Remote-canvas image support, partially working.

This commit is contained in:
James Turner 2016-12-21 10:25:58 +00:00
parent 57432c20ad
commit 6c0e9b747d
10 changed files with 246 additions and 30 deletions

View file

@ -32,6 +32,8 @@ set(SOURCES
canvastreemodel.h
fgqcanvasfontcache.cpp
fgqcanvasfontcache.h
fgqcanvasimageloader.cpp
fgqcanvasimageloader.h
)
qt5_wrap_ui(uic_sources temporarywidget.ui)

View file

@ -52,6 +52,12 @@ FGQCanvasFontCache *FGQCanvasFontCache::instance()
return s_instance;
}
void FGQCanvasFontCache::setHost(QString hostName, int portNumber)
{
m_hostName = hostName;
m_port = portNumber;
}
void FGQCanvasFontCache::onFontDownloadFinished()
{
QByteArray fontPath = sender()->property("font").toByteArray();
@ -90,7 +96,11 @@ void FGQCanvasFontCache::lookupFile(QByteArray name)
{
QString path = QStandardPaths::locate(QStandardPaths::CacheLocation, name);
if (path.isEmpty()) {
QUrl url = QUrl("http://localhost:8080/Fonts/" + name);
QUrl url;
url.setScheme("http");
url.setHost(m_hostName);
url.setPort(m_port);
url.setPath("/Fonts/" + name);
Q_FOREACH (QNetworkReply* transfer, m_transfers) {
if (transfer->url() == url) {

View file

@ -20,6 +20,7 @@ public:
static void initialise(QNetworkAccessManager* nam);
static FGQCanvasFontCache* instance();
void setHost(QString hostName, int portNumber);
signals:
void fontLoaded(QByteArray name);
@ -30,6 +31,8 @@ private slots:
private:
QNetworkAccessManager* m_downloader;
QHash<QByteArray, QFont> m_cache;
QString m_hostName;
int m_port;
void lookupFile(QByteArray name);

View file

@ -5,6 +5,7 @@
#include "fgcanvaspaintcontext.h"
#include "localprop.h"
#include "fgqcanvasimageloader.h"
FGQCanvasImage::FGQCanvasImage(FGCanvasGroup* pr, LocalProp* prop) :
FGCanvasElement(pr, prop)
@ -45,10 +46,31 @@ void FGQCanvasImage::markImageDirty()
void FGQCanvasImage::rebuildImage() const
{
qDebug() << "source" << _propertyRoot->value("source", QString());
qDebug() << "src" << _propertyRoot->value("src", QString());
qDebug() << "file" << _propertyRoot->value("file", QString());
QByteArray file = _propertyRoot->value("file", QByteArray()).toByteArray();
if (!file.isEmpty()) {
_image = FGQCanvasImageLoader::instance()->getImage(file);
if (_image.isNull()) {
// get notified when the image loads
FGQCanvasImageLoader::instance()->connectToImageLoaded(file,
const_cast<FGQCanvasImage*>(this),
SLOT(markImageDirty()));
} else {
qDebug() << "have image" << _image.size();
}
} else {
qDebug() << "source" << _propertyRoot->value("source", QString());
qDebug() << "src" << _propertyRoot->value("src", QString());
}
_destSize = QSizeF(_propertyRoot->value("size[0]", 0.0).toFloat(),
_propertyRoot->value("size[1]", 0.0).toFloat());
qDebug() << "dest-size" << _destSize;
_imageDirty = false;
}
void FGQCanvasImage::markStyleDirty()

View file

@ -7,6 +7,7 @@
class FGQCanvasImage : public FGCanvasElement
{
Q_OBJECT
public:
FGQCanvasImage(FGCanvasGroup* pr, LocalProp* prop);
@ -15,12 +16,15 @@ protected:
virtual void markStyleDirty() override;
private slots:
void markImageDirty();
private:
bool onChildAdded(LocalProp *prop) override;
void rebuildImage() const;
void markImageDirty();
private:
mutable bool _imageDirty;
mutable QPixmap _image;

View file

@ -0,0 +1,108 @@
#include "fgqcanvasimageloader.h"
#include <QDebug>
#include <QNetworkAccessManager>
static FGQCanvasImageLoader* static_instance = nullptr;
class TransferSignalHolder : public QObject
{
Q_OBJECT
public:
TransferSignalHolder(QObject* pr) : QObject(pr) { }
signals:
void trigger();
};
FGQCanvasImageLoader::FGQCanvasImageLoader(QNetworkAccessManager* dl)
: m_downloader(dl)
{
}
void FGQCanvasImageLoader::onDownloadFinished()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
QPixmap pm;
if (!pm.loadFromData(reply->readAll())) {
qWarning() << "image loading failed";
} else {
qDebug() << "did download:" << reply->property("image").toByteArray();
m_cache.insert(reply->property("image").toByteArray(), pm);
TransferSignalHolder* signalHolder = reply->findChild<TransferSignalHolder*>("holder");
if (signalHolder) {
qDebug() << "triggering image updates";
signalHolder->trigger();
}
}
m_transfers.removeOne(reply);
reply->deleteLater();
}
FGQCanvasImageLoader *FGQCanvasImageLoader::instance()
{
return static_instance;
}
void FGQCanvasImageLoader::initialise(QNetworkAccessManager *dl)
{
Q_ASSERT(static_instance == nullptr);
static_instance = new FGQCanvasImageLoader(dl);
}
void FGQCanvasImageLoader::setHost(QString hostName, int portNumber)
{
m_hostName = hostName;
m_port = portNumber;
}
QPixmap FGQCanvasImageLoader::getImage(const QByteArray &imagePath)
{
if (m_cache.contains(imagePath)) {
// cached, easy
return m_cache.value(imagePath);
}
QUrl url;
url.setScheme("http");
url.setHost(m_hostName);
url.setPort(m_port);
url.setPath("/aircraft-dir/" + imagePath);
Q_FOREACH (QNetworkReply* transfer, m_transfers) {
if (transfer->url() == url) {
return QPixmap(); // transfer already active
}
}
qDebug() << "requesting image" << url;
QNetworkReply* reply = m_downloader->get(QNetworkRequest(url));
reply->setProperty("image", imagePath);
connect(reply, &QNetworkReply::finished, this, &FGQCanvasImageLoader::onDownloadFinished);
m_transfers.append(reply);
return QPixmap();
}
void FGQCanvasImageLoader::connectToImageLoaded(const QByteArray &imagePath, QObject *receiver, const char *slot)
{
Q_FOREACH (QNetworkReply* transfer, m_transfers) {
if (transfer->property("image").toByteArray() == imagePath) {
QObject* signalHolder = transfer->findChild<QObject*>("holder");
if (!signalHolder) {
signalHolder = new TransferSignalHolder(transfer);
signalHolder->setObjectName("holder");
}
connect(signalHolder, SIGNAL(trigger()), receiver, slot);
return;
}
}
qWarning() << "no transfer active for" << imagePath;
}
#include "fgqcanvasimageloader.moc"

View file

@ -0,0 +1,49 @@
#ifndef FGQCANVASIMAGELOADER_H
#define FGQCANVASIMAGELOADER_H
#include <QObject>
#include <QPixmap>
#include <QMap>
#include <QNetworkReply>
class QNetworkAccessManager;
class FGQCanvasImageLoader : public QObject
{
Q_OBJECT
public:
static FGQCanvasImageLoader* instance();
static void initialise(QNetworkAccessManager* dl);
void setHost(QString hostName, int portNumber);
QPixmap getImage(const QByteArray& imagePath);
/**
* @brief connectToImageLoaded - allow images to discover when they are loaded
* @param imagePath - FGFS host relative path as found in the canvas (will be resolved
* against aircraft-data, and potentially other places)
* @param receiver - normal connect() receiver object
* @param slot - slot macro
*/
void connectToImageLoaded(const QByteArray& imagePath, QObject* receiver, const char* slot);
signals:
private:
explicit FGQCanvasImageLoader(QNetworkAccessManager* dl);
void onDownloadFinished();
private:
QNetworkAccessManager* m_downloader;
QString m_hostName;
int m_port;
QMap<QByteArray, QPixmap> m_cache;
QList<QNetworkReply*> m_transfers;
};
#endif // FGQCANVASIMAGELOADER_H

View file

@ -2,8 +2,11 @@
#include <QApplication>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
#include <QStandardPaths>
#include "fgqcanvasfontcache.h"
#include "fgqcanvasimageloader.h"
int main(int argc, char *argv[])
{
@ -15,10 +18,18 @@ int main(int argc, char *argv[])
QNetworkAccessManager* downloader = new QNetworkAccessManager;
QNetworkDiskCache* cache = new QNetworkDiskCache;
cache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
downloader->setCache(cache); // takes ownership
FGQCanvasFontCache::initialise(downloader);
FGQCanvasImageLoader::initialise(downloader);
TemporaryWidget w;
w.show();
return a.exec();
int result = a.exec();
delete downloader;
return result;
}

View file

@ -7,8 +7,8 @@
#include <QJsonValue>
#include <QSettings>
// ws://localhost:8080/PropertyTreeMirror/canvas/by-index/texture[3]
#include "fgqcanvasfontcache.h"
#include "fgqcanvasimageloader.h"
#include "canvastreemodel.h"
#include "localprop.h"
@ -24,24 +24,21 @@ TemporaryWidget::TemporaryWidget(QWidget *parent) :
TemporaryWidget::~TemporaryWidget()
{
disconnect(&m_webSocket, &QWebSocket::disconnected, this, &TemporaryWidget::onSocketClosed);
m_webSocket.close();
delete ui;
}
void TemporaryWidget::onStartConnect()
{
QString wsUrl = ui->socketURL->text();
if (!wsUrl.startsWith("ws")) {
qWarning() << "Not a web-socket URL" << wsUrl;
return;
}
QUrl wsUrl;
wsUrl.setScheme("ws");
wsUrl.setHost(ui->hostName->text());
wsUrl.setPort(ui->portEdit->text().toInt());
// string clean up, to ensure our root path has a leading slash but
// no trailing slash
QString propPath = ui->propertyPath->text();
if (wsUrl.endsWith('/')) {
wsUrl.truncate(wsUrl.size() - 1);
}
QString rootPath = propPath;
if (!propPath.startsWith('/')) {
rootPath = '/' + propPath;
@ -51,7 +48,7 @@ void TemporaryWidget::onStartConnect()
rootPath.chop(1);
}
QUrl url = QUrl(wsUrl + rootPath);
wsUrl.setPath("/PropertyTreeMirror" + rootPath);
rootPropertyPath = rootPath.toUtf8();
connect(&m_webSocket, &QWebSocket::connected, this, &TemporaryWidget::onConnected);
@ -59,9 +56,8 @@ void TemporaryWidget::onStartConnect()
saveSettings();
qDebug() << "starting connection to:" << url;
m_webSocket.open(url);
m_webSocket.sendTextMessage("nothing"); // forces Mongoose to respond quicker
qDebug() << "starting connection to:" << wsUrl;
m_webSocket.open(wsUrl);
}
void TemporaryWidget::onConnected()
@ -78,6 +74,12 @@ void TemporaryWidget::onConnected()
m_canvasModel = new CanvasTreeModel(ui->canvas->rootElement());
ui->treeView->setModel(m_canvasModel);
FGQCanvasFontCache::instance()->setHost(ui->hostName->text(),
ui->portEdit->text().toInt());
FGQCanvasImageLoader::instance()->setHost(ui->hostName->text(),
ui->portEdit->text().toInt());
m_webSocket.sendTextMessage("nonsense");
}
void TemporaryWidget::onTextMessageReceived(QString message)
@ -158,14 +160,16 @@ void TemporaryWidget::onSocketClosed()
void TemporaryWidget::saveSettings()
{
QSettings settings;
settings.setValue("ws-host", ui->socketURL->text());
settings.setValue("ws-host", ui->hostName->text());
settings.setValue("ws-port", ui->portEdit->text());
settings.setValue("prop-path", ui->propertyPath->text());
}
void TemporaryWidget::restoreSettings()
{
QSettings settings;
ui->socketURL->setText(settings.value("ws-host").toString());
ui->hostName->setText(settings.value("ws-host").toString());
ui->portEdit->setText(settings.value("ws-port").toString());
ui->propertyPath->setText(settings.value("prop-path").toString());
}

View file

@ -23,7 +23,7 @@
<attribute name="title">
<string>Connect</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="gridLayout" columnstretch="1,0">
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
@ -34,15 +34,12 @@
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Enter a web-socket URL: (eg 'ws://localhost:8080/PropertyTreeMirror/')</string>
<string>Enter FlightGear host-name and port</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QTreeView" name="treeView"/>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="socketURL"/>
<widget class="QLineEdit" name="hostName"/>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="connectButton">
@ -51,9 +48,15 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="1" column="1">
<widget class="QLineEdit" name="portEdit"/>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLineEdit" name="propertyPath"/>
</item>
<item row="5" column="0" colspan="2">
<widget class="QTreeView" name="treeView"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="stackPage2">