From f2781ee62aba43c4d8a864b648ce23e2eacc41bd Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 13 Apr 2011 09:27:02 -0400 Subject: [PATCH] Properly cleanup all dialogs On app close, all dialogs are properly cleaned up so there are no dangling references and python can garbage collect. While this isn't that important when the app shuts down, it ensures that lifecycle changes while the app is running (vm start/stop/remove, conn start/stop/remove) have the infrastructure to properly release resources. --- src/virtManager/baseclass.py | 13 +++- src/virtManager/clone.py | 25 ++++++ src/virtManager/connection.py | 18 ++++- src/virtManager/create.py | 39 ++++++++-- src/virtManager/createinterface.py | 20 +++++ src/virtManager/createnet.py | 23 ++++-- src/virtManager/createpool.py | 8 ++ src/virtManager/delete.py | 8 ++ src/virtManager/engine.py | 121 +++++++++++++++++++++++------ src/virtManager/host.py | 37 +++++++++ src/virtManager/manager.py | 92 ++++++++++++++++------ src/virtManager/migrate.py | 13 ++++ src/virtManager/preferences.py | 32 ++++---- src/virtManager/systray.py | 20 ++++- src/virtManager/uihelpers.py | 4 + 15 files changed, 388 insertions(+), 85 deletions(-) diff --git a/src/virtManager/baseclass.py b/src/virtManager/baseclass.py index cd3e7e7c9..feef559be 100644 --- a/src/virtManager/baseclass.py +++ b/src/virtManager/baseclass.py @@ -41,8 +41,8 @@ class vmmGObject(gobject.GObject): self._gobject_timeouts = [] self._gconf_handles = [] - self._object_key = str(self) - self.config.add_object(self._object_key) + self.object_key = str(self) + self.config.add_object(self.object_key) def cleanup(self): # Do any cleanup required to drop reference counts so object is @@ -78,6 +78,11 @@ class vmmGObject(gobject.GObject): gobject.source_remove(handle) self._gobject_timeouts.remove(handle) + def _printtrace(self, msg): + import traceback + print "%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) @@ -91,9 +96,9 @@ class vmmGObject(gobject.GObject): getattr(gobject.GObject, "__del__")(self) try: - self.config.remove_object(self._object_key) + self.config.remove_object(self.object_key) except: - logging.exception("Error removing %s" % self._object_key) + logging.exception("Error removing %s" % self.object_key) class vmmGObjectUI(vmmGObject): def __init__(self, filename, windowname): diff --git a/src/virtManager/clone.py b/src/virtManager/clone.py index 9000e21a3..d9a629591 100644 --- a/src/virtManager/clone.py +++ b/src/virtManager/clone.py @@ -131,8 +131,33 @@ class vmmCloneVM(vmmGObjectUI): self.change_mac_close() self.change_storage_close() self.topwin.hide() + + self.orig_vm = None + self.clone_design = None + self.storage_list = {} + self.target_list = [] + self.net_list = {} + self.mac_list = [] + return 1 + def cleanup(self): + self.close() + + self.conn = None + + self.change_mac.destroy() + self.change_mac = None + + self.change_storage.destroy() + self.change_storage = None + + if self.storage_browser: + self.storage_browser.cleanup() + self.storage_browser = None + + vmmGObjectUI.cleanup(self) + def change_mac_close(self, ignore1=None, ignore2=None): self.change_mac.hide() return 1 diff --git a/src/virtManager/connection.py b/src/virtManager/connection.py index c2688f966..40a415ce6 100644 --- a/src/virtManager/connection.py +++ b/src/virtManager/connection.py @@ -146,6 +146,7 @@ class vmmConnection(vmmGObject): self.hostinfo = None self.hal_helper_remove_sig = None + self.hal_handles = [] self.netdev_initialized = False self.netdev_error = "" @@ -164,6 +165,7 @@ class vmmConnection(vmmGObject): sig = hal_helper.connect("device-removed", self._haldev_removed) self.hal_helper_remove_sig = sig + self.hal_handles.append(sig) def _init_netdev(self): """ @@ -186,7 +188,8 @@ class vmmConnection(vmmGObject): else: error = hal_helper.get_init_error() if not error: - hal_helper.connect("netdev-added", self._netdev_added) + self.hal_handles.append( + hal_helper.connect("netdev-added", self._netdev_added)) self._set_hal_remove_sig(hal_helper) else: @@ -225,7 +228,8 @@ class vmmConnection(vmmGObject): else: error = hal_helper.get_init_error() if not error: - hal_helper.connect("optical-added", self._optical_added) + self.hal_handles.append( + hal_helper.connect("optical-added", self._optical_added)) self._set_hal_remove_sig(hal_helper) else: @@ -922,6 +926,16 @@ class vmmConnection(vmmGObject): self._change_state(self.STATE_DISCONNECTED) + def cleanup(self): + # Do this first, so signals are unregistered before we change state + vmmGObject.cleanup(self) + self.close() + + hal_helper = self.get_hal_helper() + if hal_helper: + for h in self.hal_handles: + hal_helper.disconnect(h) + def _open_dev_conn(self, uri): """ Allow using virtinsts connection hacking to fake capabilities diff --git a/src/virtManager/create.py b/src/virtManager/create.py index c4306b977..3f930abed 100644 --- a/src/virtManager/create.py +++ b/src/virtManager/create.py @@ -155,9 +155,33 @@ class vmmCreate(vmmGObjectUI): if self.config_window: self.config_window.close() + if self.storage_browser: + self.storage_browser.close() return 1 + def cleanup(self): + self.close() + self.remove_conn() + + self.conn = None + self.caps = None + self.capsguest = None + self.capsdomain = None + + self.guest = None + self.disk = None + self.nic = None + + try: + if self.storage_browser: + self.storage_browser.cleanup() + self.storage_browser = None + except: + logging.exception("Error cleaning up create") + + vmmGObjectUI.cleanup(self) + def remove_timers(self): try: if self.host_storage_timer: @@ -166,15 +190,20 @@ class vmmCreate(vmmGObjectUI): except: pass + def remove_conn(self): + if not self.conn: + return + + for signal in self.conn_signals: + self.conn.disconnect(signal) + self.conn_signals = [] + self.conn = None + def set_conn(self, newconn, force_validate=False): if self.conn == newconn and not force_validate: return - if self.conn: - for signal in self.conn_signals: - self.conn.disconnect(signal) - self.conn_signals = [] - + self.remove_conn() self.conn = newconn if self.conn: self.set_conn_state() diff --git a/src/virtManager/createinterface.py b/src/virtManager/createinterface.py index 77640b4cb..37fadd75b 100644 --- a/src/virtManager/createinterface.py +++ b/src/virtManager/createinterface.py @@ -153,6 +153,26 @@ class vmmCreateInterface(vmmGObjectUI): return 1 + def cleanup(self): + self.close() + + try: + self.conn = None + self.interface = None + + self.ip_config.destroy() + self.ip_config = None + + self.bridge_config.destroy() + self.bridge_config = None + + self.bond_config.destroy() + self.bond_config = None + except: + logging.exception("Error cleaning up addiface") + + vmmGObjectUI.cleanup(self) + ########################### # Initialization routines # ########################### diff --git a/src/virtManager/createnet.py b/src/virtManager/createnet.py index 8e79fb462..143f77fbc 100644 --- a/src/virtManager/createnet.py +++ b/src/virtManager/createnet.py @@ -75,6 +75,21 @@ class vmmCreateNetwork(vmmGObjectUI): self.reset_state() self.topwin.present() + def is_visible(self): + if self.topwin.flags() & gtk.VISIBLE: + return 1 + return 0 + + def close(self, ignore1=None, ignore2=None): + self.topwin.hide() + return 1 + + def cleanup(self): + self.close() + self.conn = None + + vmmGObjectUI.cleanup(self) + def set_initial_state(self): notebook = self.window.get_widget("create-pages") notebook.set_show_tabs(False) @@ -310,14 +325,6 @@ class vmmCreateNetwork(vmmGObjectUI): self.window.get_widget("create-finish").show() self.window.get_widget("create-finish").grab_focus() - def close(self, ignore1=None, ignore2=None): - self.topwin.hide() - return 1 - - def is_visible(self): - if self.topwin.flags() & gtk.VISIBLE: - return 1 - return 0 def finish(self, ignore=None): name = self.get_config_name() diff --git a/src/virtManager/createpool.py b/src/virtManager/createpool.py index f4aefae00..d03b6f610 100644 --- a/src/virtManager/createpool.py +++ b/src/virtManager/createpool.py @@ -111,6 +111,14 @@ class vmmCreatePool(vmmGObjectUI): self.topwin.hide() return 1 + def cleanup(self): + self.close() + + self.conn = None + self._pool = None + + vmmGObjectUI.cleanup(self) + def set_initial_state(self): self.window.get_widget("pool-pages").set_show_tabs(False) diff --git a/src/virtManager/delete.py b/src/virtManager/delete.py index d8d60d9b9..af26d80ce 100644 --- a/src/virtManager/delete.py +++ b/src/virtManager/delete.py @@ -83,6 +83,14 @@ class vmmDeleteDialog(vmmGObjectUI): self.conn = None return 1 + def cleanup(self): + self.close() + + self.vm = None + self.conn = None + + vmmGObjectUI.cleanup(self) + def reset_state(self): # Set VM name in title' diff --git a/src/virtManager/engine.py b/src/virtManager/engine.py index 50cbb0fbb..903156b58 100644 --- a/src/virtManager/engine.py +++ b/src/virtManager/engine.py @@ -45,10 +45,11 @@ from virtManager.create import vmmCreate from virtManager.host import vmmHost from virtManager.error import vmmErrorDialog from virtManager.systray import vmmSystray +import virtManager.uihelpers as uihelpers import virtManager.util as util # Enable this to get a report of leaked objects on app shutdown -debug_ref_leaks = False +debug_ref_leaks = True def default_uri(): tryuri = None @@ -246,8 +247,10 @@ class vmmEngine(vmmGObject): self.init_systray() - self.config.on_stats_update_interval_changed(self.reschedule_timer) - self.config.on_view_system_tray_changed(self.system_tray_changed) + self.add_gconf_handle( + self.config.on_stats_update_interval_changed(self.reschedule_timer)) + self.add_gconf_handle( + self.config.on_view_system_tray_changed(self.system_tray_changed)) self.schedule_timer() self.load_stored_uris() @@ -349,8 +352,6 @@ class vmmEngine(vmmGObject): def connect_to_uri(self, uri, readOnly=None, autoconnect=False, do_start=True): - self.windowConnect = None - try: conn = self._check_connection(uri) if not conn: @@ -368,10 +369,9 @@ class vmmEngine(vmmGObject): def _do_connect(self, src_ignore, uri): return self.connect_to_uri(uri) - def _connect_cancelled(self, connect_ignore): - self.windowConnect = None + def _connect_cancelled(self, src): if len(self.connections.keys()) == 0: - self.exit_app() + self.exit_app(src) def _do_vm_removed(self, connection, hvuri, vmuuid): @@ -393,10 +393,6 @@ class vmmEngine(vmmGObject): self.connections[hvuri]["windowDetails"][vmuuid].cleanup() del(self.connections[hvuri]["windowDetails"][vmuuid]) - if self.connections[hvuri]["windowHost"] is not None: - self.connections[hvuri]["windowHost"].close() - self.connections[hvuri]["windowHost"] = None - if (self.windowCreate and self.windowCreate.conn and self.windowCreate.conn.get_uri() == hvuri): @@ -461,18 +457,79 @@ class vmmEngine(vmmGObject): def decrement_window_counter(self): self.windows -= 1 logging.debug("window counter decremented to %s" % self.windows) + # Don't exit if system tray is enabled - if self.windows <= 0 and not self.systray.is_visible(): + if (self.windows <= 0 and + self.systray and + not self.systray.is_visible()): self.exit_app() - def exit_app(self, ignore_src=None): - conns = self.connections.values() - for conn in conns: - conn["connection"].close() - self.connections = {} + def cleanup(self): + try: + vmmGObject.cleanup(self) + uihelpers.cleanup() + self.err = None + + if self.timer != None: + gobject.source_remove(self.timer) + + if self.systray: + self.systray.cleanup() + self.systray = None + + self.get_manager() + if self.windowManager: + self.windowManager.cleanup() + self.windowManager = None + + if self.windowPreferences: + self.windowPreferences.cleanup() + self.windowPreferences = None + + if self.windowAbout: + self.windowAbout.cleanup() + self.windowAbout = None + + if self.windowConnect: + self.windowConnect.cleanup() + self.windowConnect = None + + if self.windowCreate: + self.windowCreate.cleanup() + self.windowCreate = None + + if self.windowMigrate: + self.windowMigrate.cleanup() + self.windowMigrate = None + + # Do this last, so any manually 'disconnected' signals + # take precedence over cleanup signal removal + for uri in self.connections: + self.cleanup_connection(uri) + self.connections = {} + except: + logging.exception("Error cleaning up engine") + + def exit_app(self, src=None): + if self.err is None: + # Already in cleanup + return + + self.cleanup() if debug_ref_leaks: - for name in self.config.get_objects(): + objs = self.config.get_objects() + if src and src.object_key in objs: + # Whatever UI initiates the app exit will always appear + # to leak + logging.debug("Exitting app from %s, skipping leak check" % + src.object_key) + objs.remove(src.object_key) + + # Engine will always appear to leak + objs.remove(self.object_key) + + for name in objs: logging.debug("Leaked %s" % name) logging.debug("Exiting app normally.") @@ -502,13 +559,28 @@ class vmmEngine(vmmGObject): return conn + def cleanup_connection(self, uri): + try: + if self.connections[uri]["windowHost"]: + self.connections[uri]["windowHost"].cleanup() + if self.connections[uri]["windowClone"]: + self.connections[uri]["windowClone"].cleanup() + + details = self.connections[uri]["windowDetails"] + for win in details.values(): + win.cleanup() + + self.connections[uri]["connection"].cleanup() + except: + logging.exception("Error cleaning up conn in engine") + + def remove_connection(self, uri): - conn = self.connections[uri]["connection"] - conn.close() - del self.connections[uri] + self.cleanup_connection(uri) + del(self.connections[uri]) self.emit("connection-removed", uri) - self.config.remove_connection(conn.get_uri()) + self.config.remove_connection(uri) def connect(self, name, callback, *args): handle_id = vmmGObject.connect(self, name, callback, *args) @@ -683,7 +755,8 @@ class vmmEngine(vmmGObject): def _do_show_manager(self, src): try: - self.get_manager().show() + manager = self.get_manager() + manager.show() except Exception, e: if not src: raise diff --git a/src/virtManager/host.py b/src/virtManager/host.py index 3efaa43f8..78270c95d 100644 --- a/src/virtManager/host.py +++ b/src/virtManager/host.py @@ -312,6 +312,43 @@ class vmmHost(vmmGObjectUI): self.engine.decrement_window_counter() return 1 + def cleanup(self): + self.close() + + try: + self.conn = None + self.engine = None + + + if self.addnet: + self.addnet.cleanup() + self.addnet = None + + if self.addpool: + self.addpool.cleanup() + self.addpool = None + + if self.addvol: + self.addvol.cleanup() + self.addvol = None + + if self.addinterface: + self.addinterface.cleanup() + self.addinterface = None + + self.volmenu.destroy() + self.volmenu = None + + self.cpu_usage_graph.destroy() + self.cpu_usage_graph = None + + self.memory_usage_graph.destroy() + self.memory_usage_graph = None + except: + logging.exception("Error cleaning up host dialog") + + vmmGObjectUI.cleanup(self) + def show_help(self, src_ignore): self.emit("action-show-help", "virt-manager-host-window") diff --git a/src/virtManager/manager.py b/src/virtManager/manager.py index 4055e3374..021d156b8 100644 --- a/src/virtManager/manager.py +++ b/src/virtManager/manager.py @@ -129,17 +129,18 @@ class vmmManager(vmmGObjectUI): self.topwin.set_default_size(w or 550, h or 550) self.prev_position = None - self.init_vmlist() - self.init_stats() - self.init_toolbar() - self.vmmenu = gtk.Menu() self.vmmenushutdown = gtk.Menu() self.vmmenu_items = {} self.vmmenushutdown_items = {} self.connmenu = gtk.Menu() self.connmenu_items = {} - self.init_context_menus() + + # There seem to be ref counting issues with calling + # list.get_column, so avoid it + self.diskcol = None + self.netcol = None + self.cpucol = None self.window.signal_autoconnect({ "on_menu_view_cpu_usage_activate": (self.toggle_stats_visible, @@ -171,7 +172,12 @@ class vmmManager(vmmGObjectUI): "on_menu_edit_preferences_activate": self.show_preferences, "on_menu_help_about_activate": self.show_about, "on_menu_help_activate": self.show_help, - }) + }) + + self.init_vmlist() + self.init_stats() + self.init_toolbar() + self.init_context_menus() # XXX: Help docs useless/out of date self.window.get_widget("menu_help").hide() @@ -223,6 +229,38 @@ class vmmManager(vmmGObjectUI): self.engine.decrement_window_counter() return 1 + + def cleanup(self): + self.close() + + try: + self.engine = None + self.rows = None + + self.diskcol = None + self.cpucol = None + self.netcol = None + + if self.delete_dialog: + self.delete_dialog.cleanup() + self.delete_dialog = None + + self.vmmenu.destroy() + self.vmmenu = None + self.vmmenu_items = None + self.vmmenushutdown.destroy() + self.vmmenushutdown = None + self.vmmenushutdown_items = None + self.connmenu.destroy() + self.connmenu = None + self.connmenu_items = None + + except: + logging.exception("Error cleaning up manager") + + vmmGObjectUI.cleanup(self) + + def is_visible(self): return bool(self.topwin.flags() & gtk.VISIBLE) @@ -235,19 +273,25 @@ class vmmManager(vmmGObjectUI): ################ def init_stats(self): - self.config.on_vmlist_cpu_usage_visible_changed( - self.toggle_cpu_usage_visible_widget) - self.config.on_vmlist_disk_io_visible_changed( - self.toggle_disk_io_visible_widget) - self.config.on_vmlist_network_traffic_visible_changed( - self.toggle_network_traffic_visible_widget) + self.add_gconf_handle( + self.config.on_vmlist_cpu_usage_visible_changed( + self.toggle_cpu_usage_visible_widget)) + self.add_gconf_handle( + self.config.on_vmlist_disk_io_visible_changed( + self.toggle_disk_io_visible_widget)) + self.add_gconf_handle( + self.config.on_vmlist_network_traffic_visible_changed( + self.toggle_network_traffic_visible_widget)) # Register callbacks with the global stats enable/disable values # that disable the associated vmlist widgets if reporting is disabled - self.config.on_stats_enable_disk_poll_changed(self.enable_polling, - cfg.STATS_DISK) - self.config.on_stats_enable_net_poll_changed(self.enable_polling, - cfg.STATS_NETWORK) + self.add_gconf_handle( + self.config.on_stats_enable_disk_poll_changed(self.enable_polling, + cfg.STATS_DISK)) + self.add_gconf_handle( + self.config.on_stats_enable_net_poll_changed(self.enable_polling, + cfg.STATS_NETWORK)) + self.window.get_widget("menu_view_stats_cpu").set_active( self.config.is_vmlist_cpu_usage_visible()) @@ -441,6 +485,9 @@ class vmmManager(vmmGObjectUI): model.set_sort_column_id(COL_NAME, gtk.SORT_ASCENDING) + self.diskcol = diskIOCol + self.netcol = networkTrafficCol + self.cpucol = cpuUsageCol ################## # Helper methods # @@ -1090,19 +1137,14 @@ class vmmManager(vmmGObjectUI): widget.set_label(current_text) def toggle_network_traffic_visible_widget(self, *ignore): - vmlist = self.window.get_widget("vm-list") - col = vmlist.get_column(COL_NETWORK) - col.set_visible(self.config.is_vmlist_network_traffic_visible()) + self.netcol.set_visible( + self.config.is_vmlist_network_traffic_visible()) def toggle_disk_io_visible_widget(self, *ignore): - vmlist = self.window.get_widget("vm-list") - col = vmlist.get_column(COL_DISK) - col.set_visible(self.config.is_vmlist_disk_io_visible()) + self.diskcol.set_visible(self.config.is_vmlist_disk_io_visible()) def toggle_cpu_usage_visible_widget(self, *ignore): - vmlist = self.window.get_widget("vm-list") - col = vmlist.get_column(COL_CPU) - col.set_visible(self.config.is_vmlist_cpu_usage_visible()) + self.cpucol.set_visible(self.config.is_vmlist_cpu_usage_visible()) def toggle_stats_visible(self, src, stats_id): visible = src.get_active() diff --git a/src/virtManager/migrate.py b/src/virtManager/migrate.py index 9894e169f..fabd7c822 100644 --- a/src/virtManager/migrate.py +++ b/src/virtManager/migrate.py @@ -91,6 +91,19 @@ class vmmMigrateDialog(vmmGObjectUI): self.topwin.hide() return 1 + def cleanup(self): + self.close() + + self.vm = None + self.conn = None + self.engine = None + self.destconn_rows = None + + # Not sure why we need to do this manually, but it matters + self.window.get_widget("migrate-dest").get_model().clear() + + vmmGObjectUI.cleanup(self) + def init_state(self): # [hostname, conn, can_migrate, tooltip] dest_model = gtk.ListStore(str, object, bool, str) diff --git a/src/virtManager/preferences.py b/src/virtManager/preferences.py index be3bc66ee..4a8dd995d 100644 --- a/src/virtManager/preferences.py +++ b/src/virtManager/preferences.py @@ -35,23 +35,23 @@ class vmmPreferences(vmmGObjectUI): def __init__(self): vmmGObjectUI.__init__(self, "vmm-preferences.glade", "vmm-preferences") - self.config.on_view_system_tray_changed(self.refresh_view_system_tray) - self.config.on_console_popup_changed(self.refresh_console_popup) - self.config.on_console_accels_changed(self.refresh_console_accels) - self.config.on_console_scaling_changed(self.refresh_console_scaling) - self.config.on_stats_update_interval_changed(self.refresh_update_interval) - self.config.on_stats_history_length_changed(self.refresh_history_length) - self.config.on_sound_local_changed(self.refresh_sound_local) - self.config.on_sound_remote_changed(self.refresh_sound_remote) - self.config.on_graphics_type_changed(self.refresh_graphics_type) - self.config.on_stats_enable_disk_poll_changed(self.refresh_disk_poll) - self.config.on_stats_enable_net_poll_changed(self.refresh_net_poll) + self.add_gconf_handle(self.config.on_view_system_tray_changed(self.refresh_view_system_tray)) + self.add_gconf_handle(self.config.on_console_popup_changed(self.refresh_console_popup)) + self.add_gconf_handle(self.config.on_console_accels_changed(self.refresh_console_accels)) + self.add_gconf_handle(self.config.on_console_scaling_changed(self.refresh_console_scaling)) + self.add_gconf_handle(self.config.on_stats_update_interval_changed(self.refresh_update_interval)) + self.add_gconf_handle(self.config.on_stats_history_length_changed(self.refresh_history_length)) + self.add_gconf_handle(self.config.on_sound_local_changed(self.refresh_sound_local)) + self.add_gconf_handle(self.config.on_sound_remote_changed(self.refresh_sound_remote)) + self.add_gconf_handle(self.config.on_graphics_type_changed(self.refresh_graphics_type)) + self.add_gconf_handle(self.config.on_stats_enable_disk_poll_changed(self.refresh_disk_poll)) + self.add_gconf_handle(self.config.on_stats_enable_net_poll_changed(self.refresh_net_poll)) - self.config.on_confirm_forcepoweroff_changed(self.refresh_confirm_forcepoweroff) - self.config.on_confirm_poweroff_changed(self.refresh_confirm_poweroff) - self.config.on_confirm_pause_changed(self.refresh_confirm_pause) - self.config.on_confirm_removedev_changed(self.refresh_confirm_removedev) - self.config.on_confirm_interface_changed(self.refresh_confirm_interface) + self.add_gconf_handle(self.config.on_confirm_forcepoweroff_changed(self.refresh_confirm_forcepoweroff)) + self.add_gconf_handle(self.config.on_confirm_poweroff_changed(self.refresh_confirm_poweroff)) + self.add_gconf_handle(self.config.on_confirm_pause_changed(self.refresh_confirm_pause)) + self.add_gconf_handle(self.config.on_confirm_removedev_changed(self.refresh_confirm_removedev)) + self.add_gconf_handle(self.config.on_confirm_interface_changed(self.refresh_confirm_interface)) self.refresh_view_system_tray() self.refresh_update_interval() diff --git a/src/virtManager/systray.py b/src/virtManager/systray.py index 27948aac5..56e3d334b 100644 --- a/src/virtManager/systray.py +++ b/src/virtManager/systray.py @@ -18,6 +18,8 @@ # MA 02110-1301 USA. # +import logging + import gobject import gtk @@ -90,7 +92,9 @@ class vmmSystray(vmmGObject): self.init_systray_menu() - self.config.on_view_system_tray_changed(self.show_systray) + self.add_gconf_handle( + self.config.on_view_system_tray_changed(self.show_systray)) + self.show_systray() def is_visible(self): @@ -102,6 +106,20 @@ class vmmSystray(vmmGObject): self.systray_icon and self.systray_icon.is_embedded()) + def cleanup(self): + vmmGObject.cleanup(self) + + try: + self.err = None + + if self.systray_menu: + self.systray_menu.destroy() + self.systray_menu = None + + self.systray_icon = None + except: + logging.exception("Error cleaning up systray") + # Initialization routines def init_systray_menu(self): diff --git a/src/virtManager/uihelpers.py b/src/virtManager/uihelpers.py index 70cb5fc08..b6ff5b68a 100644 --- a/src/virtManager/uihelpers.py +++ b/src/virtManager/uihelpers.py @@ -50,6 +50,10 @@ def set_error_parent(parent): err_dial.set_parent(parent) err_dial = err_dial +def cleanup(): + global err_dial + err_dial = None + ############################################################ # Helpers for shared storage UI between create/addhardware # ############################################################