[GTKUI] Refactor torrent details Tabs code

* Move common code into Tab parent class
 * The Tab init now accepts creation with name, child_widget and tab_label but
   will still accept the 'oldstyle' contructor to keep backwards compatibility
   with 3rd-party plugins.
 * Create namedtuple TabWidget with widget, format func and status_keys. The
   TabWidget are stored in a tab_widgets dict making it easier to create, save
   and update large numbers of gtk widgets.
This commit is contained in:
Calum Lind 2017-03-17 23:11:17 +00:00
parent 6d28f2c885
commit 1637da84e4
8 changed files with 197 additions and 273 deletions

View file

@ -977,6 +977,7 @@ class Torrent(object):
'download_payload_rate': lambda: self.status.download_payload_rate,
'file_priorities': self.get_file_priorities,
'hash': lambda: self.torrent_id,
'auto_managed': lambda: self.options['auto_managed'],
'is_auto_managed': lambda: self.options['auto_managed'],
'is_finished': lambda: self.is_finished,
'max_connections': lambda: self.options['max_connections'],

View file

@ -22,28 +22,17 @@ log = logging.getLogger(__name__)
class DetailsTab(Tab):
def __init__(self):
super(DetailsTab, self).__init__()
# Get the labels we need to update.
# widget name, modifier function, status keys
main_builder = component.get('MainWindow').get_builder()
super(DetailsTab, self).__init__('Details', 'details_tab', 'details_tab_label')
self._name = 'Details'
self._child_widget = main_builder.get_object('details_tab')
self._tab_label = main_builder.get_object('details_tab_label')
self.label_widgets = [
(main_builder.get_object('summary_name'), None, ('name',)),
(main_builder.get_object('summary_total_size'), fsize, ('total_size',)),
(main_builder.get_object('summary_num_files'), str, ('num_files',)),
(main_builder.get_object('summary_completed'), fdate_or_dash, ('completed_time',)),
(main_builder.get_object('summary_date_added'), fdate, ('time_added',)),
(main_builder.get_object('summary_torrent_path'), None, ('download_location',)),
(main_builder.get_object('summary_hash'), str, ('hash',)),
(main_builder.get_object('summary_comments'), str, ('comment',)),
(main_builder.get_object('summary_pieces'), fpieces_num_size, ('num_pieces', 'piece_length')),
]
self.status_keys = [status for widget in self.label_widgets for status in widget[2]]
self.add_tab_widget('summary_name', None, ('name',))
self.add_tab_widget('summary_total_size', fsize, ('total_size',))
self.add_tab_widget('summary_num_files', str, ('num_files',))
self.add_tab_widget('summary_completed', fdate_or_dash, ('completed_time',))
self.add_tab_widget('summary_date_added', fdate, ('time_added',))
self.add_tab_widget('summary_torrent_path', None, ('download_location',))
self.add_tab_widget('summary_hash', str, ('hash',))
self.add_tab_widget('summary_comments', str, ('comment',))
self.add_tab_widget('summary_pieces', fpieces_num_size, ('num_pieces', 'piece_length'))
def update(self):
# Get the first selected torrent
@ -66,14 +55,14 @@ class DetailsTab(Tab):
return
# Update all the label widgets
for widget in self.label_widgets:
txt = xml_escape(self.get_status_for_widget(widget, status))
if widget[0].get_text() != txt:
if widget[2][0] == 'comment' and is_url(txt):
widget[0].set_markup('<a href="%s">%s</a>' % (txt, txt))
for widget in self.tab_widgets.values():
txt = xml_escape(self.widget_status_as_fstr(widget, status))
if widget.obj.get_text() != txt:
if 'comment' in widget.status_keys and is_url(txt):
widget.obj.set_markup('<a href="%s">%s</a>' % (txt, txt))
else:
widget[0].set_markup(txt)
widget.obj.set_markup(txt)
def clear(self):
for widget in self.label_widgets:
widget[0].set_text('')
for widget in self.tab_widgets.values():
widget.obj.set_text('')

