gget r13 - in trunk: data gget
- From: johans svn gnome org
- To: svn-commits-list gnome org
- Subject: gget r13 - in trunk: data gget
- Date: Mon, 23 Jun 2008 08:35:59 +0000 (UTC)
Author: johans
Date: Mon Jun 23 08:35:59 2008
New Revision: 13
URL: http://svn.gnome.org/viewvc/gget?rev=13&view=rev
Log:
More work on metalink support. A lot of things still missing.
Added:
trunk/gget/DownloadManager.py
Modified:
trunk/data/gget.glade
trunk/gget/AddDownloadDialog.py
trunk/gget/Download.py
trunk/gget/Main.py
trunk/gget/MainWindow.py
trunk/gget/Makefile.am
trunk/gget/Utils.py
Modified: trunk/data/gget.glade
==============================================================================
--- trunk/data/gget.glade (original)
+++ trunk/data/gget.glade Mon Jun 23 08:35:59 2008
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.5 on Thu Jun 19 02:03:34 2008 -->
+<!--Generated with glade3 3.4.5 on Mon Jun 23 08:26:27 2008 -->
<glade-interface>
<widget class="GtkWindow" id="main_window">
<property name="width_request">800</property>
@@ -56,27 +56,6 @@
<widget class="GtkMenu" id="menu2">
<property name="visible">True</property>
<child>
- <widget class="GtkMenuItem" id="select_all_menu_item">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Select all</property>
- <property name="use_underline">True</property>
- <accelerator key="a" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="unselect_all_menu_item">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Unselect all</property>
- <property name="use_underline">True</property>
- <accelerator key="a" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="activate"/>
- </widget>
- </child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separatormenuitem2">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
<widget class="GtkImageMenuItem" id="preferences_menu_item">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-preferences</property>
@@ -237,25 +216,16 @@
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child>
- <widget class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="xalign">1</property>
- <property name="label" translatable="yes">_URL:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">url_entry</property>
- </widget>
- <packing>
- <property name="x_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <widget class="GtkEntry" id="url_entry">
+ <widget class="GtkFileChooserButton" id="download_filechooserbutton">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
+ <property name="title" translatable="yes">Select download folder</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
</packing>
</child>
<child>
@@ -273,16 +243,25 @@
</packing>
</child>
<child>
- <widget class="GtkFileChooserButton" id="download_filechooserbutton">
+ <widget class="GtkEntry" id="url_entry">
<property name="visible">True</property>
- <property name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
- <property name="title" translatable="yes">Select download folder</property>
+ <property name="can_focus">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">_URL:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">url_entry</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
</packing>
</child>
</widget>
@@ -550,8 +529,8 @@
<widget class="GtkFileChooserButton" id="default_folder_filechooserbutton">
<property name="visible">True</property>
<property name="sensitive">False</property>
- <property name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
<property name="local_only">False</property>
+ <property name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
<property name="title" translatable="yes">Select download folder</property>
</widget>
</child>
Modified: trunk/gget/AddDownloadDialog.py
==============================================================================
--- trunk/gget/AddDownloadDialog.py (original)
+++ trunk/gget/AddDownloadDialog.py Mon Jun 23 08:35:59 2008
@@ -23,11 +23,11 @@
import gtk
import GUI
+from DownloadManager import DownloadManager
from gget import NAME
class AddDownloadDialog:
- def __init__(self, main_window, config):
- self.main_window = main_window
+ def __init__(self, config):
self.config = config
self.__get_widgets()
@@ -72,8 +72,9 @@
def __set_url_from_clipboard(self, clipboard):
if clipboard.wait_is_text_available():
url = self.clipboard.wait_for_text()
- if self.__is_valid_url(url):
+ if url and self.__is_valid_url(url):
self.url_entry.set_text(url)
+ # self.url_entry.select_region(0, -1)
def __is_valid_url(self, url):
PROTOCOLS = ["http", "https", "ftp"]
@@ -88,7 +89,8 @@
self.add_button.clicked()
def __add_button_clicked(self, button):
- self.main_window.start_download(self.url_entry.get_text(),
+ download_manager = DownloadManager()
+ download_manager.start_download(self.url_entry.get_text(),
self.download_filechooserbutton.get_current_folder())
self.dialog.hide()
Modified: trunk/gget/Download.py
==============================================================================
--- trunk/gget/Download.py (original)
+++ trunk/gget/Download.py Mon Jun 23 08:35:59 2008
@@ -19,14 +19,11 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import os.path
-import thread
from gettext import gettext as _
import gtk
import gobject
-import metalink
-
import Utils
import GUI
from gget import NAME
@@ -38,18 +35,26 @@
where to save the download", "",
gobject.PARAM_READWRITE)}
- __gsignals__ = {"finished": (gobject.SIGNAL_RUN_LAST, None, (object,)),
- "update": (gobject.SIGNAL_RUN_LAST, None, (int, int,
- int))}
+ __gsignals__ = {"update": (gobject.SIGNAL_RUN_LAST, None, (int, int,
+ int)),
+ "speed-changed": (gobject.SIGNAL_RUN_LAST, None, (int,)),
+ "status-changed": (gobject.SIGNAL_RUN_LAST, None, (int,))}
def __init__(self, url, path):
+ gobject.GObject.__init__(self)
self.url = url
self.path = path
self.file_name = os.path.basename(self.url)
+
self.size = 0
+ self.block_count = 0
+ self.block_size = 0
self.percent_complete = 0
+ def __str__(self):
+ return self.url
+
def do_get_property(self, property):
if property.name == "url":
return self.url
@@ -62,28 +67,26 @@
elif property.name == "path":
self.path = path
- def start(self):
- if os.path.exists(os.path.join(self.path, self.file_name)):
- pass
- # TODO: Overwrite dialog
- else:
- thread.start_new_thread(self.__start_in_thread, ())
-
- def __start_in_thread(self):
- metalink.get(self.url, self.path, self.__update)
-
- def __update(self, block_count, block_size, total_size):
- Utils.debug_print("Update called!")
+ def update(self, block_count, block_size, total_size):
+ Utils.debug_print("Download.update called with block_count: %s block_size: %s total_size: %s" % (block_count, block_size, total_size))
+ self.block_count = block_count
+ self.block_size = block_size
self.size = total_size
+
+ current_bytes = float(block_count * block_size) / 1024 / 1024
+ total_bytes = float(total_size) / 1024 / 1024
try:
- self.percent_complete = 100 * float(block_count * block_size) / float(total_size)
+ self.percent_complete = 100 * current_bytes / total_bytes
except ZeroDivisionError:
self.percent_complete = 0
- if percent_complete > 100:
- percent_complete = 100
+ if self.percent_complete > 100:
+ self.percent_complete = 100
- self.emit("update", block_count, block_size, total_size)
+ Utils.debug_print("Percent complete: %s" % self.percent_complete)
+ gtk.gdk.threads_enter()
+ self.emit("update", block_count, block_size, total_size)
+ gtk.gdk.threads_leave()
# vim: set sw=4 et sts=4 tw=79 fo+=l:
Added: trunk/gget/DownloadManager.py
==============================================================================
--- (empty file)
+++ trunk/gget/DownloadManager.py Mon Jun 23 08:35:59 2008
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2008 Johan Svedberg <johan svedberg com>
+
+# This file is part of gget.
+
+# gget 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.
+
+# gget 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 gget; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+import os.path
+import sys
+import thread
+from gettext import gettext as _
+
+import gtk
+import gobject
+
+import metalink
+
+import Utils
+import GUI
+from Download import Download
+from Configuration import Configuration
+from gget import NAME, VERSION
+
+class DownloadManager(gobject.GObject):
+ """Singleton handling the downloads"""
+
+ __gsignals__ = {"download-added": (gobject.SIGNAL_RUN_LAST, None,
+ (object,))}
+
+ instance = None
+
+ def __new__(type, *args):
+ if DownloadManager.instance is None:
+ DownloadManager.instance = gobject.GObject.__new__(type)
+ DownloadManager.instance.__init(*args)
+ return DownloadManager.instance
+
+ def __init(self, *args):
+ gobject.GObject.__init__(self)
+ self.config = Configuration()
+ self.downloads = []
+
+ metalink.USER_AGENT = "%s %s" % (NAME, VERSION)
+
+ # TODO: Get this from system wide settings
+ # metalink.HTTP_PROXY = ""
+ # metalink.HTTPS_PROXY = ""
+ # metalink.FTP_PROXY = ""
+
+ def start_download(self, uri, path=None):
+ if path is None:
+ path = self.config.default_folder
+ download = Download(uri, path)
+
+ Utils.debug_print("Starting download %s" % download)
+ self.downloads.append(download)
+ result = thread.start_new_thread(self.__start_download_in_thread,
+ (download,))
+ self.emit("download-added", (download))
+ # self.__start_download_in_thread(download)
+ if not result:
+ print "Failed downloading of file %s" % download.url
+
+ def __start_download_in_thread(self, download):
+ # Python 2.5 seems to have a bug: sys.excepthook is not call from code
+ # in a thread, see http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html
+ # sys.excepthook(*sys.exc_info())
+
+ metalink.get(download.url, download.path, handler=download.update)
+
+
+# vim: set sw=4 et sts=4 tw=79 fo+=l:
Modified: trunk/gget/Main.py
==============================================================================
--- trunk/gget/Main.py (original)
+++ trunk/gget/Main.py Mon Jun 23 08:35:59 2008
@@ -30,6 +30,7 @@
import gnome
import GUI
+from DownloadManager import DownloadManager
from MainWindow import MainWindow
from StatusIcon import StatusIcon
from Configuration import Configuration
@@ -56,12 +57,13 @@
print_usage()
gnome.init(NAME, VERSION)
- # gtk.gdk.threads_init()
+ gtk.gdk.threads_init()
gtk.window_set_default_icon_list(*GUI.get_icon_list([16, 22, 24, 32]))
config = Configuration(debug)
+ download_manager = DownloadManager()
- main_window = MainWindow(config)
+ main_window = MainWindow(config, download_manager)
if config.show_main_window:
main_window.window.show()
@@ -69,10 +71,15 @@
if not config.show_status_icon:
status_icon.icon.set_visible(False)
+ # sys.excepthook = main_window.on_unhandled_exception
+
+ for uri in args:
+ download_manager.start_download(uri)
+
gtk.main()
def print_usage():
- print _("Usage: %s [OPTION]...") % (sys.argv[0])
+ print _("Usage: %s [OPTION]... [URI]...") % (sys.argv[0])
print ""
print _("OPTIONS:")
print " -d, --debug %s" % (_("enable debug messages"))
Modified: trunk/gget/MainWindow.py
==============================================================================
--- trunk/gget/MainWindow.py (original)
+++ trunk/gget/MainWindow.py Mon Jun 23 08:35:59 2008
@@ -18,6 +18,8 @@
# along with gget; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+import sys
+import traceback
from gettext import gettext as _
import gtk
@@ -25,15 +27,17 @@
import GUI
import Utils
+from GUI import ErrorDialog
from AboutDialog import AboutDialog
from AddDownloadDialog import AddDownloadDialog
-from Download import Download
from PreferencesDialog import PreferencesDialog
from gget import NAME
class MainWindow:
- def __init__(self, config):
+ def __init__(self, config, download_manager):
self.config = config
+ self.download_manager = download_manager
+ self.download_manager.connect("download-added", self.__download_added)
self.__get_widgets()
@@ -42,6 +46,7 @@
self.__connect_widgets()
def __get_widgets(self):
+ """Get widgets from the glade file."""
xml = gtk.glade.XML(GUI.glade_file, domain=NAME.lower())
self.window = xml.get_widget("main_window")
@@ -51,8 +56,6 @@
self.quit_menu_item = xml.get_widget("quit_menu_item")
# Edit menu
- self.select_all_menu_item = xml.get_widget("select_all_menu_item")
- self.unselect_all_menu_item = xml.get_widget("unselect_all_menu_item")
self.preferences_menu_item = xml.get_widget("preferences_menu_item")
# Help menu
@@ -68,9 +71,9 @@
self.statusbar = xml.get_widget("statusbar")
def __make_downloads_treeview(self):
+ """Constructs the treeview containing downloads."""
self.downloads_model = gtk.ListStore(object)
self.downloads_treeview.set_model(self.downloads_model)
- self.downloads_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
# Name
name_cell_renderer = gtk.CellRendererText()
@@ -122,27 +125,49 @@
self.__speed_cell_data_func)
self.downloads_treeview.append_column(speed_treeview_column)
+ # ETA
+ eta_cell_renderer = gtk.CellRendererText()
+ eta_cell_renderer.props.xpad = 3
+ eta_cell_renderer.props.ypad = 3
+ eta_treeview_column = gtk.TreeViewColumn(_("ETA"),
+ eta_cell_renderer)
+ eta_treeview_column.set_cell_data_func(eta_cell_renderer,
+ self.__eta_cell_data_func)
+ self.downloads_treeview.append_column(eta_treeview_column)
+
def __name_cell_data_func(self, column, cell, model, iter):
+ """Data function for the name of downloads."""
download = model.get_value(iter, 0)
cell.props.text = download.file_name
def __status_cell_data_func(self, column, cell, model, iter):
+ """Data function for the status of downloads."""
download = model.get_value(iter, 0)
cell.props.text = "N/A"
def __size_cell_data_func(self, column, cell, model, iter):
+ """Data function for the file size of downloads."""
download = model.get_value(iter, 0)
cell.props.text = Utils.get_readable_size(download.size)
def __progress_cell_data_func(self, column, cell, model, iter):
+ """Data function for the progress bar of downloads."""
download = model.get_value(iter, 0)
cell.props.value = download.percent_complete
+ cell.props.text = _("%s of %s complete") % (Utils.get_readable_size(float(download.block_count * download.block_size)), Utils.get_readable_size(download.size))
def __speed_cell_data_func(self, column, cell, model, iter):
+ """Data function for the speed of downloads."""
+ download = model.get_value(iter, 0)
+ cell.props.text = "N/A"
+
+ def __eta_cell_data_func(self, column, cell, model, iter):
+ """Data function for estemated time of arrival (ETA) of downloads."""
download = model.get_value(iter, 0)
cell.props.text = "N/A"
def __connect_widgets(self):
+ """Connect widgets to various signals."""
self.window.connect("destroy", self.quit)
# File menu
@@ -150,10 +175,6 @@
self.quit_menu_item.connect("activate", self.quit)
# Edit menu
- self.select_all_menu_item.connect("activate",
- self.__select_all_menu_item_activate)
- self.unselect_all_menu_item.connect("activate",
- self.__unselect_all_menu_item_activate)
self.preferences_menu_item.connect("activate",
self.preferences_menu_item_activate)
@@ -164,34 +185,76 @@
# Toolbar
self.add_tool_button.connect("clicked", self.show_add_download_dialog)
+ selection = self.downloads_treeview.get_selection()
+ selection.connect("changed",
+ self.__downloads_treeview_selection_changed)
+
def show_add_download_dialog(self, widget):
- add = AddDownloadDialog(self, self.config)
+ """Show the dialog used for adding a new download."""
+ add = AddDownloadDialog(self.config)
add.dialog.show()
def preferences_menu_item_activate(self, menu_item):
+ """Show the preferences dialog."""
pd = PreferencesDialog(self.config)
pd.dialog.show()
- def __select_all_menu_item_activate(self, menu_item):
- selection = self.downloads_treeview.get_selection()
- selection.select_all()
-
- def __unselect_all_menu_item_activate(self, menu_item):
- selection = self.downloads_treeview.get_selection()
- selection.unselect_all()
-
def __about_menu_item_activate(self, menu_item):
+ """Show the about dialog."""
ad = AboutDialog()
ad.run()
+ def __downloads_treeview_selection_changed(self, selection):
+ (downloads_model, downloads_iter) = selection.get_selected()
+ if downloads_iter:
+ self.pause_tool_button.set_sensitive(True)
+ self.cancel_tool_button.set_sensitive(True)
+ else:
+ self.pause_tool_button.set_sensitive(False)
+ self.cancel_tool_button.set_sensitive(False)
+
def quit(self, widget):
+ """Quits the application. Called from various places."""
# TODO: Shutdown gracefully
gtk.main_quit()
- def start_download(self, url, path):
- download = Download(url, path)
+ def __download_added(self, download_manager, download):
+ """Called when a new download is added to DownloadManager. Adds the
+ download to the treeview model and sets up the update handler."""
self.downloads_model.append([download])
+ download.connect("update", self.__download_update)
GUI.queue_resize(self.downloads_treeview)
- download.start()
+
+ def __download_update(self, download, block_count, block_size, total_size):
+ """Called on download updates. Finds the associated treeview row and
+ fires a row changed signal."""
+ downloads_iter = self.downloads_model.get_iter_first()
+ for row in self.downloads_model:
+ if row[0] is download:
+ downloads_path = self.downloads_model.get_path(downloads_iter)
+ self.downloads_model.row_changed(downloads_path,
+ downloads_iter)
+ break
+ downloads_iter = self.downloads_model.iter_next(downloads_iter)
+
+ def on_unhandled_exception(self, type, value, tb):
+ try:
+ list = traceback.format_tb(tb, None) + \
+ traceback.format_exception_only(type, value)
+ tracelog = '\nTraceback (most recent call last):\n' + "%-20s%s" % \
+ ("".join(list[:-1]), list[-1])
+
+ message = "An internal program error has occurred."
+ message += "\n" + tracelog
+
+ gtk.gdk.threads_enter()
+ ed = ErrorDialog(_("Unhandled exception"), message)
+ ed.run()
+ ed.destroy()
+ gtk.gdk.threads_leave()
+
+ sys.stderr.write(message)
+ except:
+ traceback.print_exc()
# vim: set sw=4 et sts=4 tw=79 fo+=l:
Modified: trunk/gget/Makefile.am
==============================================================================
--- trunk/gget/Makefile.am (original)
+++ trunk/gget/Makefile.am Mon Jun 23 08:35:59 2008
@@ -4,6 +4,7 @@
AddDownloadDialog.py \
Configuration.py \
Download.py \
+ DownloadManager.py \
GUI.py \
__init__.py \
metalink.py \
Modified: trunk/gget/Utils.py
==============================================================================
--- trunk/gget/Utils.py (original)
+++ trunk/gget/Utils.py Mon Jun 23 08:35:59 2008
@@ -25,13 +25,13 @@
def get_readable_size(bits):
for unit in ['bytes','KB','MB','GB','TB']:
if bits < 1024.0:
- return "%3.1f%s" % (bits, unit)
+ return "%3.1f %s" % (bits, unit)
bits /= 1024.0
-def debug_print(self, message):
+def debug_print(message):
config = Configuration()
if config.debug:
- print("[%s] %s" % time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
- message)
+ print("[%s] %s" % (time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
+ message))
# vim: set sw=4 et sts=4 tw=79 fo+=l:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]