mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Allow jobs to be killed from the GUI
This commit is contained in:
parent
ff46adc03e
commit
76840c13e4
@ -31,6 +31,16 @@ class JobsDialog(QDialog, Ui_JobsDialog):
|
||||
self.setWindowTitle(__appname__ + ' - Active Jobs')
|
||||
QObject.connect(self.jobs_view.model(), SIGNAL('modelReset()'),
|
||||
self.jobs_view.resizeColumnsToContents)
|
||||
QObject.connect(self.kill_button, SIGNAL('clicked()'),
|
||||
self.kill_job)
|
||||
QObject.connect(self, SIGNAL('kill_job(int, PyQt_PyObject)'),
|
||||
self.jobs_view.model().kill_job)
|
||||
|
||||
def kill_job(self):
|
||||
for index in self.jobs_view.selectedIndexes():
|
||||
row = index.row()
|
||||
self.emit(SIGNAL('kill_job(int, PyQt_PyObject)'), row, self)
|
||||
return
|
||||
|
||||
def closeEvent(self, e):
|
||||
self.jobs_view.write_settings()
|
||||
|
@ -41,6 +41,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="kill_button" >
|
||||
<property name="text" >
|
||||
<string>&Stop selected job</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
@ -12,6 +12,7 @@
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
from libprs500.gui2 import error_dialog
|
||||
import traceback, logging, collections
|
||||
|
||||
from PyQt4.QtCore import QAbstractTableModel, QMutex, QObject, SIGNAL, Qt, \
|
||||
@ -170,6 +171,7 @@ class JobManager(QAbstractTableModel):
|
||||
for job in [job for job in self.running_jobs if job.isFinished()]:
|
||||
self.running_jobs.remove(job)
|
||||
self.finished_jobs.appendleft(job)
|
||||
if job.result != self.process_server.KILL_RESULT:
|
||||
job.notify()
|
||||
self.emit(SIGNAL('job_done(int)'), job.id)
|
||||
refresh = True
|
||||
@ -317,7 +319,9 @@ class JobManager(QAbstractTableModel):
|
||||
if status == 0:
|
||||
return self.running_icon
|
||||
if status == 2:
|
||||
return self.done_icon if job.exception is None else self.error_icon
|
||||
if job.exception or job.result == self.process_server.KILL_RESULT:
|
||||
return self.error_icon
|
||||
return self.done_icon
|
||||
return NONE
|
||||
|
||||
def status_update(self, id, progress):
|
||||
@ -328,6 +332,22 @@ class JobManager(QAbstractTableModel):
|
||||
self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), index, index)
|
||||
break
|
||||
|
||||
def kill_job(self, row, gui_parent):
|
||||
job, status = self.row_to_job(row)
|
||||
if isinstance(job, DeviceJob):
|
||||
error_dialog(gui_parent, _('Cannot kill job'),
|
||||
_('Cannot kill jobs that are communicating with the device as this may cause data corruption.')).exec_()
|
||||
return
|
||||
if status == 2:
|
||||
error_dialog(gui_parent, _('Cannot kill job'),
|
||||
_('Cannot kill already completed jobs.')).exec_()
|
||||
return
|
||||
if status == 1:
|
||||
error_dialog(gui_parent, _('Cannot kill job'),
|
||||
_('Cannot kill waiting jobs.')).exec_()
|
||||
return
|
||||
self.process_server.kill(job.id)
|
||||
|
||||
class DetailView(QDialog, Ui_Dialog):
|
||||
|
||||
def __init__(self, parent, job):
|
||||
|
@ -20,7 +20,7 @@ from functools import partial
|
||||
from libprs500.ebooks.lrf.any.convert_from import main as any2lrf
|
||||
from libprs500.ebooks.lrf.web.convert_from import main as web2lrf
|
||||
from libprs500.gui2.lrf_renderer.main import main as lrfviewer
|
||||
from libprs500 import iswindows
|
||||
from libprs500 import iswindows, __appname__
|
||||
|
||||
PARALLEL_FUNCS = {
|
||||
'any2lrf' : partial(any2lrf, gui_mode=True),
|
||||
@ -47,10 +47,43 @@ def cleanup(tdir):
|
||||
|
||||
class Server(object):
|
||||
|
||||
#: Interval in seconds at which child processes are polled for status information
|
||||
INTERVAL = 0.1
|
||||
KILL_RESULT = 'Server: job killed by user|||#@#$%&*)*(*$#$%#$@&'
|
||||
|
||||
def __init__(self):
|
||||
self.tdir = tempfile.mkdtemp('', 'libprs500_IPC_')
|
||||
self.tdir = tempfile.mkdtemp('', '%s_IPC_'%__appname__)
|
||||
atexit.register(cleanup, self.tdir)
|
||||
self.stdout = {}
|
||||
self.kill_jobs = []
|
||||
|
||||
def kill(self, job_id):
|
||||
'''
|
||||
Kill the job identified by job_id.
|
||||
'''
|
||||
self.kill_jobs.append(str(job_id))
|
||||
|
||||
def _terminate(self, pid):
|
||||
'''
|
||||
Kill process identified by C{pid}.
|
||||
@param pid: On unix a process number, on windows a process handle.
|
||||
'''
|
||||
if iswindows:
|
||||
import win32api
|
||||
try:
|
||||
win32api.TerminateProcess(int(pid), -1)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
import signal
|
||||
try:
|
||||
try:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
finally:
|
||||
time.sleep(2)
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
except:
|
||||
pass
|
||||
|
||||
def run(self, job_id, func, args=[], kwdargs={}, monitor=True):
|
||||
'''
|
||||
@ -61,7 +94,8 @@ class Server(object):
|
||||
@param kwdargs: A dictionary of keyword arguments to pass to C{func}
|
||||
@param monitor: If False launch the child process and return. Do not monitor/communicate with it.
|
||||
@return: (result, exception, formatted_traceback, log) where log is the combined
|
||||
stdout + stderr of the child process; or None if monitor is True.
|
||||
stdout + stderr of the child process; or None if monitor is True. If a job is killed
|
||||
by a call to L{kill()} then result will be L{KILL_RESULT}
|
||||
'''
|
||||
job_id = str(job_id)
|
||||
job_dir = os.path.join(self.tdir, job_id)
|
||||
@ -86,7 +120,9 @@ class Server(object):
|
||||
Popen((python, '-c', cmd))
|
||||
return
|
||||
while p.returncode is None:
|
||||
self.stdout[job_id].write(p.stdout.readline())
|
||||
if job_id in self.kill_jobs:
|
||||
self._terminate(p._handle if iswindows else p.pid)
|
||||
return self.KILL_RESULT, None, None, _('Job killed by user')
|
||||
p.poll()
|
||||
time.sleep(0.5) # Wait for half a second
|
||||
self.stdout[job_id].write(p.stdout.read())
|
||||
|
Loading…
x
Reference in New Issue
Block a user