Fix for #1885 and add simple caching to the data funcs for the torrentview
* Fix for #1885 (Wrong tracker icon in torrent list) * Moved the data functions from torrentview/listview into torrentview_data_funcs.py * Added caching the current value of the cell renderer for the data functions * Reordered if-tests in deluge.common.fsize * Disable data funcs when column is hidden
This commit is contained in:
parent
feaeee0379
commit
8ecc0e11a7
|
@ -258,6 +258,17 @@ def open_url_in_browser(url):
|
|||
|
||||
## Formatting text functions
|
||||
|
||||
byte_txt = "Bytes"
|
||||
kib_txt = "KiB"
|
||||
mib_txt = "MiB"
|
||||
gib_txt = "GiB"
|
||||
|
||||
def translate_strings():
|
||||
byte_txt = _("Bytes")
|
||||
kib_txt = _("KiB")
|
||||
mib_txt = _("MiB")
|
||||
gib_txt = _("GiB")
|
||||
|
||||
def fsize(fsize_b):
|
||||
"""
|
||||
Formats the bytes value into a string with KiB, MiB or GiB units
|
||||
|
@ -273,14 +284,17 @@ def fsize(fsize_b):
|
|||
'109.6 KiB'
|
||||
|
||||
"""
|
||||
fsize_kb = fsize_b / 1024.0
|
||||
if fsize_kb < 1024:
|
||||
return "%.1f %s" % (fsize_kb, _("KiB"))
|
||||
fsize_mb = fsize_kb / 1024.0
|
||||
if fsize_mb < 1024:
|
||||
return "%.1f %s" % (fsize_mb, _("MiB"))
|
||||
fsize_gb = fsize_mb / 1024.0
|
||||
return "%.1f %s" % (fsize_gb, _("GiB"))
|
||||
# Bigger than 1 GiB
|
||||
if (fsize_b >= 1073741824):
|
||||
return "%.1f %s" % (fsize_b / 1073741824.0, gib_txt)
|
||||
# Bigger than 1 MiB
|
||||
elif (fsize_b >= 1048576):
|
||||
return "%.1f %s" % (fsize_b / 1048576.0, mib_txt)
|
||||
# Bigger than 1 KiB
|
||||
elif (fsize_b >= 1024):
|
||||
return "%.1f %s" % (fsize_b / 1024.0, kib_txt)
|
||||
else:
|
||||
return "%d %s" % (fsize_b, byte_txt)
|
||||
|
||||
def fsize_short(fsize_b):
|
||||
"""
|
||||
|
@ -797,6 +811,7 @@ def setup_translations(setup_pygtk=False):
|
|||
import gtk.glade
|
||||
gtk.glade.bindtextdomain("deluge", translations_path)
|
||||
gtk.glade.textdomain("deluge")
|
||||
translate_strings()
|
||||
except Exception, e:
|
||||
log.error("Unable to initialize gettext/locale!")
|
||||
log.exception(e)
|
||||
|
|
|
@ -12,7 +12,15 @@ class CommonTestCase(unittest.TestCase):
|
|||
pass
|
||||
|
||||
def test_fsize(self):
|
||||
self.failUnless(fsize(112245) == "109.6 KiB")
|
||||
self.assertEquals(fsize(100), "100 Bytes")
|
||||
self.assertEquals(fsize(1023), "1023 Bytes")
|
||||
self.assertEquals(fsize(1024), "1.0 KiB")
|
||||
self.assertEquals(fsize(1048575), "1024.0 KiB")
|
||||
self.assertEquals(fsize(1048576), "1.0 MiB")
|
||||
self.assertEquals(fsize(1073741823), "1024.0 MiB")
|
||||
self.assertEquals(fsize(1073741824), "1.0 GiB")
|
||||
self.assertEquals(fsize(112245), "109.6 KiB")
|
||||
self.assertEquals(fsize(110723441824), "103.1 GiB")
|
||||
|
||||
def test_fpcnt(self):
|
||||
self.failUnless(fpcnt(0.9311) == "93.11%")
|
||||
|
|
|
@ -17,7 +17,7 @@ class TrackerIconsTestCase(unittest.TestCase):
|
|||
def test_get_deluge_png(self):
|
||||
# Deluge has a png favicon link
|
||||
icon = TrackerIcon(os.path.join(dirname, "deluge.png"))
|
||||
d = icons.get("deluge-torrent.org")
|
||||
d = icons.fetch("deluge-torrent.org")
|
||||
d.addCallback(self.assertNotIdentical, None)
|
||||
d.addCallback(self.assertEquals, icon)
|
||||
return d
|
||||
|
@ -26,7 +26,7 @@ class TrackerIconsTestCase(unittest.TestCase):
|
|||
# Google doesn't have any icon links
|
||||
# So instead we'll grab its favicon.ico
|
||||
icon = TrackerIcon(os.path.join(dirname, "google.ico"))
|
||||
d = icons.get("www.google.com")
|
||||
d = icons.fetch("www.google.com")
|
||||
d.addCallback(self.assertNotIdentical, None)
|
||||
d.addCallback(self.assertEquals, icon)
|
||||
return d
|
||||
|
@ -34,7 +34,7 @@ class TrackerIconsTestCase(unittest.TestCase):
|
|||
def test_get_google_ico_with_redirect(self):
|
||||
# google.com redirects to www.google.com
|
||||
icon = TrackerIcon(os.path.join(dirname, "google.ico"))
|
||||
d = icons.get("google.com")
|
||||
d = icons.fetch("google.com")
|
||||
d.addCallback(self.assertNotIdentical, None)
|
||||
d.addCallback(self.assertEquals, icon)
|
||||
return d
|
||||
|
@ -42,7 +42,7 @@ class TrackerIconsTestCase(unittest.TestCase):
|
|||
def test_get_ubuntu_ico(self):
|
||||
# ubuntu.com has inline css which causes HTMLParser issues
|
||||
icon = TrackerIcon(os.path.join(dirname, "ubuntu.ico"))
|
||||
d = icons.get("www.ubuntu.com")
|
||||
d = icons.fetch("www.ubuntu.com")
|
||||
d.addCallback(self.assertNotIdentical, None)
|
||||
d.addCallback(self.assertEquals, icon)
|
||||
return d
|
||||
|
@ -50,19 +50,19 @@ class TrackerIconsTestCase(unittest.TestCase):
|
|||
def test_get_openbt_png(self):
|
||||
# openbittorrent.com has an incorrect type (image/gif)
|
||||
icon = TrackerIcon(os.path.join(dirname, "openbt.png"))
|
||||
d = icons.get("openbittorrent.com")
|
||||
d = icons.fetch("openbittorrent.com")
|
||||
d.addCallback(self.assertNotIdentical, None)
|
||||
d.addCallback(self.assertEquals, icon)
|
||||
return d
|
||||
|
||||
def test_get_publicbt_ico(self):
|
||||
icon = TrackerIcon(os.path.join(dirname, "publicbt.ico"))
|
||||
d = icons.get("publicbt.org")
|
||||
d = icons.fetch("publicbt.org")
|
||||
d.addCallback(self.assertNotIdentical, None)
|
||||
d.addCallback(self.assertEquals, icon)
|
||||
return d
|
||||
|
||||
def test_get_empty_string_tracker(self):
|
||||
d = icons.get("")
|
||||
d = icons.fetch("")
|
||||
d.addCallback(self.assertIdentical, None)
|
||||
return d
|
||||
|
|
|
@ -48,7 +48,7 @@ import twisted.web.error
|
|||
from deluge.ui.client import client
|
||||
from deluge.httpdownloader import download_file
|
||||
import deluge.component as component
|
||||
import listview
|
||||
from torrentview_data_funcs import cell_data_size
|
||||
from deluge.configmanager import ConfigManager
|
||||
import deluge.common
|
||||
import deluge.ui.common
|
||||
|
@ -136,7 +136,7 @@ class AddTorrentDialog(component.Component):
|
|||
render = gtk.CellRendererText()
|
||||
column = gtk.TreeViewColumn(_("Size"))
|
||||
column.pack_start(render)
|
||||
column.set_cell_data_func(render, listview.cell_data_size, 2)
|
||||
column.set_cell_data_func(render, cell_data_size, 2)
|
||||
self.listview_files.append_column(column)
|
||||
|
||||
self.listview_torrents.set_model(self.torrent_liststore)
|
||||
|
|
|
@ -142,7 +142,7 @@ class FilesTab(Tab):
|
|||
column = gtk.TreeViewColumn(_("Size"))
|
||||
render = gtk.CellRendererText()
|
||||
column.pack_start(render, False)
|
||||
column.set_cell_data_func(render, deluge.ui.gtkui.listview.cell_data_size, 1)
|
||||
column.set_cell_data_func(render, deluge.ui.gtkui.torrentview_data_funcs.cell_data_size, 1)
|
||||
column.set_sort_column_id(1)
|
||||
column.set_clickable(True)
|
||||
column.set_resizable(True)
|
||||
|
|
|
@ -228,7 +228,7 @@ class FilterTreeView(component.Component):
|
|||
self.filters[(cat, value)] = row
|
||||
|
||||
if cat == "tracker_host" and value not in ("All", "Error") and value:
|
||||
d = self.tracker_icons.get(value)
|
||||
d = self.tracker_icons.fetch(value)
|
||||
d.addCallback(on_get_icon)
|
||||
|
||||
self.treestore.set_value(row, FILTER_COLUMN, True)
|
||||
|
|
|
@ -49,71 +49,6 @@ signal_new('button-press-event', gtk.TreeViewColumn,
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Cell data functions to pass to add_func_column()
|
||||
def cell_data_speed(column, cell, model, row, data):
|
||||
"""Display value as a speed, eg. 2 KiB/s"""
|
||||
speed = model.get_value(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):
|
||||
"""Display value in terms of size, eg. 2 MB"""
|
||||
size = model.get_value(row, data)
|
||||
size_str = deluge.common.fsize(size)
|
||||
cell.set_property('text', size_str)
|
||||
|
||||
def cell_data_peer(column, cell, model, row, data):
|
||||
"""Display values as 'value1 (value2)'"""
|
||||
(first, second) = model.get(row, *data)
|
||||
# Only display a (total) if second is greater than -1
|
||||
if second > -1:
|
||||
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)
|
||||
if time <= 0:
|
||||
time_str = ""
|
||||
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 < 0:
|
||||
ratio_str = "∞"
|
||||
else:
|
||||
ratio_str = "%.3f" % ratio
|
||||
|
||||
cell.set_property('text', ratio_str)
|
||||
|
||||
def cell_data_date(column, cell, model, row, data):
|
||||
"""Display value as date, eg 05/05/08"""
|
||||
cell.set_property('text', deluge.common.fdate(model.get_value(row, data)))
|
||||
|
||||
def cell_data_date_or_never(column, cell, model, row, data):
|
||||
"""Display value as date, eg 05/05/08 or Never"""
|
||||
value = model.get_value(row, data)
|
||||
if value > 0.0:
|
||||
cell.set_property('text', deluge.common.fdate(value))
|
||||
else:
|
||||
cell.set_property('text', _("Never"))
|
||||
|
||||
def cell_data_speed_limit(column, cell, model, row, data):
|
||||
"""Display value as a speed, eg. 2 KiB/s"""
|
||||
speed = model.get_value(row, data)
|
||||
speed_str = ""
|
||||
if speed > 0:
|
||||
speed_str = deluge.common.fspeed(speed * 1024)
|
||||
|
||||
cell.set_property('text', speed_str)
|
||||
|
||||
class ListViewColumnState:
|
||||
"""Used for saving/loading column state"""
|
||||
def __init__(self, name, position, width, visible, sort, sort_order):
|
||||
|
@ -150,7 +85,7 @@ class ListView:
|
|||
self.sort_func = None
|
||||
self.sort_id = None
|
||||
|
||||
class TreeviewColumn(gtk.TreeViewColumn):
|
||||
class TreeviewColumn(gtk.TreeViewColumn, object):
|
||||
"""
|
||||
TreeViewColumn does not signal right-click events, and we need them
|
||||
This subclass is equivalent to TreeViewColumn, but it signals these events
|
||||
|
@ -165,6 +100,10 @@ class ListView:
|
|||
self.set_widget(label)
|
||||
label.show()
|
||||
label.__realize = label.connect('realize', self.onRealize)
|
||||
self.title = title
|
||||
self.data_func = None
|
||||
self.data_func_data = None
|
||||
self.cell_renderer = None
|
||||
|
||||
def onRealize(self, widget):
|
||||
widget.disconnect(widget.__realize)
|
||||
|
@ -176,6 +115,21 @@ class ListView:
|
|||
def onButtonPressed(self, widget, event):
|
||||
self.emit('button-press-event', event)
|
||||
|
||||
def set_cell_data_func_attributes(self, cell_renderer, func, func_data=None):
|
||||
"""Store the values to be set by set_cell_data_func"""
|
||||
self.data_func = func
|
||||
self.data_func_data = func_data
|
||||
self.cell_renderer = cell_renderer
|
||||
|
||||
def set_visible(self, visible):
|
||||
gtk.TreeViewColumn.set_visible(self, visible)
|
||||
if self.data_func:
|
||||
if not visible:
|
||||
# Set data function to None to prevent unecessary calls when column is hidden
|
||||
self.set_cell_data_func(self.cell_renderer, None, func_data=None)
|
||||
else:
|
||||
self.set_cell_data_func(self.cell_renderer, self.data_func, self.data_func_data)
|
||||
|
||||
def __init__(self, treeview_widget=None, state_file=None):
|
||||
log.debug("ListView initialized..")
|
||||
|
||||
|
@ -521,10 +475,10 @@ 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_attributes(render, function,
|
||||
tuple(self.columns[header].column_indices))
|
||||
else:
|
||||
column.set_cell_data_func(render, function,
|
||||
column.set_cell_data_func_attributes(render, function,
|
||||
self.columns[header].column_indices[0])
|
||||
elif column_type == "progress":
|
||||
column.pack_start(render)
|
||||
|
@ -534,12 +488,12 @@ 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_attributes(render, function,
|
||||
tuple(self.columns[header].column_indices))
|
||||
elif column_type == "texticon":
|
||||
column.pack_start(render[pixbuf], False)
|
||||
if function is not None:
|
||||
column.set_cell_data_func(render[pixbuf], function,
|
||||
column.set_cell_data_func_attributes(render[pixbuf], function,
|
||||
self.columns[header].column_indices[pixbuf])
|
||||
column.pack_start(render[text], True)
|
||||
column.add_attribute(render[text], "text",
|
||||
|
|
|
@ -41,7 +41,7 @@ from itertools import izip
|
|||
from deluge.ui.client import client
|
||||
import deluge.component as component
|
||||
import deluge.common
|
||||
from deluge.ui.gtkui.listview import cell_data_speed as cell_data_speed
|
||||
from deluge.ui.gtkui.torrentview_data_funcs import cell_data_speed_down, cell_data_speed_up
|
||||
from deluge.ui.gtkui.torrentdetails import Tab
|
||||
from deluge.ui.countries import COUNTRIES
|
||||
from deluge.ui.gtkui.common import save_pickled_state_file, load_pickled_state_file
|
||||
|
@ -139,7 +139,7 @@ class PeersTab(Tab):
|
|||
column = gtk.TreeViewColumn(_("Down Speed"))
|
||||
render = gtk.CellRendererText()
|
||||
column.pack_start(render, False)
|
||||
column.set_cell_data_func(render, cell_data_speed, 3)
|
||||
column.set_cell_data_func(render, cell_data_speed_down, 3)
|
||||
column.set_sort_column_id(3)
|
||||
column.set_clickable(True)
|
||||
column.set_resizable(True)
|
||||
|
@ -152,7 +152,7 @@ class PeersTab(Tab):
|
|||
column = gtk.TreeViewColumn(_("Up Speed"))
|
||||
render = gtk.CellRendererText()
|
||||
column.pack_start(render, False)
|
||||
column.set_cell_data_func(render, cell_data_speed, 4)
|
||||
column.set_cell_data_func(render, cell_data_speed_up, 4)
|
||||
column.set_sort_column_id(4)
|
||||
column.set_clickable(True)
|
||||
column.set_resizable(True)
|
||||
|
|
|
@ -50,101 +50,10 @@ import deluge.common
|
|||
import deluge.component as component
|
||||
from deluge.ui.client import client
|
||||
from removetorrentdialog import RemoveTorrentDialog
|
||||
import torrentview_data_funcs as funcs
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Status icons.. Create them from file only once to avoid constantly
|
||||
# re-creating them.
|
||||
icon_downloading = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("downloading16.png"))
|
||||
icon_seeding = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("seeding16.png"))
|
||||
icon_inactive = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("inactive16.png"))
|
||||
icon_alert = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("alert16.png"))
|
||||
icon_queued = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("queued16.png"))
|
||||
icon_checking = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("checking16.png"))
|
||||
|
||||
# Holds the info for which status icon to display based on state
|
||||
ICON_STATE = {
|
||||
"Allocating": icon_checking,
|
||||
"Checking": icon_checking,
|
||||
"Downloading": icon_downloading,
|
||||
"Seeding": icon_seeding,
|
||||
"Paused": icon_inactive,
|
||||
"Error": icon_alert,
|
||||
"Queued": icon_queued,
|
||||
"Checking Resume Data": icon_checking
|
||||
}
|
||||
|
||||
|
||||
def cell_data_statusicon(column, cell, model, row, data):
|
||||
"""Display text with an icon"""
|
||||
try:
|
||||
icon = ICON_STATE[model.get_value(row, data)]
|
||||
|
||||
#Supress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed
|
||||
original_filters = warnings.filters[:]
|
||||
warnings.simplefilter("ignore")
|
||||
try:
|
||||
if cell.get_property("pixbuf") != icon:
|
||||
cell.set_property("pixbuf", icon)
|
||||
finally:
|
||||
warnings.filters = original_filters
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def cell_data_trackericon(column, cell, model, row, data):
|
||||
def on_get_icon(icon):
|
||||
def create_blank_pixbuf():
|
||||
i = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, 16, 16)
|
||||
i.fill(0x00000000)
|
||||
return i
|
||||
|
||||
if icon:
|
||||
pixbuf = icon.get_cached_icon()
|
||||
if not pixbuf:
|
||||
try:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon.get_filename(), 16, 16)
|
||||
except gobject.GError, e:
|
||||
# Failed to load the pixbuf (Bad image file), so set a blank pixbuf
|
||||
pixbuf = create_blank_pixbuf()
|
||||
finally:
|
||||
icon.set_cached_icon(pixbuf)
|
||||
else:
|
||||
pixbuf = create_blank_pixbuf()
|
||||
|
||||
#Suppress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
if cell.get_property("pixbuf") != pixbuf:
|
||||
cell.set_property("pixbuf", pixbuf)
|
||||
|
||||
host = model[row][data]
|
||||
if host:
|
||||
d = component.get("TrackerIcons").get(host)
|
||||
d.addCallback(on_get_icon)
|
||||
else:
|
||||
on_get_icon(None)
|
||||
|
||||
def cell_data_progress(column, cell, model, row, data):
|
||||
"""Display progress bar with text"""
|
||||
(value, state_str) = model.get(row, *data)
|
||||
if cell.get_property("value") != value:
|
||||
cell.set_property("value", value)
|
||||
|
||||
# Marked for translate states text are in filtertreeview
|
||||
textstr = _(state_str)
|
||||
if state_str != "Seeding" and value < 100:
|
||||
textstr = "%s %.2f%%" % (textstr, value)
|
||||
if cell.get_property("text") != textstr:
|
||||
cell.set_property("text", textstr)
|
||||
|
||||
def cell_data_queue(column, cell, model, row, data):
|
||||
value = model.get_value(row, data)
|
||||
if value < 0:
|
||||
cell.set_property("text", "")
|
||||
else:
|
||||
cell.set_property("text", str(value + 1))
|
||||
|
||||
def queue_peer_seed_sort_function(v1, v2):
|
||||
if v1 == v2:
|
||||
return 0
|
||||
|
@ -339,58 +248,58 @@ class TorrentView(listview.ListView, component.Component):
|
|||
# Add the columns to the listview
|
||||
self.add_text_column("torrent_id", hidden=True, unique=True)
|
||||
self.add_bool_column("dirty", hidden=True)
|
||||
self.add_func_column("#", cell_data_queue, [int],
|
||||
self.add_func_column("#", funcs.cell_data_queue, [int],
|
||||
status_field=["queue"],
|
||||
sort_func=queue_column_sort)
|
||||
self.add_texticon_column(_("Name"),
|
||||
status_field=["state", "name"],
|
||||
function=cell_data_statusicon,
|
||||
function=funcs.cell_data_statusicon,
|
||||
default_sort=True)
|
||||
self.add_func_column(_("Size"), listview.cell_data_size,
|
||||
self.add_func_column(_("Size"), funcs.cell_data_size,
|
||||
[gobject.TYPE_UINT64],
|
||||
status_field=["total_wanted"])
|
||||
self.add_func_column(_("Downloaded"), listview.cell_data_size,
|
||||
self.add_func_column(_("Downloaded"), funcs.cell_data_size,
|
||||
[gobject.TYPE_UINT64],
|
||||
status_field=["all_time_download"], default=False)
|
||||
self.add_func_column(_("Uploaded"), listview.cell_data_size,
|
||||
self.add_func_column(_("Uploaded"), funcs.cell_data_size,
|
||||
[gobject.TYPE_UINT64],
|
||||
status_field=["total_uploaded"], default=False)
|
||||
self.add_func_column(_("Remaining"), listview.cell_data_size, [gobject.TYPE_UINT64],
|
||||
self.add_func_column(_("Remaining"), funcs.cell_data_size, [gobject.TYPE_UINT64],
|
||||
status_field=["total_remaining"], default=False)
|
||||
self.add_progress_column(_("Progress"),
|
||||
status_field=["progress", "state"],
|
||||
col_types=[float, str],
|
||||
function=cell_data_progress)
|
||||
self.add_func_column(_("Seeders"), listview.cell_data_peer, [int, int],
|
||||
status_field=["progress", "state"],
|
||||
col_types=[float, str],
|
||||
function=funcs.cell_data_progress)
|
||||
self.add_func_column(_("Seeders"), funcs.cell_data_peer, [int, int],
|
||||
status_field=["num_seeds", "total_seeds"],
|
||||
sort_func=seed_peer_column_sort, default=False)
|
||||
self.add_func_column(_("Peers"), listview.cell_data_peer, [int, int],
|
||||
self.add_func_column(_("Peers"), funcs.cell_data_peer, [int, int],
|
||||
status_field=["num_peers", "total_peers"],
|
||||
sort_func=seed_peer_column_sort, default=False)
|
||||
self.add_func_column(_("Seeders") + "/" + _("Peers"), listview.cell_data_ratio, [float],
|
||||
self.add_func_column(_("Seeders") + "/" + _("Peers"), funcs.cell_data_ratio_seeders, [float],
|
||||
status_field=["seeds_peers_ratio"], default=False)
|
||||
self.add_func_column(_("Down Speed"), listview.cell_data_speed, [float],
|
||||
self.add_func_column(_("Down Speed"), funcs.cell_data_speed_down, [float],
|
||||
status_field=["download_payload_rate"])
|
||||
self.add_func_column(_("Up Speed"), listview.cell_data_speed, [float],
|
||||
self.add_func_column(_("Up Speed"), funcs.cell_data_speed_up, [float],
|
||||
status_field=["upload_payload_rate"])
|
||||
self.add_func_column(_("Down Limit"), listview.cell_data_speed_limit, [float],
|
||||
self.add_func_column(_("Down Limit"), funcs.cell_data_speed_limit_down, [float],
|
||||
status_field=["max_download_speed"], default=False)
|
||||
self.add_func_column(_("Up Limit"), listview.cell_data_speed_limit, [float],
|
||||
self.add_func_column(_("Up Limit"), funcs.cell_data_speed_limit_up, [float],
|
||||
status_field=["max_upload_speed"], default=False)
|
||||
self.add_func_column(_("ETA"), listview.cell_data_time, [int],
|
||||
self.add_func_column(_("ETA"), funcs.cell_data_time, [int],
|
||||
status_field=["eta"], sort_func=eta_column_sort)
|
||||
self.add_func_column(_("Ratio"), listview.cell_data_ratio, [float],
|
||||
self.add_func_column(_("Ratio"), funcs.cell_data_ratio_ratio, [float],
|
||||
status_field=["ratio"], default=False)
|
||||
self.add_func_column(_("Avail"), listview.cell_data_ratio, [float],
|
||||
self.add_func_column(_("Avail"), funcs.cell_data_ratio_avail, [float],
|
||||
status_field=["distributed_copies"], default=False)
|
||||
self.add_func_column(_("Added"), listview.cell_data_date, [float],
|
||||
self.add_func_column(_("Added"), funcs.cell_data_date, [float],
|
||||
status_field=["time_added"], default=False)
|
||||
self.add_func_column(_("Last Seen Complete"),
|
||||
listview.cell_data_date_or_never, [float],
|
||||
funcs.cell_data_date_or_never, [float],
|
||||
status_field=["last_seen_complete"], default=False)
|
||||
self.add_texticon_column(_("Tracker"),
|
||||
status_field=["tracker_host", "tracker_host"],
|
||||
function=cell_data_trackericon, default=False)
|
||||
function=funcs.cell_data_trackericon, default=False)
|
||||
self.add_text_column(_("Save Path"), status_field=["save_path"], default=False)
|
||||
self.add_text_column(_("Owner"), status_field=["owner"], default=False)
|
||||
self.add_bool_column(_("Public"), status_field=["public"], default=False)
|
||||
|
|
|
@ -0,0 +1,302 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# torrentview_data_funcs.py
|
||||
#
|
||||
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# 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.,
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the OpenSSL
|
||||
# library.
|
||||
# You must obey the GNU General Public License in all respects for all of
|
||||
# the code used other than OpenSSL. If you modify file(s) with this
|
||||
# exception, you may extend this exception to your version of the file(s),
|
||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
#
|
||||
|
||||
import deluge.common as common
|
||||
import gtk
|
||||
import warnings
|
||||
import gobject
|
||||
import deluge.component as component
|
||||
|
||||
# Status icons.. Create them from file only once to avoid constantly
|
||||
# re-creating them.
|
||||
icon_downloading = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("downloading16.png"))
|
||||
icon_seeding = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("seeding16.png"))
|
||||
icon_inactive = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("inactive16.png"))
|
||||
icon_alert = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("alert16.png"))
|
||||
icon_queued = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("queued16.png"))
|
||||
icon_checking = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("checking16.png"))
|
||||
|
||||
# Holds the info for which status icon to display based on state
|
||||
ICON_STATE = {
|
||||
"Allocating": icon_checking,
|
||||
"Checking": icon_checking,
|
||||
"Downloading": icon_downloading,
|
||||
"Seeding": icon_seeding,
|
||||
"Paused": icon_inactive,
|
||||
"Error": icon_alert,
|
||||
"Queued": icon_queued,
|
||||
"Checking Resume Data": icon_checking
|
||||
}
|
||||
|
||||
def _(message): return message
|
||||
|
||||
TRANSLATE = {
|
||||
"Downloading": _("Downloading"),
|
||||
"Seeding": _("Seeding"),
|
||||
"Paused": _("Paused"),
|
||||
"Checking": _("Checking"),
|
||||
"Queued": _("Queued"),
|
||||
"Error": _("Error"),
|
||||
}
|
||||
|
||||
del _
|
||||
|
||||
def _t(text):
|
||||
if text in TRANSLATE:
|
||||
text = TRANSLATE[text]
|
||||
return _(text)
|
||||
|
||||
# Cache the key used to calculate the current value set for the specific cell
|
||||
# renderer. This is much cheaper than fetch the current value and test if
|
||||
# it's equal.
|
||||
func_last_value = {"cell_data_speed_down": None,
|
||||
"cell_data_speed_up": None,
|
||||
"cell_data_time": None,
|
||||
"cell_data_ratio_seeders": None,
|
||||
"cell_data_ratio_ratio": None,
|
||||
"cell_data_ratio_avail": None,
|
||||
"cell_data_date": None,
|
||||
"cell_data_date_or_never": None,
|
||||
"cell_data_speed_limit_down": None,
|
||||
"cell_data_speed_limit_up": None,
|
||||
"cell_data_trackericon": None,
|
||||
"cell_data_statusicon": None,
|
||||
"cell_data_queue": None,
|
||||
"cell_data_progress": [None, None],
|
||||
}
|
||||
|
||||
def cell_data_statusicon(column, cell, model, row, data):
|
||||
"""Display text with an icon"""
|
||||
try:
|
||||
state = model.get_value(row, data)
|
||||
|
||||
if func_last_value["cell_data_statusicon"] == state:
|
||||
return
|
||||
func_last_value["cell_data_statusicon"] = state
|
||||
|
||||
icon = ICON_STATE[state]
|
||||
|
||||
#Supress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed
|
||||
original_filters = warnings.filters[:]
|
||||
warnings.simplefilter("ignore")
|
||||
try:
|
||||
cell.set_property("pixbuf", icon)
|
||||
finally:
|
||||
warnings.filters = original_filters
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def create_blank_pixbuf():
|
||||
i = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, 16, 16)
|
||||
i.fill(0x00000000)
|
||||
return i
|
||||
|
||||
def set_icon(icon, cell):
|
||||
if icon:
|
||||
pixbuf = icon.get_cached_icon()
|
||||
if pixbuf is None:
|
||||
try:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon.get_filename(), 16, 16)
|
||||
except gobject.GError, e:
|
||||
# Failed to load the pixbuf (Bad image file), so set a blank pixbuf
|
||||
pixbuf = create_blank_pixbuf()
|
||||
finally:
|
||||
icon.set_cached_icon(pixbuf)
|
||||
else:
|
||||
pixbuf = create_blank_pixbuf()
|
||||
|
||||
#Suppress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
cell.set_property("pixbuf", pixbuf)
|
||||
|
||||
def cell_data_trackericon(column, cell, model, row, data):
|
||||
host = model[row][data]
|
||||
|
||||
if func_last_value["cell_data_trackericon"] == host:
|
||||
return
|
||||
if host:
|
||||
if not component.get("TrackerIcons").has(host):
|
||||
# Set blank icon while waiting for the icon to be loaded
|
||||
set_icon(None, cell)
|
||||
component.get("TrackerIcons").fetch(host)
|
||||
func_last_value["cell_data_trackericon"] = None
|
||||
else:
|
||||
set_icon(component.get("TrackerIcons").get(host), cell)
|
||||
# Only set the last value when we have found the icon
|
||||
func_last_value["cell_data_trackericon"] = host
|
||||
else:
|
||||
set_icon(None, cell)
|
||||
func_last_value["cell_data_trackericon"] = None
|
||||
|
||||
def cell_data_progress(column, cell, model, row, data):
|
||||
"""Display progress bar with text"""
|
||||
(value, state_str) = model.get(row, *data)
|
||||
if func_last_value["cell_data_progress"][0] != value:
|
||||
func_last_value["cell_data_progress"][0] = value
|
||||
cell.set_property("value", value)
|
||||
|
||||
textstr = _t(state_str)
|
||||
if state_str != "Seeding" and value < 100:
|
||||
textstr = textstr + " %.2f%%" % value
|
||||
|
||||
if func_last_value["cell_data_progress"][1] != textstr:
|
||||
func_last_value["cell_data_progress"][1] = textstr
|
||||
cell.set_property("text", textstr)
|
||||
|
||||
def cell_data_queue(column, cell, model, row, data):
|
||||
value = model.get_value(row, data)
|
||||
|
||||
if func_last_value["cell_data_queue"] == value:
|
||||
return
|
||||
func_last_value["cell_data_queue"] = value
|
||||
|
||||
if value < 0:
|
||||
cell.set_property("text", "")
|
||||
else:
|
||||
cell.set_property("text", str(value + 1))
|
||||
|
||||
def cell_data_speed(cell, model, row, data, cache_key):
|
||||
"""Display value as a speed, eg. 2 KiB/s"""
|
||||
try:
|
||||
speed = model.get_value(row, data)
|
||||
except AttributeError, e:
|
||||
print "AttributeError"
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
if func_last_value[cache_key] == speed:
|
||||
return
|
||||
func_last_value[cache_key] = speed
|
||||
|
||||
speed_str = ""
|
||||
if speed > 0:
|
||||
speed_str = common.fspeed(speed)
|
||||
cell.set_property('text', speed_str)
|
||||
|
||||
def cell_data_speed_down(column, cell, model, row, data):
|
||||
"""Display value as a speed, eg. 2 KiB/s"""
|
||||
cell_data_speed(cell, model, row, data, "cell_data_speed_down")
|
||||
|
||||
def cell_data_speed_up(column, cell, model, row, data):
|
||||
"""Display value as a speed, eg. 2 KiB/s"""
|
||||
cell_data_speed(cell, model, row, data, "cell_data_speed_up")
|
||||
|
||||
def cell_data_speed_limit(cell, model, row, data, cache_key):
|
||||
"""Display value as a speed, eg. 2 KiB/s"""
|
||||
speed = model.get_value(row, data)
|
||||
|
||||
if func_last_value[cache_key] == speed:
|
||||
return
|
||||
func_last_value[cache_key] = speed
|
||||
|
||||
speed_str = ""
|
||||
if speed > 0:
|
||||
speed_str = common.fspeed(speed * 1024)
|
||||
cell.set_property('text', speed_str)
|
||||
|
||||
def cell_data_speed_limit_down(column, cell, model, row, data):
|
||||
cell_data_speed_limit(cell, model, row, data, "cell_data_speed_limit_down")
|
||||
|
||||
def cell_data_speed_limit_up(column, cell, model, row, data):
|
||||
cell_data_speed_limit(cell, model, row, data, "cell_data_speed_limit_up")
|
||||
|
||||
def cell_data_size(column, cell, model, row, data):
|
||||
"""Display value in terms of size, eg. 2 MB"""
|
||||
size = model.get_value(row, data)
|
||||
cell.set_property('text', common.fsize(size))
|
||||
|
||||
def cell_data_peer(column, cell, model, row, data):
|
||||
"""Display values as 'value1 (value2)'"""
|
||||
(first, second) = model.get(row, *data)
|
||||
# Only display a (total) if second is greater than -1
|
||||
if second > -1:
|
||||
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)
|
||||
if func_last_value["cell_data_time"] == time:
|
||||
return
|
||||
func_last_value["cell_data_time"] = time
|
||||
|
||||
if time <= 0:
|
||||
time_str = ""
|
||||
else:
|
||||
time_str = common.ftime(time)
|
||||
cell.set_property('text', time_str)
|
||||
|
||||
def cell_data_ratio(cell, model, row, data, cache_key):
|
||||
"""Display value as a ratio with a precision of 3."""
|
||||
ratio = model.get_value(row, data)
|
||||
# Previous value in cell is the same as for this value, so ignore
|
||||
if func_last_value[cache_key] == ratio:
|
||||
return
|
||||
func_last_value[cache_key] = ratio
|
||||
cell.set_property('text', "∞" if ratio < 0 else "%.3f" % ratio)
|
||||
|
||||
def cell_data_ratio_seeders(column, cell, model, row, data):
|
||||
cell_data_ratio(cell, model, row, data, "cell_data_ratio_seeders")
|
||||
|
||||
def cell_data_ratio_ratio(column, cell, model, row, data):
|
||||
cell_data_ratio(cell, model, row, data, "cell_data_ratio_ratio")
|
||||
|
||||
def cell_data_ratio_avail(column, cell, model, row, data):
|
||||
cell_data_ratio(cell, model, row, data, "cell_data_ratio_avail")
|
||||
|
||||
def cell_data_date(column, cell, model, row, data):
|
||||
"""Display value as date, eg 05/05/08"""
|
||||
date = model.get_value(row, data)
|
||||
|
||||
if func_last_value["cell_data_date"] == date:
|
||||
return
|
||||
func_last_value["cell_data_date"] = date
|
||||
|
||||
date_str = common.fdate(date)
|
||||
cell.set_property('text', date_str)
|
||||
|
||||
def cell_data_date_or_never(column, cell, model, row, data):
|
||||
"""Display value as date, eg 05/05/08 or Never"""
|
||||
value = model.get_value(row, data)
|
||||
|
||||
if func_last_value["cell_data_date_or_never"] == value:
|
||||
return
|
||||
func_last_value["cell_data_date_or_never"] = value
|
||||
|
||||
date_str = common.fdate(value) if value > 0.0 else _("Never")
|
||||
cell.set_property('text', date_str)
|
||||
|
|
@ -178,9 +178,38 @@ class TrackerIcons(Component):
|
|||
self.pending = {}
|
||||
self.redirects = {}
|
||||
|
||||
def has(self, host):
|
||||
"""
|
||||
Returns True or False if the tracker icon for the given host exists or not.
|
||||
|
||||
:param host: the host for the TrackerIcon
|
||||
:type host: string
|
||||
:returns: True or False
|
||||
:rtype: bool
|
||||
"""
|
||||
return host.lower() in self.icons
|
||||
|
||||
def get(self, host):
|
||||
"""
|
||||
Returns a TrackerIcon for the given tracker's host
|
||||
from the icon cache.
|
||||
|
||||
:param host: the host for the TrackerIcon
|
||||
:type host: string
|
||||
:returns: the TrackerIcon for the host
|
||||
:rtype: TrackerIcon
|
||||
"""
|
||||
host = host.lower()
|
||||
if host in self.icons:
|
||||
return self.icons[host]
|
||||
else:
|
||||
return None
|
||||
|
||||
def fetch(self, host):
|
||||
"""
|
||||
Fetches (downloads) the icon for the given host.
|
||||
When the icon is downloaded a callback is fired
|
||||
on the the queue of callers to this function.
|
||||
|
||||
:param host: the host to obtain the TrackerIcon for
|
||||
:type host: string
|
||||
|
|
Loading…
Reference in New Issue