Merge pull request #3439 from birarda/master

upload a snapshot for location on location creation, cleanup interface start errors
This commit is contained in:
Brad Hefta-Gaub 2014-09-17 14:52:04 -07:00
commit fd321a5ed1
8 changed files with 151 additions and 26 deletions

View file

@ -288,6 +288,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// set the account manager's root URL and trigger a login request if we don't have the access token
accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL);
UserActivityLogger::getInstance().launch(applicationVersion());
// grab the location manager instance early so it lives in our thread
LocationManager::getInstance();
// once the event loop has started, check and signal for an access token
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
@ -927,6 +930,13 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_N:
if (isMeta) {
Menu::getInstance()->triggerOption(MenuOption::NameLocation);
}
break;
case Qt::Key_Up:
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {

View file

@ -328,9 +328,9 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersVoxelNodes,
Qt::CTRL | Qt::SHIFT | Qt::Key_1, false,
&nodeBounds, SLOT(setShowVoxelNodes(bool)));
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersModelNodes,
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes,
Qt::CTRL | Qt::SHIFT | Qt::Key_2, false,
&nodeBounds, SLOT(setShowModelNodes(bool)));
&nodeBounds, SLOT(setShowEntityNodes(bool)));
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersParticleNodes,
Qt::CTRL | Qt::SHIFT | Qt::Key_3, false,
&nodeBounds, SLOT(setShowParticleNodes(bool)));

View file

