OperatorViewWidgetController.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * \package ArmarXGui::gui-plugins::OperatorViewWidgetController
19 * \author Leonie Leven
20 * \date 2024
21 * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
26
27#include <string>
28
29#include <QDebug>
30#include <QHBoxLayout>
31#include <QMenu>
32#include <QPalette>
33#include <QStringList>
34#include <QTimer>
35
40
41namespace armarx
42{
44 DEFAULT_SETTINGS_PLUGIN_NAME("OperatorViewWidgetGuiPlugin"),
45 DEFAULT_SETTINGS_CUSTOM_TEXT("custom text")
46 {
47 widget.setupUi(getWidget());
48 this->updateTimer = new QTimer(this);
49 this->updateTimer->start(50);
50
51 ARMARX_INFO << " setup of ui finished" << flush;
52 }
53
54 void
56 {
58 demoStateProxyName =
59 settings->value("demoStateProxyName", QString::fromStdString(demoStateProxyName))
60 .toString()
61 .toStdString();
62 }
63
64 void
66 {
68 settings->setValue("demoStateProxyName", QString::fromStdString(demoStateProxyName));
69 }
70
71 void
77
78 void
80 {
81
83 getProxy(demoStateManagerPrx, demoStateProxyName);
84
85 connect(widget.sendOnly, SIGNAL(clicked()), this, SLOT(sendOnlyButtonPressed()));
86 connect(widget.insert, SIGNAL(clicked()), this, SLOT(insertButtonPressed()));
87 connect(
88 widget.exportButtons, SIGNAL(clicked()), this, SLOT(exportButtonsToPropertyFormat()));
89
90 connect(this,
92 this,
94 stopCheckStateTask.store(false);
95 checkStateTask = std::thread([&] { checkState(); });
96
97 connect(this,
99 this,
101
102
103 try
104 {
105 emit defaultValuesLoaded();
106 QObject::disconnect(this,
108 this,
110 }
111 catch (Ice::Exception const&)
112 {
113 ARMARX_WARNING << "Could not fetch states." << deactivateSpam(10);
114 }
115 }
116
117 void
119 {
121
122 while (not stopCheckStateTask.load())
123 {
124 try
125 {
126 // Might throw due to Ice.
127 auto stateAndSeverity = this->demoStateManagerPrx->getStateAndSeverity();
128 // /critical section
129
130 emit stateAndSeverityUpdated(QString::fromStdString(stateAndSeverity.state),
131 static_cast<int>(stateAndSeverity.severity));
132 }
133 catch (Ice::Exception const&)
134 {
135 ARMARX_WARNING << "Could not fetch current state and severity."
136 << deactivateSpam(10);
137 }
138
139 metronome.waitForNextTick();
140 }
141 }
142
143 void
145 {
146 std::string color = mapColor(static_cast<armarx::severity::SeverityEnum>(severity));
147 std::string coloredSeverity =
148 "<span style='color:" + color + ";'>" + state.toStdString() + "</span>";
149 std::string finalText = "Current State: " + coloredSeverity;
150
151 widget.currentState->setText(QString::fromStdString(finalText));
152 }
153
154 void
156 {
157
158 widget.groupSend->setLayout(new QVBoxLayout(getWidget()));
159
160 this->layoutNormal = new FlowLayout();
161 this->layoutWarning = new FlowLayout();
162 this->layoutError = new FlowLayout();
163 this->layoutFatal = new FlowLayout();
164
165 createButtons();
166
167 if (QVBoxLayout* hLayout = qobject_cast<QVBoxLayout*>(widget.groupSend->layout()))
168 {
169 hLayout->addLayout(this->layoutNormal);
170 hLayout->addLayout(this->layoutWarning);
171 hLayout->addLayout(this->layoutError);
172 hLayout->addLayout(this->layoutFatal);
173 }
174 }
175
176 void
178 {
179 std::string newStateSend = text.toStdString() == ""
180 ? this->widget.newState->text().toStdString()
181 : text.toStdString();
182 int newSeveritySend = severity == -1 ? this->widget.severity->currentIndex() : severity;
183
184 try
185 {
186 this->demoStateManagerPrx->insertState(
187 newStateSend, static_cast<armarx::severity::SeverityEnum>(newSeveritySend));
188 }
189 catch (Ice::Exception const&)
190 {
191 ARMARX_WARNING << "Could not send current state and severity." << deactivateSpam(10);
192 }
193
194 QString newState = text.toStdString() == "" ? this->widget.newState->text() : text;
195 int newSeverity = severity == -1 ? this->widget.severity->currentIndex() : severity;
196
197 armarx::severity::SeverityEnum buttonSeverity =
198 static_cast<armarx::severity::SeverityEnum>(newSeverity);
199
200 QPushButton* button = new QPushButton(newState, getWidget());
201 button->setToolTip("Leftclick to send to presenter view; Rightclick to delete.");
202
203
204 QString color = QString::fromStdString(mapColor(buttonSeverity));
205 if (!color.isEmpty())
206 {
207 QPalette palette = button->palette();
208 palette.setColor(QPalette::Button, QColor(color));
209 button->setPalette(palette);
210 button->setStyleSheet("background-color: " + color + ";");
211 }
212 switch (buttonSeverity)
213 {
214 case armarx::severity::SeverityEnum::normal:
215 this->layoutNormal->addWidget(button);
216 break;
217 case armarx::severity::SeverityEnum::warning:
218 this->layoutWarning->addWidget(button);
219 break;
220 case armarx::severity::SeverityEnum::error:
221 this->layoutError->addWidget(button);
222 break;
223 case armarx::severity::SeverityEnum::fatal:
224 this->layoutFatal->addWidget(button);
225 break;
226 }
227
228 connect(button,
229 &QPushButton::clicked,
230 this,
231 [this, newState, buttonSeverity]()
232 { this->defaultButtonPressed(newState.toStdString(), buttonSeverity); });
233 button->setContextMenuPolicy(Qt::CustomContextMenu);
234
235 connect(button,
236 &QPushButton::customContextMenuRequested,
237 this,
238 [this, button, newStateSend, newSeveritySend](const QPoint& pos)
239 {
240 QMenu menu;
241 QAction* deleteAction = menu.addAction("Delete");
242
243 QAction* selectedAction = menu.exec(button->mapToGlobal(pos));
244 if (selectedAction == deleteAction)
245 {
246 this->deleteButton(
247 newStateSend,
248 static_cast<armarx::severity::SeverityEnum>(newSeveritySend));
249 }
250 });
251 }
252
253 void
255 {
256 std::string newState = text.toStdString() == ""
257 ? this->widget.newState->text().toStdString()
258 : text.toStdString();
259 int newSeverity = severity == -1 ? this->widget.severity->currentIndex() : severity;
260
261 try
262 {
263 this->demoStateManagerPrx->setState(
264 newState, static_cast<armarx::severity::SeverityEnum>(newSeverity));
265 }
266 catch (Ice::Exception const&)
267 {
268 ARMARX_WARNING << "Could not send current state and severity." << deactivateSpam(10);
269 }
270 }
271
272 void
273 OperatorViewWidgetController::createButtons()
274 {
275 std::vector<StateAndSeverity> allStates;
276 try
277 {
278 allStates = demoStateManagerPrx->getCurrentStates();
279 }
280 catch (Ice::Exception const&)
281 {
282 ARMARX_WARNING << "Could not fetch states." << deactivateSpam(10);
283 }
284
285 for (const StateAndSeverity& state : allStates)
286 {
287 QPushButton* button = new QPushButton(QString::fromStdString(state.state), getWidget());
288
289 QString color = QString::fromStdString(mapColor(state.severity));
290 if (!color.isEmpty())
291 {
292 QPalette palette = button->palette();
293 palette.setColor(QPalette::Button, QColor(color));
294 button->setPalette(palette);
295 button->setStyleSheet("background-color: " + color + ";");
296 }
297 switch (state.severity)
298 {
299 case armarx::severity::SeverityEnum::normal:
300 this->layoutNormal->addWidget(button);
301 break;
302 case armarx::severity::SeverityEnum::warning:
303 this->layoutWarning->addWidget(button);
304 break;
305 case armarx::severity::SeverityEnum::error:
306 this->layoutError->addWidget(button);
307 break;
308 case armarx::severity::SeverityEnum::fatal:
309 this->layoutFatal->addWidget(button);
310 break;
311 }
312
313 button->setToolTip("Leftclick to send to presenter view; Rightclick to delete.");
314
315 connect(button,
316 &QPushButton::clicked,
317 this,
318 [this, state]() { this->defaultButtonPressed(state.state, state.severity); });
319 button->setContextMenuPolicy(Qt::CustomContextMenu);
320
321 connect(button,
322 &QPushButton::customContextMenuRequested,
323 this,
324 [this, button, state](const QPoint& pos)
325 {
326 QMenu menu;
327 QAction* deleteAction = menu.addAction("Delete");
328
329 QAction* selectedAction = menu.exec(button->mapToGlobal(pos));
330 if (selectedAction == deleteAction)
331 {
332 this->deleteButton(state.state, state.severity);
333 }
334 });
335 }
336 }
337
338 void
339 OperatorViewWidgetController::deleteButton(std::string state,
340 armarx::severity::SeverityEnum severity)
341 {
342 ARMARX_IMPORTANT << "Delete Button";
343 try
344 {
345 this->demoStateManagerPrx->deleteState(state, severity);
346 }
347 catch (Ice::Exception const&)
348 {
349 ARMARX_WARNING << "Could not delete Button." << deactivateSpam(10);
350 return;
351 }
352
353 this->cleanUI();
354 this->onConnectComponent();
355 }
356
357 void
359 armarx::severity::SeverityEnum severity)
360 {
361 try
362 {
363 this->demoStateManagerPrx->setState(state, severity);
364 }
365 catch (Ice::Exception const&)
366 {
367 ARMARX_WARNING << "Could not send current state and severity." << deactivateSpam(10);
368 }
369 }
370
371 void
373 {
374 std::string exportProperty = "";
375 QList<QPushButton*> normalButtons = getButtonsFromLayout(this->layoutNormal);
376 QList<QPushButton*> warningButtons = getButtonsFromLayout(this->layoutWarning);
377 QList<QPushButton*> errorButtons = getButtonsFromLayout(this->layoutError);
378 QList<QPushButton*> fatalButtons = getButtonsFromLayout(this->layoutFatal);
379 std::vector<QList<QPushButton*>> buttons;
380 buttons.push_back(normalButtons);
381 buttons.push_back(warningButtons);
382 buttons.push_back(errorButtons);
383 buttons.push_back(fatalButtons);
384
385 for (unsigned long i = 0; i < buttons.size(); i++)
386 {
387 auto buttonList = buttons[i];
388
389 for (auto it = buttonList.begin(); it != buttonList.end(); ++it)
390 {
391 std::string buttonText = (*it)->text().toStdString();
392 exportProperty.append(buttonText + ":" + std::to_string(i) + ";");
393 }
394 }
395 QClipboard* clipboard = QGuiApplication::clipboard();
396 clipboard->setText(QString::fromStdString(exportProperty));
397 ARMARX_INFO << "Button configuration: " << exportProperty;
398 }
399
400 QList<QPushButton*>
401 OperatorViewWidgetController::getButtonsFromLayout(QLayout* layout)
402 {
403 QList<QPushButton*> buttons;
404
405 if (!layout)
406 return buttons;
407
408 for (int i = 0; i < layout->count(); ++i)
409 {
410 if (auto* button = qobject_cast<QPushButton*>(layout->itemAt(i)->widget()))
411 {
412 buttons.append(button);
413 }
414 }
415
416 return buttons;
417 }
418
419 std::string
420 OperatorViewWidgetController::mapColor(severity::SeverityEnum severity)
421 {
422 std::string color;
423 switch (severity)
424 {
425 case severity::SeverityEnum::normal:
426 color = "green";
427 break;
428 case severity::SeverityEnum::warning:
429 color = "orange";
430 break;
431 case severity::SeverityEnum::error:
432 color = "red";
433 break;
434 case severity::SeverityEnum::fatal:
435 color = "magenta";
436 break;
437 default:
438 color = "black";
439 }
440 return color;
441 }
442
443 void
445 {
446
447 QObject::disconnect(
448 widget.sendOnly, SIGNAL(clicked()), this, SLOT(sendOnlyButtonPressed()));
449 QObject::disconnect(widget.insert, SIGNAL(clicked()), this, SLOT(insertButtonPressed()));
450 QObject::disconnect(
451 widget.exportButtons, SIGNAL(clicked()), this, SLOT(exportButtonsToPropertyFormat()));
452
453 clearLayout(this->layoutNormal);
454 clearLayout(this->layoutWarning);
455 clearLayout(this->layoutError);
456 clearLayout(this->layoutFatal);
457
458 delete this->layoutNormal;
459 delete this->layoutWarning;
460 delete this->layoutError;
461 delete this->layoutFatal;
462
463 this->layoutNormal = nullptr;
464 this->layoutWarning = nullptr;
465 this->layoutError = nullptr;
466 this->layoutFatal = nullptr;
467
468 stopCheckStateTask.store(true);
469 checkStateTask.join();
470
471
472 ARMARX_INFO << "UI cleaned successfully";
473 }
474
475 void
476 OperatorViewWidgetController::clearLayout(QLayout* layout)
477 {
478 if (!layout)
479 return;
480
481 while (QLayoutItem* item = layout->takeAt(0))
482 {
483 if (QWidget* widget = item->widget())
484 {
485 widget->deleteLater();
486 }
487 else if (QLayout* subLayout = item->layout())
488 {
489 clearLayout(subLayout);
490 }
491 delete item;
492 }
493 }
494
495 void
497 {
498
499 cleanUI();
500 ARMARX_INFO << "Disconnect";
501 }
502
503 void
508
509 QPointer<QDialog>
511 {
513
514 if (not m_config_dialog)
515 {
516 m_config_dialog = new armarx::SimpleConfigDialog{parent};
517 m_config_dialog->addProxyFinder<DemoStateManagerInterfacePrx>(
518 {"DemoStateManager", "Demo State Manager", "*"});
519 }
520 return qobject_cast<QDialog*>(m_config_dialog);
521 }
522
523 void
525 {
527
528 if (m_config_dialog)
529 {
530 demoStateProxyName = m_config_dialog->getProxyName("DemoStateManager");
531 }
532 }
533
534
535} // namespace armarx
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
void clearLayout(QLayout *layout, bool deleteWidgets=true)
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
void sendOnlyButtonPressed(QString text="", int severity=-1)
virtual void saveSettings(QSettings *settings)
Implement to save the settings as part of the GUI configuration.
virtual void loadSettings(QSettings *settings)
Implement to load the settings that are part of the GUI configuration.
virtual void onInitComponent()
Pure virtual hook for the subclass.
void stateAndSeverityUpdated(QString state, int severity)
void defaultButtonPressed(std::string state, armarx::severity::SeverityEnum severity)
void insertButtonPressed(QString text="", int severity=-1)
virtual void onDisconnectComponent()
Hook for subclass.
void configured() override
This function must be implemented by the user, if he supplies a config dialog.
virtual void onConnectComponent()
Pure virtual hook for the subclass.
QPointer< QDialog > getConfigDialog(QWidget *parent) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
void updateStateAndSeverity(QString state, int severity)
A config-dialog containing one (or multiple) proxy finders.
void addProxyFinder(const std::vector< EntryData > &entryData)
static Frequency Hertz(std::int64_t hertz)
Definition Frequency.cpp:20
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Definition Metronome.h:57
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
This file offers overloads of toIce() and fromIce() functions for STL container types.
const LogSender::manipulator flush
Definition LogSender.h:251
#define ARMARX_TRACE
Definition trace.h:77