# # Copyright (C) 2010 Red Hat, Inc. # Copyright (C) 2010 Cole Robinson # # 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 logging import os import sys import traceback import virtManager # pylint: disable=E0611 from gi.repository import Gdk from gi.repository import GLib from gi.repository import GObject from gi.repository import Gtk # pylint: enable=E0611 class vmmGObject(GObject.GObject): _leak_check = True def __init__(self): GObject.GObject.__init__(self) self.config = virtManager.util.running_config self._gobject_handles = [] self._gobject_timeouts = [] self._gconf_handles = [] self._signal_id_map = {} self._next_signal_id = 1 self.object_key = str(self) # Config might not be available if we error early in startup if self.config and self._leak_check: self.config.add_object(self.object_key) def cleanup(self): # Do any cleanup required to drop reference counts so object is # actually reaped by python. Usually means unregistering callbacks try: for h in self._gconf_handles[:]: self.remove_gconf_handle(h) for h in self._gobject_handles[:]: self.disconnect(h) for h in self._gobject_timeouts[:]: self.remove_gobject_timeout(h) self._cleanup() except: logging.exception("Error cleaning up %s", self) def _cleanup(self): raise NotImplementedError("_cleanup must be implemented in subclass") def connect(self, name, callback, *args): ret = GObject.GObject.connect(self, name, callback, *args) self._gobject_handles.append(ret) return ret def disconnect(self, handle): ret = GObject.GObject.disconnect(self, handle) self._gobject_handles.remove(handle) return ret def add_gconf_handle(self, handle): self._gconf_handles.append(handle) def remove_gconf_handle(self, handle): self.config.remove_notifier(handle) self._gconf_handles.remove(handle) def add_gobject_timeout(self, handle): self._gobject_timeouts.append(handle) def remove_gobject_timeout(self, handle): GLib.source_remove(handle) self._gobject_timeouts.remove(handle) def _logtrace(self, msg=""): if msg: msg += " " logging.debug("%s(%s %s)\n:%s", msg, self.object_key, self._refcount(), "".join(traceback.format_stack())) def _refcount(self): # Function generates 2 temporary refs, so adjust total accordingly return (sys.getrefcount(self) - 2) def connect_once(self, signal, func, *args): id_list = [] def wrap_func(*wrapargs): if id_list: self.disconnect(id_list[0]) return func(*wrapargs) conn_id = self.connect(signal, wrap_func, *args) id_list.append(conn_id) return conn_id def connect_opt_out(self, signal, func, *args): id_list = [] def wrap_func(*wrapargs): ret = func(*wrapargs) if ret and id_list: self.disconnect(id_list[0]) conn_id = self.connect(signal, wrap_func, *args) id_list.append(conn_id) return conn_id def idle_emit(self, signal, *args): """ Safe wrapper for using 'self.emit' with GLib.idle_add """ def emitwrap(_s, *_a): self.emit(_s, *_a) return False self.idle_add(emitwrap, signal, *args) def idle_add(self, func, *args): """ Make sure idle functions are run thread safe """ def cb(): try: return func(*args) except: print traceback.format_exc() return False return GLib.idle_add(cb) def timeout_add(self, timeout, func, *args): """ Make sure timeout functions are run thread safe """ def cb(): try: return func(*args) except: print traceback.format_exc() return False ret = GLib.timeout_add(timeout, cb) self.add_gobject_timeout(ret) return ret def emit(self, signal_name, *args): return GObject.GObject.emit(self, signal_name, *args) def __del__(self): try: if self.config and self._leak_check: self.config.remove_object(self.object_key) except: logging.exception("Error removing %s", self.object_key) class vmmGObjectUI(vmmGObject): def __init__(self, filename, windowname, builder=None, topwin=None): vmmGObject.__init__(self) if filename: uifile = os.path.join(self.config.get_ui_dir(), filename) self.builder = Gtk.Builder() self.builder.set_translation_domain("virt-manager") self.builder.add_from_string(file(uifile).read()) self.topwin = self.widget(windowname) self.topwin.hide() else: self.builder = builder self.topwin = topwin self.err = virtManager.error.vmmErrorDialog(self.topwin) def widget(self, name): return self.builder.get_object(name) def cleanup(self): self.close() vmmGObject.cleanup(self) self.builder = None self.topwin.destroy() self.topwin = None self.err = None def _cleanup(self): raise NotImplementedError("_cleanup must be implemented in subclass") def close(self, ignore1=None, ignore2=None): pass def bind_escape_key_close(self): def close_on_escape(src_ignore, event): if Gdk.keyval_name(event.keyval) == "Escape": self.close() self.topwin.connect("key-press-event", close_on_escape)