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