40 #include <QMessageBox>
41 #include <QStringList>
42 #include <QVBoxLayout>
44 #include <google/protobuf/reflection.h>
54 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
56 _layout =
new QVBoxLayout(
this);
57 _layout->setContentsMargins(5, 5, 5, 5);
58 _layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
61 ParameterWidget::~ParameterWidget() {}
63 QSize ParameterWidget::sizeHint()
const {
return QSize(300, 50); }
65 void ParameterWidget::addOneOfField(
const MessageParser::FieldInformation&
info)
67 if (!
info.oneof_descriptor)
return;
69 _has_parameters =
true;
72 OneOfParamWidget* oneof_widget;
75 QString oneof_widget_key;
76 auto it =
info.nested_names.begin();
77 oneof_widget_key = QString::fromStdString(*it);
79 for (; it != std::prev(
info.nested_names.end()); ++it) oneof_widget_key +=
"." + QString::fromStdString(*it);
94 auto it_oneof = _oneof_widgets.find(oneof_widget_key);
95 if (it_oneof != _oneof_widgets.end())
98 oneof_widget = it_oneof.value();
103 QString widget_label = QString::fromStdString(
info.oneof_descriptor->name());
105 oneof_widget->nestedFieldName() =
info.nested_names;
107 oneof_widget->connect(oneof_widget, &OneOfParamWidget::currentParameterChanged,
108 [
this](
const QString& label) { emit updatedOneOfField(label); });
109 oneof_widget->connect(oneof_widget, &OneOfParamWidget::signalUpdateRequested, [
this]() { emit signalUpdateRequested(); });
111 _oneof_widgets.insert(oneof_widget_key, oneof_widget);
113 addSubWidget(oneof_widget);
116 QString field_label = QString::fromStdString(
info.field_raw->name());
117 oneof_widget->registerParameter(field_label,
info);
121 void ParameterWidget::addParameterInt32(
int value,
const MessageParser::FieldInformation&
info)
123 _has_parameters =
true;
125 messages::GuiType type =
info.gui_type.first ?
info.gui_type.second : messages::GuiType::TEXTEDIT;
126 QString label =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
127 QString description =
info.description.first ? QString::fromStdString(
info.description.second) :
"";
132 if (
info.print_description) addSubWidget(
new QLabel(description));
135 if (
info.default_value.first && !_groups.empty() && std::get<2>(_groups.back()))
145 info.message_raw->GetReflection()->SetInt32(
info.message_raw,
info.field_raw, value);
150 case messages::GuiType::SLIDER:
152 LabelSliderWidget* param_widget =
new LabelSliderWidget(label, value, 1, 0);
153 param_widget->setToolTip(description);
155 if (!
info.min_value.first)
min = 0;
156 if (!
info.max_value.first)
max = 100;
157 param_widget->setMinMax(
min,
max);
158 addSubWidget(param_widget);
162 auto fun_write_param = [
info,
this](
double value) {
163 int value_int =
static_cast<int>(value);
164 info.message_raw->GetReflection()->SetInt32(
info.message_raw,
info.field_raw, value_int);
165 emit parameterInt32Updated(QString::fromStdString(MessageParser::nestedNamesToString(
info.nested_names)), value_int);
167 connect(param_widget, &LabelSliderWidget::valueChanged, fun_write_param);
170 case messages::GuiType::TEXTEDIT:
174 param_widget->setToolTip(description);
175 QRegExp regexp_no_decimal(
"^((?!\\.).)*$");
176 param_widget->widgetLineEdit()->setValidator(
new QRegExpValidator(regexp_no_decimal, param_widget->widgetLineEdit()));
177 addSubWidget(param_widget);
181 auto fun_write_param = [
info, param_widget,
min,
max,
this]() {
185 bool out_of_range =
false;
191 if (
ok && !out_of_range)
193 info.message_raw->GetReflection()->SetInt32(
info.message_raw,
info.field_raw, value);
194 emit parameterInt32Updated(
195 parentFieldNames() +
"." + QString::fromStdString(MessageParser::nestedNamesToString(
info.nested_names)), value);
200 param_widget->widgetLineEdit()->setText(
206 QMessageBox::warning(
this, tr(
"Out of Range"),
209 connect(param_widget->widgetLineEdit(), &QLineEdit::editingFinished, fun_write_param);
215 void ParameterWidget::addParameterInt32Array(
const std::vector<int>& values,
const MessageParser::FieldInformation&
info)
217 _has_parameters =
true;
219 messages::GuiType type =
info.gui_type.first ?
info.gui_type.second : messages::GuiType::TEXTEDIT;
220 QString label =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
221 QString description =
info.description.first ? QString::fromStdString(
info.description.second) :
"";
225 std::vector<int> values_mod = values;
228 if (
info.print_description) addSubWidget(
new QLabel(description));
231 if (
info.default_value.first && !_groups.empty() && std::get<2>(_groups.back()))
238 info.message_raw->GetReflection()->GetMutableRepeatedFieldRef<
int>(
info.message_raw,
info.field_raw).CopyFrom(values_mod);
242 PRINT_ERROR_NAMED(
"Cannot convert default parameter: " <<
info.default_value.second <<
" to int array.");
249 case messages::GuiType::TEXTEDIT:
253 param_widget->setToolTip(description);
254 QRegExp regexp_no_decimal(
"^((?!\\.).)*$");
255 param_widget->widgetLineEdit()->setValidator(
new QRegExpValidator(regexp_no_decimal, param_widget->widgetLineEdit()));
256 addSubWidget(param_widget);
260 auto fun_write_param = [
info, param_widget,
min,
max,
this]() {
263 std::vector<int> values;
267 bool dimension_range_exceeded =
false;
269 if (
info.dynamic_size)
271 field_size = (
int)values.size();
272 if (
info.min_size.first && field_size <
info.min_size.second)
274 dimension_range_exceeded =
true;
277 else if (
info.max_size.first && field_size <
info.max_size.second)
279 dimension_range_exceeded =
true;
285 int field_size =
info.message_raw->GetReflection()->FieldSize(*
info.message_raw,
info.field_raw);
286 if (
ok && (
int)values.size() != field_size)
ok =
false;
290 bool out_of_range =
false;
294 if (
ok && !out_of_range)
296 if (
info.dynamic_size)
298 info.message_raw->GetReflection()
299 ->GetMutableRepeatedFieldRef<
int>(
info.message_raw,
info.field_raw)
304 for (
int idx = 0; idx < (
int)values.size(); ++idx)
306 info.message_raw->GetReflection()->SetRepeatedInt32(
info.message_raw,
info.field_raw, idx, values[idx]);
313 auto orig_values =
info.message_raw->GetReflection()->GetRepeatedFieldRef<
int>(*
info.message_raw,
info.field_raw);
315 param_widget->widgetLineEdit()->setText(test);
319 if ((
int)values.size() != field_size)
321 QMessageBox::warning(
this, tr(
"Invalid Number of Elements"),
322 "Number of elements must be " + QString::number(field_size) +
", but " + QString::number(values.size()) +
323 (values.size() == 1 ?
" is" :
" are") +
" provided.");
325 else if (dimension_range_exceeded)
327 QMessageBox::warning(
this, tr(
"Invalid Number of Elements"),
331 else if (out_of_range)
333 QMessageBox::warning(
this, tr(
"Out of Range"),
337 connect(param_widget->widgetLineEdit(), &QLineEdit::editingFinished, fun_write_param);
343 void ParameterWidget::addParameterDouble(
double value,
const MessageParser::FieldInformation&
info)
345 _has_parameters =
true;
347 messages::GuiType type =
info.gui_type.first ?
info.gui_type.second : messages::GuiType::TEXTEDIT;
348 QString label =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
349 QString description =
info.description.first ? QString::fromStdString(
info.description.second) :
"";
354 if (
info.print_description) addSubWidget(
new QLabel(description));
357 if (
info.default_value.first && !_groups.empty() && std::get<2>(_groups.back()))
364 PRINT_ERROR_NAMED(
"Cannot convert default parameter: " <<
info.default_value.second <<
" to double.");
367 info.message_raw->GetReflection()->SetDouble(
info.message_raw,
info.field_raw, value);
372 case messages::GuiType::SLIDER:
374 LabelSliderWidget* param_widget =
new LabelSliderWidget(label, value, 1);
375 param_widget->setToolTip(description);
377 if (!
info.min_value.first)
min = 0;
378 if (!
info.max_value.first)
max = 100;
379 param_widget->setMinMax(
min,
max);
380 addSubWidget(param_widget);
384 auto fun_write_param = [
this,
info](
double value) {
385 info.message_raw->GetReflection()->SetDouble(
info.message_raw,
info.field_raw, value);
386 if (
info.update_signals) emit signalUpdateRequested();
388 connect(param_widget, &LabelSliderWidget::valueChanged, fun_write_param);
391 case messages::GuiType::TEXTEDIT:
395 param_widget->setToolTip(description);
396 addSubWidget(param_widget);
400 auto fun_write_param = [
info, param_widget,
min,
max,
this]() {
404 bool out_of_range =
false;
410 if (
ok && !out_of_range)
411 info.message_raw->GetReflection()->SetDouble(
info.message_raw,
info.field_raw, value);
415 param_widget->widgetLineEdit()->setText(
421 QMessageBox::warning(
422 this, tr(
"Out of Range"),
425 if (
info.update_signals) emit signalUpdateRequested();
427 connect(param_widget->widgetLineEdit(), &QLineEdit::editingFinished, fun_write_param);
433 void ParameterWidget::addParameterDoubleArray(
const std::vector<double>& values,
const MessageParser::FieldInformation&
info)
435 _has_parameters =
true;
437 messages::GuiType type =
info.gui_type.first ?
info.gui_type.second : messages::GuiType::TEXTEDIT;
438 QString label =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
439 QString description =
info.description.first ? QString::fromStdString(
info.description.second) :
"";
443 std::vector<double> values_mod = values;
446 if (
info.print_description) addSubWidget(
new QLabel(description));
449 if (
info.default_value.first && !_groups.empty() && std::get<2>(_groups.back()))
456 info.message_raw->GetReflection()->GetMutableRepeatedFieldRef<
double>(
info.message_raw,
info.field_raw).CopyFrom(values_mod);
460 PRINT_ERROR_NAMED(
"Cannot convert default parameter: " <<
info.default_value.second <<
" to double array.");
467 case messages::GuiType::TEXTEDIT:
471 param_widget->setToolTip(description);
472 addSubWidget(param_widget);
476 auto fun_write_param = [
info, param_widget,
min,
max,
this]() {
479 std::vector<double> values;
484 bool dimension_range_exceeded =
false;
485 if (
info.dynamic_size)
487 field_size = (
int)values.size();
488 if (
info.min_size.first && field_size <
info.min_size.second)
490 dimension_range_exceeded =
true;
493 else if (
info.max_size.first && field_size <
info.max_size.second)
495 dimension_range_exceeded =
true;
501 field_size =
info.message_raw->GetReflection()->FieldSize(*
info.message_raw,
info.field_raw);
502 if (
ok && (
int)values.size() != field_size)
ok =
false;
506 bool out_of_range =
false;
510 if (
ok && !out_of_range)
512 if (
info.dynamic_size)
514 info.message_raw->GetReflection()
515 ->GetMutableRepeatedFieldRef<
double>(
info.message_raw,
info.field_raw)
520 for (
int idx = 0; idx < (
int)values.size(); ++idx)
522 info.message_raw->GetReflection()->SetRepeatedDouble(
info.message_raw,
info.field_raw, idx, values[idx]);
529 auto orig_values =
info.message_raw->GetReflection()->GetRepeatedFieldRef<
double>(*
info.message_raw,
info.field_raw);
531 param_widget->widgetLineEdit()->setText(
text);
535 if ((
int)values.size() != field_size)
537 QMessageBox::warning(
this, tr(
"Invalid Number of Elements"),
538 "Number of elements must be " + QString::number(field_size) +
", but " + QString::number(values.size()) +
539 (values.size() == 1 ?
" is" :
" are") +
" provided.");
541 else if (dimension_range_exceeded)
543 QMessageBox::warning(
this, tr(
"Invalid Number of Elements"),
547 else if (out_of_range)
549 QMessageBox::warning(
550 this, tr(
"Out of Range"),
553 if (
info.update_signals) emit signalUpdateRequested();
555 connect(param_widget->widgetLineEdit(), &QLineEdit::editingFinished, fun_write_param);
561 void ParameterWidget::addParameterBool(
bool value,
const MessageParser::FieldInformation&
info)
563 _has_parameters =
true;
565 messages::GuiType type =
info.gui_type.first ?
info.gui_type.second : messages::GuiType::CHECKBOX;
566 QString label =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
567 QString description =
info.description.first ? QString::fromStdString(
info.description.second) :
"";
570 if (
info.print_description) addSubWidget(
new QLabel(description));
573 if (
info.default_value.first && !_groups.empty() && std::get<2>(_groups.back()))
583 info.message_raw->GetReflection()->SetBool(
info.message_raw,
info.field_raw, value);
588 case messages::GuiType::CHECKBOX:
591 QCheckBox* param_widget =
new QCheckBox(label);
592 param_widget->setToolTip(description);
593 param_widget->setChecked(value);
594 addSubWidget(param_widget);
598 auto fun_write_param = [
info,
this](
bool toggled) {
599 info.message_raw->GetReflection()->SetBool(
info.message_raw,
info.field_raw, toggled);
600 if (
info.update_signals) emit signalUpdateRequested();
602 connect(param_widget, &QCheckBox::toggled, fun_write_param);
609 void ParameterWidget::addParameterBoolArray(
const std::vector<bool>& values,
const MessageParser::FieldInformation&
info)
611 _has_parameters =
true;
613 messages::GuiType type =
info.gui_type.first ?
info.gui_type.second : messages::GuiType::CHECKBOX;
614 QString label =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
615 QString description =
info.description.first ? QString::fromStdString(
info.description.second) :
"";
617 std::vector<bool> values_mod = values;
620 if (
info.print_description) addSubWidget(
new QLabel(description));
623 if (
info.default_value.first && !_groups.empty() && std::get<2>(_groups.back()))
630 info.message_raw->GetReflection()->GetMutableRepeatedFieldRef<
bool>(
info.message_raw,
info.field_raw).CopyFrom(values_mod);
634 PRINT_ERROR_NAMED(
"Cannot convert default parameter: " <<
info.default_value.second <<
" to bool array.");
640 QStringList label_list = label.split(
',');
642 QString group_name = label_list.empty() ? label : label_list.first();
644 if (type == messages::GuiType::TEXTEDIT)
648 param_widget->setToolTip(description);
649 addSubWidget(param_widget);
653 auto fun_write_param = [
info, param_widget,
this]() {
656 std::vector<bool> values;
661 bool dimension_range_exceeded =
false;
662 if (
info.dynamic_size)
664 field_size = (
int)values.size();
665 if (
info.min_size.first && field_size <
info.min_size.second)
667 dimension_range_exceeded =
true;
670 else if (
info.max_size.first && field_size <
info.max_size.second)
672 dimension_range_exceeded =
true;
678 field_size =
info.message_raw->GetReflection()->FieldSize(*
info.message_raw,
info.field_raw);
679 if (
ok && (
int)values.size() != field_size)
ok =
false;
685 if (
info.dynamic_size)
687 info.message_raw->GetReflection()
688 ->GetMutableRepeatedFieldRef<
bool>(
info.message_raw,
info.field_raw)
693 for (
int idx = 0; idx < (
int)values.size(); ++idx)
695 info.message_raw->GetReflection()->SetRepeatedBool(
info.message_raw,
info.field_raw, idx, values[idx]);
702 auto orig_values =
info.message_raw->GetReflection()->GetRepeatedFieldRef<
bool>(*
info.message_raw,
info.field_raw);
704 param_widget->widgetLineEdit()->setText(
text);
708 if ((
int)values.size() != field_size)
710 QMessageBox::warning(
this, tr(
"Invalid Number of Elements"),
711 "Number of elements must be " + QString::number(field_size) +
", but " + QString::number(values.size()) +
712 (values.size() == 1 ?
" is" :
" are") +
" provided.");
714 else if (dimension_range_exceeded)
716 QMessageBox::warning(
this, tr(
"Invalid Number of Elements"),
720 if (
info.update_signals) emit signalUpdateRequested();
722 connect(param_widget->widgetLineEdit(), &QLineEdit::editingFinished, fun_write_param);
727 bool exclusive = type != messages::GuiType::CHECKBOX;
729 HoriontalButtonGroup* param_widget =
new HoriontalButtonGroup(group_name, exclusive);
730 param_widget->setToolTip(description);
732 for (
int idx = 0; idx < (
int)values_mod.size(); ++idx)
734 QString field_label = (idx + 1 < label_list.size()) ? label_list[idx + 1] :
"";
735 QAbstractButton* btn = param_widget->addButton(values_mod[idx], field_label);
739 auto fun_write_param = [
info, idx,
this](
bool toggled) {
740 info.message_raw->GetReflection()->SetRepeatedBool(
info.message_raw,
info.field_raw, idx, toggled);
741 if (
info.update_signals) emit signalUpdateRequested();
743 connect(btn, &QAbstractButton::toggled, fun_write_param);
745 addSubWidget(param_widget);
749 void ParameterWidget::addParameterEnum(
const std::string& value,
const MessageParser::FieldInformation&
info)
751 _has_parameters =
true;
753 QString label =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
754 QString description =
info.description.first ? QString::fromStdString(
info.description.second) :
"";
757 if (
info.print_description) addSubWidget(
new QLabel(description));
759 LabelComboBoxWidget* param_widget =
new LabelComboBoxWidget(label);
760 param_widget->setToolTip(description);
761 addSubWidget(param_widget);
764 const google::protobuf::EnumDescriptor* enum_descr =
info.field_raw->enum_type();
767 PRINT_WARNING(
"ParameterWidget::addParameterEnum(): provided parameter '" <<
info.field_name <<
"' is no enumerator!");
770 for (
int idx = 0; idx < enum_descr->value_count(); ++idx)
772 param_widget->widgetComboBox()->addItem(QString::fromStdString(enum_descr->value(idx)->name()));
775 param_widget->widgetComboBox()->setCurrentText(QString::fromStdString(value));
779 auto fun_write_param = [
info,
this](
int idx) {
780 info.message_raw->GetReflection()->SetEnumValue(
info.message_raw,
info.field_raw, idx);
781 if (
info.update_signals) emit signalUpdateRequested();
783 connect(param_widget->widgetComboBox(),
static_cast<void (QComboBox::*)(
int)
>(&QComboBox::currentIndexChanged), fun_write_param);
786 void ParameterWidget::addParameterString(std::string value,
const MessageParser::FieldInformation&
info)
788 _has_parameters =
true;
790 QString label =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
791 QString description =
info.description.first ? QString::fromStdString(
info.description.second) :
"";
794 if (
info.print_description) addSubWidget(
new QLabel(description));
797 if (
info.default_value.first && !_groups.empty() && std::get<2>(_groups.back()))
800 value =
info.default_value.second;
802 info.message_raw->GetReflection()->SetString(
info.message_raw,
info.field_raw, value);
806 param_widget->setToolTip(description);
807 addSubWidget(param_widget);
811 auto fun_write_param = [param_widget,
info,
this]() {
812 std::string value = param_widget->
widgetLineEdit()->text().toUtf8().constData();
813 info.message_raw->GetReflection()->SetString(
info.message_raw,
info.field_raw, value);
814 if (
info.update_signals) emit signalUpdateRequested();
816 connect(param_widget->
widgetLineEdit(), &QLineEdit::editingFinished, fun_write_param);
819 void ParameterWidget::addInfoText(
const std::string&
text,
const MessageParser::FieldInformation&
info)
821 _has_parameters =
true;
823 QString text_q = QString::fromStdString(
text);
825 if (
info.default_value.first &&
text.empty()) text_q = QString::fromStdString(
info.default_value.second);
827 addSubWidget(
new QLabel(text_q));
830 void ParameterWidget::startGroup(
const MessageParser::FieldInformation&
info)
833 QString name =
info.label.first ? QString::fromStdString(
info.label.second) : QString::fromStdString(
info.field_name);
837 QVBoxLayout* layout =
new QVBoxLayout;
838 layout->setContentsMargins(5, 15, 5, 0);
839 layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
842 void ParameterWidget::endGroup()
844 if (_groups.empty())
return;
846 std::tuple<CollapsableGroupBox*, QLayout*, bool> top = _groups.pop();
849 std::get<0>(top)->groupBox()->setLayout(std::get<1>(top));
850 std::get<0>(top)->setCollapsed(
false);
854 _layout->addWidget(std::get<0>(top));
856 std::get<1>(_groups.top())->addWidget(std::get<0>(top));
859 void ParameterWidget::addSubWidget(QWidget* widget)
865 widget->setStyleSheet(
"font-size: 11px");
869 _layout->addWidget(widget);
873 else if (std::get<1>(_groups.top()))
875 std::get<1>(_groups.top())->addWidget(widget);
880 void ParameterWidget::generateFromMessage(std::shared_ptr<google::protobuf::Message> message)
883 _param_message = message;
887 void ParameterWidget::generateFromAllocatedField(google::protobuf::Message* message,
const google::protobuf::FieldDescriptor* field)
890 parse(message, field);
893 void ParameterWidget::generateElements()
895 if (!_param_message)
return;
896 parse(_param_message.get());
899 void ParameterWidget::parse(google::protobuf::Message* message,
const google::protobuf::FieldDescriptor* field)
901 MessageParser parser;
903 using std::placeholders::_1;
904 using std::placeholders::_2;
906 parser.setCallbackValueInt32(std::bind(&ParameterWidget::addParameterInt32,
this, _1, _2));
907 parser.setCallbackValueInt32Array(std::bind(&ParameterWidget::addParameterInt32Array,
this, _1, _2));
908 parser.setCallbackValueDouble(std::bind(&ParameterWidget::addParameterDouble,
this, _1, _2));
909 parser.setCallbackValueDoubleArray(std::bind(&ParameterWidget::addParameterDoubleArray,
this, _1, _2));
910 parser.setCallbackValueBool(std::bind(&ParameterWidget::addParameterBool,
this, _1, _2));
911 parser.setCallbackValueBoolArray(std::bind(&ParameterWidget::addParameterBoolArray,
this, _1, _2));
912 parser.setCallbackValueEnum(std::bind(&ParameterWidget::addParameterEnum,
this, _1, _2));
913 parser.setCallbackValueString(std::bind(&ParameterWidget::addParameterString,
this, _1, _2));
914 parser.setCallbackValueStringInfo(std::bind(&ParameterWidget::addInfoText,
this, _1, _2));
916 parser.setCallbackMessageEvent([
this, field](corbo::MessageParser::MessageEvent ev,
const corbo::MessageParser::FieldInformation&
info) {
917 if (ev == corbo::MessageParser::MessageEvent::MessageStart)
919 else if (ev == corbo::MessageParser::MessageEvent::MessageEnd)
921 else if (ev == corbo::MessageParser::MessageEvent::OneOf)
923 if (
info.field_raw != field) addOneOfField(
info);
927 std::list<std::string> nested_field_name;
930 nested_field_name = _nested_parent_fields;
931 if (!nested_field_name.empty()) nested_field_name.pop_back();
935 parser.parseField(message, field,
false,
true,
false,
939 parser.parse(message,
false,
true, &nested_field_name);
942 bool ParameterWidget::hasParameters()
const
944 if (_has_parameters)
return true;
947 for (
auto widget : this->findChildren<ParameterWidget*>(QString(), Qt::FindChildrenRecursively))
949 if (
static_cast<ParameterWidget*
>(widget)->hasParameters())
return true;
954 void ParameterWidget::clearElements()
956 for (
auto widget : this->findChildren<QWidget*>(QString(), Qt::FindDirectChildrenOnly))
delete widget;
959 _oneof_widgets.clear();