Refactor DownloadStatusManager to DownloadStatusModel
Cleans up some GTK+ requirements of gpodder.services and makes the purpose of DownloadStatusModel vs. DownloadQueueManager clearer to the developer.
This commit is contained in:
parent
a6c54819d1
commit
965cecfa7f
|
@ -19,7 +19,7 @@
|
|||
|
||||
|
||||
#
|
||||
# download.py -- Download client using DownloadStatusManager
|
||||
# download.py -- Download queue management
|
||||
# Thomas Perl <thp@perli.net> 2007-09-15
|
||||
#
|
||||
# Based on libwget.py (2005-10-29)
|
||||
|
@ -328,8 +328,7 @@ class DownloadQueueWorker(threading.Thread):
|
|||
|
||||
|
||||
class DownloadQueueManager(object):
|
||||
def __init__(self, download_status_manager, config):
|
||||
self.download_status_manager = download_status_manager
|
||||
def __init__(self, config):
|
||||
self._config = config
|
||||
self.tasks = collections.deque()
|
||||
|
||||
|
@ -362,15 +361,8 @@ class DownloadQueueManager(object):
|
|||
with self.worker_threads_access:
|
||||
return len(self.worker_threads) > 0
|
||||
|
||||
def add_resumed_task(self, task):
|
||||
"""Simply add the task without starting the download"""
|
||||
self.download_status_manager.register_task(task)
|
||||
|
||||
def add_task(self, task):
|
||||
if task.status == DownloadTask.INIT:
|
||||
# This task is fresh, so add it to our status manager
|
||||
self.download_status_manager.register_task(task)
|
||||
else:
|
||||
if task.status != DownloadTask.INIT:
|
||||
# This task is old so update episode from db
|
||||
task.episode.reload_from_db()
|
||||
task.status = DownloadTask.QUEUED
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# gPodder - A media aggregator and podcast client
|
||||
# Copyright (c) 2005-2009 Thomas Perl and the gPodder Team
|
||||
#
|
||||
# gPodder 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 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gPodder 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# gpodder.gtkui.download -- Download management in GUIs (2009-08-24)
|
||||
# Based on code from gpodder.services (thp, 2007-08-24)
|
||||
#
|
||||
|
||||
import gpodder
|
||||
from gpodder.liblogger import log
|
||||
|
||||
from gpodder import util
|
||||
from gpodder import download
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
import collections
|
||||
|
||||
_ = gpodder.gettext
|
||||
|
||||
|
||||
class DownloadStatusModel(gtk.ListStore):
|
||||
# Symbolic names for our columns, so we know what we're up to
|
||||
C_TASK, C_NAME, C_URL, C_PROGRESS, C_PROGRESS_TEXT, C_SIZE_TEXT, \
|
||||
C_ICON_NAME, C_SPEED_TEXT, C_STATUS_TEXT = range(9)
|
||||
|
||||
def __init__(self):
|
||||
gtk.ListStore.__init__(self, object, str, str, int, str, str, str, str, str)
|
||||
|
||||
# Set up stock icon IDs for tasks
|
||||
self._status_ids = collections.defaultdict(lambda: None)
|
||||
self._status_ids[download.DownloadTask.DOWNLOADING] = gtk.STOCK_GO_DOWN
|
||||
self._status_ids[download.DownloadTask.DONE] = gtk.STOCK_APPLY
|
||||
self._status_ids[download.DownloadTask.FAILED] = gtk.STOCK_STOP
|
||||
self._status_ids[download.DownloadTask.CANCELLED] = gtk.STOCK_CANCEL
|
||||
self._status_ids[download.DownloadTask.PAUSED] = gtk.STOCK_MEDIA_PAUSE
|
||||
|
||||
def request_update(self, iter, task=None):
|
||||
if task is None:
|
||||
# Ongoing update request from UI - get task from model
|
||||
task = self.get_value(iter, self.C_TASK)
|
||||
else:
|
||||
# Initial update request - update non-changing fields
|
||||
self.set(iter,
|
||||
self.C_TASK, task,
|
||||
self.C_NAME, str(task),
|
||||
self.C_URL, task.url)
|
||||
|
||||
if task.status == task.FAILED:
|
||||
status_message = _('Failed: %s') % (task.error_message,)
|
||||
else:
|
||||
status_message = task.STATUS_MESSAGE[task.status]
|
||||
|
||||
if task.status == task.DOWNLOADING:
|
||||
speed_message = '%s/s' % util.format_filesize(task.speed)
|
||||
else:
|
||||
speed_message = ''
|
||||
|
||||
self.set(iter,
|
||||
self.C_PROGRESS, 100.*task.progress,
|
||||
self.C_PROGRESS_TEXT, '%.0f%%' % (task.progress*100.,),
|
||||
self.C_SIZE_TEXT, util.format_filesize(task.total_size),
|
||||
self.C_ICON_NAME, self._status_ids[task.status],
|
||||
self.C_SPEED_TEXT, speed_message,
|
||||
self.C_STATUS_TEXT, status_message)
|
||||
|
||||
def __add_new_task(self, task):
|
||||
iter = self.append()
|
||||
self.request_update(iter, task)
|
||||
|
||||
def register_task(self, task):
|
||||
util.idle_add(self.__add_new_task, task)
|
||||
|
||||
def tell_all_tasks_to_quit(self):
|
||||
for row in self:
|
||||
task = row[DownloadStatusModel.C_TASK]
|
||||
if task is not None:
|
||||
# Pause currently-running (and queued) downloads
|
||||
if task.status in (task.QUEUED, task.DOWNLOADING):
|
||||
task.status = task.PAUSED
|
||||
|
||||
# Delete cancelled and failed downloads
|
||||
if task.status in (task.CANCELLED, task.FAILED):
|
||||
task.removed_from_list()
|
||||
|
||||
def are_downloads_in_progress(self):
|
||||
"""
|
||||
Returns True if there are any downloads in the
|
||||
QUEUED or DOWNLOADING status, False otherwise.
|
||||
"""
|
||||
for row in self:
|
||||
task = row[DownloadStatusModel.C_TASK]
|
||||
if task is not None and \
|
||||
task.status in (task.DOWNLOADING, \
|
||||
task.QUEUED):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def cancel_by_url(self, url):
|
||||
for row in self:
|
||||
task = row[DownloadStatusModel.C_TASK]
|
||||
if task is not None and task.url == url and \
|
||||
task.status in (task.DOWNLOADING, \
|
||||
task.QUEUED):
|
||||
task.status = task.CANCELLED
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
@ -96,6 +96,7 @@ from gpodder.gtkui.model import PodcastListModel
|
|||
from gpodder.gtkui.model import EpisodeListModel
|
||||
from gpodder.gtkui.opml import OpmlListModel
|
||||
from gpodder.gtkui.config import ConfigModel
|
||||
from gpodder.gtkui.download import DownloadStatusModel
|
||||
|
||||
from gpodder.libgpodder import db
|
||||
from gpodder.libgpodder import gl
|
||||
|
@ -503,8 +504,8 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
self.tray_icon = None
|
||||
self.gpodder_episode_window = None
|
||||
|
||||
self.download_status_manager = services.DownloadStatusManager()
|
||||
self.download_queue_manager = download.DownloadQueueManager(self.download_status_manager, gl.config)
|
||||
self.download_status_model = DownloadStatusModel()
|
||||
self.download_queue_manager = download.DownloadQueueManager(gl.config)
|
||||
|
||||
self.fullscreen = False
|
||||
self.minimized = False
|
||||
|
@ -660,8 +661,6 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
self.treeDownloads.set_rubber_banding(True)
|
||||
|
||||
# columns and renderers for "download progress" tab
|
||||
DownloadStatusManager = services.DownloadStatusManager
|
||||
|
||||
# First column: [ICON] Episodename
|
||||
column = gtk.TreeViewColumn(_('Episode'))
|
||||
|
||||
|
@ -672,12 +671,12 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
cell.set_property('stock-size', gtk.ICON_SIZE_MENU)
|
||||
column.pack_start(cell, expand=False)
|
||||
column.add_attribute(cell, 'stock-id', \
|
||||
DownloadStatusManager.C_ICON_NAME)
|
||||
DownloadStatusModel.C_ICON_NAME)
|
||||
|
||||
cell = gtk.CellRendererText()
|
||||
cell.set_property('ellipsize', pango.ELLIPSIZE_END)
|
||||
column.pack_start(cell, expand=True)
|
||||
column.add_attribute(cell, 'text', DownloadStatusManager.C_NAME)
|
||||
column.add_attribute(cell, 'text', DownloadStatusModel.C_NAME)
|
||||
|
||||
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
|
||||
column.set_resizable(True)
|
||||
|
@ -686,24 +685,24 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
|
||||
# Second column: Progress
|
||||
column = gtk.TreeViewColumn(_('Progress'), gtk.CellRendererProgress(),
|
||||
value=DownloadStatusManager.C_PROGRESS, \
|
||||
text=DownloadStatusManager.C_PROGRESS_TEXT)
|
||||
value=DownloadStatusModel.C_PROGRESS, \
|
||||
text=DownloadStatusModel.C_PROGRESS_TEXT)
|
||||
self.treeDownloads.append_column(column)
|
||||
|
||||
# Third column: Size
|
||||
if gpodder.interface != gpodder.MAEMO:
|
||||
column = gtk.TreeViewColumn(_('Size'), gtk.CellRendererText(),
|
||||
text=DownloadStatusManager.C_SIZE_TEXT)
|
||||
text=DownloadStatusModel.C_SIZE_TEXT)
|
||||
self.treeDownloads.append_column(column)
|
||||
|
||||
# Fourth column: Speed
|
||||
column = gtk.TreeViewColumn(_('Speed'), gtk.CellRendererText(),
|
||||
text=DownloadStatusManager.C_SPEED_TEXT)
|
||||
text=DownloadStatusModel.C_SPEED_TEXT)
|
||||
self.treeDownloads.append_column(column)
|
||||
|
||||
# Fifth column: Status
|
||||
column = gtk.TreeViewColumn(_('Status'), gtk.CellRendererText(),
|
||||
text=DownloadStatusManager.C_STATUS_TEXT)
|
||||
text=DownloadStatusModel.C_STATUS_TEXT)
|
||||
self.treeDownloads.append_column(column)
|
||||
|
||||
# After we've set up most of the window, show it :)
|
||||
|
@ -718,7 +717,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
services.cover_downloader.register('cover-available', self.cover_download_finished)
|
||||
services.cover_downloader.register('cover-removed', self.cover_file_removed)
|
||||
|
||||
self.treeDownloads.set_model(self.download_status_manager.get_tree_model())
|
||||
self.treeDownloads.set_model(self.download_status_model)
|
||||
self.download_tasks_seen = set()
|
||||
self.download_list_update_enabled = False
|
||||
self.last_download_count = 0
|
||||
|
@ -810,7 +809,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
self.download_list_update_enabled = True
|
||||
|
||||
def on_btnCleanUpDownloads_clicked(self, button):
|
||||
model = self.treeDownloads.get_model()
|
||||
model = self.download_status_model
|
||||
|
||||
all_tasks = [(gtk.TreeRowReference(model, row.path), row[0]) for row in model]
|
||||
changed_episode_urls = []
|
||||
|
@ -842,7 +841,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
|
||||
def update_downloads_list(self):
|
||||
try:
|
||||
model = self.treeDownloads.get_model()
|
||||
model = self.download_status_model
|
||||
|
||||
downloading, failed, finished, queued, others = 0, 0, 0, 0, 0
|
||||
total_speed, total_size, done_size = 0, 0, 0
|
||||
|
@ -864,9 +863,9 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
model = ()
|
||||
|
||||
for row in model:
|
||||
self.download_status_manager.request_update(row.iter)
|
||||
self.download_status_model.request_update(row.iter)
|
||||
|
||||
task = row[self.download_status_manager.C_TASK]
|
||||
task = row[self.download_status_model.C_TASK]
|
||||
speed, size, status, progress = task.speed, task.total_size, task.status, task.progress
|
||||
|
||||
total_size += size
|
||||
|
@ -2181,7 +2180,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
Displays a confirmation dialog (and closes/hides gPodder)
|
||||
"""
|
||||
|
||||
downloading = self.download_status_manager.are_downloads_in_progress()
|
||||
downloading = self.download_status_model.are_downloads_in_progress()
|
||||
|
||||
# Only iconify if we are using the window's "X" button,
|
||||
# but not when we are using "Quit" in the menu or toolbar
|
||||
|
@ -2240,7 +2239,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
self.tray_icon.set_visible(False)
|
||||
|
||||
# Notify all tasks to to carry out any clean-up actions
|
||||
self.download_status_manager.tell_all_tasks_to_quit()
|
||||
self.download_status_model.tell_all_tasks_to_quit()
|
||||
|
||||
while gtk.events_pending():
|
||||
gtk.main_iteration(False)
|
||||
|
@ -2412,9 +2411,10 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
|
||||
if add_paused:
|
||||
task.status = task.PAUSED
|
||||
self.download_queue_manager.add_resumed_task(task)
|
||||
else:
|
||||
self.download_queue_manager.add_task(task)
|
||||
|
||||
self.download_status_model.register_task(task)
|
||||
self.enable_download_list_update()
|
||||
|
||||
def new_episodes_show(self, episodes):
|
||||
|
@ -2834,7 +2834,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
|
||||
# cancel any active downloads from this channel
|
||||
for episode in self.active_channel.get_all_episodes():
|
||||
self.download_status_manager.cancel_by_url(episode.url)
|
||||
self.download_status_model.cancel_by_url(episode.url)
|
||||
|
||||
# get the URL of the podcast we want to select next
|
||||
position = self.channels.index(self.active_channel)
|
||||
|
@ -2980,7 +2980,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
self.toolDownload.set_sensitive( False)
|
||||
self.toolPlay.set_sensitive( False)
|
||||
self.toolTransfer.set_sensitive( False)
|
||||
self.toolCancel.set_sensitive( False)#services.download_status_manager.has_items())
|
||||
self.toolCancel.set_sensitive( False)#services.download_status_model.has_items())
|
||||
|
||||
def on_treeChannels_row_activated(self, widget, path, *args):
|
||||
# double-click action of the podcast list or enter
|
||||
|
@ -3090,7 +3090,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
if self.gpodder_episode_window is None:
|
||||
log('First-time use of episode window --- creating', sender=self)
|
||||
self.gpodder_episode_window = gPodderEpisode(\
|
||||
download_status_manager=self.download_status_manager, \
|
||||
download_status_model=self.download_status_model, \
|
||||
episode_is_downloading=self.episode_is_downloading)
|
||||
self.gpodder_episode_window.show(episode=episode, download_callback=download_callback, play_callback=play_callback)
|
||||
|
||||
|
@ -3908,7 +3908,7 @@ class gPodderEpisode(BuilderWidget):
|
|||
b.place_cursor(b.get_start_iter())
|
||||
|
||||
def on_cancel(self, widget):
|
||||
self.download_status_manager.cancel_by_url(self.episode.url)
|
||||
self.download_status_model.cancel_by_url(self.episode.url)
|
||||
|
||||
def on_delete_event(self, widget, event):
|
||||
# Avoid destroying the dialog, simply hide
|
||||
|
|
|
@ -36,12 +36,10 @@ from gpodder import download
|
|||
import gtk
|
||||
import gobject
|
||||
|
||||
import collections
|
||||
import threading
|
||||
import time
|
||||
import urllib2
|
||||
import os
|
||||
import os.path
|
||||
|
||||
_ = gpodder.gettext
|
||||
|
||||
|
@ -287,101 +285,3 @@ class CoverDownloader(ObservableService):
|
|||
cover_downloader = CoverDownloader()
|
||||
|
||||
|
||||
class DownloadStatusManager(object):
|
||||
# Types of columns, needed for creation of gtk.ListStore
|
||||
COLUMNS = (object, str, str, int, str, str, str, str, str)
|
||||
|
||||
# Symbolic names for our columns, so we know what we're up to
|
||||
C_TASK, C_NAME, C_URL, C_PROGRESS, C_PROGRESS_TEXT, C_SIZE_TEXT, \
|
||||
C_ICON_NAME, C_SPEED_TEXT, C_STATUS_TEXT = range(len(COLUMNS))
|
||||
|
||||
def __init__(self):
|
||||
self.__model = gtk.ListStore(*DownloadStatusManager.COLUMNS)
|
||||
|
||||
# Shorten the name (for below)
|
||||
DownloadTask = download.DownloadTask
|
||||
|
||||
# Set up stock icon IDs for tasks
|
||||
self.status_stock_ids = collections.defaultdict(lambda: None)
|
||||
self.status_stock_ids[DownloadTask.DOWNLOADING] = gtk.STOCK_GO_DOWN
|
||||
self.status_stock_ids[DownloadTask.DONE] = gtk.STOCK_APPLY
|
||||
self.status_stock_ids[DownloadTask.FAILED] = gtk.STOCK_STOP
|
||||
self.status_stock_ids[DownloadTask.CANCELLED] = gtk.STOCK_CANCEL
|
||||
self.status_stock_ids[DownloadTask.PAUSED] = gtk.STOCK_MEDIA_PAUSE
|
||||
|
||||
def get_tree_model(self):
|
||||
return self.__model
|
||||
|
||||
def request_update(self, iter, task=None):
|
||||
if task is None:
|
||||
# Ongoing update request from UI - get task from model
|
||||
task = self.__model.get_value(iter, self.C_TASK)
|
||||
else:
|
||||
# Initial update request - update non-changing fields
|
||||
self.__model.set(iter,
|
||||
self.C_TASK, task,
|
||||
self.C_NAME, str(task),
|
||||
self.C_URL, task.url)
|
||||
|
||||
if task.status == task.FAILED:
|
||||
status_message = _('Failed: %s') % (task.error_message,)
|
||||
else:
|
||||
status_message = task.STATUS_MESSAGE[task.status]
|
||||
|
||||
if task.status == task.DOWNLOADING:
|
||||
speed_message = '%s/s' % util.format_filesize(task.speed)
|
||||
else:
|
||||
speed_message = ''
|
||||
|
||||
self.__model.set(iter,
|
||||
self.C_PROGRESS, 100.*task.progress,
|
||||
self.C_PROGRESS_TEXT, '%.0f%%' % (task.progress*100.,),
|
||||
self.C_SIZE_TEXT, util.format_filesize(task.total_size),
|
||||
self.C_ICON_NAME, self.status_stock_ids[task.status],
|
||||
self.C_SPEED_TEXT, speed_message,
|
||||
self.C_STATUS_TEXT, status_message)
|
||||
|
||||
def __add_new_task(self, task):
|
||||
iter = self.__model.append()
|
||||
self.request_update(iter, task)
|
||||
|
||||
def register_task(self, task):
|
||||
util.idle_add(self.__add_new_task, task)
|
||||
|
||||
def tell_all_tasks_to_quit(self):
|
||||
for row in self.__model:
|
||||
task = row[DownloadStatusManager.C_TASK]
|
||||
if task is not None:
|
||||
# Pause currently-running (and queued) downloads
|
||||
if task.status in (task.QUEUED, task.DOWNLOADING):
|
||||
task.status = task.PAUSED
|
||||
|
||||
# Delete cancelled and failed downloads
|
||||
if task.status in (task.CANCELLED, task.FAILED):
|
||||
task.removed_from_list()
|
||||
|
||||
def are_downloads_in_progress(self):
|
||||
"""
|
||||
Returns True if there are any downloads in the
|
||||
QUEUED or DOWNLOADING status, False otherwise.
|
||||
"""
|
||||
for row in self.__model:
|
||||
task = row[DownloadStatusManager.C_TASK]
|
||||
if task is not None and \
|
||||
task.status in (task.DOWNLOADING, \
|
||||
task.QUEUED):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def cancel_by_url(self, url):
|
||||
for row in self.__model:
|
||||
task = row[DownloadStatusManager.C_TASK]
|
||||
if task is not None and task.url == url and \
|
||||
task.status in (task.DOWNLOADING, \
|
||||
task.QUEUED):
|
||||
task.status = task.CANCELLED
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
|
Loading…
Reference in New Issue