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