Back to index

system-config-printer  1.3.9+20120706
timedops.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 
00003 ## Copyright (C) 2008, 2009, 2010 Red Hat, Inc.
00004 ## Authors:
00005 ##  Tim Waugh <twaugh@redhat.com>
00006 
00007 ## This program is free software; you can redistribute it and/or modify
00008 ## it under the terms of the GNU General Public License as published by
00009 ## the Free Software Foundation; either version 2 of the License, or
00010 ## (at your option) any later version.
00011 
00012 ## This program is distributed in the hope that it will be useful,
00013 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 ## GNU General Public License for more details.
00016 
00017 ## You should have received a copy of the GNU General Public License
00018 ## along with this program; if not, write to the Free Software
00019 ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020 
00021 import dbus.mainloop.glib
00022 import glib
00023 import gobject
00024 import gtk
00025 import subprocess
00026 import threading
00027 from gettext import gettext as _
00028 from debug import *
00029 
00030 # Initialise threading for D-Bus.  This is needed as long as it is
00031 # used from two separate threads.  We only do this in a few places
00032 # now, but in particular the troubleshooter does this (bug #662047).
00033 glib.threads_init ()
00034 dbus.mainloop.glib.threads_init ()
00035 
00036 class OperationCanceled(RuntimeError):
00037     pass
00038 
00039 class Timed:
00040     def run (self):
00041         pass
00042 
00043     def cancel (self):
00044         return False
00045 
00046 class TimedSubprocess(Timed):
00047     def __init__ (self, timeout=60000, parent=None, show_dialog=True,
00048                   **args):
00049         self.subp = subprocess.Popen (**args)
00050         self.output = dict()
00051         self.io_source = []
00052         self.watchers = 2
00053         self.timeout = timeout
00054         self.parent = parent
00055         self.show_dialog = show_dialog
00056         for f in [self.subp.stdout, self.subp.stderr]:
00057             if f != None:
00058                 source = gobject.io_add_watch (f,
00059                                                gobject.IO_IN |
00060                                                gobject.IO_HUP |
00061                                                gobject.IO_ERR,
00062                                                self.watcher)
00063                 self.io_source.append (source)
00064 
00065         self.wait_window = None
00066 
00067     def run (self):
00068         if self.show_dialog:
00069             self.wait_source = gobject.timeout_add_seconds (
00070                 1,
00071                 self.show_wait_window)
00072 
00073         self.timeout_source = gobject.timeout_add (self.timeout,
00074                                                    self.do_timeout)
00075         gtk.main ()
00076         gobject.source_remove (self.timeout_source)
00077         if self.show_dialog:
00078             gobject.source_remove (self.wait_source)
00079         for source in self.io_source:
00080             gobject.source_remove (source)
00081         if self.wait_window != None:
00082             self.wait_window.destroy ()
00083         return (self.output.get (self.subp.stdout, '').split ('\n'),
00084                 self.output.get (self.subp.stderr, '').split ('\n'),
00085                 self.subp.poll ())
00086 
00087     def do_timeout (self):
00088         gtk.main_quit ()
00089         return False
00090 
00091     def watcher (self, source, condition):
00092         if condition & gobject.IO_IN:
00093             buffer = self.output.get (source, '')
00094             buffer += source.read ()
00095             self.output[source] = buffer
00096 
00097         if condition & gobject.IO_HUP:
00098             self.watchers -= 1
00099             if self.watchers == 0:
00100                 gtk.main_quit ()
00101                 return False
00102 
00103         return True
00104 
00105     def show_wait_window (self):
00106         gtk.gdk.threads_enter ()
00107         wait = gtk.MessageDialog (self.parent,
00108                                   gtk.DIALOG_MODAL |
00109                                   gtk.DIALOG_DESTROY_WITH_PARENT,
00110                                   gtk.MESSAGE_INFO,
00111                                   gtk.BUTTONS_CANCEL,
00112                                   _("Please wait"))
00113         wait.connect ("delete_event", lambda *args: False)
00114         wait.connect ("response", self.wait_window_response)
00115         if self.parent:
00116             wait.set_transient_for (self.parent)
00117         wait.set_position (gtk.WIN_POS_CENTER_ON_PARENT)
00118         wait.format_secondary_text (_("Gathering information"))
00119         wait.show_all ()
00120         self.wait_window = wait
00121         gtk.gdk.threads_leave ()
00122         return False
00123 
00124     def wait_window_response (self, dialog, response):
00125         if response == gtk.RESPONSE_CANCEL:
00126             self.cancel ()
00127 
00128     def cancel (self):
00129         if self.watchers > 0:
00130             debugprint ("Command canceled")
00131             gtk.main_quit ()
00132             self.watchers = 0
00133 
00134         return False
00135 
00136 class OperationThread(threading.Thread):
00137     def __init__ (self, target=None, args=(), kwargs={}):
00138         threading.Thread.__init__ (self)
00139         self.setDaemon (True)
00140         self.target = target
00141         self.args = args
00142         self.kwargs = kwargs
00143         self.exception = None
00144         self.result = None
00145 
00146     def run (self):
00147         try:
00148             debugprint ("Calling %s" % self.target)
00149             self.result = self.target (*self.args, **self.kwargs)
00150             debugprint ("Done")
00151         except Exception, e:
00152             debugprint ("Caught exception %s" % e)
00153             self.exception = e
00154 
00155     def collect_result (self):
00156         if self.isAlive ():
00157             # We've been canceled.
00158             raise OperationCanceled()
00159 
00160         if self.exception:
00161             raise self.exception
00162 
00163         return self.result
00164 
00165 class TimedOperation(Timed):
00166     def __init__ (self, target, args=(), kwargs={}, parent=None,
00167                   show_dialog=False, callback=None, context=None):
00168         self.wait_window = None
00169         self.parent = parent
00170         self.show_dialog = show_dialog
00171         self.callback = callback
00172         self.context = context
00173         self.thread = OperationThread (target=target,
00174                                        args=args,
00175                                        kwargs=kwargs)
00176         self.thread.start ()
00177 
00178         self.use_callback = callback != None
00179         if self.use_callback:
00180             self.timeout_source = gobject.timeout_add (50, self._check_thread)
00181 
00182     def run (self):
00183         if self.use_callback:
00184             raise RuntimeError
00185 
00186         if self.show_dialog:
00187             wait = gtk.MessageDialog (self.parent,
00188                                       gtk.DIALOG_MODAL |
00189                                       gtk.DIALOG_DESTROY_WITH_PARENT,
00190                                       gtk.MESSAGE_INFO,
00191                                       gtk.BUTTONS_CANCEL,
00192                                       _("Please wait"))
00193             wait.connect ("delete_event", lambda *args: False)
00194             wait.connect ("response", self._wait_window_response)
00195             if self.parent:
00196                 wait.set_transient_for (self.parent)
00197 
00198             wait.set_position (gtk.WIN_POS_CENTER_ON_PARENT)
00199             wait.format_secondary_text (_("Gathering information"))
00200             wait.show_all ()
00201 
00202         self.timeout_source = gobject.timeout_add (50, self._check_thread)
00203         gtk.main ()
00204         gobject.source_remove (self.timeout_source)
00205         if self.show_dialog:
00206             wait.destroy ()
00207 
00208         return self.thread.collect_result ()
00209 
00210     def _check_thread (self):
00211         if self.thread.isAlive ():
00212             # Thread still running.
00213             return True
00214 
00215         # Thread has finished.  Stop the sub-loop or trigger callback.
00216         if self.use_callback:
00217             if self.callback != None:
00218                 if self.context != None:
00219                     self.callback (self.thread.result, self.thread.exception,
00220                                    self.context)
00221                 else:
00222                     self.callback (self.thread.result, self.thread.exception)
00223         else:
00224             gtk.main_quit ()
00225 
00226         return False
00227 
00228     def _wait_window_response (self, dialog, response):
00229         if response == gtk.RESPONSE_CANCEL:
00230             self.cancel ()
00231 
00232     def cancel (self):
00233         debugprint ("Command canceled")
00234         if self.use_callback:
00235             self.callback = None
00236         else:
00237             gtk.main_quit ()
00238 
00239         return False