diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 128919808..6fbef9b99 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -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'], diff --git a/deluge/ui/gtkui/details_tab.py b/deluge/ui/gtkui/details_tab.py index 778173ea6..e9589e506 100644 --- a/deluge/ui/gtkui/details_tab.py +++ b/deluge/ui/gtkui/details_tab.py @@ -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('%s' % (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('%s' % (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('') diff --git a/deluge/ui/gtkui/files_tab.py b/deluge/ui/gtkui/files_tab.py index 322d05ff9..ab6bd439e 100644 --- a/deluge/ui/gtkui/files_tab.py +++ b/deluge/ui/gtkui/files_tab.py @@ -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) diff --git a/deluge/ui/gtkui/options_tab.py b/deluge/ui/gtkui/options_tab.py index 1f323d7fd..18e647504 100644 --- a/deluge/ui/gtkui/options_tab.py +++ b/deluge/ui/gtkui/options_tab.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # # Copyright (C) 2008 Andrew Resch +# 2017 Calum Lind # # 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) diff --git a/deluge/ui/gtkui/peers_tab.py b/deluge/ui/gtkui/peers_tab.py index 5e7452227..7d9bd9652 100644 --- a/deluge/ui/gtkui/peers_tab.py +++ b/deluge/ui/gtkui/peers_tab.py @@ -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 = {} diff --git a/deluge/ui/gtkui/status_tab.py b/deluge/ui/gtkui/status_tab.py index 11390f664..38dbbf6fb 100644 --- a/deluge/ui/gtkui/status_tab.py +++ b/deluge/ui/gtkui/status_tab.py @@ -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']: diff --git a/deluge/ui/gtkui/torrentdetails.py b/deluge/ui/gtkui/torrentdetails.py index f230b1d07..31acb02fa 100644 --- a/deluge/ui/gtkui/torrentdetails.py +++ b/deluge/ui/gtkui/torrentdetails.py @@ -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): diff --git a/deluge/ui/gtkui/trackers_tab.py b/deluge/ui/gtkui/trackers_tab.py index 7c9dc800f..48677b536 100644 --- a/deluge/ui/gtkui/trackers_tab.py +++ b/deluge/ui/gtkui/trackers_tab.py @@ -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()