param_client_widget.py
Go to the documentation of this file.
1 # Copyright (c) 2012, Willow Garage, Inc.
2 # All rights reserved.
3 #
4 # Software License Agreement (BSD License 2.0)
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 Willow Garage, Inc. 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 # Author: Isaac Saito, Ze'ev Klapow
34 
35 from dynamic_reconfigure import (DynamicReconfigureCallbackException,
36  DynamicReconfigureParameterException)
37 from dynamic_reconfigure.encoding import Config
38 
39 from python_qt_binding.QtCore import QMargins
40 from python_qt_binding.QtGui import QIcon
41 from python_qt_binding.QtWidgets import (QFileDialog, QHBoxLayout,
42  QPushButton, QWidget)
43 
44 from rospy.service import ServiceException
45 
46 import yaml
47 
48 from rqt_reconfigure import logging
49 from rqt_reconfigure.param_editors import EditorWidget
50 from rqt_reconfigure.param_groups import find_cfg, GroupWidget
51 from rqt_reconfigure.param_updater import ParamUpdater
52 
53 
54 def config_constructor(loader, node):
55  mapping = loader.construct_mapping(node, deep=True)
56  assert 'state' in mapping
57  return Config(mapping.get('dictitems', {}))
58 
59 
60 yaml.add_constructor(
61  u'tag:yaml.org,2002:python/object/new:dynamic_reconfigure.encoding.Config',
62  config_constructor, Loader=yaml.SafeLoader)
63 
64 
66  """
67  A wrapper of dynamic_reconfigure.client instance.
68 
69  Represents a widget where users can view and modify ROS params.
70  """
71 
72  def __init__(self, reconf, node_name):
73  """
74  :type reconf: dynamic_reconfigure.client
75  :type node_name: str
76  """
77  group_desc = reconf.get_group_descriptions()
78  logging.debug('ParamClientWidget.group_desc={}'.format(group_desc))
79  super(ParamClientWidget, self).__init__(ParamUpdater(reconf),
80  group_desc, node_name)
81 
82  # Save and load buttons
83  self.button_widget = QWidget(self)
84  self.button_header = QHBoxLayout(self.button_widget)
85  self.button_header.setContentsMargins(QMargins(0, 0, 0, 0))
86 
87  self.load_button = QPushButton()
88  self.save_button = QPushButton()
89 
90  self.load_button.setIcon(QIcon.fromTheme('document-open'))
91  self.save_button.setIcon(QIcon.fromTheme('document-save'))
92 
93  self.load_button.clicked[bool].connect(self._handle_load_clicked)
94  self.save_button.clicked[bool].connect(self._handle_save_clicked)
95 
96  self.button_header.addWidget(self.save_button)
97  self.button_header.addWidget(self.load_button)
98 
99  self.setMinimumWidth(150)
100 
101  self.reconf = reconf
102  self.updater.start()
103  self.reconf.config_callback = self.config_callback
104  self._node_grn = node_name
105 
106  def get_node_grn(self):
107  return self._node_grn
108 
109  def config_callback(self, config):
110 
111  # TODO: Think about replacing callback architecture with signals.
112 
113  if config:
114  # TODO: should use config.keys but this method doesnt exist
115 
116  names = [name for name, v in config.items()]
117  # v isn't used but necessary to get key and put it into dict.
118  # logging.debug('config_callback name={} v={}'.format(name, v))
119 
120  for widget in self.editor_widgets:
121  if isinstance(widget, EditorWidget):
122  if widget.param_name in names:
123  logging.debug('EDITOR widget.param_name={}'.format(
124  widget.param_name))
125  widget.update_value(config[widget.param_name])
126  elif isinstance(widget, GroupWidget):
127  cfg = find_cfg(config, widget.param_name)
128  logging.debug('GROUP widget.param_name={}'.format(
129  widget.param_name))
130  widget.update_group(cfg)
131 
133  filename = QFileDialog.getOpenFileName(
134  self, self.tr('Load from File'), '.',
135  self.tr('YAML file {.yaml} (*.yaml)'))
136  if filename[0] != '':
137  self.load_param(filename[0])
138 
140  filename = QFileDialog.getSaveFileName(
141  self, self.tr('Save parameters to file...'), '.',
142  self.tr('YAML files {.yaml} (*.yaml)'))
143  if filename[0] != '':
144  self.save_param(filename[0])
145 
146  def save_param(self, filename):
147  configuration = self.reconf.get_configuration()
148  if configuration is not None:
149  with open(filename, 'w') as f:
150  yaml.dump(configuration, f)
151 
152  def load_param(self, filename):
153  with open(filename, 'r') as f:
154  configuration = {}
155  for doc in yaml.safe_load_all(f.read()):
156  configuration.update(doc)
157 
158  try:
159  self.reconf.update_configuration(configuration)
160  except ServiceException as e:
161  logging.warn(
162  "Call for reconfiguration wasn't successful"
163  ' because: {}'.format(e.message)
164  )
165  except DynamicReconfigureParameterException as e:
166  logging.warn(
167  "Reconfiguration wasn't successful"
168  ' because: {}'.format(e.message)
169  )
170  except DynamicReconfigureCallbackException as e:
171  logging.warn(
172  "Reconfiguration wasn't successful"
173  ' because: {}'.format(e.message)
174  )
175 
176  def close(self):
177  self.reconf.close()
178  self.updater.stop()
179 
180  for w in self.editor_widgets:
181  w.close()
182 
183  self.deleteLater()
184 
185  def filter_param(self, filter_key):
186  # TODO impl
187  pass
def find_cfg(config, name)
Definition: param_groups.py:61


rqt_reconfigure
Author(s): Isaac Saito, Ze'ev Klapow
autogenerated on Sat Mar 20 2021 02:51:58