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:
bendikro 2013-11-26 18:05:00 +01:00 committed by Calum Lind
parent feaeee0379
commit 8ecc0e11a7
11 changed files with 424 additions and 207 deletions

View File

@ -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)

View File

@ -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%")

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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",

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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