Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 from __future__ import division
00036
00037 import threading
00038 import time
00039
00040 import dynamic_reconfigure.client
00041 from python_qt_binding.QtCore import Qt
00042 from python_qt_binding.QtGui import QBrush, QStandardItem
00043 import rospy
00044 from rospy.exceptions import ROSException
00045 from rqt_py_common.data_items import ReadonlyItem
00046
00047 from . import logging
00048 from .dynreconf_client_widget import DynreconfClientWidget
00049
00050
00051 class ParamserverConnectThread(threading.Thread):
00052 def __init__(self, parent, param_name_raw):
00053 super(ParamserverConnectThread, self).__init__()
00054 self._parent = parent
00055 self._param_name_raw = param_name_raw
00056
00057 def run(self):
00058 dynreconf_client = None
00059 try:
00060 dynreconf_client = dynamic_reconfigure.client.Client(
00061 str(self._param_name_raw), timeout=5.0)
00062 logging.debug('ParamserverConnectThread dynreconf_client={}'. \
00063 format(dynreconf_client))
00064 self._parent.set_dynreconf_client(dynreconf_client)
00065 except rospy.exceptions.ROSException as e:
00066 raise type(e)(e.message +
00067 "TreenodeQstdItem. Couldn't connect to {}".format(
00068 self._param_name_raw))
00069
00070
00071 class TreenodeQstdItem(ReadonlyItem):
00072 """
00073 Extending ReadonlyItem - the display content of this item shouldn't be
00074 modified.
00075 """
00076
00077 NODE_FULLPATH = 1
00078
00079 def __init__(self, *args):
00080 """
00081 :param args[0]: str (will become 1st arg of QStandardItem)
00082 :param args[1]: integer value that indicates whether this class
00083 is node that has GRN (Graph Resource Names, see
00084 http://www.ros.org/wiki/Names). This can be None
00085 """
00086 grn_current_treenode = args[0]
00087 self._param_name_raw = grn_current_treenode
00088 self._set_param_name(grn_current_treenode)
00089 super(TreenodeQstdItem, self).__init__(grn_current_treenode)
00090
00091
00092 self._dynreconf_client = None
00093
00094 self._dynreconfclient_widget = None
00095
00096 self._is_rosnode = False
00097
00098 self._lock = threading.Lock()
00099 self._paramserver_connect_thread = None
00100
00101 try:
00102 if args[1]:
00103 self._is_rosnode = True
00104 except IndexError:
00105 logging.error('TreenodeQstdItem IndexError')
00106
00107 def set_dynreconf_client(self, dynreconf_client):
00108 """
00109 @param dynreconf_client: dynamic_reconfigure.client.Client
00110 """
00111 self._dynreconf_client = dynreconf_client
00112 logging.debug('Qitem set dynreconf_client={} param={}'.format(
00113 self._dynreconf_client,
00114 self._param_name_raw))
00115
00116 def clear_dynreconf_client(self):
00117 if self._dynreconf_client is not None:
00118 self._dynreconf_client.close()
00119 del self._dynreconf_client
00120 self._dynreconf_client = None
00121
00122 def get_dynreconf_widget(self):
00123 """
00124 @rtype: DynreconfClientWidget (QWidget)
00125 @return: None if dynreconf_client is not yet generated.
00126 @raise ROSException:
00127 """
00128
00129 if not self._dynreconfclient_widget:
00130 logging.debug('get dynreconf_client={}'.format(
00131 self._dynreconf_client))
00132 logging.debug('In get_dynreconf_widget 1')
00133 if not self._dynreconf_client:
00134 self.connect_param_server()
00135 logging.debug('In get_dynreconf_widget 2')
00136
00137 timeout = 3 * 100
00138 loop = 0
00139
00140
00141 while self._dynreconf_client == None:
00142
00143 if timeout < loop:
00144
00145 self.setEnabled(False)
00146 raise ROSException('dynreconf client failed')
00147
00148 time.sleep(0.01)
00149 loop += 1
00150 logging.debug('In get_dynreconf_widget loop#{}'.format(loop))
00151
00152 logging.debug('In get_dynreconf_widget 4')
00153 self._dynreconfclient_widget = DynreconfClientWidget(
00154 self._dynreconf_client,
00155 self._param_name_raw)
00156
00157
00158
00159 self._dynreconfclient_widget.destroyed.connect(self.clear_dynreconfclient_widget)
00160 self._dynreconfclient_widget.destroyed.connect(self.disconnect_param_server)
00161 logging.debug('In get_dynreconf_widget 5')
00162
00163 else:
00164 pass
00165 return self._dynreconfclient_widget
00166
00167 def clear_dynreconfclient_widget(self):
00168 self._dynreconfclient_widget = None
00169
00170 def connect_param_server(self):
00171 """
00172 Connect to parameter server using dynamic_reconfigure client.
00173 Behavior is delegated to a private method _connect_param_server, and
00174 its return value, client, is set to member variable.
00175
00176 @return void
00177 @raise ROSException:
00178 """
00179
00180 with self._lock:
00181 if not self._is_rosnode:
00182 logging.error('connect_param_server failed due to missing ' +
00183 'ROS Node. Return with nothing.')
00184 return
00185
00186 if not self._dynreconf_client:
00187 if self._paramserver_connect_thread:
00188 if self._paramserver_connect_thread.isAlive():
00189 self._paramserver_connect_thread.join(1)
00190 self._paramserver_connect_thread = ParamserverConnectThread(
00191 self, self._param_name_raw)
00192 self._paramserver_connect_thread.start()
00193
00194 def disconnect_param_server(self):
00195 with self._lock:
00196 if self._paramserver_connect_thread:
00197
00198 if self._paramserver_connect_thread.isAlive():
00199 self._paramserver_connect_thread.join(1)
00200 del self._paramserver_connect_thread
00201 self._paramserver_connect_thread = None
00202 self.clear_dynreconf_client()
00203
00204 def enable_param_items(self):
00205 """
00206 Create QStdItem per parameter and addColumn them to myself.
00207 :rtype: None if _dynreconf_client is not initiated.
00208 """
00209 if not self._dynreconfclient_widget:
00210 return None
00211 paramnames = self._dynreconfclient_widget.get_treenode_names()
00212 paramnames_items = []
00213 brush = QBrush(Qt.lightGray)
00214 for paramname in paramnames:
00215 item = ReadonlyItem(paramname)
00216 item.setBackground(brush)
00217 paramnames_items.append(item)
00218 logging.debug('enable_param_items len of paramnames={}'.format(
00219 len(paramnames_items)))
00220 self.appendColumn(paramnames_items)
00221
00222 def _set_param_name(self, param_name):
00223 """
00224 :param param_name: A string formatted as GRN (Graph Resource Names, see
00225 http://www.ros.org/wiki/Names).
00226 Example: /paramname/subpara/subsubpara/...
00227 """
00228 logging.debug('_set_param_name param_name={} '.format(param_name))
00229
00230
00231 self._list_treenode_names = param_name.split('/')
00232
00233
00234 del self._list_treenode_names[0]
00235
00236 self._toplevel_treenode_name = self._list_treenode_names[0]
00237
00238 logging.debug('paramname={} nodename={} _list_params[-1]={}'.format(
00239 param_name, self._toplevel_treenode_name,
00240 self._list_treenode_names[-1]))
00241
00242 def get_param_name_toplv(self):
00243 """
00244 :rtype: String of the top level param name.
00245 """
00246
00247 return self._name_top
00248
00249 def get_raw_param_name(self):
00250 return self._param_name_raw
00251
00252 def get_treenode_names(self):
00253 """
00254 :rtype: List of string. Null if param
00255 """
00256
00257
00258 return self._list_treenode_names
00259
00260 def get_node_name(self):
00261 """
00262 :return: A value of single tree node (ie. NOT the fullpath node name).
00263 Ex. suppose fullpath name is /top/sub/subsub/subsubsub and you
00264 are at 2nd from top, the return value is subsub.
00265 """
00266 return self._toplevel_treenode_name
00267
00268 def type(self):
00269 return QStandardItem.UserType