From feaee5f3443422333b7718fbc8c80ef5ed826dcc Mon Sep 17 00:00:00 2001 From: Andrew Resch Date: Sun, 26 Oct 2008 21:10:03 +0000 Subject: [PATCH] Have torrent.get_ratio return -1 when downloaded bytes is 0. Have torrentview and statistics_tab display an infinity character when ratio is -1 --- deluge/core/torrent.py | 16 +--- deluge/ui/gtkui/listview.py | 152 +++++++++++++++--------------- deluge/ui/gtkui/statistics_tab.py | 50 +++++----- 3 files changed, 106 insertions(+), 112 deletions(-) diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 85c3c2d74..02c79d018 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -314,19 +314,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 d560d03ab..b29565125 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 @@ -119,18 +119,18 @@ class ListView: # If column is 'hidden' then it will not be visible and will not # show up in any menu listing; it cannot be shown ever. self.hidden = False - + def __init__(self, treeview_widget=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() - + self.liststore = None - + self.treeview.set_model(self.liststore) self.treeview.set_rules_hint(True) self.treeview.set_reorderable(True) @@ -149,13 +149,13 @@ class ListView: # A list of menus that self.menu will be a submenu of everytime it is # created. 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 @@ -166,12 +166,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"] @@ -182,13 +182,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") @@ -196,16 +196,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 if greater than 1 column. @@ -221,33 +221,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() @@ -267,20 +267,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): @@ -291,11 +291,11 @@ 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)) - + sort_column = None if self.treeview.get_model(): # Save the liststore filter column @@ -303,11 +303,11 @@ class ListView: self.liststore = new_list self.treeview.set_model(self.liststore) - + if sort_column and sort_column != (None, None): self.treeview.get_model().set_sort_column_id(*sort_column) return - + def remove_column(self, header): """Removes the column with the name 'header' from the listview""" # Start by removing this column from the treeview @@ -324,20 +324,20 @@ 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() # Re-create the menu 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): """Adds a column to the ListView""" @@ -350,21 +350,21 @@ 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 - + # Create a new list with the added column self.create_new_liststore() - + column = gtk.TreeViewColumn(header) if column_type == "text": column.pack_start(render) @@ -377,11 +377,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: @@ -390,7 +390,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) @@ -419,7 +419,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.treeview.get_model().set_sort_column_id(column_state.sort, column_state.sort_order) column.set_visible(column_state.visible) @@ -437,8 +437,8 @@ class ListView: 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, @@ -448,63 +448,63 @@ class ListView: render = gtk.CellRendererText() self.add_column(header, render, col_type, hidden, position, status_field, sortid, column_type=column_type) - + 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"): """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) 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 0157be9d6..a92cfa996 100644 --- a/deluge/ui/gtkui/statistics_tab.py +++ b/deluge/ui/gtkui/statistics_tab.py @@ -2,19 +2,19 @@ # statistics_tab.py # # Copyright (C) 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., @@ -46,28 +46,30 @@ 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): return "%.2f%%" % value - + def fspeed(value, max_value=-1): if max_value > -1: return "%s [%s KiB/s]" % (deluge.common.fspeed(value), max_value) else: return deluge.common.fspeed(value) - + class StatisticsTab(Tab): def __init__(self): Tab.__init__(self) # Get the labels we need to update. # widgetname, modifier function, status keys glade = component.get("MainWindow").main_glade - + self._name = "Statistics" self._child_widget = glade.get_widget("statistics_tab") self._tab_label = glade.get_widget("statistics_tab_label") - + self.label_widgets = [ (glade.get_widget("summary_pieces"), fpeer_size_second, ("num_pieces", "piece_length")), (glade.get_widget("summary_availability"), fratio, ("distributed_copies",)), @@ -87,37 +89,37 @@ class StatisticsTab(Tab): (glade.get_widget("summary_auto_managed"), str, ("is_auto_managed",)), (glade.get_widget("progressbar"), fpcnt, ("progress",)) ] - + def update(self): # Get the first selected torrent selected = component.get("TorrentView").get_selected_torrents() - + # Only use the first torrent in the list or return if None selected if len(selected) != 0: selected = selected[0] else: # No torrent is selected in the torrentview return - + # Get the torrent status - status_keys = ["progress", "num_pieces", "piece_length", - "distributed_copies", "total_done", "total_payload_download", - "total_uploaded", "total_payload_upload", "download_payload_rate", + status_keys = ["progress", "num_pieces", "piece_length", + "distributed_copies", "total_done", "total_payload_download", + "total_uploaded", "total_payload_upload", "download_payload_rate", "upload_payload_rate", "num_peers", "num_seeds", "total_peers", "total_seeds", "eta", "ratio", "next_announce", - "tracker_status", "max_connections", "max_upload_slots", - "max_upload_speed", "max_download_speed", "active_time", + "tracker_status", "max_connections", "max_upload_slots", + "max_upload_speed", "max_download_speed", "active_time", "seeding_time", "seed_rank", "is_auto_managed"] - + client.get_torrent_status( self._on_get_torrent_status, selected, status_keys) - + def _on_get_torrent_status(self, status): # Check to see if we got valid data from the core if status is None: return - - # Update all the label widgets + + # Update all the label widgets for widget in self.label_widgets: if widget[1] != None: args = [] @@ -127,22 +129,22 @@ class StatisticsTab(Tab): except Exception, e: log.debug("Unable to get status value: %s", e) continue - + txt = widget[1](*args) else: txt = status[widget[2][0]] if widget[0].get_text() != txt: widget[0].set_text(txt) - + # Do the progress bar because it's a special case (not a label) w = component.get("MainWindow").main_glade.get_widget("progressbar") fraction = status["progress"] / 100 if w.get_fraction() != fraction: w.set_fraction(fraction) - + def clear(self): for widget in self.label_widgets: widget[0].set_text("") - + component.get("MainWindow").main_glade.get_widget("progressbar").set_fraction(0.0)