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