View file

@ -70,14 +70,9 @@ def cell_progress(column, cell, model, row, data):
class FilesTab(Tab):
def __init__(self):
super(FilesTab, self).__init__()
main_builder = component.get('MainWindow').get_builder()
super(FilesTab, self).__init__('Files', 'files_tab', 'files_tab_label')
self._name = 'Files'
self._child_widget = main_builder.get_object('files_tab')
self._tab_label = main_builder.get_object('files_tab_label')
self.listview = main_builder.get_object('files_listview')
self.listview = self.main_builder.get_object('files_listview')
# filename, size, progress string, progress value, priority, file index, icon id
self.treestore = gtk.TreeStore(str, TYPE_UINT64, str, float, int, int, str)
self.treestore.set_sort_column_id(0, gtk.SORT_ASCENDING)
@ -155,19 +150,19 @@ class FilesTab(Tab):
self.listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
self.file_menu = main_builder.get_object('menu_file_tab')
self.file_menu = self.main_builder.get_object('menu_file_tab')
self.file_menu_priority_items = [
main_builder.get_object('menuitem_ignore'),
main_builder.get_object('menuitem_low'),
main_builder.get_object('menuitem_normal'),
main_builder.get_object('menuitem_high'),
main_builder.get_object('menuitem_priority_sep')
self.main_builder.get_object('menuitem_ignore'),
self.main_builder.get_object('menuitem_low'),
self.main_builder.get_object('menuitem_normal'),
self.main_builder.get_object('menuitem_high'),
self.main_builder.get_object('menuitem_priority_sep')
]
self.localhost_widgets = [
main_builder.get_object('menuitem_open_file'),
main_builder.get_object('menuitem_show_file'),
main_builder.get_object('menuitem3')
self.main_builder.get_object('menuitem_open_file'),
self.main_builder.get_object('menuitem_show_file'),
self.main_builder.get_object('menuitem3')
]
self.listview.connect('row-activated', self._on_row_activated)

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
# 2017 Calum Lind <calumlind+deluge@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
@ -19,46 +20,45 @@ from deluge.ui.gtkui.torrentdetails import Tab
class OptionsTab(Tab):
def __init__(self):
super(OptionsTab, self).__init__()
main_builder = component.get('MainWindow').get_builder()
self._name = 'Options'
self._child_widget = main_builder.get_object('options_tab')
self._tab_label = main_builder.get_object('options_tab_label')
self.spin_max_download = main_builder.get_object('spin_max_download')
self.spin_max_upload = main_builder.get_object('spin_max_upload')
self.spin_max_connections = main_builder.get_object('spin_max_connections')
self.spin_max_upload_slots = main_builder.get_object('spin_max_upload_slots')
self.chk_prioritize_first_last = main_builder.get_object('chk_prioritize_first_last')
self.chk_sequential_download = main_builder.get_object('chk_sequential_download')
self.chk_auto_managed = main_builder.get_object('chk_auto_managed')
self.chk_stop_at_ratio = main_builder.get_object('chk_stop_at_ratio')
self.chk_remove_at_ratio = main_builder.get_object('chk_remove_at_ratio')
self.spin_stop_ratio = main_builder.get_object('spin_stop_ratio')
self.chk_move_completed = main_builder.get_object('chk_move_completed')
self.entry_move_completed = main_builder.get_object('entry_move_completed')
self.chk_shared = main_builder.get_object('chk_shared')
self.button_apply = main_builder.get_object('button_apply')
self.summary_owner = main_builder.get_object('summary_owner')
self.move_completed_hbox = main_builder.get_object('hbox_move_completed_path_chooser')
self.move_completed_path_chooser = PathChooser('move_completed_paths_list')
self.move_completed_path_chooser.set_sensitive(self.chk_move_completed.get_active())
self.move_completed_hbox.add(self.move_completed_path_chooser)
self.move_completed_hbox.show_all()
self.move_completed_path_chooser.connect('text-changed', self._on_path_chooser_text_changed_event)
super(OptionsTab, self).__init__('Options', 'options_tab', 'options_tab_label')
self.prev_torrent_id = None
self.prev_status = None
component.get('MainWindow').connect_signals(self)
# Create TabWidget items with widget id, get/set func name, status key.
self.add_tab_widget('spin_max_download', 'value', ['max_download_speed'])
self.add_tab_widget('spin_max_upload', 'value', ['max_upload_speed'])
self.add_tab_widget('spin_max_connections', 'value_as_int', ['max_connections'])
self.add_tab_widget('spin_max_upload_slots', 'value_as_int', ['max_upload_slots'])
self.add_tab_widget('chk_prioritize_first_last', 'active', ['prioritize_first_last_pieces'])
self.add_tab_widget('chk_sequential_download', 'active', ['sequential_download'])
self.add_tab_widget('chk_auto_managed', 'active', ['is_auto_managed'])
self.add_tab_widget('chk_stop_at_ratio', 'active', ['stop_at_ratio'])
self.add_tab_widget('chk_remove_at_ratio', 'active', ['remove_at_ratio'])
self.add_tab_widget('spin_stop_ratio', 'value', ['stop_ratio'])
self.add_tab_widget('chk_move_completed', 'active', ['move_completed'])
self.add_tab_widget('chk_shared', 'active', ['shared'])
self.add_tab_widget('summary_owner', 'text', ['owner'])
self.spin_max_download.connect('key-press-event', self._on_key_press_event)
self.spin_max_upload.connect('key-press-event', self._on_key_press_event)
self.spin_max_connections.connect('key-press-event', self._on_key_press_event)
self.spin_max_upload_slots.connect('key-press-event', self._on_key_press_event)
self.spin_stop_ratio.connect('key-press-event', self._on_key_press_event)
# Connect key press event for spin widgets.
for widget_id in self.tab_widgets:
if widget_id.startswith('spin_'):
self.tab_widgets[widget_id].obj.connect('key-press-event', self.on_key_press_event)
self.button_apply = self.main_builder.get_object('button_apply')
self.move_completed_path_chooser = PathChooser('move_completed_paths_list')
self.move_completed_path_chooser.set_sensitive(
self.tab_widgets['chk_move_completed'].obj.get_active())
self.move_completed_path_chooser.connect(
'text-changed', self.on_path_chooser_text_changed_event)
self.status_keys.append('move_completed_path')
self.move_completed_hbox = self.main_builder.get_object('hbox_move_completed_path_chooser')
self.move_completed_hbox.add(self.move_completed_path_chooser)
self.move_completed_hbox.show_all()
component.get('MainWindow').connect_signals(self)
def start(self):
pass
@ -68,11 +68,11 @@ class OptionsTab(Tab):
def update(self):
# Get the first selected torrent
torrent_id = component.get('TorrentView').get_selected_torrents()
torrent_ids = component.get('TorrentView').get_selected_torrents()
# Only use the first torrent in the list or return if None selected
if torrent_id:
torrent_id = torrent_id[0]
if torrent_ids:
torrent_id = torrent_ids[0]
self._child_widget.set_sensitive(True)
else:
# No torrent is selected in the torrentview
@ -82,147 +82,77 @@ class OptionsTab(Tab):
if torrent_id != self.prev_torrent_id:
self.prev_status = None
component.get('SessionProxy').get_torrent_status(torrent_id, [
'max_download_speed',
'max_upload_speed',
'max_connections',
'max_upload_slots',
'prioritize_first_last',
'is_auto_managed',
'stop_at_ratio',
'stop_ratio',
'remove_at_ratio',
'storage_mode',
'sequential_download',
'move_on_completed',
'move_on_completed_path',
'shared',
'owner'
]).addCallback(self._on_get_torrent_status)
component.get('SessionProxy').get_torrent_status(
torrent_id, self.status_keys
).addCallback(self.on_get_torrent_status)
self.prev_torrent_id = torrent_id
def clear(self):
self.prev_torrent_id = None
self.prev_status = None
def _on_get_torrent_status(self, status):
# We only want to update values that have been applied in the core. This
# is so we don't overwrite the user changes that haven't been applied yet.
def on_get_torrent_status(self, status):
# So we don't overwrite the user's unapplied changes we only
# want to update values that have been applied in the core.
if self.prev_status is None:
self.prev_status = {}.fromkeys(list(status), None)
self.prev_status = dict.fromkeys(status, None)
if status != self.prev_status:
if status['max_download_speed'] != self.prev_status['max_download_speed']:
self.spin_max_download.set_value(status['max_download_speed'])
if status['max_upload_speed'] != self.prev_status['max_upload_speed']:
self.spin_max_upload.set_value(status['max_upload_speed'])
if status['max_connections'] != self.prev_status['max_connections']:
self.spin_max_connections.set_value(status['max_connections'])
if status['max_upload_slots'] != self.prev_status['max_upload_slots']:
self.spin_max_upload_slots.set_value(status['max_upload_slots'])
if status['prioritize_first_last'] != self.prev_status['prioritize_first_last']:
self.chk_prioritize_first_last.set_active(status['prioritize_first_last'])
if status['is_auto_managed'] != self.prev_status['is_auto_managed']:
self.chk_auto_managed.set_active(status['is_auto_managed'])
if status['stop_at_ratio'] != self.prev_status['stop_at_ratio']:
self.chk_stop_at_ratio.set_active(status['stop_at_ratio'])
self.spin_stop_ratio.set_sensitive(status['stop_at_ratio'])
self.chk_remove_at_ratio.set_sensitive(status['stop_at_ratio'])
if status['stop_ratio'] != self.prev_status['stop_ratio']:
self.spin_stop_ratio.set_value(status['stop_ratio'])
if status['remove_at_ratio'] != self.prev_status['remove_at_ratio']:
self.chk_remove_at_ratio.set_active(status['remove_at_ratio'])
if status['move_on_completed'] != self.prev_status['move_on_completed']:
self.chk_move_completed.set_active(status['move_on_completed'])
if status['move_on_completed_path'] != self.prev_status['move_on_completed_path']:
self.move_completed_path_chooser.set_text(status['move_on_completed_path'],
cursor_end=False, default_text=True)
if status['shared'] != self.prev_status['shared']:
self.chk_shared.set_active(status['shared'])
if status['owner'] != self.prev_status['owner']:
self.summary_owner.set_text(status['owner'])
for widget in self.tab_widgets.values():
status_key = widget.status_keys[0]
if status[status_key] != self.prev_status[status_key]:
set_func = 'set_' + widget.func.replace('_as_int', '')
getattr(widget.obj, set_func)(status[status_key])
if status['prioritize_first_last'] != self.prev_status['prioritize_first_last']:
self.chk_prioritize_first_last.set_active(status['prioritize_first_last'])
if not self.chk_prioritize_first_last.get_property('visible'):
self.chk_prioritize_first_last.show()
if status['sequential_download'] != self.prev_status['sequential_download']:
self.chk_sequential_download.set_active(status['sequential_download'])
if not self.chk_sequential_download.get_property('visible'):
self.chk_sequential_download.show()
if status['move_completed_path'] != self.prev_status['move_completed_path']:
self.move_completed_path_chooser.set_text(
status['move_completed_path'], cursor_end=False, default_text=True)
if self.button_apply.is_sensitive():
self.button_apply.set_sensitive(False)
# Update sensitivity of widgets.
self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(status['stop_at_ratio'])
self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(status['stop_at_ratio'])
# Ensure apply button sensitivity is set False.
self.button_apply.set_sensitive(False)
self.prev_status = status
def on_button_apply_clicked(self, button):
options = {}
if self.spin_max_download.get_value() != self.prev_status['max_download_speed']:
options['max_download_speed'] = self.spin_max_download.get_value()
if self.spin_max_upload.get_value() != self.prev_status['max_upload_speed']:
options['max_upload_speed'] = self.spin_max_upload.get_value()
if self.spin_max_connections.get_value_as_int() != self.prev_status['max_connections']:
options['max_connections'] = self.spin_max_connections.get_value_as_int()
if self.spin_max_upload_slots.get_value_as_int() != self.prev_status['max_upload_slots']:
options['max_upload_slots'] = self.spin_max_upload_slots.get_value_as_int()
if self.chk_prioritize_first_last.get_active() != self.prev_status['prioritize_first_last']:
options['prioritize_first_last_pieces'] = self.chk_prioritize_first_last.get_active()
if self.chk_sequential_download.get_active() != self.prev_status['sequential_download']:
options['sequential_download'] = self.chk_sequential_download.get_active()
if self.chk_auto_managed.get_active() != self.prev_status['is_auto_managed']:
options['auto_managed'] = self.chk_auto_managed.get_active()
if self.chk_stop_at_ratio.get_active() != self.prev_status['stop_at_ratio']:
options['stop_at_ratio'] = self.chk_stop_at_ratio.get_active()
if self.spin_stop_ratio.get_value() != self.prev_status['stop_ratio']:
options['stop_ratio'] = self.spin_stop_ratio.get_value()
if self.chk_remove_at_ratio.get_active() != self.prev_status['remove_at_ratio']:
options['remove_at_ratio'] = self.chk_remove_at_ratio.get_active()
if self.chk_move_completed.get_active() != self.prev_status['move_on_completed']:
options['move_completed'] = self.chk_move_completed.get_active()
if self.chk_move_completed.get_active():
for widget in self.tab_widgets.values():
status_key = widget.status_keys[0]
if status_key == 'owner':
continue # A label so read-only
widget_value = getattr(widget.obj, 'get_' + widget.func)()
if widget_value != self.prev_status[status_key]:
options[status_key] = widget_value
if options.get('move_completed', False):
options['move_completed_path'] = self.move_completed_path_chooser.get_text()
if self.chk_shared.get_active() != self.prev_status['shared']:
options['shared'] = self.chk_shared.get_active()
client.core.set_torrent_options([self.prev_torrent_id], options)
self.button_apply.set_sensitive(False)
def on_chk_move_completed_toggled(self, widget):
value = self.chk_move_completed.get_active()
self.move_completed_path_chooser.set_sensitive(value)
if not self.button_apply.is_sensitive():
self.button_apply.set_sensitive(True)
self.move_completed_path_chooser.set_sensitive(widget.get_active())
self.button_apply.set_sensitive(True)
def on_chk_stop_at_ratio_toggled(self, widget):
value = widget.get_active()
self.spin_stop_ratio.set_sensitive(value)
self.chk_remove_at_ratio.set_sensitive(value)
if not self.button_apply.is_sensitive():
self.button_apply.set_sensitive(True)
is_active = widget.get_active()
self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(is_active)
self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(is_active)
self.button_apply.set_sensitive(True)
def on_chk_toggled(self, widget):
if not self.button_apply.is_sensitive():
self.button_apply.set_sensitive(True)
self.button_apply.set_sensitive(True)
def on_spin_value_changed(self, widget):
if not self.button_apply.is_sensitive():
self.button_apply.set_sensitive(True)
self.button_apply.set_sensitive(True)
def _on_key_press_event(self, widget, event):
def on_key_press_event(self, widget, event):
keyname = keyval_name(event.keyval).lstrip('KP_').lower()
if keyname.isdigit() or keyname in ['period', 'minus', 'delete', 'backspace']:
if not self.button_apply.is_sensitive():
self.button_apply.set_sensitive(True)
def on_move_completed_file_set(self, widget):
if not self.button_apply.is_sensitive():
self.button_apply.set_sensitive(True)
def _on_entry_move_completed_changed(self, widget):
if not self.button_apply.is_sensitive():
self.button_apply.set_sensitive(True)
def _on_path_chooser_text_changed_event(self, widget, path):
def on_path_chooser_text_changed_event(self, widget, path):
self.button_apply.set_sensitive(True)

