Skip to content

Commit 0d4571f

Browse files
John ShepherdchapulinaNate Koenig
committed
Insert Local Models (#173)
Signed-off-by: John Shepherd <[email protected]> Co-authored-by: Louise Poubel <[email protected]> Co-authored-by: Nate Koenig <[email protected]>
1 parent db6a467 commit 0d4571f

9 files changed

+677
-0
lines changed

src/gui/plugins/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ add_subdirectory(align_tool)
8585
add_subdirectory(component_inspector)
8686
add_subdirectory(entity_tree)
8787
add_subdirectory(grid_config)
88+
add_subdirectory(resource_spawner)
8889
add_subdirectory(scene3d)
8990
add_subdirectory(shapes)
9091
add_subdirectory(transform_control)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
gz_add_gui_plugin(ResourceSpawner
2+
SOURCES ResourceSpawner.cc
3+
QT_HEADERS ResourceSpawner.hh
4+
)
6.74 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/*
2+
* Copyright (C) 2020 Open Source Robotics Foundation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
#include <ignition/msgs/boolean.pb.h>
19+
#include <ignition/msgs/stringmsg.pb.h>
20+
21+
#include <sdf/Root.hh>
22+
#include <sdf/parser.hh>
23+
24+
#include <ignition/common/Console.hh>
25+
#include <ignition/common/Profiler.hh>
26+
#include <ignition/common/Filesystem.hh>
27+
#include <ignition/gui/Application.hh>
28+
#include <ignition/gui/MainWindow.hh>
29+
#include <ignition/plugin/Register.hh>
30+
#include <ignition/transport/Node.hh>
31+
#include <ignition/transport/Publisher.hh>
32+
33+
#include "ignition/gazebo/EntityComponentManager.hh"
34+
#include "ignition/gazebo/gui/GuiEvents.hh"
35+
36+
#include "ResourceSpawner.hh"
37+
38+
namespace ignition::gazebo
39+
{
40+
class ResourceSpawnerPrivate
41+
{
42+
/// \brief Ignition communication node.
43+
public: transport::Node node;
44+
45+
/// \brief The grid model that the qml gridview reflects
46+
public: ResourceModel resourceModel;
47+
48+
/// \brief The path list model that the qml treeview reflects
49+
public: PathModel pathModel;
50+
};
51+
}
52+
53+
using namespace ignition;
54+
using namespace gazebo;
55+
56+
/////////////////////////////////////////////////
57+
PathModel::PathModel() : QStandardItemModel()
58+
{
59+
}
60+
61+
/////////////////////////////////////////////////
62+
void PathModel::AddPath(const std::string &_path)
63+
{
64+
IGN_PROFILE_THREAD_NAME("Qt thread");
65+
IGN_PROFILE("PathModel::AddPath");
66+
QStandardItem *parentItem{nullptr};
67+
68+
parentItem = this->invisibleRootItem();
69+
70+
auto localModel = new QStandardItem(QString::fromStdString(_path));
71+
localModel->setData(QString::fromStdString(_path),
72+
this->roleNames().key("path"));
73+
74+
parentItem->appendRow(localModel);
75+
}
76+
77+
/////////////////////////////////////////////////
78+
QHash<int, QByteArray> PathModel::roleNames() const
79+
{
80+
return
81+
{
82+
std::pair(100, "path"),
83+
};
84+
}
85+
86+
/////////////////////////////////////////////////
87+
ResourceModel::ResourceModel() : QStandardItemModel()
88+
{
89+
}
90+
91+
/////////////////////////////////////////////////
92+
void ResourceModel::Clear()
93+
{
94+
QStandardItem *parentItem{nullptr};
95+
parentItem = this->invisibleRootItem();
96+
97+
while (parentItem->rowCount() > 0)
98+
{
99+
parentItem->removeRow(0);
100+
}
101+
}
102+
103+
/////////////////////////////////////////////////
104+
void ResourceModel::AddResource(Resource &_resource)
105+
{
106+
IGN_PROFILE_THREAD_NAME("Qt thread");
107+
IGN_PROFILE("GridModel::AddResource");
108+
QStandardItem *parentItem{nullptr};
109+
110+
parentItem = this->invisibleRootItem();
111+
112+
auto resource = new QStandardItem(QString::fromStdString(_resource.name));
113+
resource->setData(QString::fromStdString(_resource.thumbnailPath),
114+
this->roleNames().key("thumbnail"));
115+
resource->setData(QString::fromStdString(_resource.name),
116+
this->roleNames().key("name"));
117+
resource->setData(QString::fromStdString(_resource.sdfPath),
118+
this->roleNames().key("sdf"));
119+
120+
parentItem->appendRow(resource);
121+
}
122+
123+
/////////////////////////////////////////////////
124+
QHash<int, QByteArray> ResourceModel::roleNames() const
125+
{
126+
return
127+
{
128+
std::pair(100, "thumbnail"),
129+
std::pair(101, "name"),
130+
std::pair(102, "sdf"),
131+
};
132+
}
133+
134+
/////////////////////////////////////////////////
135+
ResourceSpawner::ResourceSpawner()
136+
: ignition::gui::Plugin(),
137+
dataPtr(std::make_unique<ResourceSpawnerPrivate>())
138+
{
139+
ignition::gui::App()->Engine()->rootContext()->setContextProperty(
140+
"ResourceList", &this->dataPtr->resourceModel);
141+
ignition::gui::App()->Engine()->rootContext()->setContextProperty(
142+
"PathList", &this->dataPtr->pathModel);
143+
}
144+
145+
/////////////////////////////////////////////////
146+
ResourceSpawner::~ResourceSpawner() = default;
147+
148+
/////////////////////////////////////////////////
149+
void ResourceSpawner::LoadLocalResource(const std::string &_path)
150+
{
151+
std::string fileName = common::basename(_path);
152+
if (!common::isFile(_path) || fileName != "model.config")
153+
return;
154+
155+
// If we have found model.config, extract thumbnail and sdf
156+
Resource resource;
157+
std::string resourcePath = common::parentPath(_path);
158+
std::string thumbnailPath = common::joinPaths(resourcePath, "thumbnails");
159+
std::string configFileName = common::joinPaths(resourcePath, "model.config");
160+
tinyxml2::XMLDocument doc;
161+
doc.LoadFile(configFileName.c_str());
162+
auto modelXml = doc.FirstChildElement("model");
163+
164+
if (modelXml)
165+
{
166+
auto modelName = modelXml->FirstChildElement("name");
167+
if (modelName)
168+
resource.name = modelName->GetText();
169+
}
170+
std::string sdfPath = sdf::getModelFilePath(resourcePath);
171+
resource.sdfPath = sdfPath;
172+
173+
// Get first thumbnail image found
174+
if (common::exists(thumbnailPath))
175+
{
176+
for (common::DirIter file(thumbnailPath);
177+
file != common::DirIter(); ++file)
178+
{
179+
std::string current(*file);
180+
if (common::isFile(current))
181+
{
182+
std::string thumbnailFileName = common::basename(current);
183+
std::string::size_type thumbnailExtensionIndex =
184+
thumbnailFileName.rfind(".");
185+
std::string thumbnailFileExtension =
186+
thumbnailFileName.substr(thumbnailExtensionIndex + 1);
187+
// The standard image types QML supports
188+
if (thumbnailFileExtension == "png" ||
189+
thumbnailFileExtension == "jpg" ||
190+
thumbnailFileExtension == "jpeg" ||
191+
thumbnailFileExtension == "svg")
192+
{
193+
resource.thumbnailPath = current;
194+
break;
195+
}
196+
}
197+
}
198+
}
199+
this->dataPtr->resourceModel.AddResource(resource);
200+
}
201+
202+
/////////////////////////////////////////////////
203+
void ResourceSpawner::FindLocalResources(const std::string &_path)
204+
{
205+
std::string path = _path;
206+
if (common::isDirectory(path))
207+
{
208+
for (common::DirIter file(path); file != common::DirIter(); ++file)
209+
{
210+
std::string currentPath(*file);
211+
if (common::isDirectory(currentPath))
212+
{
213+
std::string modelConfigPath =
214+
common::joinPaths(currentPath, "model.config");
215+
this->LoadLocalResource(modelConfigPath);
216+
}
217+
else
218+
{
219+
this->LoadLocalResource(currentPath);
220+
}
221+
}
222+
}
223+
else
224+
{
225+
this->LoadLocalResource(path);
226+
}
227+
}
228+
229+
/////////////////////////////////////////////////
230+
void ResourceSpawner::AddPath(const std::string &_path)
231+
{
232+
this->dataPtr->pathModel.AddPath(_path);
233+
}
234+
235+
/////////////////////////////////////////////////
236+
void ResourceSpawner::OnPathClicked(const QString &_path)
237+
{
238+
this->dataPtr->resourceModel.Clear();
239+
this->FindLocalResources(_path.toStdString());
240+
}
241+
242+
/////////////////////////////////////////////////
243+
void ResourceSpawner::LoadConfig(const tinyxml2::XMLElement *)
244+
{
245+
if (this->title.empty())
246+
this->title = "Resource Spawner";
247+
248+
msgs::StringMsg_V res;
249+
bool result;
250+
bool executed = this->dataPtr->node.Request(
251+
"/gazebo/resource_paths/get", 5000, res, result);
252+
if (!executed || !result || res.data_size() < 1)
253+
{
254+
ignwarn << "No paths found in IGN_GAZEBO_RESOURCE_PATH.\n";
255+
return;
256+
}
257+
258+
for (int i = 0; i < res.data_size(); i++)
259+
{
260+
const std::string path = res.data(i);
261+
this->AddPath(path);
262+
}
263+
}
264+
265+
/////////////////////////////////////////////////
266+
void ResourceSpawner::OnResourceSpawn(const QString &_sdfPath)
267+
{
268+
std::string modelSdfPath = _sdfPath.toStdString();
269+
270+
// Parse the sdf from the path
271+
std::ifstream nameFileout;
272+
nameFileout.open(modelSdfPath);
273+
std::string line;
274+
std::string modelSdfString = "";
275+
while (std::getline(nameFileout, line))
276+
modelSdfString += line + "\n";
277+
278+
auto event = new gui::events::SpawnPreviewModel(modelSdfString);
279+
ignition::gui::App()->sendEvent(
280+
ignition::gui::App()->findChild<ignition::gui::MainWindow *>(),
281+
event);
282+
}
283+
284+
// Register this plugin
285+
IGNITION_ADD_PLUGIN(ignition::gazebo::ResourceSpawner,
286+
ignition::gui::Plugin)

0 commit comments

Comments
 (0)