35 from python_qt_binding
import loadUi
36 from python_qt_binding.QtCore
import Qt, QTimer, Signal, Slot, QRegExp, pyqtSignal
37 from python_qt_binding.QtGui
import QIcon, QRegExpValidator
38 from python_qt_binding.QtWidgets
import QHBoxLayout, QVBoxLayout, QGridLayout, QLabel, QStyledItemDelegate, QPushButton, QLineEdit
39 from python_qt_binding.QtWidgets
import QHeaderView, QMenu, QTreeWidgetItem, QWidget, QSlider, QAbstractItemView
45 from rospy.exceptions
import ROSException
47 from .topic_info
import TopicInfo
49 import dynamic_reconfigure
50 import dynamic_reconfigure.client
59 _column_names = [
'topic',
'type',
'min',
'value',
'max',
'checkbox']
62 super(QStyledItemDelegate, self).
__init__(parent)
70 if index.column()
not in indices:
75 if index.column() == 3:
77 widget = QWidget(parent)
79 widget.setMinimumHeight(80)
80 widget.setAutoFillBackground(
True)
81 widget.setStyleSheet(
".QWidget { background:rgb(200, 200, 200); margin:0px; border:1px solid rgb(170, 170, 170); }")
83 text_edit = QLineEdit(widget)
86 text_edit.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
87 text_edit.setMinimumHeight(36)
89 slider = QSlider(Qt.Horizontal, widget)
90 slider.setMinimumHeight(32)
93 hbox.setContentsMargins(2,1,2,1);
94 hbox.addWidget(slider)
95 hbox.addWidget(text_edit)
97 widget.setLayout(hbox)
100 imin = index.sibling(row, self._column_names.index(
"min"))
101 imax = index.sibling(row, self._column_names.index(
"max"))
105 smin = index.model().data(imin, Qt.EditRole)
106 smax = index.model().data(imax, Qt.EditRole)
126 slider.setMinimum(int(fmin * 100))
127 slider.setMaximum(int(fmax * 100))
129 slider.setMouseTracking(
True)
132 def sliderValueChanged(value):
133 text_edit.setText(str(float(value)/100.))
134 print "slider value changed to %d" % value
137 text_edit.selectAll()
142 print "slider pressed" 148 print "slider released" 151 def editingFinished():
152 text = text_edit.text()
156 nvalue = min(nvalue, fmax)
157 nvalue = max(nvalue, fmin)
161 text_edit.setText(str(nvalue))
163 svalue = int(nvalue*100)
165 print "text changed to %s" % text
166 print "nvalue is", nvalue
167 print "svalue is %d" % svalue
169 slider.blockSignals(
True)
170 slider.setSliderPosition(svalue)
171 slider.blockSignals(
False)
177 def lineEditTextChanged(text):
178 if not isFloat(text):
185 text_edit.setText(str(nvalue))
187 svalue = int(nvalue*100)
193 slider.blockSignals(
True)
194 slider.setSliderPosition(svalue)
195 slider.blockSignals(
False)
199 slider.valueChanged.connect(sliderValueChanged)
200 slider.sliderPressed.connect(sliderDown)
201 slider.sliderReleased.connect(sliderUp)
203 text_edit.textChanged.connect(lineEditTextChanged)
204 text_edit.editingFinished.connect(editingFinished)
206 text_edit.selectAll()
209 rx = QRegExp(
"^-?([1-9]\d*|0)(\.\d*)?$")
210 v = QRegExpValidator(rx)
211 text_edit.setValidator(v)
213 widget.setFocusProxy(text_edit)
217 edt = QLineEdit(parent)
219 if index.column() == 2:
220 edt.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
221 elif index.column() == 4:
222 edt.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
224 rx = QRegExp(
"^-?([1-9]\d*|0)(\.\d*)?$")
225 v = QRegExpValidator(rx);
231 if index.column() == self._column_names.index(
"value"):
232 text_edit = editor.findChild(QLineEdit)
233 value = text_edit.text()
235 value = editor.text()
237 model.setData(index, value, Qt.EditRole)
238 model.setData(index, value, Qt.UserRole)
241 imin = index.sibling(row, self._column_names.index(
"min"))
242 imax = index.sibling(row, self._column_names.index(
"max"))
243 ival = index.sibling(row, self._column_names.index(
"value"))
245 vmin = imin.data(Qt.EditRole)
246 vmax = imax.data(Qt.EditRole)
247 val = ival.data(Qt.EditRole)
255 vmin = tryFloat(vmin)
256 vmax = tryFloat(vmax)
259 print vmin, val, vmax
261 if index.column() == self._column_names.index(
"min"):
262 if vmin !=
None and (vmax==
None or vmax<vmin):
263 model.setData(imax, str(vmin))
264 model.setData(imax, str(vmin), Qt.UserRole)
266 if vmin !=
None and (val==
None or val<vmin):
267 model.setData(ival, str(vmin))
268 model.setData(ival, str(vmin), Qt.UserRole)
270 if index.column() == self._column_names.index(
"max"):
271 if vmax !=
None and (vmin==
None or vmax<vmin):
272 model.setData(imin, str(vmax))
273 model.setData(imin, str(vmax), Qt.UserRole)
275 if vmax !=
None and (val==
None or vmax<val):
276 model.setData(ival, str(vmax))
277 model.setData(ival, str(vmax), Qt.UserRole)
279 if index.column() == self._column_names.index(
"value")
and val !=
None:
281 model.setData(imax, str(val))
282 model.setData(imax, str(val), Qt.UserRole)
284 model.setData(imin, str(val))
285 model.setData(imin, str(val), Qt.UserRole)
290 value = index.model().data(index, Qt.EditRole)
292 if index.column() == 3:
293 slider = editor.findChild(QSlider)
294 slider.setSliderDown(
True)
296 text_edit = editor.findChild(QLineEdit)
297 text_edit.setText(value)
298 text_edit.selectAll()
302 editor.setText(value)
309 main class inherits from the ui window class. 311 You can specify the topics that the topic pane. 313 ParamWidget.start must be called in order to update topic pane. 317 SELECT_BY_MSGTYPE = 1
320 _column_names = [
'topic',
'type',
'min',
'value',
'max',
'checkbox']
322 selectionChanged = pyqtSignal(dict, name=
'selectionChanged')
332 def __init__(self, plugin=None, selected_topics=None, select_topic_type=SELECT_BY_NAME):
334 @type selected_topics: list of tuples. 335 @param selected_topics: [($NAME_TOPIC$, $TYPE_TOPIC$), ...] 336 @type select_topic_type: int 337 @param select_topic_type: Can specify either the name of topics or by 338 the type of topic, to filter the topics to 339 show. If 'select_topic_type' argument is 340 None, this arg shouldn't be meaningful. 346 self.setFocusPolicy(Qt.StrongFocus)
350 rp = rospkg.RosPack()
351 ui_file = os.path.join(rp.get_path(
'rqt_dyn_tune'),
'resource',
'ParamWidget.ui')
352 loadUi(ui_file, self)
354 self.topics_tree_widget.sortByColumn(0, Qt.AscendingOrder)
355 header = self.topics_tree_widget.header()
357 setSectionResizeMode = header.setSectionResizeMode
358 except AttributeError:
359 setSectionResizeMode = header.setResizeMode
360 setSectionResizeMode(QHeaderView.ResizeToContents)
362 header.setContextMenuPolicy(Qt.CustomContextMenu)
367 header.setStretchLastSection(
False)
368 setSectionResizeMode(0, QHeaderView.Stretch)
369 setSectionResizeMode(self._column_names.index(
"value"), QHeaderView.Stretch)
370 setSectionResizeMode(self._column_names.index(
"checkbox"), QHeaderView.Fixed)
385 self.topics_tree_widget.itemExpanded.connect(self.
expanded)
386 self.topics_tree_widget.itemCollapsed.connect(self.
collapsed)
387 self.topics_tree_widget.itemChanged.connect(self.
itemChanged)
389 self.topics_tree_widget.setAlternatingRowColors(
True)
392 self.topics_tree_widget.setItemDelegate(delegate)
393 self.topics_tree_widget.setEditTriggers(QAbstractItemView.AnyKeyPressed | QAbstractItemView.DoubleClicked )
396 hitem = self.topics_tree_widget.headerItem()
397 hitem.setTextAlignment(self._column_names.index(
"min"), Qt.AlignRight | Qt.AlignVCenter)
398 hitem.setTextAlignment(self._column_names.index(
"max"), Qt.AlignLeft | Qt.AlignVCenter)
399 hitem.setTextAlignment(self._column_names.index(
"value"), Qt.AlignHCenter | Qt.AlignVCenter)
400 hitem.setTextAlignment(self._column_names.index(
"type"), Qt.AlignHCenter | Qt.AlignVCenter)
408 def selection_changed(data):
409 print "#\n#\n#\nthe selcted items are:", data,
"\n\n" 411 self.selectionChanged.connect(selection_changed)
419 This method needs to be called to start updating topic pane. 421 self._timer_refresh_topics.start(1000)
423 @Slot(
'QTreeWidgetItem', int)
427 if item._topic_name
in selected
and self.
_column_names[column]
in [
"min",
"max",
"value"]:
428 self.selectionChanged.emit(selected)
430 @Slot(
'QTreeWidgetItem')
433 name = item.data(0, Qt.UserRole)
435 if not isinstance(item, TreeWidgetItem)
or not item._is_topic:
438 print "expanded", name
439 self.
_topics[item._topic_name][
'info'].start_monitoring()
442 @Slot(
'QTreeWidgetItem')
445 name = item.data(0, Qt.UserRole)
447 if not isinstance(item, TreeWidgetItem)
or not item._is_topic:
450 print "collapsed", name
451 self.
_topics[item._topic_name][
'info'].stop_monitoring()
460 vmin = item.data(self._column_names.index(
"min"), Qt.EditRole)
461 vmax = item.data(self._column_names.index(
"max"), Qt.EditRole)
462 val = item.data(self._column_names.index(
"value"), Qt.EditRole)
470 vmin = tryFloat(vmin)
471 vmax = tryFloat(vmax)
476 desc[
"default"] = val
488 parent = self.topics_tree_widget
490 pnames = param_name.split(
"/")
504 _item.setData(0, Qt.UserRole,
"name_space")
505 _item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsTristate)
510 item.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable )
512 item.setData(self.
_column_index[
'min'], Qt.EditRole, str(param_desc[
"min"]))
513 item.setData(self.
_column_index[
'max'], Qt.EditRole, str(param_desc[
"max"]))
514 item.setData(self.
_column_index[
'value'], Qt.EditRole, str(param_desc[
"default"]))
515 item.setData(self.
_column_index[
'type'], Qt.EditRole, str(param_desc[
"type"]))
520 print param_name,
" added" 533 refresh tree view items 536 nodes = dynamic_reconfigure.find_reconfigure_services()
540 client = dynamic_reconfigure.client.Client(node, timeout=3)
541 gdesc = client.get_group_descriptions()
542 for pdesc
in gdesc[
"parameters"]:
543 param = node +
"/" + pdesc[
"name"]
544 dparams[param] = pdesc
546 for param, desc
in self._current_params.items():
547 if param
not in dparams:
551 for param, desc
in dparams.items():
564 for topic
in self._topics.values():
565 topic_info = topic[
'info']
566 if topic_info.monitoring:
568 rate, _, _, _ = topic_info.get_hz()
569 rate_text =
'%1.2f' % rate
if rate !=
None else 'unknown' 572 bytes_per_s, _, _, _ = topic_info.get_bw()
573 if bytes_per_s
is None:
574 bandwidth_text =
'unknown' 575 elif bytes_per_s < 1000:
576 bandwidth_text =
'%.2fB/s' % bytes_per_s
577 elif bytes_per_s < 1000000:
578 bandwidth_text =
'%.2fKB/s' % (bytes_per_s / 1000.)
580 bandwidth_text =
'%.2fMB/s' % (bytes_per_s / 1000000.)
584 self.
update_value(topic_info._topic_name, topic_info.last_message)
589 value_text =
'not monitored' if topic_info.error
is None else topic_info.error
594 if hasattr(message,
'__slots__')
and hasattr(message,
'_slot_types'):
595 for slot_name
in message.__slots__:
596 self.
update_value(topic_name +
'/' + slot_name, getattr(message, slot_name))
598 elif type(message)
in (list, tuple)
and (len(message) > 0)
and hasattr(message[0],
'__slots__'):
600 for index, slot
in enumerate(message):
601 if topic_name +
'[%d]' % index
in self.
_tree_items:
607 if len(message) < self.
_tree_items[topic_name].childCount():
608 for i
in range(len(message), self.
_tree_items[topic_name].childCount()):
609 item_topic_name = topic_name +
'[%d]' % i
617 if '[' in type_str
and type_str[-1] ==
']':
618 type_str, array_size_str = type_str.split(
'[', 1)
619 array_size_str = array_size_str[:-1]
620 if len(array_size_str) > 0:
621 array_size = int(array_size_str)
625 return type_str, array_size
628 if parent
is self.topics_tree_widget:
630 topic_text = topic_name
632 topic_names = topic_name.split(
'/')
634 for name
in topic_names:
638 name_space = name_space + _name
641 if name_space == topic_name:
647 _item.setData(0, Qt.UserRole, name_space)
656 topic_text = topic_name.split(
'/')[-1]
657 if '[' in topic_text:
658 topic_text = topic_text[topic_text.index(
'['):]
664 item.setData(0, Qt.UserRole, topic_name)
669 if hasattr(message,
'__slots__')
and hasattr(message,
'_slot_types'):
670 for slot_name, type_name
in zip(message.__slots__, message._slot_types):
676 base_instance = roslib.message.get_message_class(base_type_str)()
677 except (ValueError, TypeError):
679 if array_size
is not None and hasattr(base_instance,
'__slots__'):
680 for index
in range(array_size):
688 if item.checkState(CHECK_COLUMN):
689 print "start %s" % topic_name
691 self._selected_items.append(topic_name)
693 print "stop %s" % topic_name
695 self._selected_items.remove(topic_name)
696 item.setText(CHECK_COLUMN,
'')
703 def _recursive_remove_items_from_tree(item):
704 for index
in reversed(range(item.childCount())):
705 _recursive_remove_items_from_tree(item.child(index))
706 topic_name = item.data(0, Qt.UserRole)
708 _recursive_remove_items_from_tree(item)
709 item.parent().removeChild(item)
713 header = self.topics_tree_widget.header()
717 action_toggle_auto_resize = menu.addAction(
'Toggle Auto-Resize')
718 action = menu.exec_(header.mapToGlobal(pos))
721 if action
is action_toggle_auto_resize:
722 if header.resizeMode(0) == QHeaderView.ResizeToContents:
723 header.setResizeMode(QHeaderView.Interactive)
725 header.setResizeMode(QHeaderView.ResizeToContents)
729 item = self.topics_tree_widget.itemAt(pos)
735 action_item_expand = menu.addAction(QIcon.fromTheme(
'zoom-in'),
'Expand All Children')
736 action_item_collapse = menu.addAction(QIcon.fromTheme(
'zoom-out'),
'Collapse All Children')
737 action = menu.exec_(self.topics_tree_widget.mapToGlobal(pos))
740 if action
in (action_item_expand, action_item_collapse):
741 expanded = (action
is action_item_expand)
743 def recursive_set_expanded(item):
744 item.setExpanded(expanded)
745 for index
in range(item.childCount()):
746 recursive_set_expanded(item.child(index))
747 recursive_set_expanded(item)
750 for topic
in self._topics.values():
751 topic[
'info'].stop_monitoring()
752 self._timer_refresh_topics.stop()
756 @param selected_topics: list of tuple. [(topic_name, topic_type)] 757 @type selected_topics: [] 759 rospy.logdebug(
'set_selected_topics topics={}'.
format(
760 len(selected_topics)))
765 header_state = self.topics_tree_widget.header().saveState()
766 instance_settings.set_value(
'tree_widget_header_state', header_state)
769 if instance_settings.contains(
'tree_widget_header_state'):
770 header_state = instance_settings.value(
'tree_widget_header_state')
771 if not self.topics_tree_widget.header().restoreState(header_state):
772 rospy.logwarn(
"rqt_dyn_tune: Failed to restore header state.")
783 _column_names = [
'topic',
'type',
'min',
'value',
'max',
'checkbox']
786 print "checking flags" 788 return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
790 return QtCore.Qt.ItemIsEnabled
794 def __init__(self, check_state_changed_callback, topic_name, parent=None, is_topic = False):
795 super(TreeWidgetItem, self).
__init__(parent)
798 self.setCheckState(CHECK_COLUMN, Qt.Unchecked)
808 self._hbox.addWidget(self.
_slider)
811 tree = self.treeWidget()
813 widget.setLayout(self.
_hbox)
815 self.setTextAlignment(self._column_names.index(
"min"), Qt.AlignRight | Qt.AlignVCenter)
816 self.setTextAlignment(self._column_names.index(
"max"), Qt.AlignLeft | Qt.AlignVCenter)
817 self.setTextAlignment(self._column_names.index(
"value"), Qt.AlignHCenter | Qt.AlignVCenter)
818 self.setTextAlignment(self._column_names.index(
"type"), Qt.AlignHCenter | Qt.AlignVCenter)
820 self.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable )
826 self.setText(2, str(value))
830 if role == Qt.CheckStateRole:
831 state = self.checkState(column)
833 super(TreeWidgetItem, self).
setData(column, role, value)
835 if role == Qt.CheckStateRole
and state != self.checkState(column):
def format(color, style='')