Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/groovy-devel' into groovy-devel
Browse files Browse the repository at this point in the history
* upstream/groovy-devel:
  Fix version clash when PyQt5 is installed
  Remove dead nodes from memory and rebuild uri cache when nodes are restarted
  fix image shrinking problem ros-visualization#291
  rqt_reconfigure: Restore support for parameter groups
  • Loading branch information
Michael Jae-Yoon Chung committed Feb 5, 2015
2 parents 4cf4430 + a59fb63 commit b64a536
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 50 deletions.
1 change: 1 addition & 0 deletions rqt_plot/src/rqt_plot/data_plot/mat_data_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def parse_version(s):
raise ImportError('A newer matplotlib is required (at least 1.1.0)')

try:
matplotlib.use('Qt4Agg')
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
except ImportError:
# work around bug in dateutil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import rospy

from .param_editors import EditorWidget
from .param_groups import GroupWidget
from .param_groups import GroupWidget, find_cfg
from .param_updater import ParamUpdater


Expand Down Expand Up @@ -85,7 +85,7 @@ def config_callback(self, config):
widget.param_name)
widget.update_value(config[widget.param_name])
elif isinstance(widget, GroupWidget):
cfg = self.find_cfg(config, widget.param_name)
cfg = find_cfg(config, widget.param_name)
rospy.logdebug('GROUP widget.param_name=%s',
widget.param_name)
widget.update_group(cfg)
Expand Down
104 changes: 60 additions & 44 deletions rqt_reconfigure/src/rqt_reconfigure/param_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class GroupWidget(QWidget):

# public signal
sig_node_disabled_selected = Signal(str)
sig_node_state_change = Signal(bool)

def __init__(self, updater, config, nodename):
'''
Expand All @@ -97,13 +98,9 @@ def __init__(self, updater, config, nodename):
:type nodename: str
'''

#TODO figure out what data type 'config' is. It is afterall returned
# from dynamic_reconfigure.client.get_parameter_descriptions()
# ros.org/doc/api/dynamic_reconfigure/html/dynamic_reconfigure.client-pysrc.html#Client

super(GroupWidget, self).__init__()
self.state = config['state']
self.name = config['name']
self.param_name = config['name']
self._toplevel_treenode_name = nodename

# TODO: .ui file needs to be back into usage in later phase.
Expand Down Expand Up @@ -176,8 +173,8 @@ def _create_node_widgets(self, config):
widget = EnumEditor(self.updater, param)
elif param['type'] in EDITOR_TYPES:
rospy.logdebug('GroupWidget i_debug=%d param type =%s',
i_debug,
param['type'])
i_debug,
param['type'])
editor_type = EDITOR_TYPES[param['type']]
widget = eval(editor_type)(self.updater, param)

Expand All @@ -195,38 +192,41 @@ def _create_node_widgets(self, config):

for name, group in config['groups'].items():
if group['type'] == 'tab':
widget = TabGroup(self, self.updater, group)
widget = TabGroup(self, self.updater, group, self._toplevel_treenode_name)
elif group['type'] in _GROUP_TYPES.keys():
widget = eval(_GROUP_TYPES[group['type']])(self.updater, group)
widget = eval(_GROUP_TYPES[group['type']])(self.updater, group, self._toplevel_treenode_name)
else:
widget = eval(_GROUP_TYPES[''])(self.updater, group, self._toplevel_treenode_name)

self.editor_widgets.append(widget)
rospy.logdebug('groups._create_node_widgets ' +
#'num groups=%d' +
'name=%s',
name)
'name=%s',
name)

for i, ed in enumerate(self.editor_widgets):
ed.display(self.grid)

rospy.logdebug('GroupWdgt._create_node_widgets len(editor_widgets)=%d',
len(self.editor_widgets))
len(self.editor_widgets))

def display(self, grid, row):
# groups span across all columns
grid.addWidget(self, row, 0, 1, -1)
def display(self, grid):
grid.addRow(self)

def update_group(self, config):
self.state = config['state']
if 'state' in config:
old_state = self.state
self.state = config['state']
if self.state != old_state:
self.sig_node_state_change.emit(self.state)

# TODO: should use config.keys but this method doesnt exist
names = [name for name in config.items()]
names = [name for name in config.keys()]

for widget in self.editor_widgets:
if isinstance(widget, EditorWidget):
if widget.name in names:
widget.update_value(config[widget.name])
if widget.param_name in names:
widget.update_value(config[widget.param_name])
elif isinstance(widget, GroupWidget):
cfg = find_cfg(config, widget.name)
cfg = find_cfg(config, widget.param_name)
widget.update_group(cfg)

def close(self):
Expand All @@ -245,41 +245,56 @@ def _node_disable_bt_clicked(self):


class BoxGroup(GroupWidget):
def __init__(self, updater, config):
super(BoxGroup, self).__init__(updater, config)
def __init__(self, updater, config, nodename):
super(BoxGroup, self).__init__(updater, config, nodename)

