def __init__(self): QAbstractTableModel.__init__(self) SearchQueryParser.__init__(self, ['all']) self.wait_icon = (QIcon(I('jobs.png'))) self.running_icon = (QIcon(I('exec.png'))) self.error_icon = (QIcon(I('dialog_error.png'))) self.done_icon = (QIcon(I('ok.png'))) self.jobs = [] self.add_job = Dispatcher(self._add_job) self.server = Server(limit=int(config['worker_limit'] / 2.0), enforce_cpu_limit=config['enforce_cpu_limit']) self.threaded_server = ThreadedJobServer() self.changed_queue = Queue() self.timer = QTimer(self) self.timer.timeout.connect(self.update, type=Qt.QueuedConnection) self.timer.start(1000)
def __init__(self): QAbstractTableModel.__init__(self) SearchQueryParser.__init__(self, ["all"]) self.wait_icon = QVariant(QIcon(I("jobs.png"))) self.running_icon = QVariant(QIcon(I("exec.png"))) self.error_icon = QVariant(QIcon(I("dialog_error.png"))) self.done_icon = QVariant(QIcon(I("ok.png"))) self.jobs = [] self.add_job = Dispatcher(self._add_job) self.server = Server(limit=int(config["worker_limit"] / 2.0), enforce_cpu_limit=config["enforce_cpu_limit"]) self.threaded_server = ThreadedJobServer() self.changed_queue = Queue() self.timer = QTimer(self) self.timer.timeout.connect(self.update, type=Qt.QueuedConnection) self.timer.start(1000)
class JobManager(QAbstractTableModel, AdaptSQP): # {{{ job_added = pyqtSignal(int) job_done = pyqtSignal(int) def __init__(self): QAbstractTableModel.__init__(self) SearchQueryParser.__init__(self, ['all']) self.wait_icon = (QIcon(I('jobs.png'))) self.running_icon = (QIcon(I('exec.png'))) self.error_icon = (QIcon(I('dialog_error.png'))) self.done_icon = (QIcon(I('ok.png'))) self.jobs = [] self.add_job = Dispatcher(self._add_job) self.server = Server(limit=config['worker_limit'] // 2, enforce_cpu_limit=config['enforce_cpu_limit']) self.threaded_server = ThreadedJobServer() self.changed_queue = Queue() self.timer = QTimer(self) self.timer.timeout.connect(self.update, type=Qt.ConnectionType.QueuedConnection) self.timer.start(1000) def columnCount(self, parent=QModelIndex()): return 5 def rowCount(self, parent=QModelIndex()): return len(self.jobs) def headerData(self, section, orientation, role): if role != Qt.ItemDataRole.DisplayRole: return None if orientation == Qt.Orientation.Horizontal: return ({ 0: _('Job'), 1: _('Status'), 2: _('Progress'), 3: _('Running time'), 4: _('Start time'), }.get(section, '')) else: return (section + 1) def show_tooltip(self, arg): widget, pos = arg QToolTip.showText(pos, self.get_tooltip()) def get_tooltip(self): running_jobs = [j for j in self.jobs if j.run_state == j.RUNNING] waiting_jobs = [j for j in self.jobs if j.run_state == j.WAITING] lines = [ ngettext('There is a running job:', 'There are {} running jobs:', len(running_jobs)).format(len(running_jobs)) ] for job in running_jobs: desc = job.description if not desc: desc = _('Unknown job') p = 100. if job.is_finished else job.percent lines.append('%s: %.0f%% done' % (desc, p)) l = ngettext('There is a waiting job', 'There are {} waiting jobs', len(waiting_jobs)).format(len(waiting_jobs)) lines.extend(['', l]) for job in waiting_jobs: desc = job.description if not desc: desc = _('Unknown job') lines.append(desc) return '\n'.join(['calibre', ''] + lines) def data(self, index, role): try: if role not in (Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.DecorationRole): return None row, col = index.row(), index.column() job = self.jobs[row] if role == Qt.ItemDataRole.DisplayRole: if col == 0: desc = job.description if not desc: desc = _('Unknown job') return (desc) if col == 1: return (job.status_text) if col == 2: p = 100. if job.is_finished else job.percent return (p) if col == 3: rtime = job.running_time if rtime is None: return None return human_readable_interval(rtime) if col == 4 and job.start_time is not None: return (strftime('%H:%M -- %d %b', time.localtime(job.start_time))) if role == Qt.ItemDataRole.DecorationRole and col == 0: state = job.run_state if state == job.WAITING: return self.wait_icon if state == job.RUNNING: return self.running_icon if job.killed or job.failed: return self.error_icon return self.done_icon except: import traceback traceback.print_exc() return None def update(self): try: self._update() except BaseException: import traceback traceback.print_exc() def _update(self): # Update running time for i, j in enumerate(self.jobs): if j.run_state == j.RUNNING: idx = self.index(i, 3) self.dataChanged.emit(idx, idx) # Update parallel jobs jobs = set() while True: try: jobs.add(self.server.changed_jobs_queue.get_nowait()) except Empty: break # Update device jobs while True: try: jobs.add(self.changed_queue.get_nowait()) except Empty: break # Update threaded jobs while True: try: jobs.add(self.threaded_server.changed_jobs.get_nowait()) except Empty: break if jobs: needs_reset = False for job in jobs: orig_state = job.run_state job.update() if orig_state != job.run_state: needs_reset = True if job.is_finished: self.job_done.emit(len(self.unfinished_jobs())) if needs_reset: self.modelAboutToBeReset.emit() self.jobs.sort() self.modelReset.emit() else: for job in jobs: idx = self.jobs.index(job) self.dataChanged.emit(self.index(idx, 0), self.index(idx, 3)) # Kill parallel jobs that have gone on too long try: wmax_time = gprefs['worker_max_time'] * 60 except: wmax_time = 0 if wmax_time > 0: for job in self.jobs: if isinstance(job, ParallelJob): rtime = job.running_time if (rtime is not None and rtime > wmax_time and job.duration is None): job.timed_out = True self.server.kill_job(job) def _add_job(self, job): self.modelAboutToBeReset.emit() self.jobs.append(job) self.jobs.sort() self.job_added.emit(len(self.unfinished_jobs())) self.modelReset.emit() def done_jobs(self): return [j for j in self.jobs if j.is_finished] def unfinished_jobs(self): return [j for j in self.jobs if not j.is_finished] def row_to_job(self, row): return self.jobs[row] def rows_to_jobs(self, rows): return [self.jobs[row] for row in rows] def has_device_jobs(self, queued_also=False): for job in self.jobs: if isinstance(job, DeviceJob): if job.duration is None: # Running or waiting if (job.is_running or queued_also): return True return False def has_jobs(self): for job in self.jobs: if job.is_running: return True return False def run_job(self, done, name, args=[], kwargs={}, description='', core_usage=1): job = ParallelJob(name, description, done, args=args, kwargs=kwargs) job.core_usage = core_usage self.add_job(job) self.server.add_job(job) return job def run_threaded_job(self, job): self.add_job(job) self.threaded_server.add_job(job) def launch_gui_app(self, name, args=(), kwargs=None, description=''): job = ParallelJob(name, description, lambda x: x, args=list(args), kwargs=kwargs or {}) self.server.run_job(job, gui=True, redirect_output=False) def _kill_job(self, job): if isinstance(job, ParallelJob): self.server.kill_job(job) elif isinstance(job, ThreadedJob): self.threaded_server.kill_job(job) else: job.kill_on_start = True def hide_jobs(self, rows): for r in rows: self.jobs[r].hidden_in_gui = True for r in rows: self.dataChanged.emit(self.index(r, 0), self.index(r, 0)) def show_hidden_jobs(self): for j in self.jobs: j.hidden_in_gui = False for r in range(len(self.jobs)): self.dataChanged.emit(self.index(r, 0), self.index(r, 0)) def kill_job(self, job, view): if isinstance(job, DeviceJob): return error_dialog( view, _('Cannot kill job'), _('Cannot kill jobs that communicate with the device')).exec_( ) if job.duration is not None: return error_dialog(view, _('Cannot kill job'), _('Job has already run')).exec_() if not getattr(job, 'killable', True): return error_dialog(view, _('Cannot kill job'), _('This job cannot be stopped'), show=True) self._kill_job(job) def kill_multiple_jobs(self, jobs, view): devjobs = [j for j in jobs if isinstance(j, DeviceJob)] if devjobs: error_dialog( view, _('Cannot kill job'), _('Cannot kill jobs that communicate with the device')).exec_( ) jobs = [j for j in jobs if not isinstance(j, DeviceJob)] jobs = [j for j in jobs if j.duration is None] unkillable = [j for j in jobs if not getattr(j, 'killable', True)] if unkillable: names = '\n'.join(as_unicode(j.description) for j in unkillable) error_dialog( view, _('Cannot kill job'), _('Some of the jobs cannot be stopped. Click "Show details"' ' to see the list of unstoppable jobs.'), det_msg=names, show=True) jobs = [j for j in jobs if getattr(j, 'killable', True)] jobs = [j for j in jobs if j.duration is None] for j in jobs: self._kill_job(j) def kill_all_jobs(self): for job in self.jobs: if (isinstance(job, DeviceJob) or job.duration is not None or not getattr(job, 'killable', True)): continue self._kill_job(job) def terminate_all_jobs(self): self.server.killall() for job in self.jobs: if (isinstance(job, DeviceJob) or job.duration is not None or not getattr(job, 'killable', True)): continue if not isinstance(job, ParallelJob): self._kill_job(job) def universal_set(self): return { i for i, j in enumerate(self.jobs) if not getattr(j, 'hidden_in_gui', False) } def get_matches(self, location, query, candidates=None): if candidates is None: candidates = self.universal_set() ans = set() if not query: return ans query = lower(query) for j in candidates: job = self.jobs[j] if job.description and query in lower(job.description): ans.add(j) return ans def find(self, query): query = query.strip() rows = self.parse(query) return rows
class JobManager(QAbstractTableModel, SearchQueryParser): # {{{ job_added = pyqtSignal(int) job_done = pyqtSignal(int) def __init__(self): QAbstractTableModel.__init__(self) SearchQueryParser.__init__(self, ['all']) self.wait_icon = QVariant(QIcon(I('jobs.png'))) self.running_icon = QVariant(QIcon(I('exec.png'))) self.error_icon = QVariant(QIcon(I('dialog_error.png'))) self.done_icon = QVariant(QIcon(I('ok.png'))) self.jobs = [] self.add_job = Dispatcher(self._add_job) self.server = Server(limit=int(config['worker_limit']/2.0), enforce_cpu_limit=config['enforce_cpu_limit']) self.threaded_server = ThreadedJobServer() self.changed_queue = Queue() self.timer = QTimer(self) self.timer.timeout.connect(self.update, type=Qt.QueuedConnection) self.timer.start(1000) def columnCount(self, parent=QModelIndex()): return 5 def rowCount(self, parent=QModelIndex()): return len(self.jobs) def headerData(self, section, orientation, role): if role != Qt.DisplayRole: return NONE if orientation == Qt.Horizontal: return QVariant({ 0: _('Job'), 1: _('Status'), 2: _('Progress'), 3: _('Running time'), 4: _('Start time'), }.get(section, '')) else: return QVariant(section+1) def show_tooltip(self, arg): widget, pos = arg QToolTip.showText(pos, self.get_tooltip()) def get_tooltip(self): running_jobs = [j for j in self.jobs if j.run_state == j.RUNNING] waiting_jobs = [j for j in self.jobs if j.run_state == j.WAITING] lines = [_('There are %d running jobs:')%len(running_jobs)] for job in running_jobs: desc = job.description if not desc: desc = _('Unknown job') p = 100. if job.is_finished else job.percent lines.append('%s: %.0f%% done'%(desc, p)) lines.extend(['', _('There are %d waiting jobs:')%len(waiting_jobs)]) for job in waiting_jobs: desc = job.description if not desc: desc = _('Unknown job') lines.append(desc) return '\n'.join(['calibre', '']+ lines) def data(self, index, role): try: if role not in (Qt.DisplayRole, Qt.DecorationRole): return NONE row, col = index.row(), index.column() job = self.jobs[row] if role == Qt.DisplayRole: if col == 0: desc = job.description if not desc: desc = _('Unknown job') return QVariant(desc) if col == 1: return QVariant(job.status_text) if col == 2: p = 100. if job.is_finished else job.percent return QVariant(p) if col == 3: rtime = job.running_time if rtime is None: return NONE return QVariant('%dm %ds'%(int(rtime)//60, int(rtime)%60)) if col == 4 and job.start_time is not None: return QVariant(time.strftime('%H:%M -- %d %b', time.localtime(job.start_time))) if role == Qt.DecorationRole and col == 0: state = job.run_state if state == job.WAITING: return self.wait_icon if state == job.RUNNING: return self.running_icon if job.killed or job.failed: return self.error_icon return self.done_icon except: import traceback traceback.print_exc() return NONE def update(self): try: self._update() except BaseException: import traceback traceback.print_exc() def _update(self): # Update running time for i, j in enumerate(self.jobs): if j.run_state == j.RUNNING: idx = self.index(i, 3) self.dataChanged.emit(idx, idx) # Update parallel jobs jobs = set([]) while True: try: jobs.add(self.server.changed_jobs_queue.get_nowait()) except Empty: break # Update device jobs while True: try: jobs.add(self.changed_queue.get_nowait()) except Empty: break # Update threaded jobs while True: try: jobs.add(self.threaded_server.changed_jobs.get_nowait()) except Empty: break if jobs: needs_reset = False for job in jobs: orig_state = job.run_state job.update() if orig_state != job.run_state: needs_reset = True if job.is_finished: self.job_done.emit(len(self.unfinished_jobs())) if needs_reset: self.layoutAboutToBeChanged.emit() self.jobs.sort() self.layoutChanged.emit() else: for job in jobs: idx = self.jobs.index(job) self.dataChanged.emit( self.index(idx, 0), self.index(idx, 3)) # Kill parallel jobs that have gone on too long try: wmax_time = gprefs['worker_max_time'] * 60 except: wmax_time = 0 if wmax_time > 0: for job in self.jobs: if isinstance(job, ParallelJob): rtime = job.running_time if (rtime is not None and rtime > wmax_time and job.duration is None): job.timed_out = True self.server.kill_job(job) def _add_job(self, job): self.layoutAboutToBeChanged.emit() self.jobs.append(job) self.jobs.sort() self.job_added.emit(len(self.unfinished_jobs())) self.layoutChanged.emit() def done_jobs(self): return [j for j in self.jobs if j.is_finished] def unfinished_jobs(self): return [j for j in self.jobs if not j.is_finished] def row_to_job(self, row): return self.jobs[row] def has_device_jobs(self, queued_also=False): for job in self.jobs: if isinstance(job, DeviceJob): if job.duration is None: # Running or waiting if (job.is_running or queued_also): return True return False def has_jobs(self): for job in self.jobs: if job.is_running: return True return False def run_job(self, done, name, args=[], kwargs={}, description='', core_usage=1): job = ParallelJob(name, description, done, args=args, kwargs=kwargs) job.core_usage = core_usage self.add_job(job) self.server.add_job(job) return job def run_threaded_job(self, job): self.add_job(job) self.threaded_server.add_job(job) def launch_gui_app(self, name, args=[], kwargs={}, description=''): job = ParallelJob(name, description, lambda x: x, args=args, kwargs=kwargs) self.server.run_job(job, gui=True, redirect_output=False) def _kill_job(self, job): if isinstance(job, ParallelJob): self.server.kill_job(job) elif isinstance(job, ThreadedJob): self.threaded_server.kill_job(job) else: job.kill_on_start = True def hide_jobs(self, rows): for r in rows: self.jobs[r].hidden_in_gui = True for r in rows: self.dataChanged.emit(self.index(r, 0), self.index(r, 0)) def show_hidden_jobs(self): for j in self.jobs: j.hidden_in_gui = False for r in xrange(len(self.jobs)): self.dataChanged.emit(self.index(r, 0), self.index(r, 0)) def kill_job(self, row, view): job = self.jobs[row] if isinstance(job, DeviceJob): return error_dialog(view, _('Cannot kill job'), _('Cannot kill jobs that communicate with the device')).exec_() if job.duration is not None: return error_dialog(view, _('Cannot kill job'), _('Job has already run')).exec_() if not getattr(job, 'killable', True): return error_dialog(view, _('Cannot kill job'), _('This job cannot be stopped'), show=True) self._kill_job(job) def kill_multiple_jobs(self, rows, view): jobs = [self.jobs[row] for row in rows] devjobs = [j for j in jobs if isinstance(j, DeviceJob)] if devjobs: error_dialog(view, _('Cannot kill job'), _('Cannot kill jobs that communicate with the device')).exec_() jobs = [j for j in jobs if not isinstance(j, DeviceJob)] jobs = [j for j in jobs if j.duration is None] unkillable = [j for j in jobs if not getattr(j, 'killable', True)] if unkillable: names = u'\n'.join(as_unicode(j.description) for j in unkillable) error_dialog(view, _('Cannot kill job'), _('Some of the jobs cannot be stopped. Click Show details' ' to see the list of unstoppable jobs.'), det_msg=names, show=True) jobs = [j for j in jobs if getattr(j, 'killable', True)] jobs = [j for j in jobs if j.duration is None] for j in jobs: self._kill_job(j) def kill_all_jobs(self): for job in self.jobs: if (isinstance(job, DeviceJob) or job.duration is not None or not getattr(job, 'killable', True)): continue self._kill_job(job) def terminate_all_jobs(self): self.server.killall() for job in self.jobs: if (isinstance(job, DeviceJob) or job.duration is not None or not getattr(job, 'killable', True)): continue if not isinstance(job, ParallelJob): self._kill_job(job) def universal_set(self): return set([i for i, j in enumerate(self.jobs) if not getattr(j, 'hidden_in_gui', False)]) def get_matches(self, location, query, candidates=None): if candidates is None: candidates = self.universal_set() ans = set() if not query: return ans query = lower(query) for j in candidates: job = self.jobs[j] if job.description and query in lower(job.description): ans.add(j) return ans def find(self, query): query = query.strip() rows = self.parse(query) return rows