diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 115bd4f43..64ac1ab1a 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -410,19 +410,11 @@ class Torrent: else: status = self.status - up = status.all_time_upload - down = status.all_time_download + # Return -1.0 if the downloaded bytes is 0, this is to represent infinity + if status.all_time_download == 0: + return -1.0 - # Convert 'up' and 'down' to floats for proper calculation - up = float(up) - down = float(down) - - try: - ratio = up / down - except ZeroDivisionError: - return 0.0 - - return ratio + return float(status.all_time_upload) / float(status.all_time_download) def get_files(self): """Returns a list of files this torrent contains""" diff --git a/deluge/ui/gtkui/listview.py b/deluge/ui/gtkui/listview.py index fa0502eac..fab768839 100644 --- a/deluge/ui/gtkui/listview.py +++ b/deluge/ui/gtkui/listview.py @@ -2,19 +2,19 @@ # listview.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may 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. -# +# # deluge 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 deluge. If not, write to: # The Free Software Foundation, Inc., @@ -50,7 +50,7 @@ def cell_data_speed(column, cell, model, row, data): speed_str = "" if speed > 0: speed_str = deluge.common.fspeed(speed) - + cell.set_property('text', speed_str) def cell_data_size(column, cell, model, row, data): @@ -67,7 +67,7 @@ def cell_data_peer(column, cell, model, row, data): cell.set_property('text', '%d (%d)' % (first, second)) else: cell.set_property('text', '%d' % first) - + def cell_data_time(column, cell, model, row, data): """Display value as time, eg 1m10s""" time = model.get_value(row, data) @@ -76,15 +76,15 @@ def cell_data_time(column, cell, model, row, data): else: time_str = deluge.common.ftime(time) cell.set_property('text', time_str) - + def cell_data_ratio(column, cell, model, row, data): """Display value as a ratio with a precision of 3.""" ratio = model.get_value(row, data) - if ratio == -1: - ratio_str = _("Unknown") + if ratio < 0: + ratio_str = "∞" else: ratio_str = "%.3f" % ratio - + cell.set_property('text', ratio_str) class ListViewColumnState: @@ -96,7 +96,7 @@ class ListViewColumnState: self.visible = visible self.sort = sort self.sort_order = sort_order - + class ListView: """ListView is used to make custom GtkTreeViews. It supports the adding and removing of columns, creating a menu for a column toggle list and @@ -108,8 +108,8 @@ class ListView: def __init__(self, name, column_indices): # Name is how a column is identified and is also the header self.name = name - # Column_indices holds the indexes to the liststore_columns that - # this column utilizes. It is stored as a list. + # Column_indices holds the indexes to the liststore_columns that + # this column utilizes. It is stored as a list. self.column_indices = column_indices # Column is a reference to the GtkTreeViewColumn object self.column = None @@ -122,19 +122,19 @@ class ListView: # If this is set, it is used to sort the model self.sort_func = None self.sort_id = None - + def __init__(self, treeview_widget=None, state_file=None): log.debug("ListView initialized..") - + if treeview_widget is not None: # User supplied a treeview widget self.treeview = treeview_widget else: self.treeview = gtk.TreeView() - + if state_file: self.load_state(state_file) - + self.liststore = None self.model_filter = None @@ -155,13 +155,13 @@ class ListView: # A list of menus that self.menu will be a submenu of everytime it is # created. self.checklist_menus = [] - + # Create the model filter and column self.add_bool_column("filter", hidden=True) def create_model_filter(self): """create new filter-model - must be called after listview.create_new_liststore + must be called after listview.create_new_liststore """ sort_column = None if self.model_filter: @@ -171,7 +171,7 @@ class ListView: model_filter.set_visible_column( self.columns["filter"].column_indices[0]) self.model_filter = gtk.TreeModelSort(model_filter) - self.set_sort_functions() + self.set_sort_functions() self.treeview.set_model(self.model_filter) if sort_column and sort_column != (None, None): self.model_filter.set_sort_column_id(*sort_column) @@ -184,13 +184,13 @@ class ListView: column.sort_id, column.sort_func, column.sort_id) - + 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 @@ -200,12 +200,12 @@ class ListView: if self.get_column_name(id) == column.get_title(): sort = id # Append a new column state to the state list - state.append(ListViewColumnState(column.get_title(), counter, - column.get_width(), column.get_visible(), + state.append(ListViewColumnState(column.get_title(), counter, + column.get_width(), column.get_visible(), sort, 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"] @@ -216,13 +216,13 @@ class ListView: 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"] state = None - + try: log.debug("Loading ListView state file: %s", filename) state_file = open(os.path.join(config_location, filename), "rb") @@ -230,16 +230,16 @@ class ListView: state_file.close() except (EOFError, IOError), e: 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): """Set the treeview widget that this listview uses.""" self.treeview = treeview_widget self.treeview.set_model(self.liststore) return - + def get_column_index(self, name): """Get the liststore column indices belonging to this column. Will return a list. @@ -251,33 +251,33 @@ class ListView: for key, value in self.columns.items(): if index in value.column_indices: return key - + def get_state_field_column(self, field): """Returns the column number for the state field""" for column in self.columns.keys(): if self.columns[column].status_field == None: continue - + for f in self.columns[column].status_field: if field == f: return self.columns[column].column_indices[ self.columns[column].status_field.index(f)] - + def on_menuitem_toggled(self, widget): """Callback for the generated column menuitems.""" # Get the column name from the widget name = widget.get_child().get_text() - + # Set the column's visibility based on the widgets active state self.columns[name].column.set_visible(widget.get_active()) return - + def register_checklist_menu(self, menu): """Register a checklist menu with the listview. It will automatically attach any new checklist menu it makes to this menu. """ self.checklist_menus.append(menu) - + def create_checklist_menu(self): """Creates a menu used for toggling the display of columns.""" menu = gtk.Menu() @@ -297,20 +297,20 @@ class ListView: menuitem.connect("toggled", self.on_menuitem_toggled) # Add the new checkmenuitem to the menu menu.append(menuitem) - + # Attach this new menu to all the checklist_menus for _menu in self.checklist_menus: _menu.set_submenu(menu) _menu.show_all() - + return menu def create_new_liststore(self): """Creates a new GtkListStore based on the liststore_columns list""" - # Create a new liststore with added column and move the data from the + # Create a new liststore with added column and move the data from the # old one to the new one. new_list = gtk.ListStore(*tuple(self.liststore_columns)) - + # This function is used in the liststore.foreach method with user_data # being the new liststore and the columns list def copy_row(model, path, row, user_data): @@ -321,17 +321,17 @@ class ListView: value = model.get_value(row, column) # Set the value of this row and column in the new liststore new_list.set_value(new_row, column, value) - + # Do the actual row copy if self.liststore is not None: self.liststore.foreach(copy_row, (new_list, self.columns)) - + self.liststore = new_list # Create the model self.create_model_filter() - + return - + def remove_column(self, header): """Removes the column with the name 'header' from the listview""" # Start by removing this column from the treeview @@ -348,11 +348,11 @@ class ListView: # We need to shift this column_indices for index in column.column_indices: index = index - len(column_indices) - + # Remove from the liststore columns list for index in column_indices: del self.liststore_columns[index] - + # Create a new liststore self.create_new_liststore() @@ -360,8 +360,8 @@ class ListView: self.create_checklist_menu() return - - def add_column(self, header, render, col_types, hidden, position, + + def add_column(self, header, render, col_types, hidden, position, status_field, sortid, text=0, value=0, pixbuf=0, function=None, column_type=None, sort_func=None): """Adds a column to the ListView""" @@ -374,23 +374,23 @@ class ListView: else: self.liststore_columns.append(col_types) column_indices.append(len(self.liststore_columns) - 1) - + # Add to the index list so we know the order of the visible columns. if position is not None: self.column_index.insert(position, header) else: self.column_index.append(header) - + # Create a new column object and add it to the list self.columns[header] = self.ListViewColumn(header, column_indices) - + self.columns[header].status_field = status_field self.columns[header].sort_func = sort_func self.columns[header].sort_id = column_indices[sortid] - + # Create a new list with the added column self.create_new_liststore() - + column = gtk.TreeViewColumn(header) if column_type == "text": column.pack_start(render) @@ -403,11 +403,11 @@ class ListView: elif column_type == "func": column.pack_start(render, True) if len(self.columns[header].column_indices) > 1: - column.set_cell_data_func(render, function, + column.set_cell_data_func(render, function, tuple(self.columns[header].column_indices)) else: column.set_cell_data_func(render, function, - self.columns[header].column_indices[0]) + self.columns[header].column_indices[0]) elif column_type == "progress": column.pack_start(render) if function is None: @@ -416,7 +416,7 @@ class ListView: column.add_attribute(render, "value", self.columns[header].column_indices[value]) else: - column.set_cell_data_func(render, function, + column.set_cell_data_func(render, function, tuple(self.columns[header].column_indices)) elif column_type == "texticon": column.pack_start(render[pixbuf], False) @@ -445,7 +445,7 @@ class ListView: if column_state.width > 0: column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) column.set_fixed_width(column_state.width) - + if column_state.sort is not None and column_state.sort > -1: self.model_filter.set_sort_column_id(column_state.sort, column_state.sort_order) column.set_visible(column_state.visible) @@ -461,10 +461,10 @@ class ListView: self.columns[header].column = column # Re-create the menu item because of the new column self.create_checklist_menu() - + return True - - def add_text_column(self, header, col_type=str, hidden=False, + + def add_text_column(self, header, col_type=str, hidden=False, position=None, status_field=None, sortid=0, @@ -475,63 +475,63 @@ class ListView: render = gtk.CellRendererText() self.add_column(header, render, col_type, hidden, position, status_field, sortid, column_type=column_type, sort_func=sort_func) - + return True - + def add_bool_column(self, header, col_type=bool, hidden=False, position=None, status_field=None, sortid=0, column_type="bool"): - + """Add a bool column to the listview""" render = gtk.CellRendererToggle() self.add_column(header, render, col_type, hidden, position, status_field, sortid, column_type=column_type) - - def add_func_column(self, header, function, col_types, sortid=0, + + def add_func_column(self, header, function, col_types, sortid=0, hidden=False, position=None, status_field=None, column_type="func", sort_func=None): """Add a function column to the listview. Need a header name, the function and the column types.""" - + render = gtk.CellRendererText() self.add_column(header, render, col_types, hidden, position, - status_field, sortid, column_type=column_type, + status_field, sortid, column_type=column_type, function=function, sort_func=sort_func) return True - def add_progress_column(self, header, col_types=[float, str], - sortid=0, - hidden=False, - position=None, + def add_progress_column(self, header, col_types=[float, str], + sortid=0, + hidden=False, + position=None, status_field=None, function=None, column_type="progress"): """Add a progress column to the listview.""" - + render = gtk.CellRendererProgress() self.add_column(header, render, col_types, hidden, position, status_field, sortid, function=function, - column_type=column_type, + column_type=column_type, value=0, text=1) return True - + def add_texticon_column(self, header, col_types=[str, str], sortid=1, - hidden=False, - position=None, + hidden=False, + position=None, status_field=None, column_type="texticon", function=None): """Adds a texticon column to the listview.""" render1 = gtk.CellRendererPixbuf() - render2 = gtk.CellRendererText() - + render2 = gtk.CellRendererText() + self.add_column(header, (render1, render2), col_types, hidden, - position, status_field, sortid, + position, status_field, sortid, column_type=column_type, function=function, pixbuf=0, text=1) diff --git a/deluge/ui/gtkui/statistics_tab.py b/deluge/ui/gtkui/statistics_tab.py index 94a7da108..7f110d1d3 100644 --- a/deluge/ui/gtkui/statistics_tab.py +++ b/deluge/ui/gtkui/statistics_tab.py @@ -46,6 +46,8 @@ def fpeer_size_second(first, second): return "%s (%s)" % (first, deluge.common.fsize(second)) def fratio(value): + if value < 0: + return "∞" return "%.3f" % value def fpcnt(value): diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index 437af0a2d..c7ca2cd15 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -113,7 +113,7 @@ def queue_column_sort(model, iter1, iter2, data): return 1 if v2 > v1: return -1 - + class TorrentView(listview.ListView, component.Component): """TorrentView handles the listing of torrents.""" def __init__(self): @@ -130,7 +130,7 @@ class TorrentView(listview.ListView, component.Component): # We keep a copy of the previous status to compare for changes self.prev_status = {} - + # Register the columns menu with the listview so it gets updated # accordingly. self.register_checklist_menu( @@ -196,7 +196,7 @@ class TorrentView(listview.ListView, component.Component): self.on_selection_changed) self.treeview.connect("drag-drop", self.on_drag_drop) - + def start(self): """Start the torrentview""" # We need to get the core session state to know which torrents are in @@ -297,12 +297,12 @@ class TorrentView(listview.ListView, component.Component): if row[column_index[i]] != row_value: row[column_index[i]] = row_value except Exception, e: - log.debug("%s", e) - + log.debug("%s", e) + # Update the toolbar buttons just in case some state has changed component.get("ToolBar").update_buttons() component.get("MenuBar").update_menu() - + self.prev_status = status def _on_get_torrents_status(self, status):