From 9e6df6e2bbce4318a7f19ac5b79fa73c9ca86b8c Mon Sep 17 00:00:00 2001 From: Hans-Christian Ebke Date: Tue, 10 Mar 2015 23:21:28 +0000 Subject: [PATCH] Added new meta data interface. git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@20390 383ad7c9-94d9-4d36-a494-682f7c89f535 --- BasePlugin/MetadataInterface.hh | 135 ++++++++++++++++++++++++++++++++ BasePlugin/PluginFunctions.hh | 2 +- Core/Core.hh | 17 ++++ Core/PluginCommunication.cc | 67 ++++++++++++++++ Core/PluginLoader.cc | 23 ++++++ common/BaseObject.hh | 25 ++++++ 6 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 BasePlugin/MetadataInterface.hh diff --git a/BasePlugin/MetadataInterface.hh b/BasePlugin/MetadataInterface.hh new file mode 100644 index 00000000..962e5830 --- /dev/null +++ b/BasePlugin/MetadataInterface.hh @@ -0,0 +1,135 @@ +/*===========================================================================*\ +* * +* OpenFlipper * +* Copyright (C) 2001-2014 by Computer Graphics Group, RWTH Aachen * +* www.openflipper.org * +* * +*--------------------------------------------------------------------------- * +* This file is part of OpenFlipper. * +* * +* OpenFlipper is free software: you can redistribute it and/or modify * +* it under the terms of the GNU Lesser General Public License as * +* published by the Free Software Foundation, either version 3 of * +* the License, or (at your option) any later version with the * +* following exceptions: * +* * +* If other files instantiate templates or use macros * +* or inline functions from this file, or you compile this file and * +* link it with other files to produce an executable, this file does * +* not by itself cause the resulting executable to be covered by the * +* GNU Lesser General Public License. This exception does not however * +* invalidate any other reasons why the executable file might be * +* covered by the GNU Lesser General Public License. * +* * +* OpenFlipper is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU LesserGeneral Public * +* License along with OpenFlipper. If not, * +* see . * +* * +\*===========================================================================*/ + +/*===========================================================================*\ +* * +* $Revision: $ * +* $LastChangedBy: $ * +* $Date: $ * +* * +\*===========================================================================*/ + +#ifndef METADATAINTERFACE_HH +#define METADATAINTERFACE_HH + +#include + +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#endif + + + +/** \file MetadataInterface.hh + * + * Interface implementing slots related to the deserialization of meta data + * (usually encountered in screenshots). + */ + +/** \brief Enables implementers to react on deserialization of meta data. + * + * Using functions such as BaseObject::getCommentByKey() plugins can attach + * meta data to objects. This meta data is currently only serialized when + * taking viewer snapshots. "Plugin-DeserializeScreenshotMetadata" allows + * deserializing this metadata from a viewer snapshot. (In the future + * serialization and deserialization of meta data may occur in other contexts + * as well.) + * + * Whenever meta gets deserialized the slots in this interface will get + * triggered. Please refer to the documentation of the individual slots + * to find out which specific signal suits your needs. + * + * The slots are always triggered in the order + * slotGenericMetadataDeserialized(), slotObjectMetadataDeserialized(), + * slotObjectMetadataDeserializedJson(). + */ +class MetadataInterface { + private slots: + + /** + * This low-level signal is very inconvenient to use and chances are you + * want to use one of the other signals. + * + * Triggered for every top level meta data entry. Currently the only top + * level meta data entries created by OpenFlipper are "Mesh Comments", + * "Mesh Materials" and "View". "Mesh Comments" contains the raw + * concatenation of all object meta data and is very cumbersome to + * parse. In most cases it's a better idea to use + * objectMetadataDeserialized(). + */ + virtual void slotGenericMetadataDeserialized( + QString key, QString value) {}; + + /** + * Triggered for every piece of object specific meta data encapsulated + * in "Mesh Comments". + */ + virtual void slotObjectMetadataDeserialized( + QString object_name, QString value) {}; + +#if QT_VERSION >= 0x050000 + /** + * Triggered for every piece of object specific meta data encapsulated + * in "Mesh Comments" if it is valid JSON. + */ + virtual void slotObjectMetadataDeserializedJson( + QString object_name, QJsonDocument value) {}; +#endif + + signals: + /** + * Can be called by anyone who deserializes meta data, e.g. from a + * viewer snapshot PNG file. The appropriate slots within + * this interface will be triggered. + */ + virtual void metadataDeserialized( + const QVector > &data) = 0; + + public: + + /// Destructor + virtual ~MetadataInterface() {}; + +}; + +#if QT_VERSION >= 0x050000 +Q_DECLARE_INTERFACE(MetadataInterface,"OpenFlipper.MetadataInterface_qt5/1.0") +#else +Q_DECLARE_INTERFACE(MetadataInterface,"OpenFlipper.MetadataInterface_qt4/1.0") +#endif + +#endif // METADATAINTERFACE_HH diff --git a/BasePlugin/PluginFunctions.hh b/BasePlugin/PluginFunctions.hh index 5516c26b..1bcc0636 100644 --- a/BasePlugin/PluginFunctions.hh +++ b/BasePlugin/PluginFunctions.hh @@ -670,6 +670,6 @@ QString getSaveFileName(const QString &configProperty, QString * selectedFilter = 0, QFileDialog::Options options = 0, const QString & defaultSuffix = QString() ); -} +} /* namespace PluginFunctions */ #endif //PLUGINFUNCTIONS_HH diff --git a/Core/Core.hh b/Core/Core.hh index d90f882e..4a3acf17 100644 --- a/Core/Core.hh +++ b/Core/Core.hh @@ -1251,6 +1251,23 @@ private slots: void showReducedMenuBar(bool reduced); + //=========================================================================== + /** @name Metadata support + * @{ */ + //=========================================================================== + signals: + void genericMetadataDeserialized(QString key, QString value); + void objectMetadataDeserialized(QString object_name, QString value); +#if QT_VERSION >= 0x050000 + void objectMetadataDeserializedJson( + QString object_name, QJsonDocument value); +#endif + + private slots: + void slotMetadataDeserialized( + const QVector > &data); + + private : /// Core scripting engine QScriptEngine scriptEngine_; diff --git a/Core/PluginCommunication.cc b/Core/PluginCommunication.cc index ae078e29..fa3ac6b7 100644 --- a/Core/PluginCommunication.cc +++ b/Core/PluginCommunication.cc @@ -64,6 +64,8 @@ #include +#include + //== IMPLEMENTATION ========================================================== //======================================================================================== @@ -446,6 +448,71 @@ void Core::slotGetCurrentRenderer(unsigned int _viewer, QString& _rendererName) _rendererName = renderManager().active(_viewer)->name; } +void Core::slotMetadataDeserialized( + const QVector > &data) { + + QString obj_metadata; + for (QVector >::const_iterator + it = data.begin(); it != data.end(); ++it) { + if (it->first == "Mesh Comments") + obj_metadata = it->second; + + emit genericMetadataDeserialized(it->first, it->second); + } + /* + * Now handle object metadata. + */ + QRegExp re_begin("BEGIN Comments for object \"([^\\n]*)\""); + QRegExp re_end("\\nEND Comments for object \"([^\\n]*)\""); + enum STATE { + STATE_SEARCH_BEGIN, + STATE_CONSUME_INNER, + STATE_EOS, + }; + + int cursor = 0; + QString current_object_name; + for (STATE state = STATE_SEARCH_BEGIN; state != STATE_EOS; ) { + switch (state) { + case STATE_SEARCH_BEGIN: + cursor = re_begin.indexIn(obj_metadata, cursor); + if (cursor == -1) { + state = STATE_EOS; + break; + } + current_object_name = re_begin.cap(1); + cursor += re_begin.matchedLength(); + state = STATE_CONSUME_INNER; + break; + case STATE_CONSUME_INNER: + { + int next = re_end.indexIn(obj_metadata, cursor); + if (next == -1) { + state = STATE_EOS; + break; + } + + const QStringRef value = obj_metadata.midRef(cursor, next - cursor); + + emit objectMetadataDeserialized(current_object_name, value.toString()); +#if QT_VERSION >= 0x050000 + QJsonParseError json_error; + QJsonDocument json_doc = + QJsonDocument::fromJson(value.toUtf8(), &json_error); + if (json_error.error == QJsonParseError::NoError) { + emit objectMetadataDeserializedJson( + current_object_name, json_doc); + } +#endif + cursor = next + re_end.matchedLength(); + state = STATE_SEARCH_BEGIN; + break; + } + default: + throw std::logic_error("metadataDeserialized(): Invalid state."); + } + } +} //============================================================================= diff --git a/Core/PluginLoader.cc b/Core/PluginLoader.cc index 185fa7a2..ecb1c678 100644 --- a/Core/PluginLoader.cc +++ b/Core/PluginLoader.cc @@ -94,6 +94,7 @@ #include "OpenFlipper/BasePlugin/SelectionInterface.hh" #include "OpenFlipper/BasePlugin/TypeInterface.hh" #include "OpenFlipper/BasePlugin/PluginConnectionInterface.hh" +#include "OpenFlipper/BasePlugin/MetadataInterface.hh" #include "OpenFlipper/common/RendererInfo.hh" @@ -2254,6 +2255,28 @@ void Core::loadPlugin(const QString& _filename,const bool _silent, QString& _lic this , SLOT( slotEmptyObjectAdded ( int ) ),Qt::DirectConnection); } + MetadataInterface* metadataPlugin = qobject_cast< MetadataInterface * >(plugin); + if ( metadataPlugin ) { + if (checkSlot(plugin, "slotGenericMetadataDeserialized(QString,QString)")) { + connect(this, SIGNAL(genericMetadataDeserialized(QString, QString)), + plugin, SLOT(slotGenericMetadataDeserialized(QString, QString))); + } + if (checkSlot(plugin, "slotObjectMetadataDeserialized(QString,QString)")) { + connect(this, SIGNAL(objectMetadataDeserialized(QString, QString)), + plugin, SLOT(slotObjectMetadataDeserialized(QString, QString))); + } +#if QT_VERSION >= 0x050000 + if (checkSlot(plugin, "slotObjectMetadataDeserializedJson(QString,QJsonDocument)")) { + connect(this, SIGNAL(objectMetadataDeserializedJson(QString, QJsonDocument)), + plugin, SLOT(slotObjectMetadataDeserializedJson(QString, QJsonDocument))); + } +#endif + if (checkSignal(plugin, "metadataDeserialized(QVector >)")) { + connect(plugin, SIGNAL(metadataDeserialized(QVector >)), + this, SLOT(slotMetadataDeserialized(QVector >))); + } + } + emit log(LOGOUT,"================================================================================"); } diff --git a/common/BaseObject.hh b/common/BaseObject.hh index 6814f19d..493b80bd 100644 --- a/common/BaseObject.hh +++ b/common/BaseObject.hh @@ -70,6 +70,10 @@ #include #include #include "perObjectData.hh" +#if QT_VERSION >= 0x050000 +#include +#include +#endif //== TYPEDEFS ================================================================= @@ -575,10 +579,31 @@ class DLLEXPORTONLY BaseObject : public QObject { result.append(QString("BEGIN Comments for object \"%1\"").arg(name())); + /* + * Compose JSON parsable object. + */ +#if QT_VERSION >= 0x050000 + QJsonObject comment_obj; + for (QMap::const_iterator it = commentsByKey_.begin(), it_end = commentsByKey_.end(); + it != it_end; ++it) { + + QJsonParseError json_error; + QString test_json_str = QString::fromUtf8("{\"test\": %1}").arg(it.value()); + QByteArray test_json_ba = test_json_str.toUtf8(); + QJsonDocument test_json = QJsonDocument::fromJson(test_json_ba, &json_error); + if (json_error.error != QJsonParseError::NoError) { + comment_obj[it.key()] = it.value(); + } else { + comment_obj[it.key()] = test_json.object().value("test"); + } + } + result.append(QString::fromUtf8(QJsonDocument(comment_obj).toJson(QJsonDocument::Indented))); +#else for (QMap::const_iterator it = commentsByKey_.begin(), it_end = commentsByKey_.end(); it != it_end; ++it) { result.append(QString("%1: %2").arg(it.key(), it.value())); } +#endif result.append(QString("END Comments for object \"%1\"\n").arg(name())); -- GitLab