Package node_manager_fkie :: Module progress_queue
[frames] | no frames]

Source Code for Module node_manager_fkie.progress_queue

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  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 Fraunhofer 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  from python_qt_binding.QtCore import QObject, Signal 
 34  import threading 
 35   
 36  import rospy 
 37   
 38  from node_manager_fkie.common import utf8 
 39  from node_manager_fkie.detailed_msg_box import MessageBox, DetailedError 
 40  import node_manager_fkie as nm 
 41   
 42   
43 -class InteractionNeededError(Exception):
44 ''' 45 request: AuthenticationRequest 46 ''' 47
48 - def __init__(self, request, method, args):
49 Exception.__init__(self) 50 self.method = method 51 self.request = request 52 self.args = args
53
54 - def __str__(self):
55 return "InteractionNeededError"
56 57
58 -class ProgressQueue(QObject):
59 ''' 60 The queue provides a threaded execution of given tasks. 61 ''' 62 no_screen_error_signal = Signal(str, str) 63
64 - def __init__(self, progress_frame, progress_bar, progress_cancel_button, name=''):
65 QObject.__init__(self) 66 self.__progress_queue = [] 67 self.__running = False 68 self._name = name 69 self._progress_frame = progress_frame 70 self._progress_bar = progress_bar 71 self._progress_cancel_button = progress_cancel_button 72 progress_frame.setVisible(False) 73 progress_cancel_button.clicked.connect(self._on_progress_canceled)
74
75 - def stop(self):
76 ''' 77 Deletes all queued tasks and wait 3 seconds for the end of current running 78 thread. 79 ''' 80 try: 81 val = self._progress_bar.value() 82 if val < len(self.__progress_queue): 83 print " Stop progress queue '%s'..." % self._name 84 thread = self.__progress_queue[val] 85 self.__progress_queue = [] 86 if thread.is_alive(): 87 thread.join(3) 88 print " Progress queue '%s' stopped!" % self._name 89 except Exception: 90 import traceback 91 print utf8(traceback.format_exc())
92
93 - def add2queue(self, ident, descr, target=None, args=()):
94 ''' 95 Adds new task to the queue. After the task was added you need call start(). 96 :param ident: the unique identification string 97 :type ident: str 98 :param descr: the description of the task 99 :type descr: str 100 :param target: is the callable object to be invoked in a new thread. 101 Defaults to None, meaning nothing is called. 102 :param args: is the argument tuple for the target invocation. Defaults to () 103 ''' 104 pthread = ProgressThread(ident, descr, target, args) 105 pthread.finished_signal.connect(self._progress_thread_finished) 106 pthread.error_signal.connect(self._progress_thread_error) 107 pthread.request_interact_signal.connect(self._on_request_interact) 108 self.__progress_queue.append(pthread) 109 self._progress_bar.setMaximum(len(self.__progress_queue))
110
111 - def start(self):
112 ''' 113 Starts the execution of tasks in the queue. 114 ''' 115 if not self.__running and self.__progress_queue: 116 self._progress_frame.setVisible(True) 117 self.__running = True 118 self._progress_bar.setToolTip(self.__progress_queue[0].descr) 119 dscr_len = self._progress_bar.size().width() / 10 120 self._progress_bar.setFormat(''.join(['%v/%m - ', self.__progress_queue[0].descr[0:dscr_len]])) 121 self._progress_bar.setValue(0) 122 self.__progress_queue[0].start()
123
124 - def count(self):
125 ''' 126 :return: the count of tasks in the queue 127 :rtype: int 128 ''' 129 return len(self.__progress_queue)
130
131 - def has_id(self, ident):
132 ''' 133 Searches the current and planed threads for given id and returns `True` if 134 one is found. 135 ''' 136 for thread in self.__progress_queue: 137 if thread.id() == ident: 138 return True 139 return False
140
141 - def _progress_thread_finished(self, ident):
142 try: 143 val = self._progress_bar.value() 144 # be on the safe side that the finished thread is the first thread in the 145 # queue (avoid calls from canceled threads) 146 if ident == self.__progress_queue[val].id(): 147 val = val + 1 148 th = self.__progress_queue[val] 149 self._progress_bar.setToolTip(th.descr) 150 dscr_len = self._progress_bar.size().width() / 10 151 self._progress_bar.setFormat(''.join(['%v/%m - ', th.descr[0:dscr_len]])) 152 self.__progress_queue[val].start() 153 self._progress_bar.setValue(val) 154 # print "PG finished ok", id 155 except: 156 # print "PG finished err", id 157 for thread in self.__progress_queue: 158 thread.join(1) 159 self._progress_frame.setVisible(False) 160 self.__running = False 161 # print "PG finished delete all..." 162 self.__progress_queue = []
163 # print "PG finished delete all ok" 164
165 - def _progress_thread_error(self, ident, title, msg, detailed_msg):
166 btns = (MessageBox.Ignore | MessageBox.Avoid) 167 if len(self.__progress_queue) > 1: 168 btns = (btns | MessageBox.Abort) 169 res = MessageBox(MessageBox.Critical, title, msg, detailed_msg, buttons=btns).exec_() 170 if res == MessageBox.Abort: 171 self.__progress_queue = [] 172 self._progress_frame.setVisible(False) 173 self.__running = False 174 else: 175 self._progress_thread_finished(ident)
176
177 - def _on_progress_canceled(self):
178 try: 179 if self.__progress_queue: 180 try: 181 pthread = self.__progress_queue[self._progress_bar.value()] 182 pthread.finished_signal.disconnect(self._progress_thread_finished) 183 pthread.error_signal.disconnect(self._progress_thread_error) 184 pthread.request_interact_signal.disconnect(self._on_request_interact) 185 # self.__progress_queue[self._progress_bar.value()].terminate() 186 except: 187 pass 188 self.__progress_queue = [] 189 self._progress_frame.setVisible(False) 190 self.__running = False 191 except: 192 import traceback 193 print utf8(traceback.format_exc(1))
194
195 - def _on_request_interact(self, ident, descr, req):
196 ''' 197 If the interaction of the user is needed a message dialog must be exceuted 198 in the main Qt thread. The requests are done by different request exceptinos. 199 These are handled by this method. 200 ''' 201 if isinstance(req.request, nm.AuthenticationRequest): 202 res, user, pw = nm.ssh()._requestPW(req.request.user, req.request.host) 203 if not res: 204 self._on_progress_canceled() 205 return 206 if req.args and isinstance(req.args[0], nm.AdvRunCfg): 207 req.args[0].user = user 208 req.args[0].pw = pw 209 else: 210 req.args = req.args + (user, pw) 211 pt = ProgressThread(ident, descr, req.method, (req.args)) 212 pt.finished_signal.connect(self._progress_thread_finished) 213 pt.error_signal.connect(self._progress_thread_error) 214 pt.request_interact_signal.connect(self._on_request_interact) 215 pt.start() 216 elif isinstance(req.request, nm.ScreenSelectionRequest): 217 from node_manager_fkie.select_dialog import SelectDialog 218 items, _ = SelectDialog.getValue('Show screen', '', req.request.choices.keys(), False) 219 if not items: 220 self._progress_thread_finished(ident) 221 return 222 res = [req.request.choices[i] for i in items] 223 pt = ProgressThread(ident, descr, req.method, (req.args + (res,))) 224 pt.finished_signal.connect(self._progress_thread_finished) 225 pt.error_signal.connect(self._progress_thread_error) 226 pt.request_interact_signal.connect(self._on_request_interact) 227 pt.start() 228 elif isinstance(req.request, nm.BinarySelectionRequest): 229 from node_manager_fkie.select_dialog import SelectDialog 230 items, _ = SelectDialog.getValue('Multiple executables', '', req.request.choices, True) 231 if not items: 232 self._progress_thread_finished(ident) 233 return 234 res = items[0] 235 if req.args and isinstance(req.args[0], nm.AdvRunCfg): 236 req.args[0].executable = res 237 else: 238 req.args = req.args + (res,) 239 pt = ProgressThread(ident, descr, req.method, (req.args)) 240 pt.finished_signal.connect(self._progress_thread_finished) 241 pt.error_signal.connect(self._progress_thread_error) 242 pt.request_interact_signal.connect(self._on_request_interact) 243 pt.start() 244 elif isinstance(req.request, nm.LaunchArgsSelectionRequest): 245 from node_manager_fkie.parameter_dialog import ParameterDialog 246 input_dia = ParameterDialog(req.request.args_dict) 247 input_dia.setFilterVisible(False) 248 input_dia.setWindowTitle('Enter the argv for %s' % req.request.launchfile) 249 if input_dia.exec_(): 250 params = input_dia.getKeywords() 251 argv = [] 252 for prm, val in params.items(): 253 if val: 254 argv.append('%s:=%s' % (prm, val)) 255 res = argv 256 pt = ProgressThread(ident, descr, req.method, (req.args + (argv,))) 257 pt.finished_signal.connect(self._progress_thread_finished) 258 pt.error_signal.connect(self._progress_thread_error) 259 pt.request_interact_signal.connect(self._on_request_interact) 260 pt.start() 261 else: 262 self._progress_thread_finished(ident) 263 return 264 elif isinstance(req.request, nm.NoScreenOpenLogRequest): 265 self.no_screen_error_signal.emit(req.request. node, req.request.host) 266 self._progress_thread_finished(ident)
267 268
269 -class ProgressThread(QObject, threading.Thread):
270 ''' 271 A thread to execute a method in a thread. 272 ''' 273 finished_signal = Signal(str) 274 ''' 275 @ivar: finished_signal is a signal, which is emitted, if the thread is finished. 276 ''' 277 278 error_signal = Signal(str, str, str, str) 279 ''' 280 @ivar: error_signal is a signal (id, title, error message, detailed error message), 281 which is emitted, if an error while run of the thread was occurred. 282 ''' 283 284 request_interact_signal = Signal(str, str, InteractionNeededError) 285
286 - def __init__(self, ident, descr='', target=None, args=()):
287 QObject.__init__(self) 288 threading.Thread.__init__(self) 289 self._id = ident 290 self.descr = descr 291 self._target = target 292 self._args = args 293 self.setDaemon(True)
294
295 - def id(self):
296 return self._id
297
298 - def run(self):
299 ''' 300 ''' 301 try: 302 if self._target is not None: 303 if 'pqid' in self._target.func_code.co_varnames: 304 self._target(*self._args, pqid=self._id) 305 else: 306 self._target(*self._args) 307 self.finished_signal.emit(self._id) 308 else: 309 self.error_signal.emit(self._id, 'No target specified') 310 except InteractionNeededError as ine: 311 self.request_interact_signal.emit(self._id, self.descr, ine) 312 except DetailedError as err: 313 self.error_signal.emit(self._id, err.title, err.value, err.detailed_text) 314 except Exception: 315 import traceback 316 # print traceback.print_exc() 317 formatted_lines = traceback.format_exc(1).splitlines() 318 last_line = formatted_lines[-1] 319 index = 1 320 while not last_line and len(formatted_lines) > index: 321 index += 1 322 last_line = formatted_lines[-index] 323 self.error_signal.emit(self._id, 'Progress Job Error', 324 "%s failed:\n%s" % (utf8(self.descr), utf8(last_line)), 325 utf8(traceback.format_exc(4))) 326 rospy.logwarn("%s failed:\n\t%s", utf8(self.descr), utf8(last_line))
327