self.box = QGroupBox(self.name)
self.box = QGroupBox(self.param_name)
self.box.setLayout(self.grid)

def display(self, grid, row):
grid.addWidget(self.box, row, 0, 1, -1)
def display(self, grid):
grid.addRow(self.box)


class CollapseGroup(BoxGroup):
def __init__(self, updater, config):
super(CollapseGroup, self).__init__(updater, config)
def __init__(self, updater, config, nodename):
super(CollapseGroup, self).__init__(updater, config, nodename)
self.box.setCheckable(True)
self.box.clicked.connect(self.click_cb)
self.sig_node_state_change.connect(self.box.setChecked)

for child in self.box.children():
if child.isWidgetType():
self.box.toggled.connect(child.setShown)

self.box.setChecked(self.state)

def click_cb(self, on):
self.updater.update({'groups': {self.param_name: on}})


class HideGroup(BoxGroup):
def update_group(self, config):
super(HideGroup, self).update_group(config)
self.box.setVisible(self.state)
def __init__(self, updater, config, nodename):
super(HideGroup, self).__init__(updater, config, nodename)
self.box.setShown(self.state)
self.sig_node_state_change.connect(self.box.setShown)


class TabGroup(GroupWidget):
def __init__(self, parent, updater, config):
super(TabGroup, self).__init__(updater, config)
def __init__(self, parent, updater, config, nodename):
super(TabGroup, self).__init__(updater, config, nodename)
self.parent = parent

if not self.parent.tab_bar:
self.parent.tab_bar = QTabWidget()

parent.tab_bar.addTab(self, self.name)
self.wid = QWidget()
self.wid.setLayout(self.grid)

parent.tab_bar.addTab(self.wid, self.param_name)

def display(self, grid, row):
def display(self, grid):
if not self.parent.tab_bar_shown:
grid.addWidget(self.parent.tab_bar, row, 0, 1, -1)
grid.addRow(self.parent.tab_bar)
self.parent.tab_bar_shown = True

def close(self):
Expand All @@ -290,24 +305,25 @@ def close(self):

class ApplyGroup(BoxGroup):
class ApplyUpdater:
def __init__(self, updater):
def __init__(self, updater, loopback):
self.updater = updater
self.loopback = loopback
self._configs_pending = {}

def update(self, config):
for name, value in config.items():
self._configs_pending[name] = value
self.loopback(config)

def apply_update(self):
self.updater.update(self._configs_pending)
self._configs_pending = {}

def __init__(self, updater, config):
self.updater = ApplyGroup.ApplyUpdater(updater)
super(ApplyGroup, self).__init__(self.updater, config)
def __init__(self, updater, config, nodename):
self.updater = ApplyGroup.ApplyUpdater(updater, self.update_group)
super(ApplyGroup, self).__init__(self.updater, config, nodename)

self.button = QPushButton("Apply %s" % self.name)
self.button = QPushButton('Apply %s' % self.param_name)
self.button.clicked.connect(self.updater.apply_update)

rows = self.grid.rowCount()
self.grid.addWidget(self.button, rows + 1, 1, Qt.AlignRight)
self.grid.addRow(self.button)
18 changes: 14 additions & 4 deletions rqt_top/src/rqt_top/node_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@

class NodeInfo(object):
nodes = dict()
def get_node_info(self, node_name):
node_api = rosnode.get_api_uri(rospy.get_master(), node_name)

def get_node_info(self, node_name, skip_cache=False):
node_api = rosnode.get_api_uri(rospy.get_master(), node_name, skip_cache=skip_cache)
try:
code, msg, pid = xmlrpclib.ServerProxy(node_api[2]).getPid(ID)
if node_name in self.nodes:
Expand All @@ -48,11 +49,14 @@ def get_node_info(self, node_name):
except:
return False
except xmlrpclib.socket.error:
return False

if not skip_cache:
return self.get_node_info(node_name, skip_cache=True)
else:
return False

def get_all_node_info(self):
infos = []
self.remove_dead_nodes()
for node_name in rosnode.get_node_names():
info = self.get_node_info(node_name)
if info is not False: infos.append((node_name, info))
Expand All @@ -65,6 +69,12 @@ def get_all_node_fields(self, fields):
infos.append(self.as_dict(p, fields + ['cmdline', 'get_memory_info']))
infos[-1]['node_name'] = name
return infos

def remove_dead_nodes(self):
running_nodes = rosnode.get_node_names()
dead_nodes = [node_name for node_name in self.nodes if node_name not in running_nodes]
for node_name in dead_nodes:
self.nodes.pop(node_name, None)

def kill_node(self, node_name):
success, fail = rosnode.kill_nodes([node_name])
Expand Down

0 comments on commit b64a536

Please sign in to comment.