1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 from python_qt_binding import QtCore, QtGui
34
35 import os
36 import sys
37 import time
38 import threading
39 from xmlrpclib import Binary
40
41 import roslib.names
42 import roslib.msgs
43 import rospy
44 import node_manager_fkie as nm
45
46 from parameter_handler import ParameterHandler
47 from detailed_msg_box import WarningMessageBox
50 return v.lower() in ("yes", "true", "t", "1")
51
53
54 remove_item_signal = QtCore.Signal(str)
55
57 QtGui.QComboBox.__init__(self, parent=parent)
58 self.parameter_description = None
59
61 key_mod = QtGui.QApplication.keyboardModifiers()
62 if key_mod & QtCore.Qt.ShiftModifier and (event.key() == QtCore.Qt.Key_Delete):
63 try:
64 curr_text = self.currentText()
65 if curr_text:
66 for i in range(self.count()):
67 if curr_text == self.itemText(i):
68 self.removeItem(i)
69 self.remove_item_signal.emit(curr_text)
70 self.clearEditText()
71 except:
72 import traceback
73 print traceback.format_exc(1)
74 QtGui.QComboBox.keyPressEvent(self, event)
75
77 '''
78 Used for internal representation of the parameter in dialog.
79 '''
80 - def __init__(self, name, msg_type, value=None, widget=None):
81 self._name = str(name)
82 self._type = msg_type
83 if isinstance(self._type, dict):
84 self._type = 'dict'
85 elif isinstance(self._type, list):
86 self._type = 'list'
87 self._value = value
88 self._value_org = value
89 self._widget = widget
90 try:
91 self._base_type, self._is_array_type, self._array_length = roslib.msgs.parse_type(self._type)
92 except:
93 pass
94 if msg_type == 'binary':
95 self._base_type = msg_type
96
98 return ''.join([self._name, ' [', self._type, ']'])
99
101 return self._value_org
102
104 return unicode(self.origin_value()) != unicode(self._value)
105
108
114
117
126
128
129 return self._is_array_type or self._type in ['[]']
130
132 return self._array_length
133
135 result = self._base_type in roslib.msgs.PRIMITIVE_TYPES
136 result = result or self._base_type in ['int', 'float', 'time', 'duration', 'binary']
137
138 result = result or self._type in ['[]']
139 return result
140
142 return self._base_type in ['time', 'duration']
143
145 return self._base_type in ['binary']
146
148 return self._base_type
149
151 field = self.widget()
152 result = ''
153 if isinstance(field, QtGui.QCheckBox):
154 result = repr(field.isChecked())
155 elif isinstance(field, QtGui.QLineEdit):
156 result = field.text()
157 elif isinstance(field, QtGui.QComboBox):
158 result = field.currentText()
159 self.updateValue(result)
160
162 try:
163 if isinstance(value, (dict, list)):
164 self._value = value
165 elif value:
166 nm.history().addParamCache(self.fullName(), value)
167 if self.isArrayType():
168 if 'int' in self.baseType():
169 self._value = map(int, value.lstrip('[').rstrip(']').split(','))
170 elif 'float' in self.baseType():
171 self._value = map(float, value.lstrip('[').rstrip(']').split(','))
172 elif 'bool' in self.baseType():
173 self._value = map(str2bool, value.lstrip('[').rstrip(']').split(','))
174 elif self.isBinaryType():
175 self._value = value
176 else:
177
178 try:
179 import yaml
180 self._value = yaml.load("[%s]"%value)
181
182
183
184 if self._value is None:
185 self._value = []
186 except yaml.MarkedYAMLError, e:
187 raise Exception("Field [%s] yaml error: %s"%(self.fullName(), str(e)))
188 if not self.arrayLength() is None and self.arrayLength() != len(self._value):
189 raise Exception(''.join(["Field [", self.fullName(), "] has incorrect number of elements: ", str(len(self._value)), " != ", str(self.arrayLength())]))
190 else:
191 if 'int' in self.baseType():
192 self._value = int(value)
193 elif 'float' in self.baseType():
194 self._value = float(value)
195 elif 'bool' in self.baseType():
196 if isinstance(value, bool):
197 self._value = value
198 else:
199 self._value = str2bool(value)
200 elif self.isBinaryType():
201 self._value = unicode(value)
202 elif self.isTimeType():
203 if value == 'now':
204 self._value = 'now'
205 else:
206 try:
207 val = eval(value)
208 if isinstance(val, dict):
209 self._value = val
210 else:
211 secs = int(val)
212 nsecs = int((val - secs) * 1000000000)
213 self._value = {'secs': secs, 'nsecs': nsecs}
214 except:
215 self._value = {'secs': 0, 'nsecs': 0}
216 else:
217 self._value = value.encode(sys.getfilesystemencoding())
218 else:
219 if self.isArrayType():
220 arr = []
221 self._value = arr
222 else:
223 if 'int' in self.baseType():
224 self._value = 0
225 elif 'float' in self.baseType():
226 self._value = 0.0
227 elif 'bool' in self.baseType():
228 self._value = False
229 elif self.isBinaryType():
230 self._value = unicode(value)
231 elif self.isTimeType():
232 self._value = {'secs': 0, 'nsecs': 0}
233 else:
234 self._value = ''
235 nm.history().addParamCache(self.fullName(), value)
236 except Exception, e:
237 raise Exception(''.join(["Error while set value '", unicode(value), "' for '", self.fullName(), "': ", str(e)]))
238 return self._value
239
250
253
287
299
300
301
302 -class MainBox(QtGui.QWidget):
303 '''
304 Groups the parameter without visualization of the group. It is the main widget.
305 '''
306 - def __init__(self, name, param_type, collapsible=True, parent=None):
307 QtGui.QWidget.__init__(self, parent)
308 self.setObjectName(name)
309 self.name = name
310 self.type = param_type
311 self.params = []
312 self.collapsed = False
313 self.parameter_description = None
314 vLayout = QtGui.QVBoxLayout()
315 vLayout.setSpacing(0)
316 self.options_layout = QtGui.QHBoxLayout()
317 self.param_widget = QtGui.QFrame()
318 self.name_label = QtGui.QLabel(name)
319 font = self.name_label.font()
320 font.setBold(True)
321 self.name_label.setFont(font)
322 self.type_label = QtGui.QLabel(''.join([' (', param_type, ')']))
323
324 if collapsible:
325 self.hide_button = QtGui.QPushButton('-')
326 self.hide_button.setFlat(True)
327 self.hide_button.setMaximumSize(20,20)
328 self.hide_button.clicked.connect(self._on_hide_clicked)
329 self.options_layout.addWidget(self.hide_button)
330 self.options_layout.addWidget(self.name_label)
331 self.options_layout.addWidget(self.type_label)
332 self.options_layout.addStretch()
333
334 vLayout.addLayout(self.options_layout)
335
336 self.param_widget.setFrameShape(QtGui.QFrame.Box)
337 self.param_widget.setFrameShadow(QtGui.QFrame.Raised)
338
339 boxLayout = QtGui.QFormLayout()
340 boxLayout.setVerticalSpacing(0)
341 self.param_widget.setLayout(boxLayout)
342 vLayout.addWidget(self.param_widget)
343 self.setLayout(vLayout)
344 if param_type in ['std_msgs/Header']:
345 self.setCollapsed(True)
346
347 - def setCollapsed(self, value):
348 self.collapsed = value
349 self.param_widget.setVisible(not value)
350 self.hide_button.setText('+' if self.collapsed else '-')
351
353 self.setCollapsed(not self.collapsed)
354
355
356
357
358 - def createFieldFromValue(self, value):
359 self.setUpdatesEnabled(False)
360 try:
361 if isinstance(value, dict):
362 self._createFieldFromDict(value)
363 finally:
364 self.setUpdatesEnabled(True)
365
366 - def _createFieldFromDict(self, value, layout=None):
367 if layout is None:
368 layout = self.param_widget.layout()
369
370 all_params = []
371 primitives = []
372 komplex = []
373 for name, (_type, val) in value.items():
374 if _type in ['std_msgs/Header']:
375 all_params.append((name, _type, val))
376 elif isinstance(val, (dict, list)):
377 komplex.append((name, _type, val))
378 else:
379 primitives.append((name, _type, val))
380 all_params.extend(sorted(primitives))
381 all_params.extend(sorted(komplex))
382
383
384 for name, _type, val in all_params:
385 field = self.getField(name)
386 if field is None:
387 param_desc = ParameterDescription(name, _type, val)
388 field = param_desc.createTypedWidget(self)
389 param_desc.setWidget(field)
390 self.params.append(param_desc)
391 if isinstance(field, (GroupBox, ArrayBox)):
392 field.createFieldFromValue(val)
393 layout.addRow(field)
394 else:
395 label_name = name if _type == 'string' else ''.join([name, ' (', _type, ')'])
396 label = QtGui.QLabel(label_name, self)
397 label.setObjectName(''.join([name, '_label']))
398 label.setBuddy(field)
399 layout.addRow(label, field)
400 else:
401 if isinstance(field, (GroupBox, ArrayBox)):
402 field.createFieldFromValue(val)
403 else:
404 raise Exception(''.join(["Parameter with name '", name, "' already exists!"]))
405
407 result = dict()
408 for param in self.params:
409 if not param.isBinaryType():
410 result[param.name()] = param.value()
411 return result
412
413 - def set_values(self, values):
414 '''
415 Sets the values for existing fields.
416 :param values: the dictionary with values to set.
417 :type values: dict
418 :raise Exception: on errors
419 '''
420 if isinstance(values, dict):
421 for param, value in values.items():
422 field = self.getField(param)
423 if not field is None:
424 if isinstance(field, (GroupBox, ArrayBox)):
425 field.set_values(value)
426 else:
427 if isinstance(field, QtGui.QCheckBox):
428 field.setChecked(value)
429 elif isinstance(field, QtGui.QLineEdit):
430
431 field.setText(', '.join([unicode(v) for v in value]) if isinstance(value, list) else unicode(value))
432 elif isinstance(field, QtGui.QComboBox):
433 field.setEditText(', '.join([unicode(v) for v in value]) if isinstance(value, list) else unicode(value))
434 elif isinstance(values, list):
435 raise Exception("Setting 'list' values in MainBox or GroupBox not supported!!!")
436
437 - def getField(self, name):
438 for child in self.children():
439 for c in child.children():
440 if c.objectName() == name:
441 return c
442 return None
443
444 - def removeAllFields(self):
445 '''
446 Remove the references between parameter and corresponding widgets
447 (ComboBox, CheckBox, ..) and remove these widgets from layouts.
448 '''
449 for child in self.param_widget.children():
450 if isinstance(child, MyComboBox):
451 child.parameter_description.setWidget(None)
452 self.params.remove(child.parameter_description)
453 elif isinstance(child, MainBox):
454 child.removeAllFields()
455 self.param_widget.layout().removeWidget(child)
456
457 - def filter(self, arg):
458 '''
459 Hide the parameter input field, which label dosn't contains the C{arg}.
460 @param arg: the filter text
461 @type art: C{str}
462 '''
463 result = False
464 for child in self.param_widget.children():
465 if isinstance(child, (MainBox, GroupBox, ArrayBox)):
466 show = not arg or child.objectName().lower().find(arg.lower()) != -1
467 show = child.filter(arg) or show
468
469 child.setVisible(show)
470 if show:
471 child.setCollapsed(False)
472 result = True
473 elif isinstance(child, (QtGui.QWidget)) and not isinstance(child, (QtGui.QLabel)) and not isinstance(child, (QtGui.QFrame)):
474 label = child.parentWidget().layout().labelForField(child)
475 if not label is None:
476 has_text = child.objectName().lower().find(arg.lower()) == -1
477 show = not arg or (not has_text or (hasattr(child, 'currentText') and not has_text))
478
479 if show and not child.parentWidget().isVisible():
480 child.parentWidget().setVisible(show)
481 label.setVisible(show)
482 child.setVisible(show)
483 if show:
484 result = True
485 return result
486
487 - def setVisible(self, arg):
488 if arg and not self.parentWidget() is None and not self.parentWidget().isVisible():
489 self.parentWidget().setVisible(arg)
490 QtGui.QWidget.setVisible(self, arg)
491
495 '''
496 Groups the parameter of a dictionary, struct or class using the group box for
497 visualization.
498 '''
499 - def __init__(self, name, param_type, parent=None):
502
503
504
505 -class ArrayEntry(MainBox):
506 '''
507 A part of the ArrayBox to represent the elements of a list.
508 '''
509 - def __init__(self, index, param_type, parent=None):
510
511 MainBox.__init__(self, ''.join(['#',str(index)]), param_type, True, parent)
512 self.index = index
513 self.setObjectName(''.join(['[', str(index), ']']))
514 self.param_widget.setFrameShape(QtGui.QFrame.Box)
515 self.param_widget.setFrameShadow(QtGui.QFrame.Plain)
516 self.type_label.setVisible(False)
517
518
519
520
521
522
524 result = dict()
525 for param in self.params:
526 result[param.name()] = param.value()
527 return result
528
531 '''
532 Groups the parameter of a list.
533 '''
534 - def __init__(self, name, param_type, parent=None):
535 MainBox.__init__(self, name, param_type, True, parent)
536 self._dynamic_value = None
537 self._dynamic_widget = None
538 self._dynamic_items_count = 0
539
541 self._dynamic_items_count = 0
542 addButton = QtGui.QPushButton("+")
543 addButton.setMaximumSize(25,25)
544 addButton.clicked.connect(self._on_add_dynamic_entry)
545 self.options_layout.addWidget(addButton)
546 self.count_label = QtGui.QLabel('0')
547 self.options_layout.addWidget(self.count_label)
548 remButton = QtGui.QPushButton("-")
549 remButton.setMaximumSize(25,25)
550 remButton.clicked.connect(self._on_rem_dynamic_entry)
551 self.options_layout.addWidget(remButton)
552
554 self.setUpdatesEnabled(False)
555 try:
556 if not self._dynamic_value is None:
557 for v in self._dynamic_value:
558 if isinstance(v, dict):
559 entry_frame = ArrayEntry(self._dynamic_items_count, self.type)
560 self.param_widget.layout().addRow(entry_frame)
561 entry_frame._createFieldFromDict(v)
562 self._dynamic_items_count += 1
563 self.count_label.setText(str(self._dynamic_items_count))
564 finally:
565 self.setUpdatesEnabled(True)
566
568 if self._dynamic_items_count > 0:
569 self._dynamic_items_count -= 1
570 item = self.param_widget.layout().takeAt(self._dynamic_items_count)
571 self.param_widget.layout().removeItem(item)
572 try:
573
574 for child in item.widget().children():
575 if isinstance(child, MyComboBox):
576 child.parameter_description.setWidget(None)
577 self.params.remove(child.parameter_description)
578 elif isinstance(child, MainBox):
579 child.removeAllFields()
580 self.param_widget.layout().removeWidget(child)
581 child.parameter_description.setWidget(None)
582 self.params.remove(child.parameter_description)
583 item.widget().setParent(None)
584 del item
585 except:
586 import traceback
587 print traceback.format_exc(1)
588 self.count_label.setText(str(self._dynamic_items_count))
589
591 self.setUpdatesEnabled(False)
592 try:
593 if isinstance(value, list):
594 self.addDynamicBox()
595 self._dynamic_value = value
596 finally:
597 self.setUpdatesEnabled(True)
598
600 '''
601 Goes through the list and creates dictionary with values of each element.
602 '''
603 result = list()
604 for i in range(self.param_widget.layout().rowCount()):
605 item = self.param_widget.layout().itemAt(i, QtGui.QFormLayout.SpanningRole)
606 if item and isinstance(item.widget(), ArrayEntry):
607 result.append(item.widget().value())
608 return result
609
611 '''
612 Create a list of the elements and sets their values.
613 :param values: The list of dictionaries with parameter values
614 :type values: list
615 '''
616 if isinstance(values, list):
617 count_entries = 0
618
619 for i in range(self.param_widget.layout().rowCount()):
620 item = self.param_widget.layout().itemAt(i, QtGui.QFormLayout.SpanningRole)
621 if item and isinstance(item.widget(), ArrayEntry):
622 count_entries += 1
623
624 if count_entries < len(values):
625 for i in range(len(values) - count_entries):
626 self._on_add_dynamic_entry()
627 elif count_entries > len(values):
628 for i in range(count_entries - len(values)):
629 self._on_rem_dynamic_entry()
630
631 for i in range(self.param_widget.layout().rowCount()):
632 item = self.param_widget.layout().itemAt(i, QtGui.QFormLayout.SpanningRole)
633 if item and isinstance(item.widget(), ArrayEntry):
634 item.widget().set_values(values[i])
635
645
649 '''
650 This dialog creates an input mask for the given parameter and their types.
651 '''
652
653 - def __init__(self, params=dict(), buttons=QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok, sidebar_var='', parent=None):
654 '''
655 Creates an input dialog.
656 @param params: a dictionary with parameter names and (type, values).
657 The C{value}, can be a primitive value, a list with values or parameter
658 dictionary to create groups. In this case the type is the name of the group.
659 @type params: C{dict(str:(str, {value, [..], dict()}))}
660 '''
661 QtGui.QDialog.__init__(self, parent=parent)
662 self.setObjectName(' - '.join(['ParameterDialog', str(params)]))
663
664 self.__current_path = nm.settings().current_dialog_path
665 self.horizontalLayout = QtGui.QHBoxLayout(self)
666 self.horizontalLayout.setObjectName("horizontalLayout")
667 self.horizontalLayout.setContentsMargins(1, 1, 1, 1)
668 self.verticalLayout = QtGui.QVBoxLayout()
669 self.verticalLayout.setObjectName("verticalLayout")
670 self.verticalLayout.setContentsMargins(1, 1, 1, 1)
671
672 self.filter_frame = QtGui.QFrame(self)
673 filterLayout = QtGui.QHBoxLayout(self.filter_frame)
674 filterLayout.setContentsMargins(1, 1, 1, 1)
675 label = QtGui.QLabel("Filter:", self.filter_frame)
676 self.filter_field = QtGui.QLineEdit(self.filter_frame)
677 filterLayout.addWidget(label)
678 filterLayout.addWidget(self.filter_field)
679 self.filter_field.textChanged.connect(self._on_filter_changed)
680 self.filter_visible = True
681
682 self.verticalLayout.addWidget(self.filter_frame)
683
684
685 self.scrollArea = scrollArea = ScrollArea(self);
686 scrollArea.setObjectName("scrollArea")
687 scrollArea.setWidgetResizable(True)
688 self.content = MainBox('/', 'str', False, self)
689 scrollArea.setWidget(self.content)
690 self.verticalLayout.addWidget(scrollArea)
691
692
693 self.info_field = QtGui.QTextEdit(self)
694 self.info_field.setVisible(False)
695 palette = QtGui.QPalette()
696 brush = QtGui.QBrush(QtGui.QColor(255, 254, 242))
697 brush.setStyle(QtCore.Qt.SolidPattern)
698 palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
699 brush = QtGui.QBrush(QtGui.QColor(255, 254, 242))
700 brush.setStyle(QtCore.Qt.SolidPattern)
701 palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
702 brush = QtGui.QBrush(QtGui.QColor(244, 244, 244))
703 brush.setStyle(QtCore.Qt.SolidPattern)
704 palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
705 self.info_field.setPalette(palette)
706 self.info_field.setFrameShadow(QtGui.QFrame.Plain)
707 self.info_field.setReadOnly(True)
708 self.info_field.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByKeyboard|QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextBrowserInteraction|QtCore.Qt.TextSelectableByKeyboard|QtCore.Qt.TextSelectableByMouse)
709 self.info_field.setObjectName("dialog_info_field")
710 self.verticalLayout.addWidget(self.info_field)
711
712
713 self.buttonBox = QtGui.QDialogButtonBox(self)
714 self.buttonBox.setObjectName("buttonBox")
715 self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
716 self.buttonBox.setStandardButtons(buttons)
717 self.buttonBox.accepted.connect(self.accept)
718 self.buttonBox.rejected.connect(self.reject)
719 self.verticalLayout.addWidget(self.buttonBox)
720 self.horizontalLayout.addLayout(self.verticalLayout)
721
722
723 values = nm.history().cachedParamValues('/%s'%sidebar_var)
724 self.sidebar_frame = QtGui.QFrame()
725 self.sidebar_frame.setObjectName(sidebar_var)
726 sidebarframe_verticalLayout = QtGui.QVBoxLayout(self.sidebar_frame)
727 sidebarframe_verticalLayout.setObjectName("sidebarframe_verticalLayout")
728 sidebarframe_verticalLayout.setContentsMargins(1, 1, 1, 1)
729 self._sidebar_selected = 0
730 if len(values) > 1 and sidebar_var in params:
731 self.horizontalLayout.addWidget(self.sidebar_frame)
732 try:
733 self.sidebar_default_val = params[sidebar_var][1]
734 except:
735 self.sidebar_default_val = ''
736 values.sort()
737 for v in values:
738 checkbox = QtGui.QCheckBox(v)
739 checkbox.stateChanged.connect(self._on_sidebar_stateChanged)
740 self.sidebar_frame.layout().addWidget(checkbox)
741 self.sidebar_frame.layout().addItem(QtGui.QSpacerItem(100, 20, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding))
742
743 if params:
744 self.content.createFieldFromValue(params)
745 self.setInfoActive(False)
746
747 if self.filter_frame.isVisible():
748 self.filter_field.setFocus()
749 self.setMinimumSize(350,200)
750
751
755
768
782
784 self.content.filter(self.filter_field.text())
785
787 '''
788 Shows or hides the filter row.
789 '''
790 self.filter_visible = val
791 self.filter_frame.setVisible(val&self.scrollArea.isHidden())
792
794 label = QtGui.QLabel()
795 label.setWordWrap(True)
796 label.setText(''.join(["<font color='red'>Warning!\n", message, "</font>"]))
797 self.verticalLayout.insertWidget(1, label)
798
799 - def setText(self, text):
800 '''
801 Adds a label to the dialog's layout and shows the given text.
802 @param text: the text to add to the dialog
803 @type text: C{str}
804 '''
805 self.info_field.setText(text)
806 self.setInfoActive(True)
807
809 '''
810 Activates or deactivates the info field of this dialog. If info field is
811 activated, the filter frame and the input field are deactivated.
812 @type val: C{bool}
813 '''
814 if val and self.info_field.isHidden():
815 self.filter_frame.setVisible(False&self.filter_visible)
816 self.scrollArea.setVisible(False)
817 self.info_field.setVisible(True)
818 elif not val and self.scrollArea.isHidden():
819 self.filter_frame.setVisible(True&self.filter_visible)
820 self.scrollArea.setVisible(True)
821 self.info_field.setVisible(False)
822 if self.filter_frame.isVisible():
823 self.filter_field.setFocus()
824
826 field = self.content.getField(field_label)
827 if not field is None:
828 field.setFocus()
829
831 '''
832 @param only_changed: requests only changed parameter
833 @type only_changed: bool (Default: False)
834 @returns: a directory with parameter and value for all entered fields.
835 @rtype: C{dict(str(param) : str(value))}
836 '''
837
838 sidebar_list = []
839 sidebar_name = self.sidebar_frame.objectName()
840 for j in range(self.sidebar_frame.layout().count()-1):
841 w = self.sidebar_frame.layout().itemAt(j).widget()
842 if isinstance(w, QtGui.QCheckBox):
843 if w.checkState() == QtCore.Qt.Checked:
844 sidebar_list.append((w.text(), True))
845 result_value = self.content.value()
846
847 if sidebar_name in result_value:
848
849 if len(sidebar_list) == 0 or self.sidebar_default_val != result_value[sidebar_name][0]:
850 sidebar_list.append(result_value[sidebar_name])
851 result_value[sidebar_name] = ([v for v, _ in set(sidebar_list)], True)
852 result = self._remove_unchanged_parameter(result_value, only_changed)
853 return result
854
856 '''
857 Resolves the dictionary values to ROS parameter names.
858 @param keywords: the result of the getKeywords
859 @result: dictionary of (ROS parameter name : value)
860 '''
861 result = dict()
862 for param, value in keywords.items():
863 if isinstance(value, dict):
864 r = self.keywords2params(value)
865 for p, v in r.items():
866 result[roslib.names.ns_join(param, p)] = v
867 else:
868 result[param] = value
869 return result
870
872 result = dict()
873 for param, value in params.items():
874 if isinstance(value, dict):
875 r = self._remove_unchanged_parameter(value, only_changed)
876 if r:
877 result[param] = r
878 elif isinstance(value, tuple):
879 if value[1] or not only_changed:
880 result[param] = value[0]
881 else:
882 print "unknown parameter: should not happens", param, value
883 return result
884
886 try:
887 import yaml
888 (fileName, _) = QtGui.QFileDialog.getSaveFileName(self,
889 "Save parameter",
890 self.__current_path,
891 "YAML files (*.yaml);;All files (*)")
892 if fileName:
893 self.__current_path = os.path.dirname(fileName)
894 nm.settings().current_dialog_path = os.path.dirname(fileName)
895 text = yaml.dump(self.content.value(), default_flow_style=False)
896 with open(fileName, 'w+') as f:
897 f.write(text)
898 except Exception as e:
899 import traceback
900 print traceback.format_exc(1)
901 WarningMessageBox(QtGui.QMessageBox.Warning, "Save parameter Error",
902 'Error while save parameter',
903 str(e)).exec_()
904
906 try:
907 import yaml
908 (fileName, _) = QtGui.QFileDialog.getOpenFileName(self,
909 "Load parameter",
910 self.__current_path,
911 "YAML files (*.yaml);;All files (*)")
912 if fileName:
913 self.__current_path = os.path.dirname(fileName)
914 nm.settings().current_dialog_path = os.path.dirname(fileName)
915 with open(fileName, 'r') as f:
916
917 self.content.set_values(yaml.load(f.read()))
918 except Exception as e:
919 import traceback
920 print traceback.format_exc(1)
921 WarningMessageBox(QtGui.QMessageBox.Warning, "Load parameter Error",
922 'Error while load parameter',
923 str(e)).exec_()
924
925
926
927
928
929
931 self.setResult(QtGui.QDialog.Accepted)
932 self.accepted.emit()
933 if self.isModal():
934 self.hide()
935
937 self.setResult(QtGui.QDialog.Rejected)
938 self.rejected.emit()
939 self.hide()
940
943
945 '''
946 Test the open files for changes and save this if needed.
947 '''
948 self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
949 QtGui.QDialog.closeEvent(self, event)
950
954 '''
955 This dialog is an extension to the L{ParameterDialog}. The parameter and their
956 values are requested from the ROS master parameter server. The requests are
957 threaded and allows the also threaded changed of ROS parameter assigned to
958 given namespace.
959 '''
960
961 - def __init__(self, masteruri, ns='/', parent=None):
962 '''
963 @param masteruri: if the master uri is not None, the parameter are retrieved from ROS parameter server.
964 @type masteruri: C{str}
965 @param ns: namespace of the parameter retrieved from the ROS parameter server.
966 @type ns: C{str}
967 '''
968 ParameterDialog.__init__(self, dict(), parent=parent)
969 self.masteruri = masteruri
970 self.ns = ns
971 self.is_delivered = False
972 self.is_send = False
973 self.mIcon = QtGui.QIcon(":/icons/default_cfg.png")
974 self.setWindowIcon(self.mIcon)
975 self.resize(450,300)
976 self.add_new_button = QtGui.QPushButton()
977 self.add_new_button.setIcon(QtGui.QIcon(':/icons/crystal_clear_add.png'))
978 self.add_new_button.clicked.connect(self._on_add_parameter)
979 self.add_new_button.setToolTip('Adds a new parameter to the list')
980 self.add_new_button.setFlat(True)
981 self.buttonBox.addButton(self.add_new_button, QtGui.QDialogButtonBox.ActionRole)
982 self.showLoadSaveButtons()
983
984
985
986
987 self.setText(' '.join(['Obtaining parameters from the parameter server', masteruri, '...']))
988 self.parameterHandler = ParameterHandler()
989 self.parameterHandler.parameter_list_signal.connect(self._on_param_list)
990 self.parameterHandler.parameter_values_signal.connect(self._on_param_values)
991 self.parameterHandler.delivery_result_signal.connect(self._on_delivered_values)
992 self.parameterHandler.requestParameterList(masteruri, ns)
993
994
996 if not self.masteruri is None and not self.is_send:
997 try:
998 params = self.getKeywords(True)
999 params = self.keywords2params(params)
1000 ros_params = dict()
1001 for p,v in params.items():
1002 rospy.logdebug("updated parameter: %s, %s, %s", p, unicode(v), type(v))
1003 ros_params[roslib.names.ns_join(self.ns, p)] = v
1004 if ros_params:
1005 self.is_send = True
1006 self.setText('Sends parameters to the server...')
1007 self.parameterHandler.deliverParameter(self.masteruri, ros_params)
1008 else:
1009 self.close()
1010 except Exception, e:
1011 import traceback
1012 print traceback.format_exc(1)
1013 QtGui.QMessageBox.warning(self, self.tr("Warning"), str(e), QtGui.QMessageBox.Ok)
1014 elif self.masteruri is None:
1015 QtGui.QMessageBox.warning(self, self.tr("Error"), 'Invalid ROS master URI', QtGui.QMessageBox.Ok)
1016
1017
1018
1019
1020
1022 params_arg = {'namespace' : ('string', self.ns), 'name' : ('string', ''), 'type' : ('string', ['string', 'int', 'float', 'bool', 'list']), 'value' : ('string', '') }
1023 dia = ParameterDialog(params_arg)
1024 dia.setWindowTitle('Add new parameter')
1025 dia.resize(360,150)
1026 dia.setFilterVisible(False)
1027 if dia.exec_():
1028 try:
1029 params = dia.getKeywords()
1030 if params['name']:
1031 if params['type'] == 'int':
1032 value = int(params['value'])
1033 elif params['type'] == 'float':
1034 value = float(params['value'])
1035 elif params['type'] == 'bool':
1036 value = str2bool(params['value'])
1037 elif params['type'] == 'list':
1038 try:
1039 import yaml
1040 value = yaml.load("[%s]"%params['value'])
1041
1042
1043
1044 if value is None:
1045 value = []
1046 except yaml.MarkedYAMLError, e:
1047 QtGui.QMessageBox.warning(self, self.tr("Warning"), "yaml error: %s"%str(e), QtGui.QMessageBox.Ok)
1048 else:
1049 value = params['value']
1050 self._on_param_values(self.masteruri, 1, '', {roslib.names.ns_join(params['namespace'], params['name']) : (1, '', value)})
1051 else:
1052 QtGui.QMessageBox.warning(self, self.tr("Warning"), 'Empty name is not valid!', QtGui.QMessageBox.Ok)
1053 except ValueError, e:
1054 import traceback
1055 print traceback.format_exc(1)
1056 QtGui.QMessageBox.warning(self, self.tr("Warning"), unicode(e), QtGui.QMessageBox.Ok)
1057
1059 '''
1060 @param masteruri: The URI of the ROS parameter server
1061 @type masteruri: C{str}
1062 @param code: The return code of the request. If not 1, the message is set and the list can be ignored.
1063 @type code: C{int}
1064 @param msg: The message of the result.
1065 @type msg: C{str}
1066 @param params: The list the parameter names.
1067 @type param: C{[str]}
1068 '''
1069 if code == 1:
1070 params.sort()
1071 self.parameterHandler.requestParameterValues(masteruri, params)
1072 else:
1073 self.setText(msg)
1074
1076 '''
1077 @param masteruri: The URI of the ROS parameter server
1078 @type masteruri: C{str}
1079 @param code: The return code of the request. If not 1, the message is set and the list can be ignored.
1080 @type code: C{int}
1081 @param msg: The message of the result.
1082 @type msg: C{str}
1083 @param params: The dictionary the parameter names and request result.
1084 @type param: C{dict(paramName : (code, statusMessage, parameterValue))}
1085 '''
1086 if code == 1:
1087 dia_params = dict()
1088 for p, (code_n, _, val) in params.items():
1089 if code_n != 1:
1090 val = ''
1091 type_str = 'string'
1092 value = unicode(val)
1093 if isinstance(val, bool):
1094 type_str = 'bool'
1095 elif isinstance(val, int):
1096 type_str = 'int'
1097 elif isinstance(val, float):
1098 type_str = 'float'
1099 elif isinstance(val, list) or isinstance(val, dict):
1100
1101 type_str = '[]'
1102 value = ''
1103 for v in val:
1104 if len(value) > 0:
1105 value = value + ', '
1106 value = value + unicode(v)
1107 elif isinstance(val, Binary):
1108 type_str = 'binary'
1109 param = p.replace(self.ns, '')
1110 names_sep = param.split(roslib.names.SEP)
1111 param_name = names_sep.pop()
1112 if names_sep:
1113 group = dia_params
1114 for n in names_sep:
1115 group_name = n
1116 if group.has_key(group_name):
1117 group = group[group_name][1]
1118 else:
1119 tmp_dict = dict()
1120 group[group_name] = ('list', tmp_dict)
1121 group = tmp_dict
1122 group[param_name] = (type_str, [value])
1123 else:
1124 dia_params[param_name] = (type_str, [value])
1125 try:
1126 self.content.createFieldFromValue(dia_params)
1127 self.setInfoActive(False)
1128 except Exception, e:
1129 import traceback
1130 print traceback.format_exc(1)
1131 QtGui.QMessageBox.warning(self, self.tr("Warning"), unicode(e), QtGui.QMessageBox.Ok)
1132 else:
1133 self.setText(msg)
1134
1136 '''
1137 @param masteruri: The URI of the ROS parameter server
1138 @type masteruri: C{str}
1139 @param code: The return code of the request. If not 1, the message is set and the list can be ignored.
1140 @type code: C{int}
1141 @param msg: The message of the result.
1142 @type msg: C{str}
1143 @param params: The dictionary the parameter names and request result.
1144 @type param: C{dict(paramName : (code, statusMessage, parameterValue))}
1145 '''
1146 self.is_delivered = True
1147 errmsg = ''
1148 if code == 1:
1149 for _, (code_n, msg, _) in params.items():
1150 if code_n != 1:
1151 errmsg = '\n'.join([errmsg, msg])
1152 else:
1153 errmsg = msg if msg else 'Unknown error on set parameter'
1154 if errmsg:
1155 import traceback
1156 print traceback.format_exc(1)
1157 QtGui.QMessageBox.warning(self, self.tr("Warning"), errmsg, QtGui.QMessageBox.Ok)
1158 self.is_delivered = False
1159 self.is_send = False
1160 self.setInfoActive(False)
1161 if self.is_delivered:
1162 self.close()
1163
1167 '''
1168 Adds a support for calling a service to the L{ParameterDialog}. The needed
1169 input fields are created from the service request message type. The service
1170 call is executed in a thread to avoid blocking GUI.
1171 '''
1172 service_resp_signal = QtCore.Signal(str, str)
1173
1174 - def __init__(self, service, parent=None):
1175 '''
1176 @param service: Service to call.
1177 @type service: L{ServiceInfo}
1178 '''
1179 self.service = service
1180 slots = service.get_service_class(True)._request_class.__slots__
1181 types = service.get_service_class()._request_class._slot_types
1182 ParameterDialog.__init__(self, self._params_from_slots(slots, types), buttons=QtGui.QDialogButtonBox.Close, parent=parent)
1183 self.setWindowTitle(''.join(['Call ', service.name]))
1184 self.service_resp_signal.connect(self._handle_resp)
1185 self.resize(450,300)
1186 if not slots:
1187 self.setText(''.join(['Wait for response ...']))
1188 thread = threading.Thread(target=self._callService)
1189 thread.setDaemon(True)
1190 thread.start()
1191 else:
1192 self.call_service_button = QtGui.QPushButton(self.tr("&Call"))
1193 self.call_service_button.clicked.connect(self._on_call_service)
1194 self.buttonBox.addButton(self.call_service_button, QtGui.QDialogButtonBox.ActionRole)
1195 self.hide_button = QtGui.QPushButton(self.tr("&Hide/Show output"))
1196 self.hide_button.clicked.connect(self._on_hide_output)
1197 self.buttonBox.addButton(self.hide_button, QtGui.QDialogButtonBox.ActionRole)
1198 self.hide_button.setVisible(False)
1199 self.showLoadSaveButtons()
1200
1203
1205 try:
1206 self.hide_button.setVisible(True)
1207 params = self.getKeywords()
1208 self.setText(''.join(['Wait for response ...']))
1209 thread = threading.Thread(target=self._callService, args=((params,)))
1210 thread.setDaemon(True)
1211 thread.start()
1212 except Exception, e:
1213 rospy.logwarn("Error while reading parameter for %s service: %s", str(self.service.name), unicode(e))
1214 self.setText(''.join(['Error while reading parameter:\n', unicode(e)]))
1215
1217 req = unicode(params) if params else ''
1218 try:
1219 req, resp = nm.starter().callService(self.service.uri, self.service.name, self.service.get_service_class(), [params])
1220 self.service_resp_signal.emit(str(req), str(resp))
1221 except Exception, e:
1222 import traceback
1223 print traceback.format_exc(1)
1224 rospy.logwarn("Error while call service '%s': %s", str(self.service.name), str(e))
1225 self.service_resp_signal.emit(unicode(req), unicode(e))
1226
1227 @classmethod
1229 result = dict()
1230 for slot, msg_type in zip(slots, types):
1231 base_type, is_array, _ = roslib.msgs.parse_type(msg_type)
1232 if base_type in roslib.msgs.PRIMITIVE_TYPES or base_type in ['time', 'duration']:
1233 default_value = 'now' if base_type in ['time', 'duration'] else ''
1234 if slot in values and values[slot]:
1235 default_value = values[slot]
1236 result[slot] = (msg_type, default_value)
1237 else:
1238 try:
1239 list_msg_class = roslib.message.get_message_class(base_type)
1240 subresult = cls._params_from_slots(list_msg_class.__slots__, list_msg_class._slot_types, values[slot] if slot in values and values[slot] else {})
1241 result[slot] = (msg_type, [subresult] if is_array else subresult)
1242 except ValueError, e:
1243 import traceback
1244 print traceback.format_exc(1)
1245 rospy.logwarn("Error while parse message type '%s': %s", str(msg_type), str(e))
1246 return result
1247
1249 self.setWindowTitle(''.join(['Request / Response of ', self.service.name]))
1250 self.setText('\n'.join([unicode(req), '---', unicode(resp)]))
1251