mirror of
https://github.com/overte-org/overte.git
synced 2025-06-15 20:59:17 +02:00
work on metadata in the model browser + real time updates
This commit is contained in:
parent
8661bdf788
commit
03958f2fa0
7 changed files with 421 additions and 265 deletions
|
@ -29,7 +29,6 @@
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
#include <XmppClient.h>
|
#include <XmppClient.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <FileDownloader.h>
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
@ -37,7 +36,7 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "ui/InfoView.h"
|
#include "ui/InfoView.h"
|
||||||
#include "ui/MetavoxelEditor.h"
|
#include "ui/MetavoxelEditor.h"
|
||||||
#include "ui/ModelBrowser.h"
|
#include "ui/ModelsBrowser.h"
|
||||||
|
|
||||||
|
|
||||||
Menu* Menu::_instance = NULL;
|
Menu* Menu::_instance = NULL;
|
||||||
|
@ -714,8 +713,8 @@ void Menu::loginForCurrentDomain() {
|
||||||
|
|
||||||
void Menu::editPreferences() {
|
void Menu::editPreferences() {
|
||||||
Application* applicationInstance = Application::getInstance();
|
Application* applicationInstance = Application::getInstance();
|
||||||
ModelBrowser headBrowser(Head);
|
ModelsBrowser headBrowser(Head);
|
||||||
ModelBrowser skeletonBrowser(Skeleton);
|
ModelsBrowser skeletonBrowser(Skeleton);
|
||||||
|
|
||||||
const QString BROWSE_BUTTON_TEXT = "Browse";
|
const QString BROWSE_BUTTON_TEXT = "Browse";
|
||||||
|
|
||||||
|
@ -816,15 +815,23 @@ void Menu::editPreferences() {
|
||||||
if (ret == QDialog::Accepted) {
|
if (ret == QDialog::Accepted) {
|
||||||
bool shouldDispatchIdentityPacket = false;
|
bool shouldDispatchIdentityPacket = false;
|
||||||
|
|
||||||
if (headURLEdit.text() != faceURLString && !headURLEdit.text().isEmpty()) {
|
if (headURLEdit.text() != faceURLString) {
|
||||||
// change the faceModelURL in the profile, it will also update this user's BlendFace
|
// change the faceModelURL in the profile, it will also update this user's BlendFace
|
||||||
applicationInstance->getAvatar()->setFaceModelURL(QUrl(headURLEdit.text()));
|
if (headURLEdit.text().isEmpty()) {
|
||||||
|
applicationInstance->getAvatar()->setFaceModelURL(QUrl(headURLEdit.placeholderText()));
|
||||||
|
} else {
|
||||||
|
applicationInstance->getAvatar()->setFaceModelURL(QUrl(headURLEdit.text()));
|
||||||
|
}
|
||||||
shouldDispatchIdentityPacket = true;
|
shouldDispatchIdentityPacket = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skeletonURLEdit.text() != skeletonURLString && !skeletonURLEdit.text().isEmpty()) {
|
if (skeletonURLEdit.text() != skeletonURLString) {
|
||||||
// change the skeletonModelURL in the profile, it will also update this user's Body
|
// change the skeletonModelURL in the profile, it will also update this user's Body
|
||||||
applicationInstance->getAvatar()->setSkeletonModelURL(QUrl(skeletonURLEdit.text()));
|
if (skeletonURLEdit.text().isEmpty()) {
|
||||||
|
applicationInstance->getAvatar()->setSkeletonModelURL(QUrl(skeletonURLEdit.placeholderText()));
|
||||||
|
} else {
|
||||||
|
applicationInstance->getAvatar()->setSkeletonModelURL(QUrl(skeletonURLEdit.text()));
|
||||||
|
}
|
||||||
shouldDispatchIdentityPacket = true;
|
shouldDispatchIdentityPacket = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,150 +0,0 @@
|
||||||
//
|
|
||||||
// ModelBrowser.cpp
|
|
||||||
// hifi
|
|
||||||
//
|
|
||||||
// Created by Clement on 3/17/14.
|
|
||||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QXmlStreamReader>
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QDialog>
|
|
||||||
#include <QStringListModel>
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
|
|
||||||
#include <Application.h>
|
|
||||||
|
|
||||||
#include "ModelBrowser.h"
|
|
||||||
|
|
||||||
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
|
||||||
static const QString MARKER_PARAMETER_NAME = "marker";
|
|
||||||
static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
|
||||||
static const QString CONTAINER_NAME = "Contents";
|
|
||||||
static const QString KEY_NAME = "Key";
|
|
||||||
|
|
||||||
ModelBrowser::ModelBrowser(ModelType modelType, QWidget* parent) : QWidget(parent), _type(modelType) {
|
|
||||||
QUrl url(S3_URL);
|
|
||||||
QUrlQuery query;
|
|
||||||
|
|
||||||
if (_type == Head) {
|
|
||||||
query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION);
|
|
||||||
} else if (_type == Skeleton) {
|
|
||||||
query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION);
|
|
||||||
}
|
|
||||||
url.setQuery(query);
|
|
||||||
|
|
||||||
_downloader = new FileDownloader(url);
|
|
||||||
connect(_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished()));
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelBrowser::~ModelBrowser() {
|
|
||||||
delete _downloader;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelBrowser::downloadFinished() {
|
|
||||||
parseXML(_downloader->getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelBrowser::browse() {
|
|
||||||
QDialog dialog(this);
|
|
||||||
dialog.setWindowTitle("Browse models");
|
|
||||||
|
|
||||||
QGridLayout* layout = new QGridLayout(&dialog);
|
|
||||||
dialog.setLayout(layout);
|
|
||||||
|
|
||||||
QLineEdit* searchBar = new QLineEdit(&dialog);
|
|
||||||
layout->addWidget(searchBar, 0, 0);
|
|
||||||
|
|
||||||
ListView* listView = new ListView(&dialog);
|
|
||||||
listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
||||||
layout->addWidget(listView, 1, 0);
|
|
||||||
listView->connect(searchBar, SIGNAL(textChanged(const QString&)), SLOT(keyboardSearch(const QString&)));
|
|
||||||
dialog.connect(listView, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(accept()));
|
|
||||||
|
|
||||||
QStringListModel* model = new QStringListModel(_models.keys(), listView);
|
|
||||||
model->sort(0);
|
|
||||||
listView->setModel(model);
|
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
||||||
layout->addWidget(buttons, 2, 0);
|
|
||||||
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
|
||||||
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
|
||||||
|
|
||||||
if (dialog.exec() == QDialog::Rejected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString selectedKey = model->data(listView->currentIndex(), Qt::DisplayRole).toString();
|
|
||||||
|
|
||||||
emit selected(_models[selectedKey]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ModelBrowser::parseXML(QByteArray xmlFile) {
|
|
||||||
QXmlStreamReader xml(xmlFile);
|
|
||||||
QRegExp rx(".*fst");
|
|
||||||
bool truncated = false;
|
|
||||||
QString lastKey;
|
|
||||||
|
|
||||||
// Read xml until the end or an error is detected
|
|
||||||
while(!xml.atEnd() && !xml.hasError()) {
|
|
||||||
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == IS_TRUNCATED_NAME) {
|
|
||||||
while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == IS_TRUNCATED_NAME)) {
|
|
||||||
// Let's check if there is more
|
|
||||||
xml.readNext();
|
|
||||||
if (xml.text().toString() == "True") {
|
|
||||||
truncated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == CONTAINER_NAME) {
|
|
||||||
while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == CONTAINER_NAME)) {
|
|
||||||
// If a file is find, process it
|
|
||||||
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == KEY_NAME) {
|
|
||||||
xml.readNext();
|
|
||||||
lastKey = xml.text().toString();
|
|
||||||
if (rx.exactMatch(xml.text().toString())) {
|
|
||||||
// Add the found file to the list
|
|
||||||
_models.insert(QFileInfo(xml.text().toString()).baseName(),
|
|
||||||
S3_URL + "/" + xml.text().toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xml.readNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xml.readNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
if(xml.hasError()) {
|
|
||||||
_models.clear();
|
|
||||||
QMessageBox::critical(this,
|
|
||||||
"ModelBrowser::ModelBrowser()",
|
|
||||||
xml.errorString(),
|
|
||||||
QMessageBox::Ok);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we didn't all the files, download the next ones
|
|
||||||
if (truncated) {
|
|
||||||
QUrl url(S3_URL);
|
|
||||||
QUrlQuery query;
|
|
||||||
|
|
||||||
if (_type == Head) {
|
|
||||||
query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION);
|
|
||||||
} else if (_type == Skeleton) {
|
|
||||||
query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION);
|
|
||||||
}
|
|
||||||
query.addQueryItem(MARKER_PARAMETER_NAME, lastKey);
|
|
||||||
url.setQuery(query);
|
|
||||||
|
|
||||||
delete _downloader;
|
|
||||||
_downloader = new FileDownloader(url);
|
|
||||||
connect(_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
//
|
|
||||||
// ModelBrowser.h
|
|
||||||
// hifi
|
|
||||||
//
|
|
||||||
// Created by Clement on 3/17/14.
|
|
||||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __hifi__ModelBrowser__
|
|
||||||
#define __hifi__ModelBrowser__
|
|
||||||
|
|
||||||
#include <FileDownloader.h>
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
#include <QListView>
|
|
||||||
|
|
||||||
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
|
||||||
static const QString HEAD_MODELS_LOCATION = "models/heads/";
|
|
||||||
static const QString SKELETON_MODELS_LOCATION = "models/skeletons/";
|
|
||||||
|
|
||||||
enum ModelType {
|
|
||||||
Head,
|
|
||||||
Skeleton
|
|
||||||
};
|
|
||||||
|
|
||||||
class ModelBrowser : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
ModelBrowser(ModelType modelType, QWidget* parent = NULL);
|
|
||||||
~ModelBrowser();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void selected(QString filename);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void browse();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void downloadFinished();
|
|
||||||
|
|
||||||
private:
|
|
||||||
ModelType _type;
|
|
||||||
FileDownloader* _downloader;
|
|
||||||
QHash<QString, QString> _models;
|
|
||||||
|
|
||||||
bool parseXML(QByteArray xmlFile);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ListView : public QListView {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ListView(QWidget* parent) : QListView(parent) {}
|
|
||||||
public slots:
|
|
||||||
void keyboardSearch(const QString& text) {
|
|
||||||
QAbstractItemView::keyboardSearch(text);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* defined(__hifi__ModelBrowser__) */
|
|
305
interface/src/ui/ModelsBrowser.cpp
Normal file
305
interface/src/ui/ModelsBrowser.cpp
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
//
|
||||||
|
// ModelsBrowser.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Clement on 3/17/14.
|
||||||
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QStringListModel>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QHeaderView>
|
||||||
|
|
||||||
|
#include <Application.h>
|
||||||
|
|
||||||
|
#include "ModelsBrowser.h"
|
||||||
|
|
||||||
|
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
||||||
|
static const QString PUBLIC_URL = "http://public.highfidelity.io";
|
||||||
|
static const QString HEAD_MODELS_LOCATION = "models/heads";
|
||||||
|
static const QString SKELETON_MODELS_LOCATION = "models/skeletons/";
|
||||||
|
|
||||||
|
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
||||||
|
static const QString MARKER_PARAMETER_NAME = "marker";
|
||||||
|
static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
||||||
|
static const QString CONTAINER_NAME = "Contents";
|
||||||
|
static const QString KEY_NAME = "Key";
|
||||||
|
static const QString LOADING_MSG = "Loading...";
|
||||||
|
|
||||||
|
enum ModelMetaData {
|
||||||
|
Name = 0,
|
||||||
|
Creator,
|
||||||
|
UploadeDate,
|
||||||
|
Type,
|
||||||
|
Gender,
|
||||||
|
|
||||||
|
ModelMetaDataCount
|
||||||
|
};
|
||||||
|
static const QString propertiesNames[ModelMetaDataCount] = {
|
||||||
|
"Name",
|
||||||
|
"Creator",
|
||||||
|
"Upload Date",
|
||||||
|
"Type",
|
||||||
|
"Gender"
|
||||||
|
};
|
||||||
|
|
||||||
|
ModelsBrowser::ModelsBrowser(ModelType modelsType, QWidget* parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
_handler(new ModelHandler(modelsType))
|
||||||
|
{
|
||||||
|
// Connect handler
|
||||||
|
_handler->connect(this, SIGNAL(startDownloading()), SLOT(download()));
|
||||||
|
_handler->connect(this, SIGNAL(startUpdating()), SLOT(update()));
|
||||||
|
_handler->connect(this, SIGNAL(destroyed()), SLOT(exit()));
|
||||||
|
|
||||||
|
// Setup and launch update thread
|
||||||
|
QThread* thread = new QThread();
|
||||||
|
thread->connect(_handler, SIGNAL(destroyed()), SLOT(quit()));
|
||||||
|
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
|
||||||
|
_handler->moveToThread(thread);
|
||||||
|
thread->start();
|
||||||
|
emit startDownloading();
|
||||||
|
|
||||||
|
// Initialize the view
|
||||||
|
_view.setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
_view.setRootIsDecorated(false);
|
||||||
|
_view.setModel(_handler->getModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelsBrowser::~ModelsBrowser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelsBrowser::applyFilter(const QString &filter) {
|
||||||
|
QStringList filters = filter.split(" ");
|
||||||
|
|
||||||
|
// Try and match every filter with each rows
|
||||||
|
for (int i = 0; i < _handler->getModel()->rowCount(); ++i) {
|
||||||
|
bool match = false;
|
||||||
|
for (int k = 0; k < filters.count(); ++k) {
|
||||||
|
match = false;
|
||||||
|
for (int j = 0; j < ModelMetaDataCount; ++j) {
|
||||||
|
if (_handler->getModel()->item(i, j)->text().contains(filters.at(k))) {
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hid the row if it doesn't match (Make sure it's not it it does)
|
||||||
|
if (match) {
|
||||||
|
_view.setRowHidden(i, QModelIndex(), false);
|
||||||
|
} else {
|
||||||
|
_view.setRowHidden(i, QModelIndex(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelsBrowser::browse() {
|
||||||
|
QDialog dialog;
|
||||||
|
dialog.setWindowTitle("Browse models");
|
||||||
|
|
||||||
|
QGridLayout* layout = new QGridLayout(&dialog);
|
||||||
|
dialog.setLayout(layout);
|
||||||
|
|
||||||
|
QLineEdit* searchBar = new QLineEdit(&dialog);
|
||||||
|
layout->addWidget(searchBar, 0, 0);
|
||||||
|
|
||||||
|
layout->addWidget(&_view, 1, 0);
|
||||||
|
dialog.connect(&_view, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(accept()));
|
||||||
|
connect(searchBar, SIGNAL(textChanged(const QString&)), SLOT(applyFilter(const QString&)));
|
||||||
|
|
||||||
|
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
layout->addWidget(buttons, 2, 0);
|
||||||
|
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||||
|
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||||
|
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
QVariant selectedFile = _handler->getModel()->data(_view.currentIndex(), Qt::UserRole);
|
||||||
|
if (selectedFile.isValid()) {
|
||||||
|
emit selected(selectedFile.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// So that we don't have to reconstruct the view
|
||||||
|
_view.setParent(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ModelHandler::ModelHandler(ModelType modelsType, QWidget* parent) :
|
||||||
|
QObject(parent),
|
||||||
|
_initiateExit(false),
|
||||||
|
_type(modelsType)
|
||||||
|
{
|
||||||
|
connect(&_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished()));
|
||||||
|
|
||||||
|
// set headers data
|
||||||
|
QStringList headerData;
|
||||||
|
for (int i = 0; i < ModelMetaDataCount; ++i) {
|
||||||
|
headerData << propertiesNames[i];
|
||||||
|
}
|
||||||
|
_model.setHorizontalHeaderLabels(headerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelHandler::download() {
|
||||||
|
// Query models list
|
||||||
|
queryNewFiles();
|
||||||
|
|
||||||
|
QMutexLocker lockerModel(&_modelMutex);
|
||||||
|
if (_initiateExit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Show loading message
|
||||||
|
QStandardItem* loadingItem = new QStandardItem(LOADING_MSG);
|
||||||
|
loadingItem->setEnabled(false);
|
||||||
|
_model.appendRow(loadingItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelHandler::update() {
|
||||||
|
// Will be implemented in my next PR
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelHandler::exit() {
|
||||||
|
QMutexLocker lockerDownload(&_downloadMutex);
|
||||||
|
QMutexLocker lockerModel(&_modelMutex);
|
||||||
|
_initiateExit = true;
|
||||||
|
|
||||||
|
// Disconnect everything
|
||||||
|
_downloader.disconnect();
|
||||||
|
disconnect();
|
||||||
|
thread()->disconnect();
|
||||||
|
|
||||||
|
// Make sure the thread will exit correctly
|
||||||
|
thread()->connect(this, SIGNAL(destroyed()), SLOT(quit()));
|
||||||
|
thread()->connect(thread(), SIGNAL(finished()), SLOT(deleteLater()));
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelHandler::downloadFinished() {
|
||||||
|
QMutexLocker lockerDownload(&_downloadMutex);
|
||||||
|
if (_initiateExit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parseXML(_downloader.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelHandler::queryNewFiles(QString marker) {
|
||||||
|
QMutexLocker lockerDownload(&_downloadMutex);
|
||||||
|
if (_initiateExit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build query
|
||||||
|
QUrl url(S3_URL);
|
||||||
|
QUrlQuery query;
|
||||||
|
if (_type == Head) {
|
||||||
|
query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION);
|
||||||
|
} else if (_type == Skeleton) {
|
||||||
|
query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!marker.isEmpty()) {
|
||||||
|
query.addQueryItem(MARKER_PARAMETER_NAME, marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download
|
||||||
|
url.setQuery(query);
|
||||||
|
_downloader.download(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModelHandler::parseXML(QByteArray xmlFile) {
|
||||||
|
QMutexLocker lockerModel(&_modelMutex);
|
||||||
|
if (_initiateExit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmlStreamReader xml(xmlFile);
|
||||||
|
QRegExp rx(".*fst");
|
||||||
|
bool truncated = false;
|
||||||
|
QString lastKey;
|
||||||
|
|
||||||
|
// Remove loading indication
|
||||||
|
int oldLastRow = _model.rowCount() - 1;
|
||||||
|
delete _model.takeRow(oldLastRow).first();
|
||||||
|
|
||||||
|
// Read xml until the end or an error is detected
|
||||||
|
while(!xml.atEnd() && !xml.hasError()) {
|
||||||
|
if (_initiateExit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == IS_TRUNCATED_NAME) {
|
||||||
|
while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == IS_TRUNCATED_NAME)) {
|
||||||
|
// Let's check if there is more
|
||||||
|
xml.readNext();
|
||||||
|
if (xml.text().toString() == "True") {
|
||||||
|
truncated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == CONTAINER_NAME) {
|
||||||
|
while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == CONTAINER_NAME)) {
|
||||||
|
// If a file is find, process it
|
||||||
|
if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == KEY_NAME) {
|
||||||
|
xml.readNext();
|
||||||
|
lastKey = xml.text().toString();
|
||||||
|
if (rx.exactMatch(xml.text().toString())) {
|
||||||
|
// Add the found file to the list
|
||||||
|
QList<QStandardItem*> model;
|
||||||
|
model << new QStandardItem(QFileInfo(xml.text().toString()).baseName());
|
||||||
|
model.first()->setData(PUBLIC_URL + "/" + xml.text().toString(), Qt::UserRole);
|
||||||
|
|
||||||
|
// Rand properties for now (Will be taken out in the next PR)
|
||||||
|
static QString creator[] = {"Ryan", "Philip", "Andzrej"};
|
||||||
|
static QString type[] = {"human", "beast", "pet", "elfe"};
|
||||||
|
static QString gender[] = {"male", "female", "none"};
|
||||||
|
model << new QStandardItem(creator[randIntInRange(0, 2)]);
|
||||||
|
model << new QStandardItem(QDate(randIntInRange(2013, 2014),
|
||||||
|
randIntInRange(1, 12),
|
||||||
|
randIntInRange(1, 30)).toString());
|
||||||
|
model << new QStandardItem(type[randIntInRange(0, 3)]);
|
||||||
|
model << new QStandardItem(gender[randIntInRange(0, 2)]);
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
_model.appendRow(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xml.readNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xml.readNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const QString ERROR_MSG = "Error loading files";
|
||||||
|
// Error handling
|
||||||
|
if(xml.hasError()) {
|
||||||
|
_model.clear();
|
||||||
|
QStandardItem* errorItem = new QStandardItem(ERROR_MSG);
|
||||||
|
errorItem->setEnabled(false);
|
||||||
|
_model.appendRow(errorItem);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't all the files, download the next ones
|
||||||
|
if (truncated) {
|
||||||
|
// Indicate more files are being loaded
|
||||||
|
QStandardItem* loadingItem = new QStandardItem(LOADING_MSG);
|
||||||
|
loadingItem->setEnabled(false);
|
||||||
|
_model.appendRow(loadingItem);
|
||||||
|
|
||||||
|
// query those files
|
||||||
|
queryNewFiles(lastKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
79
interface/src/ui/ModelsBrowser.h
Normal file
79
interface/src/ui/ModelsBrowser.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// ModelsBrowser.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Clement on 3/17/14.
|
||||||
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__ModelsBrowser__
|
||||||
|
#define __hifi__ModelsBrowser__
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QStandardItem>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QTreeView>
|
||||||
|
|
||||||
|
#include <FileDownloader.h>
|
||||||
|
|
||||||
|
typedef
|
||||||
|
enum {
|
||||||
|
Head,
|
||||||
|
Skeleton
|
||||||
|
} ModelType;
|
||||||
|
|
||||||
|
class ModelHandler : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ModelHandler(ModelType modelsType, QWidget* parent = NULL);
|
||||||
|
QStandardItemModel* getModel() { QMutexLocker locker(&_modelMutex); return &_model; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void doneDownloading();
|
||||||
|
void doneUpdating();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void download();
|
||||||
|
void update();
|
||||||
|
void exit();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void downloadFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _initiateExit;
|
||||||
|
ModelType _type;
|
||||||
|
QMutex _downloadMutex;
|
||||||
|
FileDownloader _downloader;
|
||||||
|
QMutex _modelMutex;
|
||||||
|
QStandardItemModel _model;
|
||||||
|
|
||||||
|
void queryNewFiles(QString marker = QString());
|
||||||
|
bool parseXML(QByteArray xmlFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ModelsBrowser : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
ModelsBrowser(ModelType modelsType, QWidget* parent = NULL);
|
||||||
|
~ModelsBrowser();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void startDownloading();
|
||||||
|
void startUpdating();
|
||||||
|
void selected(QString filename);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void browse();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void applyFilter(const QString& filter);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ModelHandler* _handler;
|
||||||
|
QTreeView _view;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(__hifi__ModelBrowser__) */
|
|
@ -14,14 +14,25 @@
|
||||||
|
|
||||||
#include "FileDownloader.h"
|
#include "FileDownloader.h"
|
||||||
|
|
||||||
FileDownloader::FileDownloader(const QUrl dataURL, QObject* parent) :
|
FileDownloader::FileDownloader(QObject* parent) : QObject(parent) {
|
||||||
QObject(parent),
|
connect(&_networkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(processReply(QNetworkReply*)));
|
||||||
_done(false)
|
}
|
||||||
{
|
|
||||||
connect(&_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(processReply(QNetworkReply*)));
|
|
||||||
|
|
||||||
|
void FileDownloader::download(const QUrl dataURL, QNetworkAccessManager::Operation operation) {
|
||||||
QNetworkRequest request(dataURL);
|
QNetworkRequest request(dataURL);
|
||||||
_networkAccessManager.get(request);
|
|
||||||
|
_downloadedData.clear();
|
||||||
|
switch (operation) {
|
||||||
|
case QNetworkAccessManager::GetOperation:
|
||||||
|
_networkAccessManager.get(request);
|
||||||
|
break;
|
||||||
|
case QNetworkAccessManager::HeadOperation:
|
||||||
|
_networkAccessManager.head(request);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
emit done(QNetworkReply::ProtocolInvalidOperationError);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileDownloader::processReply(QNetworkReply *reply) {
|
void FileDownloader::processReply(QNetworkReply *reply) {
|
||||||
|
@ -30,36 +41,5 @@ void FileDownloader::processReply(QNetworkReply *reply) {
|
||||||
}
|
}
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
_done = true;
|
|
||||||
emit done(reply->error());
|
emit done(reply->error());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileDownloader::waitForFile(int timeout) {
|
|
||||||
QTimer timer;
|
|
||||||
QEventLoop loop;
|
|
||||||
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
|
||||||
connect(this, SIGNAL(done(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
|
|
||||||
|
|
||||||
if (!_done) {
|
|
||||||
if (timeout > 0) {
|
|
||||||
timer.start(timeout);
|
|
||||||
}
|
|
||||||
loop.exec();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray FileDownloader::download(const QUrl dataURL, int timeout) {
|
|
||||||
QTimer timer;
|
|
||||||
QEventLoop loop;
|
|
||||||
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit));
|
|
||||||
|
|
||||||
FileDownloader downloader(dataURL);
|
|
||||||
connect(&downloader, SIGNAL(done(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
|
|
||||||
|
|
||||||
if (timeout > 0) {
|
|
||||||
timer.start(timeout);
|
|
||||||
}
|
|
||||||
loop.exec();
|
|
||||||
|
|
||||||
return downloader.getData();
|
|
||||||
}
|
|
|
@ -13,31 +13,28 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
class FileDownloader : public QObject {
|
class FileDownloader : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileDownloader(const QUrl dataURL, QObject* parent = NULL);
|
FileDownloader(QObject* parent = NULL);
|
||||||
|
|
||||||
void waitForFile(int timeout = 0);
|
|
||||||
|
|
||||||
QByteArray getData() const { return _downloadedData; }
|
QByteArray getData() const { return _downloadedData; }
|
||||||
bool done() { return _done; }
|
|
||||||
|
|
||||||
static QByteArray download(const QUrl dataURL, int timeout = 0);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void done(QNetworkReply::NetworkError error);
|
void done(QNetworkReply::NetworkError error);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void download(const QUrl dataURL, QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void processReply(QNetworkReply* reply);
|
void processReply(QNetworkReply* reply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QNetworkAccessManager _networkAccessManager;
|
QNetworkAccessManager _networkAccessManager;
|
||||||
QByteArray _downloadedData;
|
QByteArray _downloadedData;
|
||||||
|
|
||||||
bool _done;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue