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  try: 
 35      from python_qt_binding.QtGui import QMessageBox 
 36  except: 
 37      from python_qt_binding.QtWidgets import QMessageBox 
 38  import threading 
 39   
 40  import rospy 
 41   
 42  from node_manager_fkie.detailed_msg_box import WarningMessageBox, DetailedError 
 43  import node_manager_fkie as nm 
 44   
 45   
46 -class InteractionNeededError(Exception):
47 ''' 48 request: AuthenticationRequest 49 ''' 50
51 - def __init__(self, request, method, args):
52 Exception.__init__(self) 53 self.method = method 54 self.request = request 55 self.args = args
56
57 - def __str__(self):
58 return "InteractionNeededError"
59 60
61 -class ProgressQueue(QObject):
62 ''' 63 The queue provides a threaded execution of given tasks. 64 ''' 65
66 - def __init__(self, progress_frame, progress_bar, progress_cancel_button):
67 QObject.__init__(self) 68 self.__ignore_err_list = [] 69 self.__progress_queue = [] 70 self.__running = False 71 self._progress_frame = progress_frame 72 self._progress_bar = progress_bar 73 self._progress_cancel_button = progress_cancel_button 74 progress_frame.setVisible(False) 75 progress_cancel_button.clicked.connect(self._on_progress_canceled)
76
77 - def stop(self):
78 ''' 79 Deletes all queued tasks and wait 3 seconds for the end of current running 80 thread. 81 ''' 82 try: 83 val = self._progress_bar.value() 84 if val < len(self.__progress_queue): 85 print " Shutdown progress queue..." 86 thread = self.__progress_queue[val] 87 self.__progress_queue = [] 88 thread.join(3) 89 print " Progress queue is off!" 90 except: 91 pass
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 if detailed_msg in self.__ignore_err_list: 167 self._progress_thread_finished(ident) 168 return 169 btns = (QMessageBox.Ignore) 170 if len(self.__progress_queue) > 1: 171 btns = (QMessageBox.Ignore | QMessageBox.Abort) 172 res = WarningMessageBox(QMessageBox.Warning, title, msg, detailed_msg, 173 buttons=btns).exec_() 174 if res == QMessageBox.Abort: 175 self.__progress_queue = [] 176 self._progress_frame.setVisible(False) 177 self.__running = False 178 else: 179 # HACK: the number is returned, if "Don't show again" is pressed, 180 # instead 'QMessageBox.HelpRole' (4) 181 if res == 1 or res == 0: 182 self.__ignore_err_list.append(detailed_msg) 183 self._progress_thread_finished(ident)
184
185 - def _on_progress_canceled(self):
186 try: 187 if self.__progress_queue: 188 try: 189 pthread = self.__progress_queue[self._progress_bar.value()] 190 pthread.finished_signal.disconnect(self._progress_thread_finished) 191 pthread.error_signal.disconnect(self._progress_thread_error) 192 pthread.request_interact_signal.disconnect(self._on_request_interact) 193 # self.__progress_queue[self._progress_bar.value()].terminate() 194 except: 195 pass 196 self.__progress_queue = [] 197 self._progress_frame.setVisible(False) 198 self.__running = False 199 except: 200 import traceback 201 print traceback.format_exc(1)
202
203 - def _on_request_interact(self, ident, descr, req):
204 ''' 205 If the interaction of the user is needed a message dialog must be exceuted 206 in the main Qt thread. The requests are done by different request exceptinos. 207 These are handled by this method. 208 ''' 209 if isinstance(req.request, nm.AuthenticationRequest): 210 res, user, pw = nm.ssh()._requestPW(req.request.user, req.request.host) 211 if not res: 212 self._on_progress_canceled() 213 return 214 if req.args and isinstance(req.args[0], nm.AdvRunCfg): 215 req.args[0].user = user 216 req.args[0].pw = pw 217 else: 218 req.args = req.args + (user, pw) 219 pt = ProgressThread(ident, descr, req.method, (req.args)) 220 pt.finished_signal.connect(self._progress_thread_finished) 221 pt.error_signal.connect(self._progress_thread_error) 222 pt.request_interact_signal.connect(self._on_request_interact) 223 pt.start() 224 elif isinstance(req.request, nm.ScreenSelectionRequest): 225 from node_manager_fkie.select_dialog import SelectDialog 226 items, _ = SelectDialog.getValue('Show screen', '', req.request.choices.keys(), False) 227 if not items: 228 self._progress_thread_finished(ident) 229 return 230 res = [req.request.choices[i] for i in items] 231 pt = ProgressThread(ident, descr, req.method, (req.args + (res,))) 232 pt.finished_signal.connect(self._progress_thread_finished) 233 pt.error_signal.connect(self._progress_thread_error) 234 pt.request_interact_signal.connect(self._on_request_interact) 235 pt.start() 236 elif isinstance(req.request, nm.BinarySelectionRequest): 237 from node_manager_fkie.select_dialog import SelectDialog 238 items, _ = SelectDialog.getValue('Multiple executables', '', req.request.choices, True) 239 if not items: 240 self._progress_thread_finished(ident) 241 return 242 res = items[0] 243 if req.args and isinstance(req.args[0], nm.AdvRunCfg): 244 req.args[0].executable = res 245 else: 246 req.args = req.args + (res,) 247 pt = ProgressThread(ident, descr, req.method, (req.args)) 248 pt.finished_signal.connect(self._progress_thread_finished) 249 pt.error_signal.connect(self._progress_thread_error) 250 pt.request_interact_signal.connect(self._on_request_interact) 251 pt.start() 252 elif isinstance(req.request, nm.LaunchArgsSelectionRequest): 253 from node_manager_fkie.parameter_dialog import ParameterDialog 254 input_dia = ParameterDialog(req.request.args_dict) 255 input_dia.setFilterVisible(False) 256 input_dia.setWindowTitle('Enter the argv for %s' % req.request.launchfile) 257 if input_dia.exec_(): 258 params = input_dia.getKeywords() 259 argv = [] 260 for prm, val in params.items(): 261 if val: 262 argv.append('%s:=%s' % (prm, val)) 263 res = argv 264 pt = ProgressThread(ident, descr, req.method, (req.args + (argv,))) 265 pt.finished_signal.connect(self._progress_thread_finished) 266 pt.error_signal.connect(self._progress_thread_error) 267 pt.request_interact_signal.connect(self._on_request_interact) 268 pt.start() 269 else: 270 self._progress_thread_finished(ident) 271 return
272 273
274 -class ProgressThread(QObject, threading.Thread):
275 ''' 276 A thread to execute a method in a thread. 277 ''' 278 finished_signal = Signal(str) 279 ''' 280 @ivar: finished_signal is a signal, which is emitted, if the thread is finished. 281 ''' 282 283 error_signal = Signal(str, str, str, str) 284 ''' 285 @ivar: error_signal is a signal (id, title, error message, detailed error message), 286 which is emitted, if an error while run of the thread was occurred. 287 ''' 288 289 request_interact_signal = Signal(str, str, InteractionNeededError) 290
291 - def __init__(self, ident, descr='', target=None, args=()):
292 QObject.__init__(self) 293 threading.Thread.__init__(self) 294 self._id = ident 295 self.descr = descr 296 self._target = target 297 self._args = args 298 self.setDaemon(True)
299
300 - def id(self):
301 return self._id
302
303 - def run(self):
304 ''' 305 ''' 306 try: 307 if self._target is not None: 308 if 'pqid' in self._target.func_code.co_varnames: 309 self._target(*self._args, pqid=self._id) 310 else: 311 self._target(*self._args) 312 self.finished_signal.emit(self._id) 313 else: 314 self.error_signal.emit(self._id, 'No target specified') 315 except InteractionNeededError as ine: 316 self.request_interact_signal.emit(self._id, self.descr, ine) 317 except DetailedError as err: 318 self.error_signal.emit(self._id, err.title, err.value, err.detailed_text) 319 except: 320 import traceback 321 # print traceback.print_exc() 322 formatted_lines = traceback.format_exc(1).splitlines() 323 last_line = formatted_lines[-1] 324 index = 1 325 while not last_line and len(formatted_lines) > index: 326 index += 1 327 last_line = formatted_lines[-index] 328 rospy.logwarn("%s failed:\n\t%s", str(self.descr), last_line) 329 self.error_signal.emit(self._id, 'Progress Job Error', 330 "%s failed:\n%s" % (str(self.descr), last_line), 331 traceback.format_exc(4))
332