Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert Local Models #173

Merged
merged 50 commits into from
Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
e610b80
insert model init
Jun 1, 2020
f8ad140
Update cmakelists
Jun 1, 2020
356b0b1
Add config/sdf search
Jun 2, 2020
38fbd52
init attempt at qml listview
Jun 3, 2020
b24acec
Add grid view component
Jun 3, 2020
9f388f6
Add prototype local model ui
Jun 3, 2020
4de9ee5
Add docs and lint
Jun 3, 2020
d707e8c
Merge branch 'ign-gazebo2' into jshep1/local_models
Jun 3, 2020
4e03306
Remove unnecessary files
Jun 3, 2020
84bd069
Change some path logic
Jun 3, 2020
2fee326
add more image types
Jun 3, 2020
e98ba78
Add generalizable path logic, no thumbnail image
Jun 4, 2020
73ec648
Move test Relay to its own file
chapulina Jun 4, 2020
d09cca3
Resource env var, with transport interface
chapulina Jun 2, 2020
53f38e4
backport resources.md without changes
chapulina Jun 3, 2020
840b173
Update resources tutorial
chapulina Jun 3, 2020
f07d872
Requested changes
Jun 9, 2020
da5ff9e
style fix
Jun 9, 2020
b8e0b5e
Add logic fixes
Jun 9, 2020
47f214c
Add some qml updates
Jun 9, 2020
5770c67
Merge branch 'ign-gazebo2' into jshep1/local_models
Jun 9, 2020
d3efb89
Merge branch 'chapulina/resource_path' into jshep1/local_models
Jun 9, 2020
0719ee1
Add env var path
Jun 9, 2020
d1869fe
merged from ign-gazebo2
chapulina Jun 12, 2020
45e76f4
Also add paths for client, needs tests
chapulina Jun 14, 2020
ad8b3b5
Add path tests for the GUI
chapulina Jun 15, 2020
899d538
Merge branch 'ign-gazebo2' into chapulina/resource_path
chapulina Jun 15, 2020
f023b26
fix OSX
chapulina Jun 16, 2020
0630001
Merge branch 'ign-gazebo2' into chapulina/resource_path
chapulina Jul 1, 2020
e2979ef
subscribe to path updates
chapulina Jul 1, 2020
0fe84c4
PR feedback
chapulina Jul 8, 2020
d66efe2
Merge branch 'ign-gazebo2' into chapulina/resource_path
chapulina Jul 8, 2020
00d9235
Merge branch 'chapulina/resource_path' into jshep1/local_models
Jul 8, 2020
a086574
remove global node for now
chapulina Jul 9, 2020
40f4bfc
Merge branch 'chapulina/resource_path' into jshep1/local_models
Jul 9, 2020
7e679e9
Fix running ign gazebo, more PR feedback, more tests
chapulina Jul 11, 2020
7bdc38a
Merge branch 'chapulina/resource_path' into jshep1/local_models
Jul 11, 2020
cc84c74
Merge branch 'ign-gazebo2' into chapulina/resource_path
chapulina Jul 13, 2020
88859ab
Merge branch 'chapulina/resource_path' into jshep1/local_models
Jul 13, 2020
47a1e64
Add split view selection
Jul 14, 2020
e827c61
Add splitview window
Jul 15, 2020
f5add64
Fix dark mode text
Jul 15, 2020
956354f
Fix spelling
Jul 15, 2020
44e8f4b
Merge branch 'chapulina/resource_path' into jshep1/local_models
Jul 15, 2020
c7a3535
Fixes, I think (#245)
nkoenig Jul 15, 2020
ae84fae
Some UI tweaks to Resource Spawner (#253)
chapulina Jul 27, 2020
c6fff7f
merged from ign-gazebo2
chapulina Aug 4, 2020
7638910
fix merge
chapulina Aug 4, 2020
96e39b1
Update naming and address feedback
Aug 6, 2020
b155ec0
Merge branch 'ign-gazebo2' into jshep1/local_models
Aug 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/gui/plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ add_subdirectory(modules)
add_subdirectory(component_inspector)
add_subdirectory(entity_tree)
add_subdirectory(grid_config)
add_subdirectory(insert_model)
add_subdirectory(scene3d)
add_subdirectory(shapes)
add_subdirectory(transform_control)
Expand Down
4 changes: 4 additions & 0 deletions src/gui/plugins/insert_model/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
gz_add_gui_plugin(InsertModel
JShep1 marked this conversation as resolved.
Show resolved Hide resolved
SOURCES InsertModel.cc
QT_HEADERS InsertModel.hh
)
221 changes: 221 additions & 0 deletions src/gui/plugins/insert_model/InsertModel.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*
* Copyright (C) 2020 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <ignition/msgs/boolean.pb.h>
#include <ignition/msgs/stringmsg.pb.h>

#include <sdf/Root.hh>
#include <sdf/parser.hh>

#include <ignition/common/Console.hh>
#include <ignition/common/Profiler.hh>
#include <ignition/common/Filesystem.hh>
#include <ignition/gui/Application.hh>
#include <ignition/gui/MainWindow.hh>
#include <ignition/plugin/Register.hh>
#include <ignition/transport/Node.hh>
#include <ignition/transport/Publisher.hh>

#include "ignition/gazebo/EntityComponentManager.hh"
#include "ignition/gazebo/gui/GuiEvents.hh"

#include "InsertModel.hh"

namespace ignition::gazebo
{
class InsertModelPrivate
{
/// \brief Ignition communication node.
public: transport::Node node;

/// \brief Mutex to protect mode
public: std::mutex mutex;

/// \brief Transform control service name
public: std::string service;
JShep1 marked this conversation as resolved.
Show resolved Hide resolved

/// \brief The grid model that the qml gridview reflects
public: GridModel gridModel;
};
}

using namespace ignition;
using namespace gazebo;

/////////////////////////////////////////////////
GridModel::GridModel() : QStandardItemModel()
{
}

/////////////////////////////////////////////////
void GridModel::AddLocalModel(LocalModel &_model)
{
IGN_PROFILE_THREAD_NAME("Qt thread");
IGN_PROFILE("GridModel::AddLocalModel");
QStandardItem *parentItem{nullptr};

parentItem = this->invisibleRootItem();

auto localModel = new QStandardItem(QString::fromStdString(_model.name));
localModel->setData(QString::fromStdString(_model.thumbnailPath),
this->roleNames().key("thumbnail"));
localModel->setData(QString::fromStdString(_model.name),
this->roleNames().key("name"));
localModel->setData(QString::fromStdString(_model.sdfPath),
this->roleNames().key("sdf"));

parentItem->appendRow(localModel);
}

/////////////////////////////////////////////////
QHash<int, QByteArray> GridModel::roleNames() const
{
return {
JShep1 marked this conversation as resolved.
Show resolved Hide resolved
std::pair(100, "thumbnail"),
std::pair(101, "name"),
std::pair(102, "sdf"),
};
}

/////////////////////////////////////////////////
InsertModel::InsertModel()
: ignition::gui::Plugin(),
dataPtr(std::make_unique<InsertModelPrivate>())
{
ignition::gui::App()->Engine()->rootContext()->setContextProperty(
"LocalModelList", &this->dataPtr->gridModel);
}

/////////////////////////////////////////////////
InsertModel::~InsertModel() = default;

/////////////////////////////////////////////////
void InsertModel::FindLocalModels(const std::string &_path)
{
std::string path = _path;
// Recurse if directory
if (common::isDirectory(path))
JShep1 marked this conversation as resolved.
Show resolved Hide resolved
{
for (common::DirIter file(path); file != common::DirIter(); ++file)
{
std::string current(*file);
this->FindLocalModels(current);
}
}
else if (common::isFile(path))
{
std::string fileName = common::basename(path);

// If we have found model.config, extract thumbnail and sdf
if (fileName == "model.config")
JShep1 marked this conversation as resolved.
Show resolved Hide resolved
{
LocalModel model;
model.configPath = path;
std::string modelPath = common::parentPath(path);
std::string thumbnailPath = common::joinPaths(modelPath, "thumbnails");
std::string configFileName = common::joinPaths(modelPath, "model.config");
tinyxml2::XMLDocument doc;
doc.LoadFile(configFileName.c_str());
auto modelXml = doc.FirstChildElement("model");

if (modelXml)
{
auto modelName = modelXml->FirstChildElement("name");
if (modelName)
model.name = modelName->GetText();
}
std::string sdfPath = sdf::getModelFilePath(modelPath);
model.sdfPath = sdfPath;

// Get first thumbnail image found
if (common::exists(thumbnailPath))
{
for (common::DirIter file(thumbnailPath);
file != common::DirIter(); ++file)
{
std::string current(*file);
if (common::isFile(current))
{
std::string thumbnailFileName = common::basename(current);
std::string::size_type thumbnailExtensionIndex =
thumbnailFileName.rfind(".");
std::string thumbnailFileExtension =
thumbnailFileName.substr(thumbnailExtensionIndex + 1);
// The standard image types QML supports
if (thumbnailFileExtension == "png" ||
thumbnailFileExtension == "jpg" ||
thumbnailFileExtension == "jpeg" ||
thumbnailFileExtension == "svg")
{
model.thumbnailPath = current;
break;
}
}
}
}
this->dataPtr->gridModel.AddLocalModel(model);
}
}
}

/////////////////////////////////////////////////
void InsertModel::FindLocalModels(const std::vector<std::string> &_paths)
{
for (const auto &path : _paths)
this->FindLocalModels(path);
}

/////////////////////////////////////////////////
void InsertModel::LoadConfig(const tinyxml2::XMLElement *)
{
if (this->title.empty())
this->title = "InsertModel";

// For shapes requests
ignition::gui::App()->findChild
<ignition::gui::MainWindow *>()->installEventFilter(this);

// TODO(john): create vector of paths from IGN_GAZEBO_RESOURCE_PATH here
JShep1 marked this conversation as resolved.
Show resolved Hide resolved
std::string path =
"/home/john/.ignition/fuel/fuel.ignitionrobotics.org/openrobotics/models";
std::vector<std::string> paths;
paths.push_back(path);

this->FindLocalModels(paths);
}

/////////////////////////////////////////////////
void InsertModel::OnMode(const QString &_sdfPath)
{
std::string modelSdfPath = _sdfPath.toStdString();

// Parse the sdf from the path
std::ifstream nameFileout;
nameFileout.open(modelSdfPath);
std::string line;
std::string modelSdfString = "";
while (std::getline(nameFileout, line))
modelSdfString += line + "\n";

auto event = new gui::events::SpawnPreviewModel(modelSdfString);
ignition::gui::App()->sendEvent(
ignition::gui::App()->findChild<ignition::gui::MainWindow *>(),
event);
}

// Register this plugin
IGNITION_ADD_PLUGIN(ignition::gazebo::InsertModel,
ignition::gui::Plugin)
98 changes: 98 additions & 0 deletions src/gui/plugins/insert_model/InsertModel.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2020 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#ifndef IGNITION_GAZEBO_GUI_INSERT_MODEL_HH_
#define IGNITION_GAZEBO_GUI_INSERT_MODEL_HH_

#include <memory>
#include <string>
#include <vector>

#include <ignition/gui/Plugin.hh>

namespace ignition
{
namespace gazebo
{
class InsertModelPrivate;

/// \brief Local model used to update the GridModel
struct LocalModel
{
std::string configPath = "";
std::string sdfPath = "";
std::string thumbnailPath = "";
std::string name = "";
JShep1 marked this conversation as resolved.
Show resolved Hide resolved
};

/// \brief Provides a model by which the insert model qml plugin pulls
/// and updates from
class GridModel : public QStandardItemModel
{
Q_OBJECT

/// \brief Constructor
public: explicit GridModel();

/// \brief Destructor
public: ~GridModel() override = default;

/// \brief Add a local model to the grid view.
/// param[in] _model The local model to be added
public slots: void AddLocalModel(LocalModel &_model);

// Documentation inherited
public: QHash<int, QByteArray> roleNames() const override;
};

/// \brief Provides interface for communicating to backend for generation
/// of local models
class InsertModel : public ignition::gui::Plugin
{
Q_OBJECT

/// \brief Constructor
public: InsertModel();

/// \brief Destructor
public: ~InsertModel() override;

// Documentation inherited
public: void LoadConfig(const tinyxml2::XMLElement *_pluginElem) override;

/// \brief Callback in Qt thread when mode changes.
/// \param[in] _mode New transform mode
public slots: void OnMode(const QString &_mode);
JShep1 marked this conversation as resolved.
Show resolved Hide resolved

/// \brief Recursively searches the provided paths for all model.config's
/// and populates a vector of local models with the information
/// \param[in] _paths The paths to search
public: void FindLocalModels(const std::vector<std::string> &_paths);

/// \brief Recursively searches the provided path for all model.config's
/// and populates a vector of local models with the information
/// \param[in] _path The path to search
public: void FindLocalModels(const std::string &_path);

/// \internal
/// \brief Pointer to private data.
private: std::unique_ptr<InsertModelPrivate> dataPtr;
};
}
}

#endif
64 changes: 64 additions & 0 deletions src/gui/plugins/insert_model/InsertModel.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2020 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.2
import QtQuick.Controls.Material.impl 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4

Rectangle {
Copy link
Contributor

@chapulina chapulina Jun 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've already talked about this in person, but I'll just leave some UI notes here so we don't forget:

  • All text should be the same size
  • Use Text.ElideRight for names
  • Support light / dark modes
  • Use Material.elevation to give each item a "card" look
  • Add more padding to the edges of the plugin, and between the items
  • Change the cursor to Qt.WaitCursor while the preview is being spawned

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First four points are resolved in 47f214c

id: insertmodel
color: "transparent"
Layout.minimumWidth: 200
Layout.minimumHeight: 500
anchors.fill: parent
GridView {
id: gridView
anchors.fill: parent

model: LocalModelList
delegate: Rectangle {
width: gridView.cellWidth
height: gridView.cellHeight
color: "transparent"
Image {
anchors.fill: parent
anchors.margins: 1
source: model.thumbnail == "" ? "NoThumbnail.png" : model.thumbnail
fillMode: Image.PreserveAspectFit
}
MouseArea {
anchors.fill: parent
onClicked: {
InsertModel.OnMode(model.sdf);
}
}
Text {
text: model.name
width: parent.width
height: parent.height
minimumPointSize: 5
font.pointSize: 60
fontSizeMode: Text.Fit
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
Loading