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 <algorithm>
25 #include <functional>
26 
27 #include <QAction>
28 #include <QHeaderView>
29 #include <QMenu>
30 #include <qabstractitemview.h>
31 #include <qaction.h>
32 #include <qcolor.h>
33 #include <qfont.h>
34 #include <qglobal.h>
35 #include <qhashfunctions.h>
36 #include <qheaderview.h>
37 #include <qlist.h>
38 #include <qmenu.h>
39 #include <qnamespace.h>
40 #include <qobjectdefs.h>
41 #include <qpair.h>
42 #include <qpoint.h>
43 
45 
48 
49 #include "utils.h"
50 
52 {
53 
55  {
56  QStringList columns{"Name", "X [mm]", "Y [mm]", "Yaw [\u00b0]"};
57  setColumnCount(columns.size());
58  setHorizontalHeaderLabels(columns);
59  horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
60  horizontalHeader()->setVisible(true);
61 
62  setEditTriggers(QAbstractItemView::NoEditTriggers);
63  setSortingEnabled(true);
64 
65  setAlternatingRowColors(true);
66 
67  // QString styleSheet = this->styleSheet();
68  // styleSheet = styleSheet + "\n" + "selection-background-color: #FF8000;";
69  // setStyleSheet(styleSheet);
70 
71  setContextMenuPolicy(Qt::CustomContextMenu);
72  connect(this, &This::customContextMenuRequested, this, &This::makeContextMenu);
73  }
74 
75  QTableWidgetItem*
77  {
78  // We need to disable sorting to prevent the new row from being moved away.
79  setSortingEnabled(false);
80 
81  QTableWidgetItem* result = nullptr;
82  {
83  int row = rowCount();
84  setRowCount(row + 1);
85 
86  for (int col = 0; col < 4; ++col)
87  {
88  // Just fill with vanilla items, they will get values in the update.
89  QTableWidgetItem* item = new QTableWidgetItem{};
90  setItem(row, col, item);
91 
92  if (col >= 1)
93  {
94  item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
95  }
96  if (col == 0)
97  {
98  result = item;
99  }
100  }
101  }
102 
103  // Enable sorting - `row` could now be moved away.
104  setSortingEnabled(true);
105 
106  ARMARX_CHECK_NOT_NULL(result);
107  return result;
108  }
109 
110  void
111  VertexTableWidget::updateVertex(GuiGraph::Vertex vertex)
112  {
113  const core::Pose_d pose = core::Pose(vertex.attrib().getPose().toEigen()).cast<qreal>();
114  char format = 'f';
115  const int precision = 2;
116 
117  // Changing the values may trigger a re-sort.
118  setSortingEnabled(false);
119 
120 
121  int row = this->row(vertex.attrib().tableWidgetItem);
122 
123  QString displayName = QString::fromStdString(vertex.attrib().getName());
124  if (vertex.attrib().changed)
125  {
126  displayName += "*";
127  }
128  item(row, 0)->setText(displayName);
129  item(row, 0)->setData(Qt::UserRole, QString::fromStdString(vertex.attrib().getName()));
130  item(row, 1)->setText(QString::number(pose(0, 3), format, precision));
131  item(row, 2)->setText(QString::number(pose(1, 3), format, precision));
132  item(row, 3)->setText(QString::number(getYawAngleDegree(pose), format, precision));
133 
134  setSortingEnabled(true);
135 
136  QColor bgColor = vertex.attrib().highlighted ? bgColorSelected : bgColorDefault;
137  QFont font;
138  font.setBold(vertex.attrib().highlighted);
139  for (int col = 0; col < 4; ++col)
140  {
141  auto* item = this->item(row, col);
142  ARMARX_CHECK_NOT_NULL(item);
143 
144  item->setData(Qt::BackgroundRole, bgColor);
145  item->setFont(font);
146  }
147  }
148 
149  void
150  VertexTableWidget::removeVertex(GuiGraph::Vertex& vertex)
151  {
152  if (currentItem() == vertex.attrib().tableWidgetItem)
153  {
154  setCurrentItem(nullptr);
155  }
156 
157  removeRow(row(vertex.attrib().tableWidgetItem));
158  vertex.attrib().tableWidgetItem = nullptr;
159  }
160 
161  QList<QTableWidgetItem*>
163  {
164  return utils::getSelectedItemsOfColumn(this, 0);
165  }
166 
167  QString
168  VertexTableWidget::_nameOf(QTableWidgetItem* item)
169  {
170  return item->data(Qt::UserRole).toString();
171  }
172 
173  void
175  {
176  QList<QTableWidgetItem*> items = selectedVertexItems();
177 
178  QMenu menu;
179  if (items.size() == 0)
180  {
181  QAction* action = menu.addSection("No locations selected");
182  action->setEnabled(false);
183  }
184 
185  // Partners selected
186  if (items.size() == 2)
187  {
188  menu.addSection("Selected pair of locations");
189 
190  // Generate actions for connecting these two nodes.
191  std::sort(items.begin(),
192  items.end(),
193  [](QTableWidgetItem* first, QTableWidgetItem* second)
194  { return _nameOf(first) < _nameOf(second); });
195  QTableWidgetItem* first = items[0];
196  QTableWidgetItem* second = items[1];
197 
198  connect(menu.addAction("Add edge '" + _nameOf(first) + "' " + utils::arrowRight + " '" +
199  _nameOf(second) + "'"),
200  &QAction::triggered,
201  [this, first, second]() { emit newEdgesRequested({{first, second}}); });
202  connect(menu.addAction("Add edge '" + _nameOf(first) + "' " + utils::arrowLeft + " '" +
203  _nameOf(second) + "'"),
204  &QAction::triggered,
205  [this, first, second]() { emit newEdgesRequested({{second, first}}); });
206  connect(menu.addAction("Add edges '" + _nameOf(first) + "' " + utils::arrowBoth + " '" +
207  _nameOf(second) + "'"),
208  &QAction::triggered,
209  [this, first, second]()
210  { emit newEdgesRequested({{first, second}, {second, first}}); });
211  }
212 
213  // Partners via context menu
214  if (items.size() > 0)
215  {
216  QString edges = items.size() == 1 ? "edge" : "edges";
217  QString desc = items.size() == 1 ? "'" + _nameOf(items[0]) + "'"
218  : QString::number(items.size()) + " locations";
219 
220  if (items.size() == 1)
221  {
222  // QAction* deleteAction = menu.addAction("Delete location '" + );
223  menu.addSection("Selected single location " + desc);
224  }
225  else
226  {
227  menu.addSection("Selected bulk of " + desc);
228  }
229 
230  using Item = QTableWidgetItem;
231  using ListOfEdges = QList<QPair<Item*, Item*>>;
232  using AppendFunc =
233  std::function<void(ListOfEdges & edges, Item * selected, Item * action)>;
234 
235  auto addBulkAddEdgeActions = [this, &items](QMenu* submenu, AppendFunc appendFunc)
236  {
237  if (items.size() == rowCount())
238  {
239  QAction* a = submenu->addAction("No other locations");
240  a->setDisabled(true);
241  }
242  for (int row = 0; row < rowCount(); ++row)
243  {
244  QTableWidgetItem* action = this->item(row, 0);
245  if (items.count(action) == 0) // Do no generate self-edges
246  {
247  QAction* a = submenu->addAction(_nameOf(action));
248  connect(a,
249  &QAction::triggered,
250  this,
251  [this, items, action, appendFunc]()
252  {
253  QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> edges;
254  for (auto* selected : items)
255  {
256  appendFunc(edges, selected, action);
257  }
258  emit newEdgesRequested(edges);
259  });
260  }
261  }
262  };
263 
264  addBulkAddEdgeActions(
265  menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowRight + " ..."),
266  [](ListOfEdges& edges, Item* selected, Item* action)
267  { edges.append(QPair{selected, action}); });
268  addBulkAddEdgeActions(
269  menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowLeft + " ..."),
270  [](ListOfEdges& edges, Item* selected, Item* action)
271  { edges.append(QPair{action, selected}); });
272  addBulkAddEdgeActions(
273  menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowBoth + " ..."),
274  [](ListOfEdges& edges, Item* selected, Item* action)
275  {
276  edges.append(QPair{selected, action});
277  edges.append(QPair{action, selected});
278  });
279 
280 
281  auto connectBulkRemoveEdgeAction =
282  [this, &items](QAction* action, utils::EdgeDirection edgeDirection)
283  {
284  connect(action,
285  &QAction::triggered,
286  this,
287  [this, items, edgeDirection]()
288  { emit edgeRemovalRequested(items, edgeDirection); });
289  };
290 
291  connectBulkRemoveEdgeAction(
292  menu.addAction("Remove all edges " + utils::arrowRight + " " + desc),
293  utils::EdgeDirection::To);
294  connectBulkRemoveEdgeAction(
295  menu.addAction("Remove all edges " + utils::arrowLeft + " " + desc),
296  utils::EdgeDirection::From);
297  connectBulkRemoveEdgeAction(
298  menu.addAction("Remove all edges " + utils::arrowBoth + " " + desc),
299  utils::EdgeDirection::Bidirectional);
300  }
301 
302 
303  menu.addSection("Manage Locations");
304  connect(menu.addAction("Create new location ..."),
305  &QAction::triggered,
306  this,
307  [this]() { emit newVertexRequested(); });
308 
309  menu.exec(mapToGlobal(pos));
310  }
311 
312 } // 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:111
VertexTableWidget.h
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::addVertex
QTableWidgetItem * addVertex()
Definition: VertexTableWidget.cpp:76
armarx::navigation::qt_plugins::location_graph_editor::utils::arrowRight
const QString arrowRight
->
Definition: utils.cpp:36
armarx::navigation::core::Pose
Eigen::Isometry3f Pose
Definition: basic_types.h:31
basic_types.h
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:38
armarx::navigation::qt_plugins::location_graph_editor::utils::arrowLeft
const QString arrowLeft
<-
Definition: utils.cpp:34
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:100
armarx::navigation::qt_plugins::location_graph_editor::utils::EdgeDirection
EdgeDirection
Definition: utils.h:43
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:85
armarx::navigation::qt_plugins::location_graph_editor
Definition: GuiGraph.cpp:34
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::selectedVertexItems
QList< QTableWidgetItem * > selectedVertexItems()
Definition: VertexTableWidget.cpp:162
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::removeVertex
void removeVertex(GuiGraph::Vertex &vertex)
Definition: VertexTableWidget.cpp:150
ExpressionException.h
utils.h
GuiGraph.h
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::bgColorDefault
QColor bgColorDefault
Definition: VertexTableWidget.h:84
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::VertexTableWidget
VertexTableWidget()
Definition: VertexTableWidget.cpp:54
armarx::navigation::qt_plugins::location_graph_editor::VertexTableWidget::makeContextMenu
void makeContextMenu(QPoint pos)
Definition: VertexTableWidget.cpp:174
armarx::navigation::qt_plugins::location_graph_editor::utils::getSelectedItemsOfColumn
QList< QTableWidgetItem * > getSelectedItemsOfColumn(QTableWidget *widget, int column)
Definition: utils.cpp:41