Add ListView state saving.

Have TorrentView save and load state on startup/shutdown.
Add new method to components shutdown() which is called when the UI is 
exiting to allow components to clean-up.
Clean up some debug output.
This commit is contained in:
Andrew Resch 2007-12-08 05:02:29 +00:00
commit 589df97add
9 changed files with 107 additions and 10 deletions

1
TODO
View file

@ -1,4 +1,3 @@
* Add state saving to listview.. this includes saving column size and position.
* Queue plugin 'apply_queue' stuff.. Just finishing the queue plugin and it's * Queue plugin 'apply_queue' stuff.. Just finishing the queue plugin and it's
intended functionality. intended functionality.
* Figure out easy way for user-made plugins to add i18n support. * Figure out easy way for user-made plugins to add i18n support.

View file

@ -55,6 +55,7 @@ from deluge.core.signalmanager import SignalManager
from deluge.log import LOG as log from deluge.log import LOG as log
DEFAULT_PREFS = { DEFAULT_PREFS = {
"config_location": deluge.common.get_config_dir(),
"daemon_port": 58846, "daemon_port": 58846,
"allow_remote": False, "allow_remote": False,
"compact_allocation": True, "compact_allocation": True,

View file

@ -63,7 +63,6 @@ class PluginManagerBase:
self.enable_plugin(name) self.enable_plugin(name)
def shutdown(self): def shutdown(self):
log.debug("PluginManager shutting down..")
for plugin in self.plugins.values(): for plugin in self.plugins.values():
plugin.disable() plugin.disable()
del self.plugins del self.plugins

View file

@ -60,6 +60,9 @@ class Component:
def _stop(self): def _stop(self):
self._state = COMPONENT_STATE.index("Stopped") self._state = COMPONENT_STATE.index("Stopped")
def shutdown(self):
pass
def update(self): def update(self):
pass pass
@ -100,6 +103,7 @@ class ComponentRegistry:
# Only start if the component is stopped. # Only start if the component is stopped.
if self.components[name].get_state() == \ if self.components[name].get_state() == \
COMPONENT_STATE.index("Stopped"): COMPONENT_STATE.index("Stopped"):
log.debug("Starting component %s..", name)
self.components[name].start() self.components[name].start()
self.components[name]._start() self.components[name]._start()
@ -107,6 +111,7 @@ class ComponentRegistry:
def stop(self): def stop(self):
"""Stops all components""" """Stops all components"""
for component in self.components.keys(): for component in self.components.keys():
log.debug("Stopping component %s..", component)
self.components[component].stop() self.components[component].stop()
self.components[component]._stop() self.components[component]._stop()
# Stop the update timer # Stop the update timer
@ -122,6 +127,17 @@ class ComponentRegistry:
return True return True
def shutdown(self):
"""Shuts down all components. This should be called when the program
exits so that components can do any necessary clean-up."""
for component in self.components.keys():
log.debug("Shutting down component %s..", component)
try:
self.components[component].shutdown()
except Exception, e:
log.debug("Unable to call shutdown(): %s", e)
_ComponentRegistry = ComponentRegistry() _ComponentRegistry = ComponentRegistry()
def register(name, obj, depend=None): def register(name, obj, depend=None):
@ -140,6 +156,10 @@ def update():
"""Updates all components""" """Updates all components"""
_ComponentRegistry.update() _ComponentRegistry.update()
def shutdown():
"""Shutdowns all components"""
_ComponentRegistry.shutdown()
def get(component): def get(component):
"""Return a reference to the component""" """Return a reference to the component"""
return _ComponentRegistry.get(component) return _ComponentRegistry.get(component)

View file

@ -55,10 +55,12 @@ from pluginmanager import PluginManager
from dbusinterface import DbusInterface from dbusinterface import DbusInterface
from queuedtorrents import QueuedTorrents from queuedtorrents import QueuedTorrents
from deluge.configmanager import ConfigManager from deluge.configmanager import ConfigManager
import deluge.common
from deluge.log import LOG as log from deluge.log import LOG as log
import deluge.configmanager import deluge.configmanager
DEFAULT_PREFS = { DEFAULT_PREFS = {
"config_location": deluge.common.get_config_dir(),
"interactive_add": False, "interactive_add": False,
"enable_files_dialog": False, "enable_files_dialog": False,
"enable_system_tray": True, "enable_system_tray": True,
@ -151,6 +153,8 @@ class GtkUI:
del config del config
# Clean-up # Clean-up
# Shutdown all components
component.shutdown()
del self.mainwindow del self.mainwindow
del self.systemtray del self.systemtray
del self.menubar del self.menubar

View file

@ -31,11 +31,15 @@
# this exception statement from your version. If you delete this exception # this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here. # statement from all source files in the program, then also delete it here.
import cPickle
import os.path
import pygtk import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk
import gettext import gettext
from deluge.configmanager import ConfigManager
import deluge.common import deluge.common
from deluge.log import LOG as log from deluge.log import LOG as log
@ -84,6 +88,16 @@ def cell_data_ratio(column, cell, model, row, data):
ratio_str = "%.3f" % ratio ratio_str = "%.3f" % ratio
cell.set_property('text', ratio_str) cell.set_property('text', ratio_str)
class ListViewColumnState:
"""Used for saving/loading column state"""
def __init__(self, name, position, width, visible, sort, sort_order):
self.name = name
self.position = position
self.width = width
self.visible = visible
self.sort = sort
self.sort_order = sort_order
class ListView: class ListView:
"""ListView is used to make custom GtkTreeViews. It supports the adding """ListView is used to make custom GtkTreeViews. It supports the adding
and removing of columns, creating a menu for a column toggle list and and removing of columns, creating a menu for a column toggle list and
@ -106,8 +120,7 @@ class ListView:
# If column is 'hidden' then it will not be visible and will not # If column is 'hidden' then it will not be visible and will not
# show up in any menu listing; it cannot be shown ever. # show up in any menu listing; it cannot be shown ever.
self.hidden = False self.hidden = False
def __init__(self, treeview_widget=None): def __init__(self, treeview_widget=None):
log.debug("ListView initialized..") log.debug("ListView initialized..")
@ -138,6 +151,50 @@ class ListView:
# created. # created.
self.checklist_menus = [] self.checklist_menus = []
def save_state(self, filename):
"""Saves the listview state (column positions and visibility) to
filename."""
# A list of ListViewColumnStates
state = []
# Get the list of TreeViewColumns from the TreeView
treeview_columns = self.treeview.get_columns()
counter = 0
for column in treeview_columns:
# Append a new column state to the state list
state.append(ListViewColumnState(column.get_title(), counter,
column.get_width(), column.get_visible(),
column.get_sort_indicator(), int(column.get_sort_order())))
# Increase the counter because this is how we determine position
counter += 1
# Get the config location for saving the state file
config_location = ConfigManager("gtkui.conf")["config_location"]
try:
log.debug("Saving ListView state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "wb")
cPickle.dump(state, state_file)
state_file.close()
except IOError, e:
log.warning("Unable to save state file: %s", e)
def load_state(self, filename):
"""Load the listview state from filename."""
# Get the config location for loading the state file
config_location = ConfigManager("gtkui.conf")["config_location"]
try:
log.debug("Loading ListView state file: %s", filename)
state_file = open(os.path.join(config_location, filename), "rb")
state = cPickle.load(state_file)
state_file.close()
except IOError:
log.warning("Unable to load state file: %s", e)
# Keep the state in self.state so we can access it as we add new columns
self.state = state
def set_treeview(self, treeview_widget): def set_treeview(self, treeview_widget):
"""Set the treeview widget that this listview uses.""" """Set the treeview widget that this listview uses."""
self.treeview = treeview_widget self.treeview = treeview_widget
@ -317,6 +374,7 @@ class ListView:
elif column_type == None: elif column_type == None:
return return
column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
column.set_sort_column_id(self.columns[header].column_indices[sortid]) column.set_sort_column_id(self.columns[header].column_indices[sortid])
column.set_clickable(True) column.set_clickable(True)
column.set_resizable(True) column.set_resizable(True)
@ -324,10 +382,23 @@ class ListView:
column.set_min_width(10) column.set_min_width(10)
column.set_reorderable(True) column.set_reorderable(True)
column.set_visible(not hidden) column.set_visible(not hidden)
# Check for loaded state and apply
for column_state in self.state:
if header == column_state.name:
# We found a loaded state
if column_state.width > 0:
column.set_fixed_width(column_state.width)
column.set_sort_indicator(column_state.sort)
column.set_sort_order(column_state.sort_order)
column.set_visible(column_state.visible)
position = column_state.position
if position is not None: if position is not None:
self.treeview.insert_column(column, position) self.treeview.insert_column(column, position)
else: else:
self.treeview.append_column(column) self.treeview.append_column(column)
# Set hidden in the column # Set hidden in the column
self.columns[header].hidden = hidden self.columns[header].hidden = hidden
self.columns[header].column = column self.columns[header].column = column

View file

@ -107,7 +107,6 @@ class StatusBar(component.Component):
self.show_not_connected() self.show_not_connected()
def start(self): def start(self):
log.debug("StatusBar start..")
# Add in images and labels # Add in images and labels
self.remove_item(self.not_connected_item) self.remove_item(self.not_connected_item)
self.connections_item = StatusBarItem( self.connections_item = StatusBarItem(

View file

@ -99,7 +99,6 @@ class SystemTray(component.Component):
self.tray_glade.get_widget(widget).hide() self.tray_glade.get_widget(widget).hide()
def start(self): def start(self):
log.debug("SystemTray start..")
if self.config["enable_system_tray"]: if self.config["enable_system_tray"]:
# Show widgets in the hide list because we've connected to a host # Show widgets in the hide list because we've connected to a host
for widget in self.hide_widget_list: for widget in self.hide_widget_list:
@ -109,7 +108,6 @@ class SystemTray(component.Component):
self.build_tray_bwsetsubmenu() self.build_tray_bwsetsubmenu()
def stop(self): def stop(self):
log.debug("SystemTray stop..")
try: try:
# Hide widgets in hide list because we're not connected to a host # Hide widgets in hide list because we're not connected to a host
for widget in self.hide_widget_list: for widget in self.hide_widget_list:

View file

@ -116,7 +116,9 @@ class TorrentView(listview.ListView, component.Component):
listview.ListView.__init__(self, listview.ListView.__init__(self,
self.window.main_glade.get_widget("torrent_view")) self.window.main_glade.get_widget("torrent_view"))
log.debug("TorrentView Init..") log.debug("TorrentView Init..")
# Try to load the state file if available
self.load_state("torrentview.state")
# Register the columns menu with the listview so it gets updated # Register the columns menu with the listview so it gets updated
# accordingly. # accordingly.
self.register_checklist_menu( self.register_checklist_menu(
@ -185,7 +187,7 @@ class TorrentView(listview.ListView, component.Component):
# changes. # changes.
self.treeview.get_selection().connect("changed", self.treeview.get_selection().connect("changed",
self.on_selection_changed) self.on_selection_changed)
def start(self): def start(self):
"""Start the torrentview""" """Start the torrentview"""
# We need to get the core session state to know which torrents are in # We need to get the core session state to know which torrents are in
@ -198,7 +200,11 @@ class TorrentView(listview.ListView, component.Component):
"""Stops the torrentview""" """Stops the torrentview"""
# We need to clear the liststore # We need to clear the liststore
self.liststore.clear() self.liststore.clear()
def shutdown(self):
"""Called when GtkUi is exiting"""
self.save_state("torrentview.state")
def set_filter(self, field, condition): def set_filter(self, field, condition):
"""Sets filters for the torrentview..""" """Sets filters for the torrentview.."""
self.filter = (field, condition) self.filter = (field, condition)