Package node_manager_fkie :: Module parameter_dialog
[frames] | no frames]

Source Code for Module node_manager_fkie.parameter_dialog

   1  # Software License Agreement (BSD License) 
   2  # 
   3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
   4  # All rights reserved. 
   5  # 
   6  # Redistribution and use in source and binary forms, with or without 
   7  # modification, are permitted provided that the following conditions 
   8  # are met: 
   9  # 
  10  #  * Redistributions of source code must retain the above copyright 
  11  #    notice, this list of conditions and the following disclaimer. 
  12  #  * Redistributions in binary form must reproduce the above 
  13  #    copyright notice, this list of conditions and the following 
  14  #    disclaimer in the documentation and/or other materials provided 
  15  #    with the distribution. 
  16  #  * Neither the name of Fraunhofer nor the names of its 
  17  #    contributors may be used to endorse or promote products derived 
  18  #    from this software without specific prior written permission. 
  19  # 
  20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
  21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
  22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
  23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
  24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
  25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
  26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
  27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
  28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
  29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
  30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  31  # POSSIBILITY OF SUCH DAMAGE. 
  32   
  33  from python_qt_binding.QtCore import Qt, Signal 
  34  from python_qt_binding.QtGui import QBrush, QColor, QIcon, QPalette 
  35  from xmlrpclib import Binary 
  36  import os 
  37  import roslib.msgs 
  38  import roslib.names 
  39  import rospy 
  40  import sys 
  41  import threading 
  42   
  43  from node_manager_fkie.detailed_msg_box import WarningMessageBox 
  44  from node_manager_fkie.editor.line_edit import EnchancedLineEdit 
  45  from node_manager_fkie.parameter_handler import ParameterHandler 
  46   
  47  import node_manager_fkie as nm 
  48  try: 
  49      from python_qt_binding.QtGui import QApplication, QComboBox, QCheckBox, QLineEdit, QMessageBox, QScrollArea, QWidget 
  50      from python_qt_binding.QtGui import QFormLayout, QHBoxLayout, QVBoxLayout, QSpacerItem, QSizePolicy 
  51      from python_qt_binding.QtGui import QFrame, QDialog, QDialogButtonBox, QFileDialog, QLabel, QPushButton, QTextEdit 
  52  except: 
  53      from python_qt_binding.QtWidgets import QApplication, QComboBox, QCheckBox, QLineEdit, QMessageBox, QScrollArea, QWidget 
  54      from python_qt_binding.QtWidgets import QFormLayout, QHBoxLayout, QVBoxLayout, QSpacerItem, QSizePolicy 
  55      from python_qt_binding.QtWidgets import QFrame, QDialog, QDialogButtonBox, QFileDialog, QLabel, QPushButton, QTextEdit 
