Viewing file: timedops.py (8.21 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#!/usr/bin/python3
## Copyright (C) 2008, 2009, 2010, 2012, 2014 Red Hat, Inc. ## Authors: ## Tim Waugh <twaugh@redhat.com>
## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version.
## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details.
## 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.
import dbus.mainloop.glib from gi.repository import GObject from gi.repository import GLib from gi.repository import Gdk from gi.repository import Gtk import subprocess import threading import config import gettext gettext.install(domain=config.PACKAGE, localedir=config.localedir) from debug import *
# Initialise threading for D-Bus. This is needed as long as it is # used from two separate threads. We only do this in a few places # now, but in particular the troubleshooter does this (bug #662047). Gdk.threads_init () dbus.mainloop.glib.threads_init ()
class OperationCanceled(RuntimeError): pass
class Timed: def run (self): pass
def cancel (self): return False
class TimedSubprocess(Timed): def __init__ (self, timeout=60000, parent=None, show_dialog=True, **args): self.subp = subprocess.Popen (**args) self.output = dict() self.io_source = [] self.watchers = 2 self.timeout = timeout self.parent = parent self.show_dialog = show_dialog for f in [self.subp.stdout, self.subp.stderr]: if f is not None: source = GLib.io_add_watch (f, GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP | GLib.IO_ERR, self.watcher) self.io_source.append (source)
self.wait_window = None
def run (self): if self.show_dialog: self.wait_source = GLib.timeout_add_seconds ( 1, self.show_wait_window)
self.timeout_source = GLib.timeout_add (self.timeout, self.do_timeout) Gtk.main () if self.timeout_source: GLib.source_remove (self.timeout_source) if self.show_dialog: GLib.source_remove (self.wait_source) for source in self.io_source: GLib.source_remove (source) if self.wait_window is not None: self.wait_window.destroy () return (self.output.get (self.subp.stdout, '').split ('\n'), self.output.get (self.subp.stderr, '').split ('\n'), self.subp.poll ())
def do_timeout (self): self.timeout_source = None Gtk.main_quit () return False
def watcher (self, source, condition): if condition & GLib.IO_IN: buffer = self.output.get (source, '') buffer += (source.read ()).decode("utf-8") self.output[source] = buffer
if condition & GLib.IO_HUP: self.watchers -= 1 if self.watchers == 0: Gtk.main_quit ()
return True
def show_wait_window (self): Gdk.threads_enter () wait = Gtk.MessageDialog (parent=self.parent, modal=True, destroy_with_parent=True, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.CANCEL, text=_("Please wait")) wait.connect ("delete_event", lambda *args: False) wait.connect ("response", self.wait_window_response) if self.parent: wait.set_transient_for (self.parent) wait.set_position (Gtk.WindowPosition.CENTER_ON_PARENT) wait.format_secondary_text (_("Gathering information")) wait.show_all () self.wait_window = wait Gdk.threads_leave () return False
def wait_window_response (self, dialog, response): if response == Gtk.ResponseType.CANCEL: self.cancel ()
def cancel (self): if self.watchers > 0: debugprint ("Command canceled") Gtk.main_quit () self.watchers = 0
return False
class OperationThread(threading.Thread): def __init__ (self, target=None, args=(), kwargs={}): threading.Thread.__init__ (self) self.setDaemon (True) self.target = target self.args = args self.kwargs = kwargs self.exception = None self.result = None
def run (self): try: debugprint ("Calling %s" % self.target) self.result = self.target (*self.args, **self.kwargs) debugprint ("Done") except Exception as e: debugprint ("Caught exception %s" % e) self.exception = e
def collect_result (self): if self.isAlive (): # We've been canceled. raise OperationCanceled()
if self.exception: raise self.exception
return self.result
class TimedOperation(Timed): def __init__ (self, target, args=(), kwargs={}, parent=None, show_dialog=False, callback=None, context=None): self.wait_window = None self.parent = parent self.show_dialog = show_dialog self.callback = callback self.context = context self.thread = OperationThread (target=target, args=args, kwargs=kwargs) self.thread.start ()
self.use_callback = callback is not None if self.use_callback: self.timeout_source = GLib.timeout_add (50, self._check_thread)
def run (self): if self.use_callback: raise RuntimeError
if self.show_dialog: wait = Gtk.MessageDialog (parent=self.parent, modal=True, destroy_with_parent=True, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.CANCEL, text=_("Please wait")) wait.connect ("delete_event", lambda *args: False) wait.connect ("response", self._wait_window_response) if self.parent: wait.set_transient_for (self.parent)
wait.set_position (Gtk.WindowPosition.CENTER_ON_PARENT) wait.format_secondary_text (_("Gathering information")) wait.show_all ()
self.timeout_source = GLib.timeout_add (50, self._check_thread) Gtk.main () if self.timeout_source: GLib.source_remove (self.timeout_source) if self.show_dialog: wait.destroy ()
return self.thread.collect_result ()
def _check_thread (self): if self.thread.isAlive (): # Thread still running. return True
# Thread has finished. Stop the sub-loop or trigger callback. self.timeout_source = False if self.use_callback: if self.callback is not None: if self.context is not None: self.callback (self.thread.result, self.thread.exception, self.context) else: self.callback (self.thread.result, self.thread.exception) else: Gtk.main_quit ()
return False
def _wait_window_response (self, dialog, response): if response == Gtk.ResponseType.CANCEL: self.cancel ()
def cancel (self): debugprint ("Command canceled") if self.use_callback: self.callback = None else: Gtk.main_quit ()
return False
|