View file

@ -30,20 +30,17 @@ log = logging.getLogger(__name__)
class PeersTab(Tab):
def __init__(self):
super(PeersTab, self).__init__()
main_builder = component.get('MainWindow').get_builder()
super(PeersTab, self).__init__('Peers', 'peers_tab', 'peers_tab_label')
self._name = 'Peers'
self._child_widget = main_builder.get_object('peers_tab')
self._tab_label = main_builder.get_object('peers_tab_label')
self.peer_menu = main_builder.get_object('menu_peer_tab')
self.peer_menu = self.main_builder.get_object('menu_peer_tab')
component.get('MainWindow').connect_signals(self)
self.listview = main_builder.get_object('peers_listview')
self.listview = self.main_builder.get_object('peers_listview')
self.listview.props.has_tooltip = True
self.listview.connect('button-press-event', self._on_button_press_event)
self.listview.connect('query-tooltip', self._on_query_tooltip)
# country pixbuf, ip, client, downspeed, upspeed, country code, int_ip, seed/peer icon, progress
# flag, ip, client, downspd, upspd, country code, int_ip, seed/peer icon, progress
self.liststore = ListStore(Pixbuf, str, str, int, int, str, float, Pixbuf, float)
self.cached_flag_pixbufs = {}

View file

@ -17,50 +17,40 @@ from deluge.configmanager import ConfigManager
from deluge.ui.gtkui.piecesbar import PiecesBar
from deluge.ui.gtkui.tab_data_funcs import (fdate_or_never, fpcnt, fratio, fseed_rank_or_dash, fspeed_max,
ftime_or_dash, ftotal_sized)
from deluge.ui.gtkui.torrentdetails import Tab
from deluge.ui.gtkui.torrentdetails import Tab, TabWidget
log = logging.getLogger(__name__)
class StatusTab(Tab):
def __init__(self):
super(StatusTab, self).__init__()
# Get the labels we need to update.
# widget name, modifier function, status keys
main_builder = component.get('MainWindow').get_builder()
super(StatusTab, self).__init__('Status', 'status_tab', 'status_tab_label')
self._name = 'Status'
self._child_widget = main_builder.get_object('status_tab')
self._tab_label = main_builder.get_object('status_tab_label')
self.config = ConfigManager('gtkui.conf')
self.progressbar = main_builder.get_object('progressbar')
self.progressbar = self.main_builder.get_object('progressbar')
self.piecesbar = None
self.piecesbar_label_widget = None
self.label_widgets = [
(main_builder.get_object('summary_availability'), fratio, ('distributed_copies',)),
(main_builder.get_object('summary_total_downloaded'), ftotal_sized, ('all_time_download',
'total_payload_download')),
(main_builder.get_object('summary_total_uploaded'), ftotal_sized, ('total_uploaded',
'total_payload_upload')),
(main_builder.get_object('summary_download_speed'), fspeed_max, ('download_payload_rate',
'max_download_speed')),
(main_builder.get_object('summary_upload_speed'), fspeed_max, ('upload_payload_rate',
'max_upload_speed')),
(main_builder.get_object('summary_seeds'), fpeer, ('num_seeds', 'total_seeds')),
(main_builder.get_object('summary_peers'), fpeer, ('num_peers', 'total_peers')),
(main_builder.get_object('summary_eta'), ftime_or_dash, ('eta',)),
(main_builder.get_object('summary_share_ratio'), fratio, ('ratio',)),
(main_builder.get_object('summary_active_time'), ftime_or_dash, ('active_time',)),
(main_builder.get_object('summary_seed_time'), ftime_or_dash, ('seeding_time',)),
(main_builder.get_object('summary_seed_rank'), fseed_rank_or_dash, ('seed_rank', 'seeding_time')),
(main_builder.get_object('progressbar'), fpcnt, ('progress', 'state', 'message')),
(main_builder.get_object('summary_last_seen_complete'), fdate_or_never, ('last_seen_complete',)),
(main_builder.get_object('summary_last_transfer'), ftime_or_dash, ('time_since_transfer',)),
]
self.add_tab_widget('summary_availability', fratio, ('distributed_copies',))
self.add_tab_widget('summary_total_downloaded', ftotal_sized,
('all_time_download', 'total_payload_download'))
self.add_tab_widget('summary_total_uploaded', ftotal_sized,
('total_uploaded', 'total_payload_upload'))
self.add_tab_widget('summary_download_speed', fspeed_max,
('download_payload_rate', 'max_download_speed'))
self.add_tab_widget('summary_upload_speed', fspeed_max,
('upload_payload_rate', 'max_upload_speed'))
self.add_tab_widget('summary_seeds', fpeer, ('num_seeds', 'total_seeds'))
self.add_tab_widget('summary_peers', fpeer, ('num_peers', 'total_peers'))
self.add_tab_widget('summary_eta', ftime_or_dash, ('eta',))
self.add_tab_widget('summary_share_ratio', fratio, ('ratio',))
self.add_tab_widget('summary_active_time', ftime_or_dash, ('active_time',))
self.add_tab_widget('summary_seed_time', ftime_or_dash, ('seeding_time',))
self.add_tab_widget('summary_seed_rank', fseed_rank_or_dash, ('seed_rank', 'seeding_time'))
self.add_tab_widget('progressbar', fpcnt, ('progress', 'state', 'message'))
self.add_tab_widget('summary_last_seen_complete', fdate_or_never, ('last_seen_complete',))
self.add_tab_widget('summary_last_transfer', ftime_or_dash, ('time_since_transfer',))
self.status_keys = [status for widget in self.label_widgets for status in widget[2]]
self.config.register_set_function('show_piecesbar', self.on_show_piecesbar_config_changed, apply_now=True)
def update(self):
@ -86,8 +76,8 @@ class StatusTab(Tab):
return
# Update all the label widgets
for widget in self.label_widgets:
txt = self.get_status_for_widget(widget, status)
for widget in self.tab_widgets.values():
txt = self.widget_status_as_fstr(widget, status)
if widget[0].get_text() != txt:
widget[0].set_text(txt)
@ -114,10 +104,9 @@ class StatusTab(Tab):
def show_piecesbar(self):
if self.piecesbar is None:
self.piecesbar = PiecesBar()
main_builder = component.get('MainWindow').get_builder()
main_builder.get_object('status_progress_vbox').pack_start(self.piecesbar, False, False, 0)
self.piecesbar_label_widget = (self.piecesbar, fpcnt, ('progress', 'state', 'message'))
self.label_widgets.append(self.piecesbar_label_widget)
self.main_builder.get_object(
'status_progress_vbox').pack_start(self.piecesbar, False, False, 0)
self.tab_widgets['piecesbar'] = TabWidget(self.piecesbar, fpcnt, ('progress', 'state', 'message'))
self.piecesbar.show()
self.progressbar.hide()
@ -125,11 +114,11 @@ class StatusTab(Tab):
self.progressbar.show()
if self.piecesbar:
self.piecesbar.hide()
self.label_widgets.remove(self.piecesbar_label_widget)
self.piecesbar = self.piecesbar_label_widget = None
self.tab_widgets.pop('piecesbar', None)
self.piecesbar = None
def clear(self):
for widget in self.label_widgets:
for widget in self.tab_widgets.values():
widget[0].set_text('')
if self.config['show_piecesbar']:

