Files
virt-manager/virtManager/baseclass.py
T

288 lines
8.7 KiB
Python
Raw Normal View History

2010-12-08 17:26:19 -05:00
#
# Copyright (C) 2010, 2013 Red Hat, Inc.
2010-12-08 17:26:19 -05:00
# Copyright (C) 2010 Cole Robinson <crobinso@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 logging
2010-12-08 17:26:19 -05:00
import os
import sys
import threading
import traceback
2010-12-08 17:26:19 -05:00
2013-04-18 16:03:43 -04:00
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import GObject
2013-04-18 16:03:43 -04:00
from gi.repository import Gtk
2010-12-08 17:26:19 -05:00
2016-04-18 16:42:12 -04:00
from . import config
class vmmGObject(GObject.GObject):
# Objects can set this to false to disable leak tracking
2011-04-28 17:13:05 -04:00
_leak_check = True
# This saves a bunch of imports and typing
RUN_FIRST = GObject.SignalFlags.RUN_FIRST
@staticmethod
def idle_add(func, *args, **kwargs):
"""
Make sure idle functions are run thread safe
"""
def cb():
return func(*args, **kwargs)
return GLib.idle_add(cb)
2010-12-08 17:26:19 -05:00
def __init__(self):
GObject.GObject.__init__(self)
2010-12-08 17:26:19 -05:00
self._gobject_handles = []
self._gobject_timeouts = []
2014-09-28 13:37:16 +02:00
self._gsettings_handles = []
2011-07-22 18:07:57 -04:00
self._signal_id_map = {}
self._next_signal_id = 1
2011-04-13 09:27:02 -04:00
self.object_key = str(self)
# Config might not be available if we error early in startup
2018-03-13 12:13:11 -04:00
if config.vmmConfig.is_initialized() 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:
2014-09-28 13:37:16 +02:00
for h in self._gsettings_handles[:]:
self.remove_gsettings_handle(h)
for h in self._gobject_handles[:]:
if GObject.GObject.handler_is_connected(self, h):
self.disconnect(h)
for h in self._gobject_timeouts[:]:
self.remove_gobject_timeout(h)
2011-07-23 21:16:54 -04:00
self._cleanup()
2017-07-24 09:26:48 +01:00
except Exception:
logging.exception("Error cleaning up %s", self)
2011-07-23 21:16:54 -04:00
def _cleanup(self):
raise NotImplementedError("_cleanup must be implemented in subclass")
2017-07-17 16:55:15 -04:00
def __del__(self):
try:
2018-03-13 12:13:11 -04:00
if config.vmmConfig.is_initialized() and self._leak_check:
2017-07-17 16:55:15 -04:00
self.config.remove_object(self.object_key)
2017-07-24 09:26:48 +01:00
except Exception:
2017-07-17 16:55:15 -04:00
logging.exception("Error removing %s", self.object_key)
2018-03-13 12:13:11 -04:00
@property
def config(self):
return config.vmmConfig.get_instance()
2014-03-22 11:21:19 -04:00
# pylint: disable=arguments-differ
# Newer pylint can detect, but warns that overridden arguments are wrong
def connect(self, name, callback, *args):
2017-07-17 16:55:15 -04:00
"""
GObject connect() wrapper to simplify callers, and track handles
for easy cleanup
"""
ret = GObject.GObject.connect(self, name, callback, *args)
self._gobject_handles.append(ret)
return ret
2011-07-22 18:07:57 -04:00
def disconnect(self, handle):
2017-07-17 16:55:15 -04:00
"""
GObject disconnect() wrapper to simplify callers
"""
ret = GObject.GObject.disconnect(self, handle)
self._gobject_handles.remove(handle)
return ret
2017-07-17 16:55:15 -04:00
def timeout_add(self, timeout, func, *args):
"""
GLib timeout_add wrapper to simplify callers, and track handles
for easy cleanup
"""
ret = GLib.timeout_add(timeout, func, *args)
self.add_gobject_timeout(ret)
return ret
def emit(self, signal_name, *args):
"""
GObject emit() wrapper to simplify callers
"""
return GObject.GObject.emit(self, signal_name, *args)
2014-09-28 13:37:16 +02:00
def add_gsettings_handle(self, handle):
self._gsettings_handles.append(handle)
def remove_gsettings_handle(self, handle):
self.config.remove_notifier(handle)
2014-09-28 13:37:16 +02:00
self._gsettings_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",
2013-04-25 12:07:57 -04:00
msg, self.object_key, self._refcount(),
"".join(traceback.format_stack()))
2011-04-13 09:27:02 -04:00
2013-04-25 12:07:57 -04:00
def _refcount(self):
# Function generates 2 temporary refs, so adjust total accordingly
return (sys.getrefcount(self) - 2)
def _start_thread(self, target=None, name=None, args=None, kwargs=None):
# Helper for starting a daemonized thread
t = threading.Thread(target=target, name=name,
args=args or [], kwargs=kwargs or {})
t.daemon = True
t.start()
2017-07-17 16:55:15 -04:00
##############################
# Custom signal/idle helpers #
##############################
2011-04-17 18:27:41 -04:00
def connect_once(self, signal, func, *args):
2017-07-17 16:55:15 -04:00
"""
Like standard glib connect(), but only runs the signal handler
once, then unregisters it
"""
2011-04-17 18:27:41 -04:00
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):
2017-07-17 16:55:15 -04:00
"""
Like standard glib connect(), but allows the signal handler to
unregister itself if it returns True
"""
2011-04-17 18:27:41 -04:00
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
2011-04-18 11:12:36 -04:00
def idle_emit(self, signal, *args):
"""
Safe wrapper for using 'self.emit' with GLib.idle_add
2011-04-18 11:12:36 -04:00
"""
def emitwrap(_s, *_a):
self.emit(_s, *_a)
return False
2012-02-10 14:07:51 -05:00
self.idle_add(emitwrap, signal, *args)
2011-04-18 11:12:36 -04:00
2013-04-13 14:34:52 -04:00
2010-12-08 17:26:19 -05:00
class vmmGObjectUI(vmmGObject):
@staticmethod
def bind_escape_key_close_helper(topwin, close_cb):
def close_on_escape(src_ignore, event):
if Gdk.keyval_name(event.keyval) == "Escape":
close_cb()
topwin.connect("key-press-event", close_on_escape)
2013-06-08 19:25:36 -04:00
def __init__(self, filename, windowname, builder=None, topwin=None):
2010-12-08 17:26:19 -05:00
vmmGObject.__init__(self)
2014-01-28 13:59:31 -05:00
self._external_topwin = bool(topwin)
2010-12-08 17:26:19 -05:00
if filename:
2013-06-08 19:25:36 -04:00
uifile = os.path.join(self.config.get_ui_dir(), filename)
2012-02-01 17:26:46 -05:00
2013-02-16 13:31:46 -05:00
self.builder = Gtk.Builder()
self.builder.set_translation_domain("virt-manager")
2017-12-20 16:04:36 -05:00
self.builder.add_from_file(uifile)
2012-02-01 17:26:46 -05:00
2013-08-02 10:18:47 -04:00
if not topwin:
self.topwin = self.widget(windowname)
self.topwin.hide()
else:
self.topwin = topwin
2013-06-08 19:25:36 -04:00
else:
self.builder = builder
self.topwin = topwin
2010-12-08 17:26:19 -05:00
2013-09-01 20:24:15 -04:00
self._err = None
def _get_err(self):
if self._err is None:
2014-09-12 16:10:45 -04:00
from . import error
2013-09-01 20:24:15 -04:00
self._err = error.vmmErrorDialog(self.topwin)
return self._err
err = property(_get_err)
def widget(self, name):
2013-02-16 13:31:46 -05:00
return self.builder.get_object(name)
def cleanup(self):
self.close()
vmmGObject.cleanup(self)
2013-02-16 13:31:46 -05:00
self.builder = None
2014-01-28 13:59:31 -05:00
if not self._external_topwin:
self.topwin.destroy()
self.topwin = None
2013-09-01 20:24:15 -04:00
self._err = None
2011-07-23 21:16:54 -04:00
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):
self.bind_escape_key_close_helper(self.topwin, self.close)
2017-04-27 15:00:17 -04:00
def set_finish_cursor(self):
self.topwin.set_sensitive(False)
2017-05-03 11:09:53 +02:00
gdk_window = self.topwin.get_window()
cursor = Gdk.Cursor.new_from_name(gdk_window.get_display(), "progress")
gdk_window.set_cursor(cursor)
2017-04-27 15:00:17 -04:00
def reset_finish_cursor(self, topwin=None):
if not topwin:
topwin = self.topwin
topwin.set_sensitive(True)
2017-05-03 11:09:53 +02:00
gdk_window = topwin.get_window()
if not gdk_window:
2017-04-27 15:00:17 -04:00
return
2017-05-03 11:09:53 +02:00
cursor = Gdk.Cursor.new_from_name(gdk_window.get_display(), "default")
gdk_window.set_cursor(cursor)