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 PySide import QtCore, QtGui 
 34   
 35  import sys 
 36  import threading 
 37   
 38  import roslib 
 39  import rospy 
 40  import node_manager_fkie as nm 
 41   
 42  from parameter_handler import ParameterHandler 
43 44 -def str2bool(v):
45 return v.lower() in ("yes", "true", "t", "1")
46
47 -class MyComboBox(QtGui.QComboBox):
48 49 remove_item_signal = QtCore.Signal(str) 50
51 - def keyPressEvent(self, event):
52 key_mod = QtGui.QApplication.keyboardModifiers() 53 if key_mod & QtCore.Qt.ShiftModifier and (event.key() == QtCore.Qt.Key_Delete): 54 try: 55 if self.currentText(): 56 for i in range(self.count()): 57 if self.currentText() == self.itemText(i): 58 self.removeItem(i) 59 self.remove_item_signal.emit(self.currentText()) 60 self.clearEditText() 61 except: 62 import traceback 63 print traceback.format_exc() 64 QtGui.QComboBox.keyPressEvent(self, event)
65
66 -class ParameterDescription(object):
67 ''' 68 Used for internal representation of the parameter in dialog. 69 '''
70 - def __init__(self, name, msg_type, value=None, widget=None):
71 self._name = name 72 self._type = msg_type 73 if isinstance(self._type, dict): 74 self._type = 'dict' 75 elif isinstance(self._type, list): 76 self._type = 'list' 77 self._value = value 78 self._widget = widget 79 self._base_type, self._is_array_type, self._array_length = roslib.msgs.parse_type(self._type) 80 self._is_primitive_type = self._base_type in roslib.msgs.PRIMITIVE_TYPES or self._base_type in ['int', 'float', 'time', 'duration'] 81 self._is_time_type = self._base_type in ['time', 'duration']
82
83 - def name(self):
84 return self._name
85
86 - def setWidget(self, widget):
87 self._widget = widget 88 self.addCachedValuesToWidget()
89
90 - def widget(self):
91 return self._widget
92
93 - def fullName(self):
94 result = self.name() 95 widget = self._widget 96 while not widget is None: 97 if isinstance(widget, (MainBox, GroupBox, ArrayBox)): 98 result = roslib.names.ns_join(widget.name, result) 99 widget = widget.parent() 100 return result
101
102 - def isArrayType(self):
103 return self._is_array_type
104
105 - def arrayLength(self):
106 return self._array_length
107
108 - def isPrimitiveType(self):
109 return self._is_primitive_type
110
111 - def isTimeType(self):
112 return self._is_time_type
113
114 - def baseType(self):
115 return self._base_type
116
117 - def updateValueFromField(self):
118 field = self.widget() 119 result = '' 120 if isinstance(field, QtGui.QCheckBox): 121 result = repr(field.isChecked()) 122 elif isinstance(field, QtGui.QLineEdit): 123 result = field.text() 124 elif isinstance(field, QtGui.QComboBox): 125 result = field.currentText() 126 self.setValue(result)
127
128 - def setValue(self, value):
129 error_str = '' 130 try: 131 if isinstance(value, (dict, list)): 132 self._value = value 133 elif value: 134 nm.history().addParamCache(self.fullName(), value) 135 if self.isArrayType(): 136 if 'int' in self.baseType(): 137 self._value = map(int, value.split(',')) 138 elif 'float' in self.baseType(): 139 self._value = map(float, value.split(',')) 140 elif 'bool' in self.baseType(): 141 self._value = map(str2bool, value.split(',')) 142 else: 143 self._value = [ s.encode(sys.getfilesystemencoding()) for s in value.split(',')] 144 if not self.arrayLength() is None and self.arrayLength() != len(self._value): 145 raise Exception(''.join(["Field [", self.fullName(), "] has incorrect number of elements: ", str(len(self._value)), " != ", str(self.arrayLength())])) 146 else: 147 if 'int' in self.baseType(): 148 self._value = int(value) 149 elif 'float' in self.baseType(): 150 self._value = float(value) 151 elif 'bool' in self.baseType(): 152 if isinstance(value, bool): 153 self._value = value 154 else: 155 self._value = str2bool(value) 156 elif self.isTimeType(): 157 if value == 'now': 158 self._value = 'now' 159 else: 160 val = float(value) 161 secs = int(val) 162 nsecs = int((val - secs) * 1000000000) 163 self._value = {'secs': secs, 'nsecs': nsecs} 164 else: 165 self._value = value.encode(sys.getfilesystemencoding()) 166 else: 167 if self.isArrayType(): 168 arr = [] 169 self._value = arr 170 else: 171 if 'int' in self.baseType(): 172 self._value = 0 173 elif 'float' in self.baseType(): 174 self._value = 0.0 175 elif 'bool' in self.baseType(): 176 self._value = False 177 elif self.isTimeType(): 178 self._value = {'secs': 0, 'nsecs': 0} 179 else: 180 self._value = '' 181 nm.history().addParamCache(self.fullName(), value) 182 except Exception, e: 183 raise Exception(''.join(["Error while set value '", unicode(value), "' for '", self.fullName(), "': ", str(e)])) 184 return self._value
185
186 - def value(self):
187 return self._value
188
189 - def removeCachedValue(self, value):
191
192 - def createTypedWidget(self, parent):
193 result = None 194 if self.isPrimitiveType(): 195 if 'bool' in self.baseType(): 196 result = QtGui.QCheckBox(parent=parent) 197 result.setObjectName(self.name()) 198 value = self.value() 199 if not isinstance(value, bool): 200 value = str2bool(self.value()) 201 result.setChecked(value) 202 else: 203 result = MyComboBox(parent=parent) 204 result.setObjectName(self.name()) 205 result.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)) 206 result.setEditable(True) 207 result.remove_item_signal.connect(self.removeCachedValue) 208 items = [] 209 if isinstance(self.value(), list): 210 items[len(items):] = self.value() 211 else: 212 if not self.value() is None and self.value(): 213 items.append(unicode(self.value())) 214 elif self.isTimeType(): 215 items.append(now) 216 result.addItems(items) 217 else: 218 if self.isArrayType(): 219 result = ArrayBox(self.name(), self._type, parent=parent) 220 else: 221 result = GroupBox(self.name(), self._type, parent=parent) 222 return result
223
224 - def addCachedValuesToWidget(self):
225 if isinstance(self.widget(), QtGui.QComboBox): 226 values = nm.history().cachedParamValues(self.fullName()) 227 for i in range(self.widget().count()): 228 try: 229 values.remove(self.widget().itemText(i)) 230 except: 231 pass 232 if self.widget().count() == 0: 233 values.insert(0, '') 234 self.widget().addItems(values)
235
236 237 238 -class MainBox(QtGui.QWidget):
239 ''' 240 Groups the parameter without visualization of the group. It is the main widget. 241 '''
242 - def __init__(self, name, type, parent=None):
243 QtGui.QWidget.__init__(self, parent) 244 self.setObjectName(name) 245 self.name = name 246 self.type = type 247 self.createLayout()
248
249 - def createLayout(self):
250 boxLayout = QtGui.QFormLayout() 251 boxLayout.setVerticalSpacing(0) 252 self.setLayout(boxLayout)
253
254 - def createFieldFromValue(self, value):
255 self.setUpdatesEnabled(False) 256 try: 257 if isinstance(value, list): 258 # self.layout().addRow(QtGui.QPushButton("add to list")) 259 for v in value: 260 if isinstance(v, dict): 261 self._createFieldFromDict(v) 262 line = QtGui.QFrame() 263 line.setFrameShape(QtGui.QFrame.HLine) 264 line.setFrameShadow(QtGui.QFrame.Sunken) 265 line.setObjectName("__line__") 266 self.layout().addRow(line) 267 #@TODO add an ADD button 268 elif isinstance(value, dict): 269 self._createFieldFromDict(value) 270 finally: 271 self.setUpdatesEnabled(True)
272
273 - def _createFieldFromDict(self, value):
274 for name, (_type, val) in sorted(value.iteritems(), key=lambda (k,v): (k.lower(),v)): 275 if not hasattr(self, 'params'): 276 self.params = [] 277 field = self.getField(name) 278 if field is None: 279 param_desc = ParameterDescription(name, _type, val) 280 self.params.append(param_desc) 281 field = param_desc.createTypedWidget(self) 282 param_desc.setWidget(field) 283 if isinstance(field, (GroupBox, ArrayBox)): 284 field.createFieldFromValue(val) 285 self.layout().addRow(field) 286 else: 287 label_name = name if _type == 'string' else ''.join([name, ' (', _type, ')']) 288 label = QtGui.QLabel(label_name, self) 289 label.setObjectName(''.join([name, '_label'])) 290 label.setBuddy(field) 291 self.layout().addRow(label, field) 292 else: 293 if isinstance(field, (GroupBox, ArrayBox)): 294 field.createFieldFromValue(val) 295 else: 296 raise Exception(''.join(["Parameter with name '", name, "' already exists!"]))
297
298 - def value(self):
299 if isinstance(self, ArrayBox): 300 result = list() 301 result_dict = dict() 302 result.append(result_dict) 303 else: 304 result = result_dict = dict() 305 if hasattr(self, 'params'): 306 for param in self.params: 307 if param.isPrimitiveType(): 308 param.updateValueFromField() 309 result_dict[param.name()] = param.value() 310 elif isinstance(param.widget(), (GroupBox, GroupBox)): 311 result_dict[param.name()] = param.widget().value() 312 return result
313
314 - def getField(self, name):
315 for child in self.children(): 316 if child.objectName() == name: 317 return child 318 return None
319
320 - def filter(self, arg):
321 ''' 322 Hide the parameter input field, which label dosn't contains the C{arg}. 323 @param arg: the filter text 324 @type art: C{str} 325 ''' 326 for child in self.children(): 327 if isinstance(child, (GroupBox, ArrayBox)): 328 child.filter(arg) 329 show_group = False 330 # hide group, if no parameter are visible 331 for cchild in child.children(): 332 if isinstance(cchild, (QtGui.QWidget)) and cchild.objectName() != '__line__' and cchild.isVisible(): 333 show_group = True 334 break 335 child.setVisible(show_group) 336 elif isinstance(child, (QtGui.QWidget)) and not isinstance(child, (QtGui.QLabel)): 337 label = child.parentWidget().layout().labelForField(child) 338 if not label is None: 339 show = not (child.objectName().lower().find(arg.lower()) == -1) 340 # set the parent group visible if it is not visible 341 if show and not child.parentWidget().isVisible(): 342 child.parentWidget().setVisible(show) 343 label.setVisible(show) 344 child.setVisible(show)
345
346 - def setVisible(self, arg):
347 if arg and not self.parentWidget() is None and not self.parentWidget().isVisible(): 348 self.parentWidget().setVisible(arg) 349 QtGui.QWidget.setVisible(self, arg)
350
351 352 353 -class GroupBox(QtGui.QGroupBox, MainBox):
354 ''' 355 Groups the parameter of a dictionary, struct or class using the group box for 356 visualization. 357 '''
358 - def __init__(self, name, type, parent=None):
359 QtGui.QGroupBox.__init__(self, ''.join([name, ' (', type, ')']), parent) 360 self.setObjectName(name) 361 self.name = name 362 self.type = type 363 self.setAlignment(QtCore.Qt.AlignLeft) 364 self.createLayout()
365
366 367 368 -class ArrayBox(GroupBox):
369 ''' 370 Groups the parameter of a list. 371 '''
372 - def __init__(self, name, type, parent=None):
373 GroupBox.__init__(self, name, type, parent) 374 self.setFlat(True)
375
376 377 378 -class ScrollArea(QtGui.QScrollArea):
379 ''' 380 ScrollArea provides the maximal width of the internal widget. 381 ''' 382
383 - def viewportEvent(self, arg):
384 if self.widget() and self.viewport().size().width() != self.widget().maximumWidth(): 385 self.widget().setMaximumWidth(self.viewport().size().width()) 386 return QtGui.QScrollArea.viewportEvent(self, arg)
387
388 389 390 -class ParameterDialog(QtGui.QDialog):
391 ''' 392 This dialog creates an input mask for the given parameter and their types. 393 ''' 394
395 - def __init__(self, params=dict(), buttons=QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok, parent=None):
396 ''' 397 Creates an input dialog. 398 @param params: a dictionary with parameter names and (type, values). 399 The C{value}, can be a primitive value, a list with values or parameter 400 dictionary to create groups. In this case the type is the name of the group. 401 @type params: C{dict(str:(str, {value, [..], dict()}))} 402 ''' 403 QtGui.QDialog.__init__(self, parent=parent) 404 self.setObjectName(' - '.join(['ParameterDialog', str(params)])) 405 406 self.verticalLayout = QtGui.QVBoxLayout(self) 407 self.verticalLayout.setObjectName("verticalLayout") 408 self.verticalLayout.setContentsMargins(1, 1, 1, 1) 409 # add filter row 410 self.filter_frame = QtGui.QFrame(self) 411 filterLayout = QtGui.QHBoxLayout(self.filter_frame) 412 filterLayout.setContentsMargins(1, 1, 1, 1) 413 label = QtGui.QLabel("Filter:", self.filter_frame) 414 self.filter_field = QtGui.QLineEdit(self.filter_frame) 415 filterLayout.addWidget(label) 416 filterLayout.addWidget(self.filter_field) 417 self.filter_field.textChanged.connect(self._on_filter_changed) 418 self.filter_visible = True 419 420 self.verticalLayout.addWidget(self.filter_frame) 421 422 # create area for the parameter 423 self.scrollArea = scrollArea = ScrollArea(self); 424 scrollArea.setObjectName("scrollArea") 425 scrollArea.setWidgetResizable(True) 426 self.content = MainBox('/', 'str', self) 427 scrollArea.setWidget(self.content) 428 self.verticalLayout.addWidget(scrollArea) 429 430 # add info text field 431 self.info_field = QtGui.QTextEdit(self) 432 self.info_field.setVisible(False) 433 palette = QtGui.QPalette() 434 brush = QtGui.QBrush(QtGui.QColor(255, 254, 242)) 435 brush.setStyle(QtCore.Qt.SolidPattern) 436 palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush) 437 brush = QtGui.QBrush(QtGui.QColor(255, 254, 242)) 438 brush.setStyle(QtCore.Qt.SolidPattern) 439 palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush) 440 brush = QtGui.QBrush(QtGui.QColor(244, 244, 244)) 441 brush.setStyle(QtCore.Qt.SolidPattern) 442 palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush) 443 self.info_field.setPalette(palette) 444 self.info_field.setFrameShadow(QtGui.QFrame.Plain) 445 self.info_field.setReadOnly(True) 446 self.info_field.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByKeyboard|QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextBrowserInteraction|QtCore.Qt.TextSelectableByKeyboard|QtCore.Qt.TextSelectableByMouse) 447 self.info_field.setObjectName("dialog_info_field") 448 self.verticalLayout.addWidget(self.info_field) 449 450 # create buttons 451 self.buttonBox = QtGui.QDialogButtonBox(self) 452 self.buttonBox.setObjectName("buttonBox") 453 self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 454 self.buttonBox.setStandardButtons(buttons) 455 self.buttonBox.accepted.connect(self.accept) 456 self.buttonBox.rejected.connect(self.reject) 457 self.verticalLayout.addWidget(self.buttonBox) 458 459 # set the input fields 460 if params: 461 self.content.createFieldFromValue(params) 462 self.setInfoActive(False) 463 464 if self.filter_frame.isVisible(): 465 self.filter_field.setFocus()
466 # print '=============== create', self.objectName() 467 # 468 # def __del__(self): 469 # print "************ destroy", self.objectName() 470
471 - def _on_filter_changed(self):
472 self.content.filter(self.filter_field.text())
473
474 - def setFilterVisible(self, val):
475 ''' 476 Shows or hides the filter row. 477 ''' 478 self.filter_visible = val 479 self.filter_frame.setVisible(val&self.scrollArea.isHidden())
480
481 - def setText(self, text):
482 ''' 483 Adds a label to the dialog's layout and shows the given text. 484 @param text: the text to add to the dialog 485 @type text: C{str} 486 ''' 487 self.info_field.setText(text) 488 self.setInfoActive(True)
489
490 - def setInfoActive(self, val):
491 ''' 492 Activates or deactivates the info field of this dialog. If info field is 493 activated, the filter frame and the input field are deactivated. 494 @type val: C{bool} 495 ''' 496 if val and self.info_field.isHidden(): 497 self.filter_frame.setVisible(False&self.filter_visible) 498 self.scrollArea.setVisible(False) 499 self.info_field.setVisible(True) 500 elif not val and self.scrollArea.isHidden(): 501 self.filter_frame.setVisible(True&self.filter_visible) 502 self.scrollArea.setVisible(True) 503 self.info_field.setVisible(False) 504 if self.filter_frame.isVisible(): 505 self.filter_field.setFocus()
506
507 - def setFocusField(self, field_label):
508 field = self.content.getField(field_label) 509 if not field is None: 510 field.setFocus()
511
512 - def getKeywords(self):
513 ''' 514 @returns: a directory with parameter and value for all entered fields. 515 @rtype: C{dict(str(param) : str(value))} 516 ''' 517 return self.content.value()
518 519 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 520 #%%%%%%%%%%%%%%%%%% close handling %%%%%%%%%%%%%%%%%%%%% 521 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 522
523 - def accept(self):
524 self.setResult(QtGui.QDialog.Accepted) 525 self.hide()
526
527 - def reject(self):
528 self.setResult(QtGui.QDialog.Rejected) 529 self.hide()
530
531 - def hideEvent(self, event):
532 self.close()
533
534 - def closeEvent (self, event):
535 ''' 536 Test the open files for changes and save this if needed. 537 ''' 538 self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) 539 QtGui.QDialog.closeEvent(self, event)
540
541 542 543 -class MasterParameterDialog(ParameterDialog):
544 ''' 545 This dialog is an extension to the L{ParameterDialog}. The parameter and their 546 values are requested from the ROS master parameter server. The requests are 547 threaded and allows the also threaded changed of ROS parameter assigned to 548 given namespace. 549 ''' 550
551 - def __init__(self, masteruri, ns='/', parent=None):
552 ''' 553 @param masteruri: if the master uri is not None, the parameter are retrieved from ROS parameter server. 554 @type masteruri: C{str} 555 @param ns: namespace of the parameter retrieved from the ROS parameter server. 556 @type ns: C{str} 557 ''' 558 ParameterDialog.__init__(self, dict(), parent=parent) 559 self.masteruri = masteruri 560 self.ns = ns 561 self.is_delivered = False 562 self.is_send = False 563 self.mIcon = QtGui.QIcon(":/icons/default_cfg.png") 564 self.setWindowIcon(self.mIcon) 565 self.resize(450,300) 566 self.add_new_button = QtGui.QPushButton(self.tr("&Add")) 567 self.add_new_button.clicked.connect(self._on_add_parameter) 568 self.buttonBox.addButton(self.add_new_button, QtGui.QDialogButtonBox.ActionRole) 569 # self.apply_button = QtGui.QPushButton(self.tr("&Ok")) 570 # self.apply_button.clicked.connect(self._on_apply) 571 # self.buttonBox.addButton(self.apply_button, QtGui.QDialogButtonBox.ApplyRole) 572 # self.buttonBox.accepted.connect(self._on_apply) 573 self.setText(' '.join(['Obtaining parameters from the parameter server', masteruri, '...'])) 574 self.parameterHandler = ParameterHandler() 575 self.parameterHandler.parameter_list_signal.connect(self._on_param_list) 576 self.parameterHandler.parameter_values_signal.connect(self._on_param_values) 577 self.parameterHandler.delivery_result_signal.connect(self._on_delivered_values) 578 self.parameterHandler.requestParameterList(masteruri, ns)
579 # self.apply_button.setFocus(QtCore.Qt.OtherFocusReason) 580
581 - def accept(self):
582 if not self.masteruri is None and not self.is_send: 583 try: 584 params = self.getKeywords() 585 ros_params = dict() 586 for p,v in params.items(): 587 ros_params[roslib.names.ns_join(self.ns, p)] = v 588 if ros_params: 589 self.is_send = True 590 self.setText('Send the parameter into server...') 591 self.parameterHandler.deliverParameter(self.masteruri, ros_params) 592 except Exception, e: 593 QtGui.QMessageBox.warning(self, self.tr("Warning"), str(e), QtGui.QMessageBox.Ok) 594 elif self.masteruri is None: 595 QtGui.QMessageBox.warning(self, self.tr("Error"), 'Invalid ROS master URI', QtGui.QMessageBox.Ok)
596 597 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 598 #%%%%%%%%%%%%%%%%%% ROS parameter handling %%%%%%%%%%%%%%%%%%%%% 599 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 600
601 - def _on_add_parameter(self):
602 params_arg = {'namespace' : ('string', self.ns), 'name' : ('string', ''), 'type' : ('string', ['string', 'int', 'float', 'bool']), 'value' : ('string', '') } 603 dia = ParameterDialog(params_arg) 604 dia.setFilterVisible(False) 605 if dia.exec_(): 606 try: 607 params = dia.getKeywords() 608 if params['name']: 609 if params['type'] == 'int': 610 value = int(params['value']) 611 elif params['type'] == 'float': 612 value = float(params['value']) 613 elif params['type'] == 'bool': 614 value = str2bool(params['value']) 615 else: 616 value = params['value'] 617 self._on_param_values(self.masteruri, 1, '', {roslib.names.ns_join(params['namespace'], params['name']) : (1, '', value)}) 618 else: 619 QtGui.QMessageBox.warning(self, self.tr("Warning"), 'Empty name is not valid!', QtGui.QMessageBox.Ok) 620 except ValueError, e: 621 QtGui.QMessageBox.warning(self, self.tr("Warning"), unicode(e), QtGui.QMessageBox.Ok)
622
623 - def _on_param_list(self, masteruri, code, msg, params):
624 ''' 625 @param masteruri: The URI of the ROS parameter server 626 @type masteruri: C{str} 627 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 628 @type code: C{int} 629 @param msg: The message of the result. 630 @type msg: C{str} 631 @param params: The list the parameter names. 632 @type param: C{[str]} 633 ''' 634 if code == 1: 635 params.sort() 636 self.parameterHandler.requestParameterValues(masteruri, params) 637 else: 638 self.setText(msg)
639
640 - def _on_param_values(self, masteruri, code, msg, params):
641 ''' 642 @param masteruri: The URI of the ROS parameter server 643 @type masteruri: C{str} 644 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 645 @type code: C{int} 646 @param msg: The message of the result. 647 @type msg: C{str} 648 @param params: The dictionary the parameter names and request result. 649 @type param: C{dict(paramName : (code, statusMessage, parameterValue))} 650 ''' 651 if code == 1: 652 values = dict() 653 dia_params = dict() 654 for p, (code_n, msg_n, val) in params.items(): 655 if code_n != 1: 656 val = '' 657 type_str = 'string' 658 value = val 659 if isinstance(val, bool): 660 type_str = 'bool' 661 elif isinstance(val, int): 662 type_str = 'int' 663 elif isinstance(val, float): 664 type_str = 'float' 665 elif isinstance(val, list) or isinstance(val, dict): 666 value = unicode(val) 667 param = p.replace(self.ns, '') 668 names_sep = param.split(roslib.names.SEP) 669 param_name = names_sep.pop() 670 if names_sep: 671 group = dia_params 672 for n in names_sep: 673 group_name = n 674 if group.has_key(group_name): 675 group = group[group_name][1] 676 else: 677 tmp_dict = dict() 678 group[group_name] = (n, tmp_dict) 679 group = tmp_dict 680 group[param_name] = (type_str, value) 681 else: 682 dia_params[param_name] = (type_str, value) 683 try: 684 self.content.createFieldFromValue(dia_params) 685 self.setInfoActive(False) 686 except Exception, e: 687 QtGui.QMessageBox.warning(self, self.tr("Warning"), unicode(e), QtGui.QMessageBox.Ok) 688 else: 689 self.setText(msg)
690
691 - def _on_delivered_values(self, masteruri, code, msg, params):
692 ''' 693 @param masteruri: The URI of the ROS parameter server 694 @type masteruri: C{str} 695 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 696 @type code: C{int} 697 @param msg: The message of the result. 698 @type msg: C{str} 699 @param params: The dictionary the parameter names and request result. 700 @type param: C{dict(paramName : (code, statusMessage, parameterValue))} 701 ''' 702 self.is_delivered = True 703 errmsg = '' 704 if code == 1: 705 for p, (code_n, msg, val) in params.items(): 706 if code_n != 1: 707 errmsg = '\n'.join([errmsg, msg]) 708 else: 709 errmsg = msg if msg else 'Unknown error on set parameter' 710 if errmsg: 711 QtGui.QMessageBox.warning(self, self.tr("Warning"), errmsg, QtGui.QMessageBox.Ok) 712 self.is_delivered = False 713 self.is_send = False 714 self.setInfoActive(False) 715 if self.is_delivered: 716 self.close()
717
718 719 720 -class ServiceDialog(ParameterDialog):
721 ''' 722 Adds a support for calling a service to the L{ParameterDialog}. The needed 723 input fields are created from the service request message type. The service 724 call is executed in a thread to avoid blocking GUI. 725 ''' 726 service_resp_signal = QtCore.Signal(str, str) 727
728 - def __init__(self, service, parent=None):
729 ''' 730 @param service: Service to call. 731 @type service: L{ServiceInfo} 732 ''' 733 self.service = service 734 slots = service.get_service_class(True)._request_class.__slots__ 735 types = service.get_service_class()._request_class._slot_types 736 ParameterDialog.__init__(self, self._params_from_slots(slots, types), buttons=QtGui.QDialogButtonBox.Close, parent=parent) 737 self.setWindowTitle(''.join(['Call ', service.name])) 738 self.service_resp_signal.connect(self._handle_resp) 739 self.resize(450,300) 740 if not slots: 741 self.setText(''.join(['Wait for response ...'])) 742 thread = threading.Thread(target=self._callService) 743 thread.setDaemon(True) 744 thread.start() 745 else: 746 self.call_service_button = QtGui.QPushButton(self.tr("&Call")) 747 self.call_service_button.clicked.connect(self._on_call_service) 748 self.buttonBox.addButton(self.call_service_button, QtGui.QDialogButtonBox.ActionRole) 749 self.hide_button = QtGui.QPushButton(self.tr("&Hide/Show output")) 750 self.hide_button.clicked.connect(self._on_hide_output) 751 self.buttonBox.addButton(self.hide_button, QtGui.QDialogButtonBox.ActionRole) 752 self.hide_button.setVisible(False)
753
754 - def _on_hide_output(self):
755 self.setInfoActive(not self.info_field.isVisible())
756
757 - def _on_call_service(self):
758 try: 759 self.hide_button.setVisible(True) 760 params = self.getKeywords() 761 self.setText(''.join(['Wait for response ...'])) 762 thread = threading.Thread(target=self._callService, args=((params,))) 763 thread.setDaemon(True) 764 thread.start() 765 except Exception, e: 766 rospy.logwarn("Error while reading parameter for %s service: %s", str(self.service.name), unicode(e)) 767 self.setText(''.join(['Error while reading parameter:\n', unicode(e)]))
768
769 - def _callService(self, params={}):
770 req = unicode(params) if params else '' 771 try: 772 req, resp = nm.starter().callService(self.service.uri, self.service.name, self.service.get_service_class(), [params]) 773 self.service_resp_signal.emit(str(req), str(resp)) 774 except Exception, e: 775 import traceback 776 print traceback.format_exc() 777 rospy.logwarn("Error while call service '%s': %s", str(self.service.name), str(e)) 778 self.service_resp_signal.emit(unicode(req), unicode(e))
779 780 @classmethod
781 - def _params_from_slots(cls, slots, types):
782 result = dict() 783 for slot, msg_type in zip(slots, types): 784 base_type, is_array, array_length = roslib.msgs.parse_type(msg_type) 785 if base_type in roslib.msgs.PRIMITIVE_TYPES or base_type in ['time', 'duration']: 786 result[slot] = (msg_type, 'now' if base_type in ['time', 'duration'] else '') 787 else: 788 try: 789 list_msg_class = roslib.message.get_message_class(base_type) 790 subresult = cls._params_from_slots(list_msg_class.__slots__, list_msg_class._slot_types) 791 result[slot] = (msg_type, [subresult] if is_array else subresult) 792 except ValueError, e: 793 import traceback 794 print traceback.format_exc() 795 rospy.logwarn("Error while parse message type '%s': %s", str(msg_type), str(e)) 796 return result
797
798 - def _handle_resp(self, req, resp):
799 self.setWindowTitle(''.join(['Request / Response of ', self.service.name])) 800 self.setText('\n'.join([unicode(req), '---', unicode(resp)]))
801