View file

@ -12,6 +12,7 @@
from __future__ import unicode_literals
import logging
from collections import namedtuple
from gtk import CheckMenuItem, Menu, SeparatorMenuItem
@ -21,13 +22,23 @@ from deluge.ui.gtkui.common import load_pickled_state_file, save_pickled_state_f
log = logging.getLogger(__name__)
TabWidget = namedtuple('TabWidget', ('obj', 'func', 'status_keys'))
class Tab(object):
def __init__(self):
def __init__(self, name=None, child_widget=None, tab_label=None):
self._name = name
self.is_visible = True
self.position = -1
self.weight = -1
self.main_builder = component.get('MainWindow').get_builder()
self._child_widget = self.main_builder.get_object(child_widget)if child_widget else None
self._tab_label = self.main_builder.get_object(tab_label) if tab_label else None
self.tab_widgets = {}
self.status_keys = []
def get_name(self):
return self._name
@ -46,18 +57,41 @@ class Tab(object):
return self._tab_label
def get_status_for_widget(self, widget, status):
def widget_status_as_fstr(self, widget, status):
"""Use TabWidget status_key and func to format status string.
Args:
widget (TabWidget): A tuple of widget object, func and status_keys.
status (dict): Torrent status dict.
Returns:
str: The formatted status string.
"""
try:
if widget[1] is None:
txt = status[widget[2][0]]
if widget.func is None:
txt = status[widget.status_keys[0]]
else:
args = [status[key] for key in widget[2]]
txt = widget[1](*args)
args = [status[key] for key in widget.status_keys]
txt = widget.func(*args)
except KeyError as ex:
log.warn('Unable to get status value: %s', ex)
txt = ''
return txt
def add_tab_widget(self, widget_id, format_func, status_keys):
"""Create TabWidget item in tab_widgets dictionary.
Args:
widget_id (str): The widget id used to retrieve widget from mainwindow builder.
format_func (str): A func name related to widget e.g. string label formatter.
status_keys (list): List of status keys to lookup for the widget.
"""
widget_obj = self.main_builder.get_object(widget_id)
self.status_keys.extend(status_keys)
# Store the widget in a tab_widgets dict with name as key for faster lookup.
self.tab_widgets[widget_id] = TabWidget(widget_obj, format_func, status_keys)
class TorrentDetails(component.Component):
def __init__(self):

