VertexTableWidget.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu )
17  * @date 2021
18  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
19  * GNU General Public License
20  */
21 
22 #include "VertexTableWidget.h"
23 
24 #include <QAction>
25 #include <QHeaderView>
26 #include <QMenu>
27 
30 
31 #include "utils.h"
32 
34 {
35 
37  {
38  QStringList columns{"Name", "X [mm]", "Y [mm]", "Yaw [\u00b0]"};
39  setColumnCount(columns.size());
40  setHorizontalHeaderLabels(columns);
41  horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
42  horizontalHeader()->setVisible(true);
43 
44  setEditTriggers(QAbstractItemView::NoEditTriggers);
45  setSortingEnabled(true);
46 
47  setAlternatingRowColors(true);
48 
49  QString styleSheet = this->styleSheet();
50  styleSheet = styleSheet + "\n" + "selection-background-color: #FF8000;";
51  setStyleSheet(styleSheet);
52 
53  setContextMenuPolicy(Qt::CustomContextMenu);
54  connect(this, &This::customContextMenuRequested, this, &This::makeContextMenu);
55  }
56 
57  QTableWidgetItem*
59  {
60  // We need to disable sorting to prevent the new row from being moved away.
61  setSortingEnabled(false);
62 
63  QTableWidgetItem* result = nullptr;
64  {
65  int row = rowCount();
66  setRowCount(row + 1);
67 
68  for (int col = 0; col < 4; ++col)
69  {
70  // Just fill with vanilla items, they will get values in the update.
71  QTableWidgetItem* item = new QTableWidgetItem{};
72  setItem(row, col, item);
73 
74  if (col >= 1)
75  {
76  item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
77  }
78  if (col == 0)
79  {
80  result = item;
81  }
82  }
83  }
84 
85  // Enable sorting - `row` could now be moved away.
86  setSortingEnabled(true);
87 
88  ARMARX_CHECK_NOT_NULL(result);
89  return result;
90  }
91 
92  void
93  VertexTableWidget::updateVertex(GuiGraph::Vertex vertex)
94  {
95  const core::Pose_d pose = core::Pose(vertex.attrib().getPose().toEigen()).cast<qreal>();
96  char format = 'f';
97  const int precision = 2;
98 
99  // Changing the values may trigger a re-sort.
100  setSortingEnabled(false);
101 
102 
103  int row = this->row(vertex.attrib().tableWidgetItem);
104 
105  QString displayName = QString::fromStdString(vertex.attrib().getName());
106  if (vertex.attrib().changed)
107  {
108  displayName += "*";
109  }
110  item(row, 0)->setText(displayName);
111  item(row, 0)->setData(Qt::UserRole, QString::fromStdString(vertex.attrib().getName()));
112  item(row, 1)->setText(QString::number(pose(0, 3), format, precision));
113  item(row, 2)->setText(QString::number(pose(1, 3), format, precision));
114  item(row, 3)->setText(QString::number(getYawAngleDegree(pose), format, precision));
115 
116  setSortingEnabled(true);
117 
118  QColor bgColor = vertex.attrib().highlighted ? bgColorSelected : bgColorDefault;
119  QFont font;
120  font.setBold(vertex.attrib().highlighted);
121  for (int col = 0; col < 4; ++col)
122  {
123  auto* item = this->item(row, col);
124  ARMARX_CHECK_NOT_NULL(item);
125 
126  item->setData(Qt::BackgroundRole, bgColor);
127  item->setFont(font);
128  }
129  }
130 
131  void
132  VertexTableWidget::removeVertex(GuiGraph::Vertex& vertex)
133  {
134  if (currentItem() == vertex.attrib().tableWidgetItem)
135  {
136  setCurrentItem(nullptr);
137  }
138 
139  removeRow(row(vertex.attrib().tableWidgetItem));
140  vertex.attrib().tableWidgetItem = nullptr;
141  }
142 
143  QList<QTableWidgetItem*>
145  {
146  return utils::getSelectedItemsOfColumn(this, 0);
147  }
148 
149  QString
150  VertexTableWidget::_nameOf(QTableWidgetItem* item)
151  {
152  return item->data(Qt::UserRole).toString();
153  }
154 
155  void
157  {
158  QList<QTableWidgetItem*> items = selectedVertexItems();
159 
160  QMenu menu;
161  if (items.size() == 0)
162  {
163  QAction* action = menu.addSection("No locations selected");
164  action->setEnabled(false);
165  }
166 
167  // Partners selected
168  if (items.size() == 2)
169  {
170  menu.addSection("Selected pair of locations");
171 
172  // Generate actions for connecting these two nodes.
173  std::sort(items.begin(),
174  items.end(),
175  [](QTableWidgetItem* first, QTableWidgetItem* second)
176  { return _nameOf(first) < _nameOf(second); });
177  QTableWidgetItem* first = items[0];
178  QTableWidgetItem* second = items[1];
179 
180  connect(menu.addAction("Add edge '" + _nameOf(first) + "' " + utils::arrowRight + " '" +
181  _nameOf(second) + "'"),
182  &QAction::triggered,
183  [this, first, second]() {
184  emit newEdgesRequested({{first, second}});
185  });
186  connect(menu.addAction("Add edge '" + _nameOf(first) + "' " + utils::arrowLeft + " '" +
187  _nameOf(second) + "'"),
188  &QAction::triggered,
189  [this, first, second]() {
190  emit newEdgesRequested({{second, first}});
191  });
192  connect(menu.addAction("Add edges '" + _nameOf(first) + "' " + utils::arrowBoth + " '" +
193  _nameOf(second) + "'"),
194  &QAction::triggered,
195  [this, first, second]() {
196  emit newEdgesRequested({{first, second}, {second, first}});
197  });
198  }
199 
200  // Partners via context menu
201  if (items.size() > 0)
202  {
203  QString edges = items.size() == 1 ? "edge" : "edges";
204  QString desc = items.size() == 1 ? "'" + _nameOf(items[0]) + "'"
205  : QString::number(items.size()) + " locations";
206 
207  if (items.size() == 1)
208  {
209  // QAction* deleteAction = menu.addAction("Delete location '" + );
210  menu.addSection("Selected single location " + desc);
211  }
212  else
213  {
214  menu.addSection("Selected bulk of " + desc);
215  }
216 
217  using Item = QTableWidgetItem;
218  using ListOfEdges = QList<QPair<Item*, Item*>>;
219  using AppendFunc =
220  std::function<void(ListOfEdges & edges, Item * selected, Item * action)>;
221 
222  auto addBulkAddEdgeActions = [this, &items](QMenu* submenu, AppendFunc appendFunc)
223  {
224  if (items.size() == rowCount())
225  {
226  QAction* a = submenu->addAction("No other locations");
227  a->setDisabled(true);
228  }
229  for (int row = 0; row < rowCount(); ++row)
230  {
231  QTableWidgetItem* action = this->item(row, 0);
232  if (items.count(action) == 0) // Do no generate self-edges
233  {
234  QAction* a = submenu->addAction(_nameOf(action));
235  connect(a,
236  &QAction::triggered,
237  this,
238  [this, items, action, appendFunc]()
239  {
240  QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> edges;
241  for (auto* selected : items)
242  {
243  appendFunc(edges, selected, action);
244  }
245  emit newEdgesRequested(edges);
246  });
247  }
248  }
249  };
250 
251  addBulkAddEdgeActions(
252  menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowRight + " ..."),
253  [](ListOfEdges& edges, Item* selected, Item* action) {
254  edges.append(QPair{selected, action});
255  });
256  addBulkAddEdgeActions(
257  menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowLeft + " ..."),
258  [](ListOfEdges& edges, Item* selected, Item* action) {
259  edges.append(QPair{action, selected});
260  });
261  addBulkAddEdgeActions(
262  menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowBoth + " ..."),
263  [](ListOfEdges& edges, Item* selected, Item* action)
264  {
265  edges.append(QPair{selected, action});
266  edges.append(QPair{action, selected});
267  });
268 
269 
270  auto connectBulkRemoveEdgeAction =
271  [this, &items](QAction* action, utils::EdgeDirection edgeDirection)
272  {
273  connect(action,
274  &QAction::triggered,
275  this,
276  [this, items, edgeDirection]()
277  { emit edgeRemovalRequested(items, edgeDirection); });
278  };
279 
280  connectBulkRemoveEdgeAction(
281  menu.addAction("Remove all edges " + utils::arrowRight + " " + desc),
282  utils::EdgeDirection::To);
283  connectBulkRemoveEdgeAction(
284  menu.addAction("Remove all edges " + utils::arrowLeft + " " + desc),
285  utils::EdgeDirection::From);
286  connectBulkRemoveEdgeAction(
287  menu.addAction("Remove all edges " + utils::arrowBoth + " " + desc),
288  utils::EdgeDirection::Bidirectional);
289  }
290 
291 
292  menu.addSection("Manage Locations");
293  connect(menu.addAction("Create new location ..."),
294  &QAction::triggered,
295  this,
296  [this]() { emit newVertexRequested(); });
297 
298  menu.exec(mapToGlobal(pos));
299  }
300 
301 } // namespace armarx::navigation::qt_plugins::location_graph_editor
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::updateVertex
void updateVertex(GuiGraph::Vertex vertex)
Definition: VertexTableWidget.cpp:93
VertexTableWidget.h
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::addVertex
QTableWidgetItem * addVertex()
Definition: VertexTableWidget.cpp:58
armarx::navigation::qt_plugins::location_graph_editor::utils::arrowRight
const QString arrowRight
->
Definition: utils.cpp:34
armarx::navigation::core::Pose
Eigen::Isometry3f Pose
Definition: basic_types.h:31
ARMARX_CHECK_NOT_NULL
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
Definition: ExpressionException.h:206
armarx::navigation::qt_plugins::location_graph_editor::utils::arrowBoth
const QString arrowBoth
<->
Definition: utils.cpp:36
armarx::navigation::qt_plugins::location_graph_editor::utils::arrowLeft
const QString arrowLeft
<-
Definition: utils.cpp:32
armarx::navigation::core::Pose_d
Eigen::Isometry3d Pose_d
Definition: basic_types.h:32
armarx::navigation::qt_plugins::location_graph_editor::getYawAngleDegree
float getYawAngleDegree(const core::Pose &pose)
Definition: GuiGraph.cpp:97
armarx::navigation::qt_plugins::location_graph_editor::utils::EdgeDirection
EdgeDirection
Definition: utils.h:42
armarx::ctrlutil::a
double a(double t, double a0, double j)
Definition: CtrlUtil.h:45
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::newEdgesRequested
void newEdgesRequested(QList< QPair< QTableWidgetItem *, QTableWidgetItem * >> edges)
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::bgColorSelected
QColor bgColorSelected
Definition: VertexTableWidget.h:79
armarx::navigation::qt_plugins::location_graph_editor
Definition: GuiGraph.cpp:29
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::selectedVertexItems
QList< QTableWidgetItem * > selectedVertexItems()
Definition: VertexTableWidget.cpp:144
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::removeVertex
void removeVertex(GuiGraph::Vertex &vertex)
Definition: VertexTableWidget.cpp:132
ExpressionException.h
utils.h
Logging.h
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::bgColorDefault
QColor bgColorDefault
Definition: VertexTableWidget.h:78
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::VertexTableWidget
VertexTableWidget()
Definition: VertexTableWidget.cpp:36
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::makeContextMenu
void makeContextMenu(QPoint pos)
Definition: VertexTableWidget.cpp:156
armarx::navigation::qt_plugins::location_graph_editor::utils::getSelectedItemsOfColumn
QList< QTableWidgetItem * > getSelectedItemsOfColumn(QTableWidget *widget, int column)
Definition: utils.cpp:40