@ -439,7 +439,7 @@ namespace MenuOption {
const QString ScriptEditor = "Script Editor...";
const QString SettingsExport = "Export Settings";
const QString SettingsImport = "Import Settings";
const QString ShowBordersModelNodes = "Show Model Nodes";
const QString ShowBordersEntityNodes = "Show Entity Nodes";
const QString ShowBordersParticleNodes = "Show Particle Nodes";
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
const QString ShowIKConstraints = "Show IK Constraints";

View file

@ -42,17 +42,15 @@ ScriptsModel::ScriptsModel(QObject* parent) :
_localDirectory(),
_fsWatcher(),
_localFiles(),
_remoteFiles() {
QString scriptPath = Menu::getInstance()->getScriptsLocation();
_localDirectory.setPath(scriptPath);
_remoteFiles()
{
_localDirectory.setFilter(QDir::Files | QDir::Readable);
_localDirectory.setNameFilters(QStringList("*.js"));
_fsWatcher.addPath(_localDirectory.absolutePath());
updateScriptsLocation(Menu::getInstance()->getScriptsLocation());
connect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &ScriptsModel::reloadLocalFiles);
connect(Menu::getInstance(), &Menu::scriptLocationChanged, this, &ScriptsModel::updateScriptsLocation);
reloadLocalFiles();
@ -88,8 +86,13 @@ int ScriptsModel::rowCount(const QModelIndex& parent) const {
void ScriptsModel::updateScriptsLocation(const QString& newPath) {
_fsWatcher.removePath(_localDirectory.absolutePath());
_localDirectory.setPath(newPath);
_fsWatcher.addPath(_localDirectory.absolutePath());
if (!_localDirectory.absolutePath().isEmpty()) {
_fsWatcher.addPath(_localDirectory.absolutePath());
}
reloadLocalFiles();
}

View file

@ -9,10 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <qhttpmultipart.h>
#include <qjsonobject.h>
#include <AccountManager.h>
#include "Application.h"
#include "ui/Snapshot.h"
#include "LocationManager.h"
const QString POST_LOCATION_CREATE = "/api/v1/locations/";
@ -24,13 +28,17 @@ LocationManager& LocationManager::getInstance() {
const QString UNKNOWN_ERROR_MESSAGE = "Unknown error creating named location. Please try again!";
void LocationManager::namedLocationDataReceived(const QJsonObject& data) {
if (data.isEmpty()) {
return;
}
const QString LOCATION_OBJECT_KEY = "location";
const QString LOCATION_ID_KEY = "id";
if (data.contains("status") && data["status"].toString() == "success") {
void LocationManager::namedLocationDataReceived(const QJsonObject& rootObject) {
if (rootObject.contains("status") && rootObject["status"].toString() == "success") {
emit creationCompleted(QString());
// successfuly created a location - grab the ID from the response and create a snapshot to upload
QString locationIDString = rootObject[LOCATION_OBJECT_KEY].toObject()[LOCATION_ID_KEY].toString();
updateSnapshotForExistingLocation(locationIDString);
} else {
emit creationCompleted(UNKNOWN_ERROR_MESSAGE);
}
@ -87,3 +95,57 @@ void LocationManager::errorDataReceived(QNetworkReply& errorReply) {
creationCompleted(UNKNOWN_ERROR_MESSAGE);
}
}
void LocationManager::locationImageUpdateSuccess(const QJsonObject& rootObject) {
qDebug() << "Successfuly updated a location image.";
}
void LocationManager::updateSnapshotForExistingLocation(const QString& locationID) {
// first create a snapshot and save it
Application* application = Application::getInstance();
QTemporaryFile* tempImageFile = Snapshot::saveTempSnapshot(application->getGLWidget(), application->getAvatar());
if (tempImageFile && tempImageFile->open()) {
AccountManager& accountManager = AccountManager::getInstance();
// setup a multipart that is in the AccountManager thread - we need this so it can be cleaned up after the QNetworkReply
QHttpMultiPart* imageFileMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
imageFileMultiPart->moveToThread(accountManager.thread());
// parent the temp file to the QHttpMultipart after moving it to account manager thread
tempImageFile->moveToThread(accountManager.thread());
tempImageFile->setParent(imageFileMultiPart);
qDebug() << "Uploading a snapshot from" << QFileInfo(*tempImageFile).absoluteFilePath()
<< "as location image for" << locationID;
const QString LOCATION_IMAGE_NAME = "location[image]";
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"" + LOCATION_IMAGE_NAME + "\";"
" filename=\"" + QFileInfo(tempImageFile->fileName()).fileName().toUtf8() + "\""));
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
imagePart.setBodyDevice(tempImageFile);
imageFileMultiPart->append(imagePart);
const QString LOCATION_IMAGE_PUT_PATH = "api/v1/locations/%1/image";
JSONCallbackParameters imageCallbackParams;
imageCallbackParams.jsonCallbackReceiver = this;
imageCallbackParams.jsonCallbackMethod = "locationImageUpdateSuccess";
// make an authenticated request via account manager to upload the image
// don't do anything with error or success for now
AccountManager::getInstance().authenticatedRequest(LOCATION_IMAGE_PUT_PATH.arg(locationID),
QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), QByteArray(), imageFileMultiPart);
} else {
qDebug() << "Couldn't open snapshot file to upload as location image. No location image will be stored.";
return;
}
}

View file

@ -35,8 +35,12 @@ signals:
void creationCompleted(const QString& errorMessage);
private slots:
void namedLocationDataReceived(const QJsonObject& data);
void namedLocationDataReceived(const QJsonObject& jsonObject);
void errorDataReceived(QNetworkReply& errorReply);
void locationImageUpdateSuccess(const QJsonObject& jsonObject);
private:
void updateSnapshotForExistingLocation(const QString& locationID);
};

View file

@ -65,6 +65,24 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
}
QString Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
QFile* snapshotFile = savedFileForSnapshot(widget, avatar, false);
// we don't need the snapshot file, so close it, grab its filename and delete it
snapshotFile->close();
QString snapshotPath = QFileInfo(*snapshotFile).absoluteFilePath();
delete snapshotFile;
return snapshotPath;
}
QTemporaryFile* Snapshot::saveTempSnapshot(QGLWidget* widget, Avatar* avatar) {
// return whatever we get back from saved file for snapshot
return static_cast<QTemporaryFile*>(savedFileForSnapshot(widget, avatar, true));;
}
QFile* Snapshot::savedFileForSnapshot(QGLWidget* widget, Avatar* avatar, bool isTemporary) {
QImage shot = widget->grabFrameBuffer();
glm::vec3 location = avatar->getPosition();
@ -91,16 +109,40 @@ QString Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
username.replace(QRegExp("[^A-Za-z0-9_]"), "-");
QDateTime now = QDateTime::currentDateTime();
QString fileName = Menu::getInstance()->getSnapshotsLocation();
if (!fileName.endsWith(QDir::separator())) {
fileName.append(QDir::separator());
}
fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation)));
shot.save(fileName, 0, 100);
return fileName;
QString filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation);
const int IMAGE_QUALITY = 100;
if (!isTemporary) {
QString snapshotFullPath = Menu::getInstance()->getSnapshotsLocation();
if (!snapshotFullPath.endsWith(QDir::separator())) {
snapshotFullPath.append(QDir::separator());
}
snapshotFullPath.append(filename);
QFile* imageFile = new QFile(snapshotFullPath);
imageFile->open(QIODevice::WriteOnly);
shot.save(imageFile, 0, IMAGE_QUALITY);
imageFile->close();
return imageFile;
} else {
QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename);
if (!imageTempFile->open()) {
qDebug() << "Unable to open QTemporaryFile for temp snapshot. Will not save.";
return NULL;
}
shot.save(imageTempFile, 0, IMAGE_QUALITY);
imageTempFile->close();
return imageTempFile;
}
}

View file

@ -42,7 +42,11 @@ class Snapshot {
public:
static QString saveSnapshot(QGLWidget* widget, Avatar* avatar);
static QTemporaryFile* saveTempSnapshot(QGLWidget* widget, Avatar* avatar);
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
private:
static QFile* savedFileForSnapshot(QGLWidget* widget, Avatar* avatar, bool isTemporary);
};
#endif // hifi_Snapshot_h