View file

@ -21,24 +21,13 @@ log = logging.getLogger(__name__)
class TrackersTab(Tab):
def __init__(self):
super(TrackersTab, self).__init__()
# Get the labels we need to update.
# widget name, modifier function, status keys
main_builder = component.get('MainWindow').get_builder()
super(TrackersTab, self).__init__('Trackers', 'trackers_tab', 'trackers_tab_label')
self._name = 'Trackers'
self._child_widget = main_builder.get_object('trackers_tab')
self._tab_label = main_builder.get_object('trackers_tab_label')
self.label_widgets = [
(main_builder.get_object('summary_next_announce'), ftime, ('next_announce',)),
(main_builder.get_object('summary_tracker'), None, ('tracker_host',)),
(main_builder.get_object('summary_tracker_status'), ftranslate, ('tracker_status',)),
(main_builder.get_object('summary_tracker_total'), fcount, ('trackers',)),
(main_builder.get_object('summary_private'), fyes_no, ('private',)),
]
self.status_keys = [status for widget in self.label_widgets for status in widget[2]]
self.add_tab_widget('summary_next_announce', ftime, ('next_announce',))
self.add_tab_widget('summary_tracker', None, ('tracker_host',))
self.add_tab_widget('summary_tracker_status', ftranslate, ('tracker_status',))
self.add_tab_widget('summary_tracker_total', fcount, ('trackers',))
self.add_tab_widget('summary_private', fyes_no, ('private',))
component.get('MainWindow').connect_signals(self)
@ -61,15 +50,15 @@ class TrackersTab(Tab):
if not status:
return
# Update all the label widgets
for widget in self.label_widgets:
txt = self.get_status_for_widget(widget, status)
if widget[0].get_text() != txt:
widget[0].set_text(txt)
# Update all the tab label widgets
for widget in self.tab_widgets.values():
txt = self.widget_status_as_fstr(widget, status)
if widget.obj.get_text() != txt:
widget.obj.set_text(txt)
def clear(self):
for widget in self.label_widgets:
widget[0].set_text('')
for widget in self.tab_widgets.values():
widget.obj.set_text('')
def on_button_edit_trackers_clicked(self, button):
torrent_id = component.get('TorrentView').get_selected_torrent()