treenode_qstditem.py
Go to the documentation of this file.
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2012, Willow Garage, Inc.
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 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 threading
38 import time
39 
40 import dynamic_reconfigure.client
41 
42 from python_qt_binding.QtCore import Qt
43 from python_qt_binding.QtGui import QBrush, QStandardItem
44 
45 from rospy.exceptions import ROSException
46 
47 from rqt_py_common.data_items import ReadonlyItem
48 
49 from . import logging
50 from .dynreconf_client_widget import DynreconfClientWidget
51 
52 
53 class ParamserverConnectThread(threading.Thread):
54 
55  def __init__(self, parent, param_name_raw):
56  super(ParamserverConnectThread, self).__init__()
57  self._parent = parent
58  self._raw_param_name = param_name_raw
59 
60  def run(self):
61  dynreconf_client = None
62  try:
63  dynreconf_client = dynamic_reconfigure.client.Client(
64  str(self._raw_param_name), timeout=5.0)
65  logging.debug(
66  'ParamserverConnectThread dynreconf_client={}'.format(
67  dynreconf_client
68  ))
69  self._parent.set_dynreconf_client(dynreconf_client)
70  except ROSException as e:
71  raise type(e)(
72  e.message + "TreenodeQstdItem. Couldn't connect to {}".format(
73  self._raw_param_name
74  ))
75 
76 
78  """
79  Extending ReadonlyItem - the display content of this item shouldn't be
80  modified.
81  """
82 
83  NODE_FULLPATH = 1
84 
85  def __init__(self, *args):
86  """
87  :param args[0]: str (will become 1st arg of QStandardItem)
88  :param args[1]: integer value that indicates whether this class
89  is node that has GRN (Graph Resource Names, see
90  http://www.ros.org/wiki/Names). This can be None
91  """
92  grn_current_treenode = args[0]
93  self._raw_param_name = grn_current_treenode
94  self._set_param_name(grn_current_treenode)
95  super(TreenodeQstdItem, self).__init__(grn_current_treenode)
96 
97  # dynamic_reconfigure.client.Client
98  self._dynreconf_client = None
99  # DynreconfClientWidget
101 
102  self._is_rosnode = False
103 
104  self._lock = threading.Lock()
106 
107  try:
108  if args[1]:
109  self._is_rosnode = True
110  except IndexError: # tuple index out of range etc.
111  logging.error('TreenodeQstdItem IndexError')
112 
113  def set_dynreconf_client(self, dynreconf_client):
114  """
115  @param dynreconf_client: dynamic_reconfigure.client.Client
116  """
117  self._dynreconf_client = dynreconf_client
118  logging.debug('Qitem set dynreconf_client={} param={}'.format(
120  ))
121 
123  if self._dynreconf_client is not None:
124  self._dynreconf_client.close()
125  del self._dynreconf_client
126  self._dynreconf_client = None
127 
129  """
130  @rtype: DynreconfClientWidget (QWidget)
131  @return: None if dynreconf_client is not yet generated.
132  @raise ROSException:
133  """
134  if not self._dynreconfclient_widget:
135  logging.debug('get dynreconf_client={}'.format(
136  self._dynreconf_client
137  ))
138  logging.debug('In get_dynreconf_widget 1')
139  if not self._dynreconf_client:
140  self.connect_param_server()
141  logging.debug('In get_dynreconf_widget 2')
142 
143  timeout = 3 * 100
144  loop = 0
145  # Loop until _dynreconf_client is set. self._dynreconf_client gets
146  # set from different thread (in ParamserverConnectThread).
147  while self._dynreconf_client is None:
148  # Avoid deadlock
149  if timeout < loop:
150  # Make itself unclickable
151  self.setEnabled(False)
152  raise ROSException('dynreconf client failed')
153 
154  time.sleep(0.01)
155  loop += 1
156  logging.debug('In get_dynreconf_widget loop#{}'.format(loop))
157 
158  logging.debug('In get_dynreconf_widget 4')
159  self._dynreconfclient_widget = DynreconfClientWidget(
161  )
162  # Creating the DynreconfClientWidget transfers ownership of the
163  # _dynreconf_client to it. If it is destroyed from Qt, we need to
164  # clear our reference to it and stop the param server thread we
165  # had.
166  self._dynreconfclient_widget.destroyed.connect(
168  self._dynreconfclient_widget.destroyed.connect(
170  logging.debug('In get_dynreconf_widget 5')
171 
172  else:
173  pass
174  return self._dynreconfclient_widget
175 
177  self._dynreconfclient_widget = None
178 
180  """
181  Connect to parameter server using dynamic_reconfigure client.
182  Behavior is delegated to a private method _connect_param_server, and
183  its return value, client, is set to member variable.
184 
185  @return void
186  @raise ROSException:
187  """
188  # If the treenode doesn't represent ROS Node, return None.
189  with self._lock:
190  if not self._is_rosnode:
191  logging.error('connect_param_server failed due to missing '
192  'ROS Node. Return with nothing.')
193  return
194 
195  if not self._dynreconf_client:
197  if self._paramserver_connect_thread.isAlive():
198  self._paramserver_connect_thread.join(1)
200  self, self._raw_param_name)
201  self._paramserver_connect_thread.start()
202 
204  with self._lock:
206  # Try to stop the thread
207  if self._paramserver_connect_thread.isAlive():
208  self._paramserver_connect_thread.join(1)
210  self._paramserver_connect_thread = None
212 
214  """
215  Create QStdItem per parameter and addColumn them to myself.
216  :rtype: None if _dynreconf_client is not initiated.
217  """
218  if not self._dynreconfclient_widget:
219  return None
220  param_names = self._dynreconfclient_widget.get_treenode_names()
221  param_names_items = []
222  brush = QBrush(Qt.lightGray)
223  for param_name in param_names:
224  item = ReadonlyItem(param_name)
225  item.setBackground(brush)
226  param_names_items.append(item)
227  logging.debug('enable_param_items len of paramnames={}'.format(
228  len(param_names_items)
229  ))
230  self.appendColumn(param_names_items)
231 
232  def _set_param_name(self, param_name):
233  """
234  :param param_name: A string formatted as GRN (Graph Resource Names, see
235  http://www.ros.org/wiki/Names).
236  Example: /paramname/subpara/subsubpara/...
237  """
238  logging.debug('_set_param_name param_name={} '.format(param_name))
239 
240  # separate param_name by forward slash
241  self._list_treenode_names = param_name.split('/')
242 
243  # Deleting the 1st elem which is zero-length str.
244  del self._list_treenode_names[0]
245 
247 
248  logging.debug('paramname={} nodename={} _list_params[-1]={}'.format(
249  param_name, self._toplevel_treenode_name,
250  self._list_treenode_names[-1]
251  ))
252 
254  """
255  :rtype: String of the top level param name.
256  """
257  return self._name_top
258 
260  return self._raw_param_name
261 
263  """
264  :rtype: List of string. Null if param
265  """
266  # TODO: what if self._list_treenode_names is empty or null?
267  return self._list_treenode_names
268 
269  def get_node_name(self):
270  """
271  :return: A value of single tree node (ie. NOT the fullpath node name).
272  Ex. suppose fullpath name is /top/sub/subsub/subsubsub and you
273  are at 2nd from top, the return value is subsub.
274  """
275  return self._toplevel_treenode_name
276 
277  def type(self):
278  return QStandardItem.UserType


rqt_reconfigure
Author(s): Isaac Saito, Ze'ev Klapow
autogenerated on Wed Jul 10 2019 04:02:40