param_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
34 
35 from __future__ import division
36 
37 import sys
38 
39 from python_qt_binding.QtCore import QMargins, Signal
40 from python_qt_binding.QtWidgets import (
41  QHBoxLayout, QLabel, QSplitter, QVBoxLayout, QWidget
42 )
43 
44 import rospkg
45 
46 import rospy
47 
48 from rqt_reconfigure import logging
49 from rqt_reconfigure.node_selector_widget import NodeSelectorWidget
50 from rqt_reconfigure.paramedit_widget import ParameditWidget
51 from rqt_reconfigure.text_filter import TextFilter
52 from rqt_reconfigure.text_filter_widget import TextFilterWidget
53 
54 
55 class ParamWidget(QWidget):
56  _TITLE_PLUGIN = 'Dynamic Reconfigure'
57 
58  # To be connected to PluginContainerWidget
59  sig_sysmsg = Signal(str)
60  sig_sysprogress = Signal(str)
61 
62  # To make selections from CLA
63  sig_selected = Signal(str, bool)
64 
65  def __init__(self, context, node=None):
66  """
67  This class is intended to be called by rqt plugin framework class.
68  Currently (12/12/2012) the whole widget is splitted into 2 panes:
69  one on left allows you to choose the node(s) you work on. Right side
70  pane lets you work with the parameters associated with the node(s) you
71  select on the left.
72 
73  (12/27/2012) Despite the pkg name is changed to rqt_reconfigure to
74  reflect the available functionality, file & class names remain
75  'param', expecting all the parameters will become handle-able.
76  """
77  super(ParamWidget, self).__init__()
78  self.setObjectName(self._TITLE_PLUGIN)
79  self.setWindowTitle(self._TITLE_PLUGIN)
80 
81  rp = rospkg.RosPack()
82 
83  # TODO: .ui file needs to replace the GUI components declaration
84  # below. For unknown reason, referring to another .ui files
85  # from a .ui that is used in this class failed. So for now,
86  # I decided not use .ui in this class.
87  # If someone can tackle this I'd appreciate.
88  _hlayout_top = QHBoxLayout(self)
89  _hlayout_top.setContentsMargins(QMargins(0, 0, 0, 0))
90  self._splitter = QSplitter(self)
91  _hlayout_top.addWidget(self._splitter)
92 
93  _vlayout_nodesel_widget = QWidget()
94  _vlayout_nodesel_side = QVBoxLayout()
95  _hlayout_filter_widget = QWidget(self)
96  _hlayout_filter = QHBoxLayout()
99  self.filterkey_label = QLabel('&Filter key:')
100  self.filterkey_label.setBuddy(self.filter_lineedit)
101  _hlayout_filter.addWidget(self.filterkey_label)
102  _hlayout_filter.addWidget(self.filter_lineedit)
103  _hlayout_filter_widget.setLayout(_hlayout_filter)
105  self, rp, self.sig_sysmsg
106  )
107  _vlayout_nodesel_side.addWidget(_hlayout_filter_widget)
108  _vlayout_nodesel_side.addWidget(self._nodesel_widget)
109  _vlayout_nodesel_side.setSpacing(1)
110  _vlayout_nodesel_widget.setLayout(_vlayout_nodesel_side)
111 
113 
114  self._splitter.insertWidget(0, _vlayout_nodesel_widget)
115  self._splitter.insertWidget(1, self._param_edit_widget)
116  # 1st column, _vlayout_nodesel_widget, to minimize width.
117  # 2nd col to keep the possible max width.
118  self._splitter.setStretchFactor(0, 0)
119  self._splitter.setStretchFactor(1, 1)
120 
121  # Signal from paramedit widget to node selector widget.
122  self._param_edit_widget.sig_node_disabled_selected.connect(
123  self._nodesel_widget.node_deselected
124  )
125  # Pass name of node to editor widget
126  self._nodesel_widget.sig_node_selected.connect(
127  self._param_edit_widget.show_reconf
128  )
129 
130  if not node:
131  title = self._TITLE_PLUGIN
132  else:
133  title = self._TITLE_PLUGIN + ' %s' % node
134  self.setObjectName(title)
135 
136  # Connect filter signal-slots.
137  self._text_filter.filter_changed_signal.connect(
139  )
140 
141  # Signal from widget to open a new editor widget
142  self.sig_selected.connect(self._nodesel_widget.node_selected)
143 
144  self._explicit_nodes_to_select = [rospy.resolve_name(c) for c in context.argv()]
145 
146  def shutdown(self):
147  # TODO: Needs implemented. Trigger dynamic_reconfigure to unlatch
148  # subscriber.
149  pass
150 
151  def save_settings(self, plugin_settings, instance_settings):
152  instance_settings.set_value('splitter', self._splitter.saveState())
153  self.filter_lineedit.save_settings(instance_settings)
154  self._nodesel_widget.save_settings(instance_settings)
155  instance_settings.set_value(
156  'selected_nodes', list(self._param_edit_widget.get_active_grns()))
157 
158  def restore_settings(self, plugin_settings, instance_settings):
159  if instance_settings.contains('splitter'):
160  self._splitter.restoreState(instance_settings.value('splitter'))
161  else:
162  self._splitter.setSizes([100, 100, 200])
163  self.filter_lineedit.restore_settings(instance_settings)
164  self._nodesel_widget.restore_settings(instance_settings)
165 
166  # Ignore previously open nodes if we were given an explicit list
168  nodes_to_select = self._explicit_nodes_to_select
169  explicit = True
170  else:
171  nodes_to_select = instance_settings.value('selected_nodes') or []
172  explicit = False
173 
174  for rn in nodes_to_select:
175  if rn in self._nodesel_widget.get_paramitems():
176  self.sig_selected.emit(rn, explicit)
177  elif explicit:
178  logging.warn(
179  'Could not find a dynamic reconfigure client'
180  " named '{}'".format(str(rn))
181  )
182 
183  def get_filter_text(self):
184  """
185  :rtype: QString
186  """
187  return self.filter_lineedit.text()
188 
190  self._nodesel_widget.set_filter(self._text_filter)
191 
192  # TODO: This method should be integrated into common architecture. I just
193  # can't think of how to do so within current design.
194  def emit_sysmsg(self, msg_str):
195  self.sig_sysmsg.emit(msg_str)
196 
197 
198 if __name__ == '__main__':
199  # main should be used only for debug purpose.
200  # This launches this QWidget as a standalone rqt gui.
201  from rqt_gui.main import Main
202 
203  main = Main()
204  sys.exit(main.main(sys.argv, standalone='rqt_reconfigure'))
def restore_settings(self, plugin_settings, instance_settings)
def __init__(self, context, node=None)
Definition: param_widget.py:65
def save_settings(self, plugin_settings, instance_settings)


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