56 57 58 -def str2bool(val):
59 return val.lower() in ("yes", "true", "t", "1")
60
61 62 -class MyComboBox(QComboBox):
63 64 remove_item_signal = Signal(str) 65
66 - def __init__(self, parent=None):
67 QComboBox.__init__(self, parent=parent) 68 self.parameter_description = None
69
70 - def keyPressEvent(self, event):
71 key_mod = QApplication.keyboardModifiers() 72 if key_mod & Qt.ShiftModifier and (event.key() == Qt.Key_Delete): 73 try: 74 curr_text = self.currentText() 75 if curr_text: 76 for i in range(self.count()): 77 if curr_text == self.itemText(i): 78 self.removeItem(i) 79 self.remove_item_signal.emit(curr_text) 80 self.clearEditText() 81 except: 82 import traceback 83 print traceback.format_exc(1) 84 QComboBox.keyPressEvent(self, event)
85
86 87 -class ParameterDescription(object):
88 ''' 89 Used for internal representation of the parameter in dialog. 90 ''' 91
92 - def __init__(self, name, msg_type, value=None, widget=None):
93 self._name = str(name) 94 self._type = msg_type 95 if isinstance(self._type, dict): 96 self._type = 'dict' 97 elif isinstance(self._type, list): 98 self._type = 'list' 99 self._value = value 100 self._value_org = value 101 self._widget = widget 102 try: 103 self._base_type, self._is_array_type, self._array_length = roslib.msgs.parse_type(self._type) 104 except: 105 pass 106 if msg_type == 'binary': 107 self._base_type = msg_type
108
109 - def __repr__(self):
110 return ''.join([self._name, ' [', self._type, ']'])
111
112 - def origin_value(self):
113 return self._value_org
114
115 - def changed(self):
116 return unicode(self.origin_value()) != unicode(self._value)
117
118 - def name(self):
119 return self._name
120
121 - def setWidget(self, widget):
122 self._widget = widget 123 if widget is not None: 124 widget.parameter_description = self 125 self.addCachedValuesToWidget()
126
127 - def widget(self):
128 return self._widget
129
130 - def fullName(self):
131 result = self.name() 132 widget = self._widget 133 while widget is not None: 134 if isinstance(widget, (MainBox, GroupBox, ArrayBox)): 135 result = roslib.names.ns_join(widget.name, result) 136 widget = widget.parent() 137 return result
138
139 - def isArrayType(self):
140 # handle representation of `rosparam` 141 return self._is_array_type or self._type in ['[]']
142
143 - def arrayLength(self):
144 return self._array_length
145
146 - def isPrimitiveType(self):
147 result = self._base_type in roslib.msgs.PRIMITIVE_TYPES 148 result = result or self._base_type in ['int', 'float', 'time', 'duration', 'binary'] 149 # if value is a string, the list is represented as a string, see `rosparam` 150 result = result or self._type in ['[]'] 151 return result
152
153 - def isTimeType(self):
154 return self._base_type in ['time', 'duration']
155
156 - def isBinaryType(self):
157 return self._base_type in ['binary']
158
159 - def baseType(self):
160 return self._base_type
161
162 - def updateValueFromField(self):
163 field = self.widget() 164 result = '' 165 if isinstance(field, QCheckBox): 166 result = repr(field.isChecked()) 167 elif isinstance(field, QLineEdit): 168 result = field.text() 169 elif isinstance(field, QComboBox): 170 result = field.currentText() 171 self.updateValue(result)
172
173 - def updateValue(self, value):
174 try: 175 if isinstance(value, (dict, list)): 176 self._value = value 177 elif value: 178 nm.history().addParamCache(self.fullName(), value) 179 if self.isArrayType(): 180 if 'int' in self.baseType() or 'byte' in self.baseType(): 181 self._value = map(int, value.lstrip('[').rstrip(']').split(',')) 182 elif 'float' in self.baseType(): 183 self._value = map(float, value.lstrip('[').rstrip(']').split(',')) 184 elif 'bool' in self.baseType(): 185 self._value = map(str2bool, value.lstrip('[').rstrip(']').split(',')) 186 elif self.isBinaryType(): 187 self._value = value 188 else: 189 try: 190 import yaml 191 self._value = yaml.load("[%s]" % value) 192 # if there is no YAML, load() will return an 193 # empty string. We want an empty dictionary instead 194 # for our representation of empty. 195 if self._value is None: 196 self._value = [] 197 except yaml.MarkedYAMLError, e: 198 raise Exception("Field [%s] yaml error: %s" % (self.fullName(), str(e))) 199 if not self.arrayLength() is None and self.arrayLength() != len(self._value): 200 raise Exception(''.join(["Field [", self.fullName(), "] has incorrect number of elements: ", str(len(self._value)), " != ", str(self.arrayLength())])) 201 else: 202 if 'int' in self.baseType() or 'byte' in self.baseType(): 203 self._value = int(value) 204 elif 'float' in self.baseType(): 205 self._value = float(value) 206 elif 'bool' in self.baseType(): 207 if isinstance(value, bool): 208 self._value = value 209 else: 210 self._value = str2bool(value) 211 elif self.isBinaryType(): 212 self._value = unicode(value) 213 elif self.isTimeType(): 214 if value == 'now': 215 self._value = 'now' 216 else: 217 try: 218 val = eval(value) 219 if isinstance(val, dict): 220 self._value = val 221 else: 222 secs = int(val) 223 nsecs = int((val - secs) * 1000000000) 224 self._value = {'secs': secs, 'nsecs': nsecs} 225 except: 226 self._value = {'secs': 0, 'nsecs': 0} 227 else: 228 self._value = value.encode(sys.getfilesystemencoding()) 229 else: 230 if self.isArrayType(): 231 arr = [] 232 self._value = arr 233 else: 234 if 'int' in self.baseType() or 'byte' in self.baseType(): 235 self._value = 0 236 elif 'float' in self.baseType(): 237 self._value = 0.0 238 elif 'bool' in self.baseType(): 239 self._value = False 240 elif self.isBinaryType(): 241 self._value = unicode(value) 242 elif self.isTimeType(): 243 self._value = {'secs': 0, 'nsecs': 0} 244 else: 245 self._value = '' 246 nm.history().addParamCache(self.fullName(), value) 247 except Exception, e: 248 raise Exception(''.join(["Error while set value '", unicode(value), "' for '", self.fullName(), "': ", str(e)])) 249 return self._value
250
251 - def value(self):
252 if not self.isPrimitiveType() and not self.widget() is None: 253 return self.widget().value() 254 elif self.isPrimitiveType(): 255 self.updateValueFromField() 256 # if self.isTimeType() and self._value == 'now': 257 # # FIX: rostopic does not support 'now' values in sub-headers 258 # t = time.time() 259 # return ({'secs': int(t), 'nsecs': int((t-int(t))*1000000)}, self.changed()) 260 return (self._value, self.changed())
261
262 - def removeCachedValue(self, value):
264
265 - def createTypedWidget(self, parent):
266 result = None 267 if self.isPrimitiveType(): 268 value = self._value 269 if 'bool' in self.baseType(): 270 result = QCheckBox(parent=parent) 271 result.setObjectName(self.name()) 272 if not isinstance(value, bool): 273 value = str2bool(value[0] if isinstance(value, list) else value) 274 self._value_org = value 275 result.setChecked(value) 276 else: 277 result = MyComboBox(parent=parent) 278 result.setObjectName(self.name()) 279 result.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) 280 result.setEditable(True) 281 result.remove_item_signal.connect(self.removeCachedValue) 282 items = [] 283 if isinstance(value, list): 284 if self.isArrayType(): 285 items.append(','.join([str(val) for val in value])) 286 else: 287 items[len(items):] = value 288 else: 289 if value is not None and value: 290 items.append(unicode(value) if not isinstance(value, Binary) else '{binary data!!! updates will be ignored!!!}') 291 elif self.isTimeType(): 292 items.append('now') 293 self._value_org = items[0] if items else '' 294 result.addItems(items) 295 else: 296 if self.isArrayType(): 297 result = ArrayBox(self.name(), self._type, parent=parent) 298 else: 299 result = GroupBox(self.name(), self._type, parent=parent) 300 return result
301
302 - def addCachedValuesToWidget(self):
303 if isinstance(self.widget(), QComboBox): 304 values = nm.history().cachedParamValues(self.fullName()) 305 for i in range(self.widget().count()): 306 try: 307 values.remove(self.widget().itemText(i)) 308 except: 309 pass 310 if self.widget().count() == 0: 311 values.insert(0, '') 312 self.widget().addItems(values)
313
314 315 -class MainBox(QWidget):
316 ''' 317 Groups the parameter without visualization of the group. It is the main widget. 318 ''' 319
320 - def __init__(self, name, param_type, collapsible=True, parent=None):
321 QWidget.__init__(self, parent) 322 self.setObjectName(name) 323 self.name = name 324 self.type = param_type 325 self.params = [] 326 self.collapsed = False 327 self.parameter_description = None 328 vLayout = QVBoxLayout() 329 vLayout.setSpacing(0) 330 self.options_layout = QHBoxLayout() 331 self.param_widget = QFrame() 332 self.name_label = QLabel(name) 333 font = self.name_label.font() 334 font.setBold(True) 335 self.name_label.setFont(font) 336 self.type_label = QLabel(''.join([' (', param_type, ')'])) 337 338 if collapsible: 339 self.hide_button = QPushButton('-') 340 self.hide_button.setFlat(True) 341 self.hide_button.setMaximumSize(20, 20) 342 self.hide_button.clicked.connect(self._on_hide_clicked) 343 self.options_layout.addWidget(self.hide_button) 344 self.options_layout.addWidget(self.name_label) 345 self.options_layout.addWidget(self.type_label) 346 self.options_layout.addStretch() 347 348 vLayout.addLayout(self.options_layout) 349 350 self.param_widget.setFrameShape(QFrame.Box) 351 self.param_widget.setFrameShadow(QFrame.Raised) 352 353 boxLayout = QFormLayout() 354 boxLayout.setVerticalSpacing(0) 355 self.param_widget.setLayout(boxLayout) 356 vLayout.addWidget(self.param_widget) 357 self.setLayout(vLayout) 358 if param_type in ['std_msgs/Header']: 359 self.setCollapsed(True)
360
361 - def setCollapsed(self, value):
362 self.collapsed = value 363 self.param_widget.setVisible(not value) 364 self.hide_button.setText('+' if self.collapsed else '-')
365
366 - def _on_hide_clicked(self):
367 self.setCollapsed(not self.collapsed)
368 # self.param_widget.setVisible(not self.param_widget.isVisible()) 369 # vis = self.param_widget.isVisible() 370 # self.hide_button.setText('-' if vis else '+') 371
372 - def createFieldFromValue(self, value):
373 self.setUpdatesEnabled(False) 374 try: 375 if isinstance(value, (dict, list)): 376 self._createFieldFromDict(value) 377 finally: 378 self.setUpdatesEnabled(True)
379
380 - def _createFieldFromDict(self, value, layout=None):
381 if layout is None: 382 layout = self.param_widget.layout() 383 # sort the items: 1. header, 2. all premitives (sorted), 3. list, dict (sorted) 384 all_params = [] 385 primitives = [] 386 komplex = [] 387 for name, (_type, val) in value.items(): 388 if _type in ['std_msgs/Header']: 389 all_params.append((name, _type, val)) 390 elif isinstance(val, (dict, list)): 391 komplex.append((name, _type, val)) 392 else: 393 primitives.append((name, _type, val)) 394 all_params.extend(sorted(primitives)) 395 all_params.extend(sorted(komplex)) 396 397 # create widgets 398 for name, _type, val in all_params: 399 field = self.getField(name) 400 if field is None: 401 param_desc = ParameterDescription(name, _type, val) 402 field = param_desc.createTypedWidget(self) 403 param_desc.setWidget(field) 404 self.params.append(param_desc) 405 if isinstance(field, (GroupBox, ArrayBox)): 406 field.createFieldFromValue(val) 407 layout.addRow(field) 408 else: 409 label_name = name if _type == 'string' else ''.join([name, ' (', _type, ')']) 410 label = QLabel(label_name, self) 411 label.setObjectName(''.join([name, '_label'])) 412 label.setBuddy(field) 413 layout.addRow(label, field) 414 else: 415 if isinstance(field, (GroupBox, ArrayBox)): 416 field.createFieldFromValue(val) 417 else: 418 raise Exception(''.join(["Parameter with name '", name, "' already exists!"]))
419
420 - def value(self):
421 result = dict() 422 for param in self.params: 423 if not param.isBinaryType(): 424 result[param.name()] = param.value() 425 return result
426
427 - def set_values(self, values):
428 ''' 429 Sets the values for existing fields. 430 :param values: the dictionary with values to set. 431 :type values: dict 432 :raise Exception: on errors 433 ''' 434 if isinstance(values, dict): 435 for param, val in values.items(): 436 value = val 437 _type = 'unknown' 438 if isinstance(val, tuple): 439 (_type, value) = val 440 field = self.getField(param) 441 if field is not None: 442 if isinstance(field, (GroupBox, ArrayBox)): 443 field.set_values(value) 444 else: 445 if isinstance(field, QCheckBox): 446 if not isinstance(value, bool): 447 value = str2bool(value[0] if isinstance(value, list) else value) 448 field.setChecked(value) 449 elif isinstance(field, QLineEdit): 450 # avoid ' or " that escapes the string values 451 field.setText(', '.join([unicode(v) for v in value]) if isinstance(value, list) else unicode(value)) 452 elif isinstance(field, QComboBox): 453 field.setEditText(', '.join([unicode(v) for v in value]) if isinstance(value, list) else unicode(value)) 454 elif isinstance(values, list): 455 raise Exception("Setting 'list' values in MainBox or GroupBox not supported!!!")
456
457 - def getField(self, name, recursive=False):
458 for child in self.children(): 459 for c in child.children(): 460 if recursive and isinstance(c, MainBox): 461 result = c.getField(name, recursive=recursive) 462 if result is not None: 463 return result 464 elif c.objectName() == name: 465 return c 466 return None
467
468 - def removeAllFields(self):
469 ''' 470 Remove the references between parameter and corresponding widgets 471 (ComboBox, CheckBox, ..) and remove these widgets from layouts. 472 ''' 473 for child in self.param_widget.children(): 474 if isinstance(child, MyComboBox): 475 child.parameter_description.setWidget(None) 476 self.params.remove(child.parameter_description) 477 elif isinstance(child, MainBox): 478 child.removeAllFields() 479 self.param_widget.layout().removeWidget(child)
480
481 - def filter(self, arg):
482 ''' 483 Hide the parameter input field, which label dosn't contains the C{arg}. 484 @param arg: the filter text 485 @type arg: C{str} 486 ''' 487 result = False 488 for child in self.param_widget.children(): 489 if isinstance(child, (MainBox, GroupBox, ArrayBox)): 490 show = not arg or child.objectName().lower().find(arg.lower()) != -1 491 show = child.filter(arg) or show 492 # hide group, if no parameter are visible 493 child.setVisible(show) 494 if show: 495 child.setCollapsed(False) 496 result = True 497 elif isinstance(child, (QWidget)) and not isinstance(child, (QLabel)) and not isinstance(child, (QFrame)): 498 label = child.parentWidget().layout().labelForField(child) 499 if label is not None: 500 has_text = child.objectName().lower().find(arg.lower()) == -1 501 show = not arg or (not has_text or (hasattr(child, 'currentText') and not has_text)) 502 # set the parent group visible if it is not visible 503 if show and not child.parentWidget().isVisible(): 504 child.parentWidget().setVisible(show) 505 label.setVisible(show) 506 child.setVisible(show) 507 if show: 508 result = True 509 return result
510
511 - def setVisible(self, arg):
512 if arg and not self.parentWidget() is None and not self.parentWidget().isVisible(): 513 self.parentWidget().setVisible(arg) 514 QWidget.setVisible(self, arg)
515
516 517 -class GroupBox(MainBox):
518 ''' 519 Groups the parameter of a dictionary, struct or class using the group box for 520 visualization. 521 ''' 522
523 - def __init__(self, name, param_type, parent=None):
524 MainBox.__init__(self, name, param_type, True, parent) 525 self.setObjectName(name)
526
527 528 -class ArrayEntry(MainBox):
529 ''' 530 A part of the ArrayBox to represent the elements of a list. 531 ''' 532
533 - def __init__(self, index, param_type, parent=None):
534 MainBox.__init__(self, ''.join(['#', str(index)]), param_type, True, parent) 535 self.index = index 536 self.setObjectName(''.join(['[', str(index), ']'])) 537 self.param_widget.setFrameShape(QFrame.Box) 538 self.param_widget.setFrameShadow(QFrame.Plain) 539 self.type_label.setVisible(False)
540 # boxLayout = QFormLayout() 541 # boxLayout.setVerticalSpacing(0) 542 # label = QLabel(''.join(['[', str(index), ']'])) 543 # self.param_widget.layout().addRow(label) 544 # self.setLayout(boxLayout) 545
546 - def value(self):
547 result = dict() 548 for param in self.params: 549 result[param.name()] = param.value() 550 return result
551
552 553 -class ArrayBox(MainBox):
554 ''' 555 Groups the parameter of a list. 556 ''' 557
558 - def __init__(self, name, param_type, parent=None):
559 MainBox.__init__(self, name, param_type, True, parent) 560 self._dynamic_value = None 561 self._dynamic_widget = None 562 self._dynamic_items_count = 0
563
564 - def addDynamicBox(self):
565 self._dynamic_items_count = 0 566 addButton = QPushButton("+") 567 addButton.setMaximumSize(25, 25) 568 addButton.clicked.connect(self._on_add_dynamic_entry) 569 self.options_layout.addWidget(addButton) 570 self.count_label = QLabel('0') 571 self.options_layout.addWidget(self.count_label) 572 remButton = QPushButton("-") 573 remButton.setMaximumSize(25, 25) 574 remButton.clicked.connect(self._on_rem_dynamic_entry) 575 self.options_layout.addWidget(remButton)
576
577 - def _on_add_dynamic_entry(self):
578 self.setUpdatesEnabled(False) 579 try: 580 if self._dynamic_value is not None: 581 for v in self._dynamic_value: 582 if isinstance(v, dict): 583 entry_frame = ArrayEntry(self._dynamic_items_count, self.type) 584 self.param_widget.layout().addRow(entry_frame) 585 entry_frame._createFieldFromDict(v) 586 self._dynamic_items_count += 1 587 self.count_label.setText(str(self._dynamic_items_count)) 588 break 589 finally: 590 self.setUpdatesEnabled(True)
591
592 - def _on_rem_dynamic_entry(self):
593 if self._dynamic_items_count > 0: 594 self._dynamic_items_count -= 1 595 item = self.param_widget.layout().takeAt(self._dynamic_items_count) 596 self.param_widget.layout().removeItem(item) 597 try: 598 # remove the referenced parameter, too 599 for child in item.widget().children(): 600 if isinstance(child, MyComboBox): 601 child.parameter_description.setWidget(None) 602 self.params.remove(child.parameter_description) 603 elif isinstance(child, MainBox): 604 child.removeAllFields() 605 self.param_widget.layout().removeWidget(child) 606 child.parameter_description.setWidget(None) 607 self.params.remove(child.parameter_description) 608 item.widget().setParent(None) 609 del item 610 except: 611 import traceback 612 print traceback.format_exc(1) 613 self.count_label.setText(str(self._dynamic_items_count))
614
615 - def createFieldFromValue(self, value):
616 self.setUpdatesEnabled(False) 617 try: 618 if isinstance(value, list): 619 self.addDynamicBox() 620 self._dynamic_value = value 621 self.set_values(value) 622 finally: 623 self.setUpdatesEnabled(True)
624
625 - def value(self):
626 ''' 627 Goes through the list and creates dictionary with values of each element. 628 ''' 629 result = list() 630 for i in range(self.param_widget.layout().rowCount()): 631 item = self.param_widget.layout().itemAt(i, QFormLayout.SpanningRole) 632 if item and isinstance(item.widget(), ArrayEntry): 633 result.append(item.widget().value()) 634 return result
635
636 - def set_values(self, values):
637 ''' 638 Create a list of the elements and sets their values. 639 :param values: The list of dictionaries with parameter values 640 :type values: list 641 ''' 642 if isinstance(values, list): 643 count_entries = 0 644 # determine the count of existing elements 645 for i in range(self.param_widget.layout().rowCount()): 646 item = self.param_widget.layout().itemAt(i, QFormLayout.SpanningRole) 647 if item and isinstance(item.widget(), ArrayEntry): 648 count_entries += 1 649 # create the list of the elements of the length of values 650 if count_entries < len(values): 651 for i in range(len(values) - count_entries): 652 self._on_add_dynamic_entry() 653 elif count_entries > len(values): 654 for i in range(count_entries - len(values)): 655 self._on_rem_dynamic_entry() 656 # set the values 657 for i in range(self.param_widget.layout().rowCount()): 658 item = self.param_widget.layout().itemAt(i, QFormLayout.SpanningRole) 659 if item and isinstance(item.widget(), ArrayEntry): 660 item.widget().set_values(values[i])
661
662 663 -class ScrollArea(QScrollArea):
664 ''' 665 ScrollArea provides the maximal width of the internal widget. 666 ''' 667
668 - def viewportEvent(self, arg):
669 if self.widget() and self.viewport().size().width() != self.widget().maximumWidth(): 670 self.widget().setMaximumWidth(self.viewport().size().width()) 671 return QScrollArea.viewportEvent(self, arg)
672
673 674 -class ParameterDialog(QDialog):
675 ''' 676 This dialog creates an input mask for the given parameter and their types. 677 ''' 678
679 - def __init__(self, params=dict(), buttons=QDialogButtonBox.Cancel | QDialogButtonBox.Ok, sidebar_var='', parent=None):
680 ''' 681 Creates an input dialog. 682 @param params: a dictionary with parameter names and (type, values). 683 The C{value}, can be a primitive value, a list with values or parameter 684 dictionary to create groups. In this case the type is the name of the group. 685 @type params: C{dict(str:(str, {value, [..], dict()}))} 686 ''' 687 QDialog.__init__(self, parent=parent) 688 self.setObjectName('ParameterDialog - %s' % str(params)) 689 690 self.__current_path = nm.settings().current_dialog_path 691 self.horizontalLayout = QHBoxLayout(self) 692 self.horizontalLayout.setObjectName("horizontalLayout") 693 self.horizontalLayout.setContentsMargins(1, 1, 1, 1) 694 self.verticalLayout = QVBoxLayout() 695 self.verticalLayout.setObjectName("verticalLayout") 696 self.verticalLayout.setContentsMargins(1, 1, 1, 1) 697 # add filter row 698 self.filter_frame = QFrame(self) 699 filterLayout = QHBoxLayout(self.filter_frame) 700 filterLayout.setContentsMargins(1, 1, 1, 1) 701 label = QLabel("Filter:", self.filter_frame) 702 self.filter_field = EnchancedLineEdit(self.filter_frame) 703 filterLayout.addWidget(label) 704 filterLayout.addWidget(self.filter_field) 705 self.filter_field.textChanged.connect(self._on_filter_changed) 706 self.filter_visible = True 707 708 self.verticalLayout.addWidget(self.filter_frame) 709 710 # create area for the parameter 711 self.scrollArea = scrollArea = ScrollArea(self) 712 scrollArea.setObjectName("scrollArea") 713 scrollArea.setWidgetResizable(True) 714 self.content = MainBox('/', 'str', False, self) 715 scrollArea.setWidget(self.content) 716 self.verticalLayout.addWidget(scrollArea) 717 718 # add info text field 719 self.info_field = QTextEdit(self) 720 self.info_field.setVisible(False) 721 palette = QPalette() 722 brush = QBrush(QColor(255, 254, 242)) 723 brush.setStyle(Qt.SolidPattern) 724 palette.setBrush(QPalette.Active, QPalette.Base, brush) 725 brush = QBrush(QColor(255, 254, 242)) 726 brush.setStyle(Qt.SolidPattern) 727 palette.setBrush(QPalette.Inactive, QPalette.Base, brush) 728 brush = QBrush(QColor(244, 244, 244)) 729 brush.setStyle(Qt.SolidPattern) 730 palette.setBrush(QPalette.Disabled, QPalette.Base, brush) 731 self.info_field.setPalette(palette) 732 self.info_field.setFrameShadow(QFrame.Plain) 733 self.info_field.setReadOnly(True) 734 self.info_field.setTextInteractionFlags(Qt.LinksAccessibleByKeyboard | Qt.LinksAccessibleByMouse | Qt.TextBrowserInteraction | Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse) 735 self.info_field.setObjectName("dialog_info_field") 736 self.verticalLayout.addWidget(self.info_field) 737 738 # create buttons 739 self.buttonBox = QDialogButtonBox(self) 740 self.buttonBox.setObjectName("buttonBox") 741 self.buttonBox.setOrientation(Qt.Horizontal) 742 self.buttonBox.setStandardButtons(buttons) 743 self.buttonBox.accepted.connect(self.accept) 744 self.buttonBox.rejected.connect(self.reject) 745 self.verticalLayout.addWidget(self.buttonBox) 746 self.horizontalLayout.addLayout(self.verticalLayout) 747 748 # add side bar for checklist 749 values = nm.history().cachedParamValues('/%s' % sidebar_var) 750 self.sidebar_frame = QFrame() 751 self.sidebar_frame.setObjectName(sidebar_var) 752 sidebarframe_verticalLayout = QVBoxLayout(self.sidebar_frame) 753 sidebarframe_verticalLayout.setObjectName("sidebarframe_verticalLayout") 754 sidebarframe_verticalLayout.setContentsMargins(1, 1, 1, 1) 755 self._sidebar_selected = 0 756 if len(values) > 1 and sidebar_var in params: 757 self.horizontalLayout.addWidget(self.sidebar_frame) 758 try: 759 self.sidebar_default_val = params[sidebar_var][1] 760 except: 761 self.sidebar_default_val = '' 762 values.sort() 763 for v in values: 764 checkbox = QCheckBox(v) 765 checkbox.stateChanged.connect(self._on_sidebar_stateChanged) 766 self.sidebar_frame.layout().addWidget(checkbox) 767 self.sidebar_frame.layout().addItem(QSpacerItem(100, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) 768 # set the input fields 769 if params: 770 self.content.createFieldFromValue(params) 771 self.setInfoActive(False) 772 773 if self.filter_frame.isVisible(): 774 self.filter_field.setFocus() 775 self.setMinimumSize(350, 200)
776
777 - def __del__(self):
778 self.content.removeAllFields()
779
780 - def _on_sidebar_stateChanged(self, state):
781 if state == Qt.Checked: 782 self._sidebar_selected += 1 783 elif state == Qt.Unchecked: 784 self._sidebar_selected -= 1 785 if self._sidebar_selected in [0, 1]: 786 try: 787 field = self.content.getField(self.sidebar_frame.objectName()) 788 if field is not None and field.currentText() == self.sidebar_default_val: 789 field.setEnabled(True if self._sidebar_selected == 0 else False) 790 except: 791 pass
792
793 - def showLoadSaveButtons(self):
794 self.load_button = QPushButton() 795 self.load_button.setIcon(QIcon(':/icons/load.png')) 796 self.load_button.clicked.connect(self._load_parameter) 797 self.load_button.setToolTip('Load parameters from YAML file') 798 self.load_button.setFlat(True) 799 self.buttonBox.addButton(self.load_button, QDialogButtonBox.ActionRole) 800 self.save_button = QPushButton() 801 self.save_button.clicked.connect(self._save_parameter) 802 self.save_button.setIcon(QIcon(':/icons/save.png')) 803 self.save_button.setToolTip('Save parameters to YAML file') 804 self.save_button.setFlat(True) 805 self.buttonBox.addButton(self.save_button, QDialogButtonBox.ActionRole)
806
807 - def _on_filter_changed(self):
808 self.content.filter(self.filter_field.text())
809
810 - def setFilterVisible(self, val):
811 ''' 812 Shows or hides the filter row. 813 ''' 814 self.filter_visible = val 815 self.filter_frame.setVisible(val & self.scrollArea.isHidden())
816
817 - def add_warning(self, message):
818 label = QLabel() 819 label.setWordWrap(True) 820 label.setText(''.join(["<font color='red'>Warning!\n", message, "</font>"])) 821 self.verticalLayout.insertWidget(1, label)
822
823 - def setText(self, text):
824 ''' 825 Adds a label to the dialog's layout and shows the given text. 826 @param text: the text to add to the dialog 827 @type text: C{str} 828 ''' 829 self.info_field.setText(text) 830 self.setInfoActive(True)
831
832 - def setInfoActive(self, val):
833 ''' 834 Activates or deactivates the info field of this dialog. If info field is 835 activated, the filter frame and the input field are deactivated. 836 @type val: C{bool} 837 ''' 838 if val and self.info_field.isHidden(): 839 self.filter_frame.setVisible(False & self.filter_visible) 840 self.scrollArea.setVisible(False) 841 self.info_field.setVisible(True) 842 elif not val and self.scrollArea.isHidden(): 843 self.filter_frame.setVisible(True & self.filter_visible) 844 self.scrollArea.setVisible(True) 845 self.info_field.setVisible(False) 846 if self.filter_frame.isVisible(): 847 self.filter_field.setFocus()
848
849 - def setFocusField(self, field_label):
850 field = self.content.getField(field_label, recursive=True) 851 if field is not None: 852 field.setFocus()
853
854 - def getKeywords(self, only_changed=False):
855 ''' 856 @param only_changed: requests only changed parameter 857 @type only_changed: bool (Default: False) 858 @returns: a directory with parameter and value for all entered fields. 859 @rtype: C{dict(str(param) : str(value))} 860 ''' 861 # get the results of sidebar 862 sidebar_list = [] 863 sidebar_name = self.sidebar_frame.objectName() 864 for j in range(self.sidebar_frame.layout().count() - 1): 865 w = self.sidebar_frame.layout().itemAt(j).widget() 866 if isinstance(w, QCheckBox): 867 if w.checkState() == Qt.Checked: 868 sidebar_list.append((w.text(), True)) 869 result_value = self.content.value() 870 # add the sidebar results 871 if sidebar_name in result_value: 872 # skip the default value, if elements are selected in the side_bar 873 if len(sidebar_list) == 0 or self.sidebar_default_val != result_value[sidebar_name][0]: 874 sidebar_list.append(result_value[sidebar_name]) 875 result_value[sidebar_name] = ([v for v, _ in set(sidebar_list)], True) # _:=changed 876 result = self._remove_change_state(result_value, only_changed) 877 return result
878
879 - def keywords2params(self, keywords):
880 ''' 881 Resolves the dictionary values to ROS parameter names. 882 @param keywords: the result of the getKeywords 883 @return: dictionary of (ROS parameter name : value) 884 ''' 885 result = dict() 886 for param, value in keywords.items(): 887 if isinstance(value, dict): 888 r = self.keywords2params(value) 889 for p, v in r.items(): 890 result[roslib.names.ns_join(param, p)] = v 891 else: 892 result[param] = value 893 return result
894
895 - def _remove_change_state(self, params, only_changed):
896 result = dict() 897 for param, value in params.items(): 898 if isinstance(value, dict): 899 r = self._remove_change_state(value, only_changed) 900 if r: 901 result[param] = r 902 elif isinstance(value, list): 903 new_val = [] 904 for val in value: 905 r = self._remove_change_state(val, only_changed) 906 if r: 907 new_val.append(r) 908 if new_val: 909 result[param] = new_val 910 elif isinstance(value, tuple): 911 if value[1] or not only_changed: 912 result[param] = value[0] 913 else: 914 print "unknown parameter: should not happens", param, value 915 return result
916
917 - def _save_parameter(self):
918 try: 919 import yaml 920 (fileName, _) = QFileDialog.getSaveFileName(self, 921 "Save parameter", 922 self.__current_path, 923 "YAML files (*.yaml);;All files (*)") 924 if fileName: 925 self.__current_path = os.path.dirname(fileName) 926 nm.settings().current_dialog_path = os.path.dirname(fileName) 927 content = self._remove_change_state(self.content.value(), False) 928 text = yaml.dump(content, default_flow_style=False) 929 with open(fileName, 'w+') as f: 930 f.write(text) 931 except Exception as e: 932 import traceback 933 print traceback.format_exc(1) 934 WarningMessageBox(QMessageBox.Warning, "Save parameter Error", 935 'Error while save parameter', str(e)).exec_()
936
937 - def _load_parameter(self):
938 try: 939 import yaml 940 (fileName, _) = QFileDialog.getOpenFileName(self, "Load parameter", 941 self.__current_path, 942 "YAML files (*.yaml);;All files (*)") 943 if fileName: 944 self.__current_path = os.path.dirname(fileName) 945 nm.settings().current_dialog_path = os.path.dirname(fileName) 946 with open(fileName, 'r') as f: 947 # print yaml.load(f.read()) 948 self.content.set_values(yaml.load(f.read())) 949 except Exception as e: 950 import traceback 951 print traceback.format_exc(1) 952 WarningMessageBox(QMessageBox.Warning, "Load parameter Error", 953 'Error while load parameter', 954 str(e)).exec_()
955 956 957 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 958 # %%%%%%%%%%%%%%%%%% close handling %%%%%%%%%%%%%%%%%%%%% 959 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 960
961 - def accept(self):
962 self.setResult(QDialog.Accepted) 963 self.accepted.emit() 964 if self.isModal(): 965 self.hide()
966
967 - def reject(self):
968 self.setResult(QDialog.Rejected) 969 self.rejected.emit() 970 self.hide()
971
972 - def hideEvent(self, event):
973 self.close()
974
975 - def closeEvent(self, event):
976 ''' 977 Test the open files for changes and save this if needed. 978 ''' 979 self.setAttribute(Qt.WA_DeleteOnClose, True) 980 QDialog.closeEvent(self, event)
981
982 983 -class MasterParameterDialog(ParameterDialog):
984 ''' 985 This dialog is an extension to the L{ParameterDialog}. The parameter and their 986 values are requested from the ROS master parameter server. The requests are 987 threaded and allows the also threaded changed of ROS parameter assigned to 988 given namespace. 989 ''' 990
991 - def __init__(self, masteruri, ns='/', parent=None):
992 ''' 993 @param masteruri: if the master uri is not None, the parameter are retrieved from ROS parameter server. 994 @type masteruri: C{str} 995 @param ns: namespace of the parameter retrieved from the ROS parameter server. 996 @type ns: C{str} 997 ''' 998 ParameterDialog.__init__(self, dict(), parent=parent) 999 self.masteruri = masteruri 1000 self.ns = ns 1001 self.is_delivered = False 1002 self.is_send = False 1003 self.mIcon = QIcon(":/icons/default_cfg.png") 1004 self.setWindowIcon(self.mIcon) 1005 self.resize(450, 300) 1006 self.add_new_button = QPushButton() 1007 self.add_new_button.setIcon(QIcon(':/icons/crystal_clear_add.png')) 1008 self.add_new_button.clicked.connect(self._on_add_parameter) 1009 self.add_new_button.setToolTip('Adds a new parameter to the list') 1010 self.add_new_button.setFlat(True) 1011 self.buttonBox.addButton(self.add_new_button, QDialogButtonBox.ActionRole) 1012 self.showLoadSaveButtons() 1013 # self.apply_button = QPushButton(self.tr("&Ok")) 1014 # self.apply_button.clicked.connect(self._on_apply) 1015 # self.buttonBox.addButton(self.apply_button, QDialogButtonBox.ApplyRole) 1016 # self.buttonBox.accepted.connect(self._on_apply) 1017 self.setText(' '.join(['Obtaining parameters from the parameter server', masteruri, '...'])) 1018 self.parameterHandler = ParameterHandler() 1019 self.parameterHandler.parameter_list_signal.connect(self._on_param_list) 1020 self.parameterHandler.parameter_values_signal.connect(self._on_param_values) 1021 self.parameterHandler.delivery_result_signal.connect(self._on_delivered_values) 1022 self.parameterHandler.requestParameterList(masteruri, ns)
1023 # self.apply_button.setFocus(Qt.OtherFocusReason) 1024
1025 - def accept(self):
1026 if self.masteruri is not None and not self.is_send: 1027 try: 1028 params = self.getKeywords(True) 1029 params = self.keywords2params(params) 1030 ros_params = dict() 1031 for p, v in params.items(): 1032 rospy.logdebug("updated parameter: %s, %s, %s", p, unicode(v), type(v)) 1033 ros_params[roslib.names.ns_join(self.ns, p)] = v 1034 if ros_params: 1035 self.is_send = True 1036 self.setText('Sends parameters to the server...') 1037 self.parameterHandler.deliverParameter(self.masteruri, ros_params) 1038 else: 1039 self.close() 1040 except Exception, e: 1041 import traceback 1042 print traceback.format_exc(1) 1043 QMessageBox.warning(self, self.tr("Warning"), str(e), QMessageBox.Ok) 1044 elif self.masteruri is None: 1045 QMessageBox.warning(self, self.tr("Error"), 'Invalid ROS master URI', QMessageBox.Ok)
1046 1047 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1048 # %%%%%%%%%%%%%%%%%% ROS parameter handling %%%%%%%%%%%%%%%%%%%%% 1049 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1050
1051 - def _on_add_parameter(self):
1052 params_arg = {'namespace': ('string', self.ns), 'name': ('string', ''), 'type': ('string', ['string', 'int', 'float', 'bool', 'list']), 'value': ('string', '')} 1053 dia = ParameterDialog(params_arg) 1054 dia.setWindowTitle('Add new parameter') 1055 dia.resize(360, 150) 1056 dia.setFilterVisible(False) 1057 if dia.exec_(): 1058 try: 1059 params = dia.getKeywords() 1060 if params['name']: 1061 if params['type'] == 'int': 1062 value = int(params['value']) 1063 elif params['type'] == 'float': 1064 value = float(params['value']) 1065 elif params['type'] == 'bool': 1066 value = str2bool(params['value']) 1067 elif params['type'] == 'list': 1068 try: 1069 import yaml 1070 value = yaml.load("[%s]" % params['value']) 1071 # if there is no YAML, load() will return an 1072 # empty string. We want an empty dictionary instead 1073 # for our representation of empty. 1074 if value is None: 1075 value = [] 1076 except yaml.MarkedYAMLError, e: 1077 QMessageBox.warning(self, self.tr("Warning"), "yaml error: %s" % str(e), QMessageBox.Ok) 1078 else: 1079 value = params['value'] 1080 self._on_param_values(self.masteruri, 1, '', {roslib.names.ns_join(params['namespace'], params['name']): (1, '', value)}) 1081 else: 1082 QMessageBox.warning(self, self.tr("Warning"), 'Empty name is not valid!', QMessageBox.Ok) 1083 except ValueError, e: 1084 import traceback 1085 print traceback.format_exc(1) 1086 QMessageBox.warning(self, self.tr("Warning"), unicode(e), QMessageBox.Ok)
1087
1088 - def _on_param_list(self, masteruri, code, msg, params):
1089 ''' 1090 @param masteruri: The URI of the ROS parameter server 1091 @type masteruri: C{str} 1092 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 1093 @type code: C{int} 1094 @param msg: The message of the result. 1095 @type msg: C{str} 1096 @param params: The list the parameter names. 1097 @type params: C{[str]} 1098 ''' 1099 if code == 1: 1100 params.sort() 1101 self.parameterHandler.requestParameterValues(masteruri, params) 1102 else: 1103 self.setText(msg)
1104
1105 - def _on_param_values(self, masteruri, code, msg, params):
1106 ''' 1107 @param masteruri: The URI of the ROS parameter server 1108 @type masteruri: C{str} 1109 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 1110 @type code: C{int} 1111 @param msg: The message of the result. 1112 @type msg: C{str} 1113 @param params: The dictionary the parameter names and request result. 1114 @type params: C{dict(paramName : (code, statusMessage, parameterValue))} 1115 ''' 1116 if code == 1: 1117 dia_params = dict() 1118 for p, (code_n, _, val) in params.items(): # _:=msg_n 1119 if code_n != 1: 1120 val = '' 1121 type_str = 'string' 1122 value = unicode(val) 1123 if isinstance(val, bool): 1124 type_str = 'bool' 1125 elif isinstance(val, int): 1126 type_str = 'int' 1127 elif isinstance(val, float): 1128 type_str = 'float' 1129 elif isinstance(val, list) or isinstance(val, dict): 1130 # handle representation of `rosparam` 1131 type_str = '[]' 1132 value = '' 1133 for v in val: 1134 if len(value) > 0: 1135 value = value + ', ' 1136 value = value + unicode(v) 1137 elif isinstance(val, Binary): 1138 type_str = 'binary' 1139 param = p.replace(self.ns, '') 1140 names_sep = param.split(roslib.names.SEP) 1141 param_name = names_sep.pop() 1142 if names_sep: 1143 group = dia_params 1144 for n in names_sep: 1145 group_name = n 1146 if group_name in group: 1147 group = group[group_name][1] 1148 else: 1149 tmp_dict = dict() 1150 group[group_name] = ('list', tmp_dict) 1151 group = tmp_dict 1152 group[param_name] = (type_str, [value]) 1153 else: 1154 dia_params[param_name] = (type_str, [value]) 1155 try: 1156 self.content.createFieldFromValue(dia_params) 1157 self.setInfoActive(False) 1158 except Exception, e: 1159 import traceback 1160 print traceback.format_exc(1) 1161 QMessageBox.warning(self, self.tr("Warning"), unicode(e), QMessageBox.Ok) 1162 else: 1163 self.setText(msg)
1164
1165 - def _on_delivered_values(self, masteruri, code, msg, params):
1166 ''' 1167 @param masteruri: The URI of the ROS parameter server 1168 @type masteruri: C{str} 1169 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 1170 @type code: C{int} 1171 @param msg: The message of the result. 1172 @type msg: C{str} 1173 @param params: The dictionary the parameter names and request result. 1174 @type params: C{dict(paramName : (code, statusMessage, parameterValue))} 1175 ''' 1176 self.is_delivered = True 1177 errmsg = '' 1178 if code == 1: 1179 for _, (code_n, msg, _) in params.items(): # _:=param, val 1180 if code_n != 1: 1181 errmsg = '\n'.join([errmsg, msg]) 1182 else: 1183 errmsg = msg if msg else 'Unknown error on set parameter' 1184 if errmsg: 1185 import traceback 1186 print traceback.format_exc(1) 1187 QMessageBox.warning(self, self.tr("Warning"), errmsg, QMessageBox.Ok) 1188 self.is_delivered = False 1189 self.is_send = False 1190 self.setInfoActive(False) 1191 if self.is_delivered: 1192 self.close()
1193
1194 1195 -class ServiceDialog(ParameterDialog):
1196 ''' 1197 Adds a support for calling a service to the L{ParameterDialog}. The needed 1198 input fields are created from the service request message type. The service 1199 call is executed in a thread to avoid blocking GUI. 1200 ''' 1201 service_resp_signal = Signal(str, str) 1202
1203 - def __init__(self, service, parent=None):
1204 ''' 1205 @param service: Service to call. 1206 @type service: U{master_discovery_fkie.ServiceInfo<http://docs.ros.org/kinetic/api/master_discovery_fkie/html/modules.html#master_discovery_fkie.master_info.ServiceInfo>} 1207 ''' 1208 self.service = service 1209 slots = service.get_service_class(True)._request_class.__slots__ 1210 types = service.get_service_class()._request_class._slot_types 1211 ParameterDialog.__init__(self, self._params_from_slots(slots, types), buttons=QDialogButtonBox.Close, parent=parent) 1212 self.setWindowTitle(''.join(['Call ', service.name])) 1213 self.service_resp_signal.connect(self._handle_resp) 1214 self.resize(450, 300) 1215 if not slots: 1216 self.setText(''.join(['Wait for response ...'])) 1217 thread = threading.Thread(target=self._callService) 1218 thread.setDaemon(True) 1219 thread.start() 1220 else: 1221 self.call_service_button = QPushButton(self.tr("&Call")) 1222 self.call_service_button.clicked.connect(self._on_call_service) 1223 self.buttonBox.addButton(self.call_service_button, QDialogButtonBox.ActionRole) 1224 self.hide_button = QPushButton(self.tr("&Hide/Show output")) 1225 self.hide_button.clicked.connect(self._on_hide_output) 1226 self.buttonBox.addButton(self.hide_button, QDialogButtonBox.ActionRole) 1227 self.hide_button.setVisible(False) 1228 self.showLoadSaveButtons()
1229
1230 - def _on_hide_output(self):
1231 self.setInfoActive(not self.info_field.isVisible())
1232
1233 - def _on_call_service(self):
1234 try: 1235 self.hide_button.setVisible(True) 1236 params = self.getKeywords() 1237 self.setText(''.join(['Wait for response ...'])) 1238 thread = threading.Thread(target=self._callService, args=((params,))) 1239 thread.setDaemon(True) 1240 thread.start() 1241 except Exception, e: 1242 rospy.logwarn("Error while reading parameter for %s service: %s", str(self.service.name), unicode(e)) 1243 self.setText(''.join(['Error while reading parameter:\n', unicode(e)]))
1244
1245 - def _callService(self, params={}):
1246 req = unicode(params) if params else '' 1247 try: 1248 req, resp = nm.starter().callService(self.service.uri, self.service.name, self.service.get_service_class(), [params]) 1249 self.service_resp_signal.emit(str(req), str(resp)) 1250 except Exception, e: 1251 import traceback 1252 print traceback.format_exc(1) 1253 rospy.logwarn("Error while call service '%s': %s", str(self.service.name), str(e)) 1254 self.service_resp_signal.emit(unicode(req), unicode(e))
1255 1256 @classmethod
1257 - def _params_from_slots(cls, slots, types, values={}):
1258 result = dict() 1259 for slot, msg_type in zip(slots, types): 1260 base_type, is_array, _ = roslib.msgs.parse_type(msg_type) # _:=array_length 1261 if base_type in roslib.msgs.PRIMITIVE_TYPES or base_type in ['time', 'duration']: 1262 default_value = 'now' if base_type in ['time', 'duration'] else '' 1263 if slot in values and values[slot]: 1264 default_value = values[slot] 1265 result[slot] = (msg_type, default_value) 1266 else: 1267 try: 1268 list_msg_class = roslib.message.get_message_class(base_type) 1269 if is_array and slot in values: 1270 subresult = [] 1271 for slot_value in values[slot]: 1272 subvalue = cls._params_from_slots(list_msg_class.__slots__, list_msg_class._slot_types, slot_value if slot in values and slot_value else {}) 1273 subresult.append(subvalue) 1274 result[slot] = (msg_type, subresult) 1275 else: 1276 subresult = cls._params_from_slots(list_msg_class.__slots__, list_msg_class._slot_types, values[slot] if slot in values and values[slot] else {}) 1277 result[slot] = (msg_type, [subresult] if is_array else subresult) 1278 except ValueError, e: 1279 import traceback 1280 print traceback.format_exc(1) 1281 rospy.logwarn("Error while parse message type '%s': %s", str(msg_type), str(e)) 1282 return result
1283
1284 - def _handle_resp(self, req, resp):
1285 self.setWindowTitle(''.join(['Request / Response of ', self.service.name])) 1286 self.setText('\n'.join([unicode(req), '---', unicode(resp)]))
1287