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 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 
48 49 -def str2bool(v):
50 return v.lower() in ("yes", "true", "t", "1")
51
52 -class MyComboBox(QtGui.QComboBox):
53 54 remove_item_signal = QtCore.Signal(str) 55
56 - def __init__(self, parent=None):
57 QtGui.QComboBox.__init__(self, parent=parent) 58 self.parameter_description = None
59
60 - def keyPressEvent(self, event):
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
76 -class ParameterDescription(object):
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
97 - def __repr__(self):
98 return ''.join([self._name, ' [', self._type, ']'])
99
100 - def origin_value(self):
101 return self._value_org
102
103 - def changed(self):
104 return unicode(self.origin_value()) != unicode(self._value)
105
106 - def name(self):
107 return self._name
108
109 - def setWidget(self, widget):
110 self._widget = widget 111 if not widget is None: 112 widget.parameter_description = self 113 self.addCachedValuesToWidget()
114
115 - def widget(self):
116 return self._widget
117
118 - def fullName(self):
119 result = self.name() 120 widget = self._widget 121 while not widget is None: 122 if isinstance(widget, (MainBox, GroupBox, ArrayBox)): 123 result = roslib.names.ns_join(widget.name, result) 124 widget = widget.parent() 125 return result
126
127 - def isArrayType(self):
128 # handle representation of `rosparam` 129 return self._is_array_type or self._type in ['[]']
130
131 - def arrayLength(self):
132 return self._array_length
133
134 - def isPrimitiveType(self):
135 result = self._base_type in roslib.msgs.PRIMITIVE_TYPES 136 result = result or self._base_type in ['int', 'float', 'time', 'duration', 'binary'] 137 # if value is a string, the list is represented as a string, see `rosparam` 138 result = result or self._type in ['[]'] 139 return result
140
141 - def isTimeType(self):
142 return self._base_type in ['time', 'duration']
143
144 - def isBinaryType(self):
145 return self._base_type in ['binary']
146
147 - def baseType(self):
148 return self._base_type
149
150 - def updateValueFromField(self):
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
161 - def updateValue(self, value):
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 # self._value = map(str, value)#[ s.encode(sys.getfilesystemencoding()) for s in value] 178 try: 179 import yaml 180 self._value = yaml.load("[%s]"%value) 181 # if there is no YAML, load() will return an 182 # empty string. We want an empty dictionary instead 183 # for our representation of empty. 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
240 - def value(self):
241 if not self.isPrimitiveType() and not self.widget() is None: 242 return self.widget().value() 243 elif self.isPrimitiveType(): 244 self.updateValueFromField() 245 if self.isTimeType() and self._value == 'now': 246 # FIX: rostopic does not support 'now' values in sub-headers 247 t = time.time() 248 return ({'secs': int(t), 'nsecs': int((t-int(t))*1000000)}, self.changed()) 249 return (self._value, self.changed())
250
251 - def removeCachedValue(self, value):
253
254 - def createTypedWidget(self, parent):
255 result = None 256 if self.isPrimitiveType(): 257 value = self._value 258 if 'bool' in self.baseType(): 259 result = QtGui.QCheckBox(parent=parent) 260 result.setObjectName(self.name()) 261 if not isinstance(value, bool): 262 value = str2bool(value[0] if isinstance(value, list) else value) 263 self._value_org = value 264 result.setChecked(value) 265 else: 266 result = MyComboBox(parent=parent) 267 result.setObjectName(self.name()) 268 result.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)) 269 result.setEditable(True) 270 result.remove_item_signal.connect(self.removeCachedValue) 271 items = [] 272 if isinstance(value, list): 273 items[len(items):] = value 274 else: 275 if not value is None and value: 276 items.append(unicode(value) if not isinstance(value, Binary) else '{binary data!!! updates will be ignored!!!}') 277 elif self.isTimeType(): 278 items.append('now') 279 self._value_org = items[0] if items else '' 280 result.addItems(items) 281 else: 282 if self.isArrayType(): 283 result = ArrayBox(self.name(), self._type, parent=parent) 284 else: 285 result = GroupBox(self.name(), self._type, parent=parent) 286 return result
287
288 - def addCachedValuesToWidget(self):
289 if isinstance(self.widget(), QtGui.QComboBox): 290 values = nm.history().cachedParamValues(self.fullName()) 291 for i in range(self.widget().count()): 292 try: 293 values.remove(self.widget().itemText(i)) 294 except: 295 pass 296 if self.widget().count() == 0: 297 values.insert(0, '') 298 self.widget().addItems(values)
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
352 - def _on_hide_clicked(self):
353 self.setCollapsed(not self.collapsed)
354 # self.param_widget.setVisible(not self.param_widget.isVisible()) 355 # vis = self.param_widget.isVisible() 356 # self.hide_button.setText('-' if vis else '+') 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 # sort the items: 1. header, 2. all premitives (sorted), 3. list, dict (sorted) 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 # create widgets 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
406 - def value(self):
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 #avoid ' or " that escapes the string values 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 # hide group, if no parameter are visible 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 # set the parent group visible if it is not visible 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
492 493 494 -class GroupBox(MainBox):
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):
500 MainBox.__init__(self, name, param_type, True, parent) 501 self.setObjectName(name)
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 # QtGui.QFrame.__init__(self, parent) 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 # boxLayout = QtGui.QFormLayout() 518 # boxLayout.setVerticalSpacing(0) 519 # label = QtGui.QLabel(''.join(['[', str(index), ']'])) 520 # self.param_widget.layout().addRow(label) 521 # self.setLayout(boxLayout) 522
523 - def value(self):
524 result = dict() 525 for param in self.params: 526 result[param.name()] = param.value() 527 return result
528
529 530 -class ArrayBox(MainBox):
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
540 - def addDynamicBox(self):
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
553 - def _on_add_dynamic_entry(self):
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
567 - def _on_rem_dynamic_entry(self):
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 # remove the referenced parameter, too 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
590 - def createFieldFromValue(self, value):
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
599 - def value(self):
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
610 - def set_values(self, values):
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 #determine the count of existing elements 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 # create the list of the elements of the length of values 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 # set the values 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
636 -class ScrollArea(QtGui.QScrollArea):
637 ''' 638 ScrollArea provides the maximal width of the internal widget. 639 ''' 640
641 - def viewportEvent(self, arg):
642 if self.widget() and self.viewport().size().width() != self.widget().maximumWidth(): 643 self.widget().setMaximumWidth(self.viewport().size().width()) 644 return QtGui.QScrollArea.viewportEvent(self, arg)
645
646 647 648 -class ParameterDialog(QtGui.QDialog):
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 # add filter row 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 # create area for the parameter 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 # add info text field 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 # create buttons 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 # add side bar for checklist 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 # set the input fields 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 # print '=============== create', self.objectName() 751 #
752 - def __del__(self):
753 # print "************ destroy", self.objectName() 754 self.content.removeAllFields()
755
756 - def _on_sidebar_stateChanged(self, state):
757 if state == QtCore.Qt.Checked: 758 self._sidebar_selected += 1 759 elif state == QtCore.Qt.Unchecked: 760 self._sidebar_selected -= 1 761 if self._sidebar_selected in [0, 1]: 762 try: 763 field = self.content.getField(self.sidebar_frame.objectName()) 764 if not field is None and field.currentText() == self.sidebar_default_val: 765 field.setEnabled(True if self._sidebar_selected == 0 else False) 766 except: 767 pass
768
769 - def showLoadSaveButtons(self):
770 self.load_button = QtGui.QPushButton() 771 self.load_button.setIcon(QtGui.QIcon(':/icons/load.png')) 772 self.load_button.clicked.connect(self._load_parameter) 773 self.load_button.setToolTip('Load parameters from YAML file') 774 self.load_button.setFlat(True) 775 self.buttonBox.addButton(self.load_button, QtGui.QDialogButtonBox.ActionRole) 776 self.save_button = QtGui.QPushButton() 777 self.save_button.clicked.connect(self._save_parameter) 778 self.save_button.setIcon(QtGui.QIcon(':/icons/save.png')) 779 self.save_button.setToolTip('Save parameters to YAML file') 780 self.save_button.setFlat(True) 781 self.buttonBox.addButton(self.save_button, QtGui.QDialogButtonBox.ActionRole)
782
783 - def _on_filter_changed(self):
784 self.content.filter(self.filter_field.text())
785
786 - def setFilterVisible(self, val):
787 ''' 788 Shows or hides the filter row. 789 ''' 790 self.filter_visible = val 791 self.filter_frame.setVisible(val&self.scrollArea.isHidden())
792
793 - def add_warning(self, message):
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
808 - def setInfoActive(self, val):
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
825 - def setFocusField(self, field_label):
826 field = self.content.getField(field_label) 827 if not field is None: 828 field.setFocus()
829
830 - def getKeywords(self, only_changed=False):
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 # get the results of sidebar 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 # add the sidebar results 847 if sidebar_name in result_value: 848 # skip the default value, if elements are selected in the side_bar 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)#_:=changed 852 result = self._remove_unchanged_parameter(result_value, only_changed) 853 return result
854
855 - def keywords2params(self, keywords):
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
871 - def _remove_unchanged_parameter(self, params, only_changed):
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
885 - def _save_parameter(self):
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
905 - def _load_parameter(self):
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 # print yaml.load(f.read()) 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 #%%%%%%%%%%%%%%%%%% close handling %%%%%%%%%%%%%%%%%%%%% 928 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 929
930 - def accept(self):
931 self.setResult(QtGui.QDialog.Accepted) 932 self.accepted.emit() 933 if self.isModal(): 934 self.hide()
935
936 - def reject(self):
937 self.setResult(QtGui.QDialog.Rejected) 938 self.rejected.emit() 939 self.hide()
940
941 - def hideEvent(self, event):
942 self.close()
943
944 - def closeEvent (self, event):
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
951 952 953 -class MasterParameterDialog(ParameterDialog):
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 # self.apply_button = QtGui.QPushButton(self.tr("&Ok")) 984 # self.apply_button.clicked.connect(self._on_apply) 985 # self.buttonBox.addButton(self.apply_button, QtGui.QDialogButtonBox.ApplyRole) 986 # self.buttonBox.accepted.connect(self._on_apply) 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 # self.apply_button.setFocus(QtCore.Qt.OtherFocusReason) 994
995 - def accept(self):
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 #%%%%%%%%%%%%%%%%%% ROS parameter handling %%%%%%%%%%%%%%%%%%%%% 1019 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1020
1021 - def _on_add_parameter(self):
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 # if there is no YAML, load() will return an 1042 # empty string. We want an empty dictionary instead 1043 # for our representation of empty. 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
1058 - def _on_param_list(self, masteruri, code, msg, params):
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
1075 - def _on_param_values(self, masteruri, code, msg, params):
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():#_:=msg_n 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 # handle representation of `rosparam` 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
1135 - def _on_delivered_values(self, masteruri, code, msg, params):
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():#_:=param, val 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
1164 1165 1166 -class ServiceDialog(ParameterDialog):
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
1201 - def _on_hide_output(self):
1202 self.setInfoActive(not self.info_field.isVisible())
1203
1204 - def _on_call_service(self):
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
1216 - def _callService(self, params={}):
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
1228 - def _params_from_slots(cls, slots, types, values={}):
1229 result = dict() 1230 for slot, msg_type in zip(slots, types): 1231 base_type, is_array, _ = roslib.msgs.parse_type(msg_type)#_:=array_length 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
1248 - def _handle_resp(self, req, resp):
1249 self.setWindowTitle(''.join(['Request / Response of ', self.service.name])) 1250 self.setText('\n'.join([unicode(req), '---', unicode(resp)]))
1251