1 #include <pcl/apps/cloud_composer/project_model.h>
2 #include <pcl/apps/cloud_composer/tool_interface/abstract_tool.h>
3 #include <pcl/apps/cloud_composer/commands.h>
4 #include <pcl/apps/cloud_composer/work_queue.h>
5 #include <pcl/apps/cloud_composer/items/cloud_item.h>
6 #include <pcl/apps/cloud_composer/cloud_view.h>
7 #include <pcl/apps/cloud_composer/merge_selection.h>
8 #include <pcl/apps/cloud_composer/transform_clouds.h>
9 #include <pcl/memory.h>
10
11 #include <QAction>
12 #include <QFileDialog>
13 #include <QItemSelectionModel>
14 #include <QMessageBox>
15 #include <QThread>
16
17 #include <vtkSmartPointer.h>
18 #include <vtkImageData.h>
19 #include <vtkImageReader2Factory.h>
20 #include <vtkImageReader2.h>
21
ProjectModel(QObject * parent)22 pcl::cloud_composer::ProjectModel::ProjectModel (QObject* parent)
23 : QStandardItemModel (parent)
24 {
25
26 last_directory_ = QDir (".");
27
28 selection_model_ = new QItemSelectionModel (this);
29
30 undo_stack_ = new QUndoStack (this);
31 undo_stack_->setUndoLimit (10);
32
33 work_thread_ = new QThread();
34 work_queue_ = new WorkQueue ();
35 work_queue_->moveToThread (work_thread_);
36
37 connect (this, SIGNAL (enqueueNewAction (AbstractTool*, ConstItemList)),
38 work_queue_, SLOT (enqueueNewAction (AbstractTool*, ConstItemList)));
39 connect (work_queue_, SIGNAL (commandComplete (CloudCommand*)),
40 this, SLOT (commandCompleted (CloudCommand*)));
41 work_thread_->start ();
42
43 connect (this, SIGNAL (rowsInserted ( const QModelIndex, int, int)),
44 this, SIGNAL (modelChanged ()));
45 connect (this, SIGNAL (rowsRemoved ( const QModelIndex, int, int)),
46 this, SIGNAL (modelChanged ()));
47
48 connect (this, SIGNAL (rowsAboutToBeRemoved ( const QModelIndex, int, int)),
49 this, SLOT (clearSelection()));
50 connect (selection_model_, SIGNAL (selectionChanged(QItemSelection,QItemSelection)),
51 this, SLOT (emitAllStateSignals ()));
52 connect (selection_model_, SIGNAL (selectionChanged(QItemSelection,QItemSelection)),
53 this, SLOT (itemSelectionChanged (QItemSelection,QItemSelection)));
54
55 selected_style_map_.insert (interactor_styles::PCL_VISUALIZER, true);
56 selected_style_map_.insert (interactor_styles::CLICK_TRACKBALL, false);
57 selected_style_map_.insert (interactor_styles::RECTANGULAR_FRUSTUM, false);
58 selected_style_map_.insert (interactor_styles::SELECTED_TRACKBALL, false);
59 }
60
ProjectModel(const ProjectModel &)61 pcl::cloud_composer::ProjectModel::ProjectModel (const ProjectModel&)
62 : QStandardItemModel ()
63 {
64 }
65
~ProjectModel()66 pcl::cloud_composer::ProjectModel::~ProjectModel ()
67 {
68 work_thread_->quit ();
69 work_thread_->deleteLater ();
70 work_queue_->deleteLater ();
71 }
72
ProjectModel(QString project_name,QObject * parent)73 pcl::cloud_composer::ProjectModel::ProjectModel (QString project_name, QObject* parent)
74 : QStandardItemModel (parent)
75 {
76 selection_model_ = new QItemSelectionModel(this);
77 setName (std::move(project_name));
78 }
79
80 void
setName(const QString & new_name)81 pcl::cloud_composer::ProjectModel::setName (const QString& new_name)
82 {
83 //If it hasn't been set yet
84 if (!horizontalHeaderItem (0))
85 setHorizontalHeaderItem (0, new QStandardItem (new_name));
86 else
87 {
88 QStandardItem* header = horizontalHeaderItem (0);
89 header->setText(new_name);
90 }
91 }
92
93 void
setCloudView(CloudView * view)94 pcl::cloud_composer::ProjectModel::setCloudView (CloudView* view)
95 {
96 cloud_view_ = view;
97 // Initialize status variables tied to the view
98 setAxisVisibility (true);
99
100 }
101
102 void
setPointSelection(const std::shared_ptr<SelectionEvent> & selected_event)103 pcl::cloud_composer::ProjectModel::setPointSelection (const std::shared_ptr<SelectionEvent>& selected_event)
104 {
105 selection_event_ = selected_event;
106 //Get all the items in this project that are clouds
107 QList <CloudItem*> project_clouds;
108 for (int i = 0; i < this->rowCount (); ++i)
109 {
110 CloudItem* cloud_item = dynamic_cast <CloudItem*> (this->item (i));
111 if ( cloud_item )
112 project_clouds.append ( cloud_item );
113 }
114
115 selected_item_index_map_.clear ();
116 // Find all indices in the selected points which are present in the clouds
117 foreach (CloudItem* cloud_item, project_clouds)
118 {
119 pcl::PointIndices::Ptr found_indices = pcl::make_shared<pcl::PointIndices>();
120 selected_event->findIndicesInItem (cloud_item, found_indices);
121 if (!found_indices->indices.empty ())
122 {
123 qDebug () << "Found "<<found_indices->indices.size ()<<" points in "<<cloud_item->text ();
124 selected_item_index_map_. insert (cloud_item, found_indices);
125 cloud_item->setForeground (QBrush (Qt::green));
126 }
127 }
128 setSelectedStyle (interactor_styles::PCL_VISUALIZER);
129 emit mouseStyleState (interactor_styles::PCL_VISUALIZER);
130 }
131
132 void
manipulateClouds(const std::shared_ptr<ManipulationEvent> & manip_event)133 pcl::cloud_composer::ProjectModel::manipulateClouds (const std::shared_ptr<ManipulationEvent>& manip_event)
134 {
135
136 //Get all the items in this project that are clouds
137 QList <CloudItem*> project_clouds;
138 for (int i = 0; i < this->rowCount (); ++i)
139 {
140 CloudItem* cloud_item = dynamic_cast <CloudItem*> (this->item (i));
141 if ( cloud_item )
142 project_clouds.append ( cloud_item );
143 }
144
145 QMap <QString, vtkSmartPointer<vtkMatrix4x4> > transform_map = manip_event->getEndMap ();
146 QList <QString> ids = transform_map.keys ();
147 ConstItemList input_data;
148
149 TransformClouds* transform_tool = new TransformClouds (transform_map);
150 foreach (CloudItem* cloud_item, project_clouds)
151 {
152 if (ids.contains (cloud_item->getId ()))
153 {
154 qDebug () << "Found matching item for actor "<<cloud_item->getId ();
155 input_data.append (cloud_item);
156 }
157 }
158
159 //Move the tool object to the work queue thread
160 transform_tool->moveToThread (work_thread_);
161 //Emit signal which tells work queue to enqueue this new action
162 emit enqueueNewAction (transform_tool, input_data);
163
164
165 }
166
167 void
insertNewCloudFromFile()168 pcl::cloud_composer::ProjectModel::insertNewCloudFromFile ()
169 {
170 qDebug () << "Inserting cloud from file...";
171 QString filename = QFileDialog::getOpenFileName (nullptr,tr ("Select cloud to open"), last_directory_.absolutePath (), tr ("PointCloud(*.pcd)"));
172 if ( filename.isNull ())
173 {
174 qWarning () << "No file selected, no cloud loaded";
175 return;
176 }
177 QFileInfo file_info (filename);
178 last_directory_ = file_info.absoluteDir ();
179
180 pcl::PCLPointCloud2::Ptr cloud_blob (new pcl::PCLPointCloud2);
181 Eigen::Vector4f origin;
182 Eigen::Quaternionf orientation;
183 int version;
184
185 pcl::PCDReader pcd;
186 if (pcd.read (filename.toStdString (), *cloud_blob, origin, orientation, version) < 0)
187 {
188 qDebug () << "Failed to read cloud from file";
189 return;
190 }
191 if (cloud_blob->width * cloud_blob->height == 0)
192 {
193 qDebug () << "Cloud read has zero size!";
194 return;
195 }
196
197 QString short_filename = file_info.baseName ();
198 //Check if this name already exists in the project - if so, append digit
199 QList <QStandardItem*> items = findItems (short_filename);
200 if (!items.empty ())
201 {
202 int k = 2;
203 items = findItems (short_filename+ tr ("-%1").arg (k));
204 while (!items.empty ())
205 {
206 ++k;
207 items = findItems (short_filename+ tr ("-%1").arg (k));
208 }
209 short_filename += tr ("-%1").arg (k);
210 }
211 CloudItem* new_item = new CloudItem (short_filename, cloud_blob, origin, orientation, true);
212
213 insertNewCloudComposerItem (new_item, invisibleRootItem());
214
215 }
216
217 void
insertNewCloudFromRGBandDepth()218 pcl::cloud_composer::ProjectModel::insertNewCloudFromRGBandDepth ()
219 {
220 qDebug () << "Inserting cloud from RGB and Depth files...";
221 QString rgb_filename = QFileDialog::getOpenFileName (nullptr,tr ("Select rgb image file to open"), last_directory_.absolutePath (), tr ("Images(*.png *.bmp *.tif *.ppm)"));
222 QString depth_filename;
223 if ( rgb_filename.isNull ())
224 {
225 qWarning () << "No file selected, no cloud loaded";
226 return;
227 }
228 QFileInfo file_info (rgb_filename);
229 last_directory_ = file_info.absoluteDir ();
230 QString base_name = file_info.baseName ();
231 QStringList depth_filter;
232 depth_filter << base_name.split("_").at(0) + "_depth.*";
233 last_directory_.setNameFilters (depth_filter);
234 QFileInfoList depth_info_list = last_directory_.entryInfoList ();
235 if (depth_info_list.empty ())
236 {
237 qCritical () << "Could not find depth file in format (rgb file base name)_depth.*";
238 return;
239 }
240 if (depth_info_list.size () > 1)
241 {
242 qWarning () << "Found more than one file which matches depth naming format, using first one!";
243 }
244 depth_filename = depth_info_list.at (0).absoluteFilePath ();
245
246 //Read the images
247 vtkSmartPointer<vtkImageReader2Factory> reader_factory = vtkSmartPointer<vtkImageReader2Factory>::New ();
248 vtkImageReader2* rgb_reader = reader_factory->CreateImageReader2 (rgb_filename.toStdString ().c_str ());
249 qDebug () << "RGB File="<<rgb_filename;
250 if ( ! rgb_reader->CanReadFile (rgb_filename.toStdString ().c_str ()))
251 {
252 qCritical () << "Cannot read rgb image file!";
253 return;
254 }
255 rgb_reader->SetFileName (rgb_filename.toStdString ().c_str ());
256 rgb_reader->Update ();
257 qDebug () << "Depth File="<<depth_filename;
258 vtkImageReader2* depth_reader = reader_factory->CreateImageReader2 (depth_filename.toStdString ().c_str ());
259 if ( ! depth_reader->CanReadFile (depth_filename.toStdString ().c_str ()))
260 {
261 qCritical () << "Cannot read depth image file!";
262 return;
263 }
264 depth_reader->SetFileName (depth_filename.toStdString ().c_str ());
265 depth_reader->Update ();
266
267 vtkSmartPointer<vtkImageData> rgb_image = rgb_reader->GetOutput ();
268 int *rgb_dims = rgb_image->GetDimensions ();
269 vtkSmartPointer<vtkImageData> depth_image = depth_reader->GetOutput ();
270 int *depth_dims = depth_image->GetDimensions ();
271
272 if (rgb_dims[0] != depth_dims[0] || rgb_dims[1] != depth_dims[1])
273 {
274 qCritical () << "Depth and RGB dimensions to not match!";
275 qDebug () << "RGB Image is of size "<<rgb_dims[0] << " by "<<rgb_dims[1];
276 qDebug () << "Depth Image is of size "<<depth_dims[0] << " by "<<depth_dims[1];
277 return;
278 }
279 qDebug () << "Images loaded, making cloud";
280 PointCloud<PointXYZRGB>::Ptr cloud (new PointCloud<PointXYZRGB>);
281 cloud->points.reserve (depth_dims[0] * depth_dims[1]);
282 cloud->width = depth_dims[0];
283 cloud->height = depth_dims[1];
284 cloud->is_dense = false;
285
286
287 // Fill in image data
288 int centerX = static_cast<int>(cloud->width / 2.0);
289 int centerY = static_cast<int>(cloud->height / 2.0);
290 unsigned short* depth_pixel;
291 unsigned char* color_pixel;
292 float scale = 1.0f/1000.0f;
293 float focal_length = 525.0f;
294 float fl_const = 1.0f / focal_length;
295 depth_pixel = static_cast<unsigned short*>(depth_image->GetScalarPointer (depth_dims[0]-1,depth_dims[1]-1,0));
296 color_pixel = static_cast<unsigned char*> (rgb_image->GetScalarPointer (depth_dims[0]-1,depth_dims[1]-1,0));
297
298 for (std::uint32_t y=0; y<cloud->height; ++y)
299 {
300 for (std::uint32_t x=0; x<cloud->width; ++x, --depth_pixel, color_pixel-=3)
301 {
302 PointXYZRGB new_point;
303 // std::uint8_t* p_i = &(cloud_blob->data[y * cloud_blob->row_step + x * cloud_blob->point_step]);
304 float depth = (float)(*depth_pixel) * scale;
305 // qDebug () << "Depth = "<<depth;
306 if (depth == 0.0f)
307 {
308 new_point.x = new_point.y = new_point.z = std::numeric_limits<float>::quiet_NaN ();
309 }
310 else
311 {
312 new_point.x = ((float)(x - centerX)) * depth * fl_const;
313 new_point.y = ((float)(centerY - y)) * depth * fl_const; // vtk seems to start at the bottom left image corner
314 new_point.z = depth;
315 }
316
317 new_point.r = color_pixel[0];
318 new_point.g = color_pixel[1];
319 new_point.b = color_pixel[2];
320 cloud->points.push_back (new_point);
321 // qDebug () << "depth = "<<depth << "x,y,z="<<data[0]<<","<<data[1]<<","<<data[2];
322 //qDebug() << "r ="<<color_pixel[0]<<" g="<<color_pixel[1]<<" b="<<color_pixel[2];
323
324 }
325 }
326 qDebug () << "Done making cloud!";
327
328 QString short_filename = file_info.baseName ();
329 //Check if this name already exists in the project - if so, append digit
330 QList <QStandardItem*> items = findItems (short_filename);
331 if (!items.empty ())
332 {
333 int k = 2;
334 items = findItems (short_filename+ tr ("-%1").arg (k));
335 while (!items.empty ())
336 {
337 ++k;
338 items = findItems (short_filename+ tr ("-%1").arg (k));
339 }
340 short_filename += tr ("-%1").arg (k);
341 }
342
343 CloudItem* new_item = CloudItem::createCloudItemFromTemplate<PointXYZRGB> (short_filename,cloud);
344
345 insertNewCloudComposerItem (new_item, invisibleRootItem());
346
347 }
348 void
saveSelectedCloudToFile()349 pcl::cloud_composer::ProjectModel::saveSelectedCloudToFile ()
350 {
351 qDebug () << "Saving cloud to file...";
352 QModelIndexList selected_indexes = selection_model_->selectedIndexes ();
353 if (selected_indexes.empty ())
354 {
355 QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "No Cloud Selected", "Cannot save, no cloud is selected in the browser or cloud view");
356 return;
357 }
358 if (selected_indexes.size () > 1)
359 {
360 QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "Too many clouds Selected", "Cannot save, currently only support saving one cloud at a time");
361 return;
362 }
363
364 QStandardItem* item = this->itemFromIndex (selected_indexes.value (0));
365 CloudItem* cloud_to_save = dynamic_cast <CloudItem*> (item);
366 if (!cloud_to_save )
367 {
368 QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "Not a Cloud!", "Selected item is not a cloud, not saving!");
369 return;
370 }
371
372 QString filename = QFileDialog::getSaveFileName (nullptr,tr ("Save Cloud"), last_directory_.absolutePath (), tr ("PointCloud(*.pcd)"));
373 if ( filename.isNull ())
374 {
375 qWarning () << "No file selected, not saving";
376 return;
377 }
378 QFileInfo file_info (filename);
379 last_directory_ = file_info.absoluteDir ();
380
381 pcl::PCLPointCloud2::ConstPtr cloud = cloud_to_save->data (ItemDataRole::CLOUD_BLOB).value <pcl::PCLPointCloud2::ConstPtr> ();
382 Eigen::Vector4f origin = cloud_to_save->data (ItemDataRole::ORIGIN).value <Eigen::Vector4f> ();
383 Eigen::Quaternionf orientation = cloud_to_save->data (ItemDataRole::ORIENTATION).value <Eigen::Quaternionf> ();
384 pcl::io::savePCDFile (filename.toStdString (), *cloud, origin, orientation );
385
386 }
387
388 void
enqueueToolAction(AbstractTool * tool)389 pcl::cloud_composer::ProjectModel::enqueueToolAction (AbstractTool* tool)
390 {
391 qDebug () << "Enqueuing tool action "<<tool->getToolName ()<<" in project model "<<this->getName ();
392 //Get the currently selected item(s), put them in a list, and create the command
393 ConstItemList input_data;
394 QModelIndexList selected_indexes = selection_model_->selectedIndexes ();
395 if (selected_indexes.empty ())
396 {
397 QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "No Items Selected", "Cannot use tool, no item is selected in the browser or cloud view");
398 return;
399 }
400 foreach (QModelIndex index, selected_indexes)
401 {
402 QStandardItem* item = this->itemFromIndex (index);
403 if ( dynamic_cast <CloudComposerItem*> (item))
404 input_data.append (dynamic_cast <CloudComposerItem*> (item));
405 }
406 qDebug () << "Input for tool is "<<input_data.size () << " element(s)";
407
408 //Move the tool object to the work queue thread
409 tool->moveToThread (work_thread_);
410 //Emit signal which tells work queue to enqueue this new action
411 emit enqueueNewAction (tool, input_data);
412 }
413
414
415 void
commandCompleted(CloudCommand * command)416 pcl::cloud_composer::ProjectModel::commandCompleted (CloudCommand* command)
417 {
418 //We set the project model here - this wasn't done earlier so model is never exposed to plugins
419 command->setProjectModel (this);
420 qDebug () << "Applying command changes to model and pushing onto undo stack";
421 //command->redo ();
422 undo_stack_->push (command);
423
424 }
425
426 void
insertNewCloudComposerItem(CloudComposerItem * new_item,QStandardItem * parent_item)427 pcl::cloud_composer::ProjectModel::insertNewCloudComposerItem (CloudComposerItem* new_item, QStandardItem* parent_item)
428 {
429 parent_item->appendRow (new_item);
430 }
431
432 ///////////////////////////////////////////////////////////////
433 //Slots for commands arriving from GUI
434 ///////////////////////////////////////////////////////////////
435 void
clearSelection()436 pcl::cloud_composer::ProjectModel::clearSelection ()
437 {
438 getSelectionModel ()->clearSelection ();
439
440 //Clear the point selector as well if it has an active selection
441 if (selection_event_)
442 selection_event_.reset ();
443
444 foreach (CloudItem* selected_item, selected_item_index_map_.keys())
445 {
446 qDebug () << "Setting item color back to black";
447 selected_item->setForeground (QBrush (Qt::black));;
448 }
449
450 selected_item_index_map_.clear ();
451 }
452
453 void
deleteSelectedItems()454 pcl::cloud_composer::ProjectModel::deleteSelectedItems ()
455 {
456
457 QModelIndexList selected_indexes = selection_model_->selectedIndexes ();
458 if (selected_indexes.empty ())
459 {
460 QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "No Items Selected", "Cannot execute delete command, no item is selected in the browser or cloud view");
461 return;
462 }
463
464 ConstItemList input_data;
465 foreach (QModelIndex index, selected_indexes)
466 {
467 QStandardItem* item = this->itemFromIndex (index);
468 //qDebug () << item->text () << " selected!";
469 if ( dynamic_cast <CloudComposerItem*> (item))
470 input_data.append (dynamic_cast <CloudComposerItem*> (item));
471 }
472 // qDebug () << "Input for command is "<<input_data.size () << " element(s)";
473 DeleteItemCommand* delete_command = new DeleteItemCommand (ConstItemList ());
474 delete_command->setInputData (input_data);
475 if (delete_command->runCommand (nullptr))
476 commandCompleted(delete_command);
477 else
478 qCritical () << "Execution of delete command failed!";
479 }
480
481 void
setAxisVisibility(bool visible)482 pcl::cloud_composer::ProjectModel::setAxisVisibility (bool visible)
483 {
484 //qDebug () << "Setting axis visibility to "<<visible;
485 axis_visible_ = visible;
486 cloud_view_->setAxisVisibility (axis_visible_);
487 }
488
489 void
mouseStyleChanged(QAction * new_style_action)490 pcl::cloud_composer::ProjectModel::mouseStyleChanged (QAction* new_style_action)
491 {
492 interactor_styles::INTERACTOR_STYLES selected_style = new_style_action->data ().value<interactor_styles::INTERACTOR_STYLES> ();
493 qDebug () << "Selected style ="<<selected_style;
494 setSelectedStyle (selected_style);
495
496 // Now set the correct interactor
497 if (cloud_view_)
498 cloud_view_->setInteractorStyle (selected_style);
499 else
500 qWarning () << "No Cloud View active, can't change interactor style!";
501
502
503 }
504
505 void
createNewCloudFromSelection()506 pcl::cloud_composer::ProjectModel::createNewCloudFromSelection ()
507 {
508 // We need only clouds to be selected
509 if (!onlyCloudItemsSelected ())
510 {
511 qCritical () << "Only Clouds Selected = False -- Cannot create a new cloud from non-cloud selection";
512 return;
513 }
514 //Add selected items into input data list
515 QModelIndexList selected_indexes = selection_model_->selectedIndexes ();
516 ConstItemList input_data;
517 foreach (QModelIndex index, selected_indexes)
518 {
519 QStandardItem* item = this->itemFromIndex (index);
520 //qDebug () << item->text () << " selected!";
521 if ( dynamic_cast <CloudComposerItem*> (item))
522 input_data.append (dynamic_cast <CloudComposerItem*> (item));
523 }
524
525 QMap <const CloudItem*, pcl::PointIndices::ConstPtr> selected_const_map;
526 foreach ( CloudItem* item, selected_item_index_map_.keys ())
527 selected_const_map.insert (item, selected_item_index_map_.value (item));
528 MergeSelection* merge_tool = new MergeSelection (selected_const_map);
529
530 //We don't call the enqueueToolAction function since that would abort if we only have a green selection
531 //Move the tool object to the work queue thread
532 merge_tool->moveToThread (work_thread_);
533 //Emit signal which tells work queue to enqueue this new action
534 emit enqueueNewAction (merge_tool, input_data);
535
536
537 }
538
539 void
selectAllItems(QStandardItem * item)540 pcl::cloud_composer::ProjectModel::selectAllItems (QStandardItem* item)
541 {
542
543 if (!item)
544 item = this->invisibleRootItem ();
545 else
546 selection_model_->select (item->index (), QItemSelectionModel::Select);
547 //qDebug () << "Select all!"<< item->rowCount();
548 for (int i = 0; i < item->rowCount (); ++i)
549 {
550 if (item->child (i))
551 selectAllItems(item->child (i));
552 }
553
554 }
555
556 /////////////////////////////////////////////////////////
557 //Slots for Model State
558 ////////////////////////////////////////////////////////
559 void
emitAllStateSignals()560 pcl::cloud_composer::ProjectModel::emitAllStateSignals ()
561 {
562 emit axisVisible (axis_visible_);
563 emit deleteAvailable (selection_model_->hasSelection ());
564 emit newCloudFromSelectionAvailable (onlyCloudItemsSelected ());
565
566 //Find out which style is active, emit the signal
567 foreach (interactor_styles::INTERACTOR_STYLES style, selected_style_map_.keys())
568 {
569 if (selected_style_map_.value (style))
570 {
571 emit mouseStyleState (style);
572 break;
573 }
574 }
575
576
577 }
578
579 void
itemSelectionChanged(const QItemSelection &,const QItemSelection &)580 pcl::cloud_composer::ProjectModel::itemSelectionChanged ( const QItemSelection &, const QItemSelection &)
581 {
582 //qDebug () << "Item selection changed!";
583 //Set all point selected cloud items back to green text, since if they are selected they get changed to white
584 foreach (CloudItem* selected_item, selected_item_index_map_.keys())
585 {
586 selected_item->setForeground (QBrush (Qt::green));;
587 }
588 }
589
590
591 //////////////////////////////////////////////////////
592 // Private Support Functions
593 //////////////////////////////////////////////////////
594
595
596 bool
onlyCloudItemsSelected()597 pcl::cloud_composer::ProjectModel::onlyCloudItemsSelected ()
598 {
599 QModelIndexList selected_indexes = selection_model_->selectedIndexes();
600 foreach (QModelIndex model_index, selected_indexes)
601 {
602 if (this->itemFromIndex (model_index)->type () != CloudComposerItem::CLOUD_ITEM )
603 {
604 return false;
605 }
606 }
607 return true;
608 }
609
610 void
setSelectedStyle(interactor_styles::INTERACTOR_STYLES style)611 pcl::cloud_composer::ProjectModel::setSelectedStyle (interactor_styles::INTERACTOR_STYLES style)
612 {
613 QMap<interactor_styles::INTERACTOR_STYLES, bool>::iterator itr = selected_style_map_.begin();
614 while (itr != selected_style_map_.end ())
615 {
616 itr.value() = false;
617 ++itr;
618 }
619 selected_style_map_[style] = true;
620
621 }
622
623
624