mirror of
https://github.com/codex-storage/deluge.git
synced 2025-01-12 04:24:27 +00:00
[GTKUI] Reorganise layout of tab items and add Tracker tab
* Changed layout of Status, Details and Options tabs. * Moved the Tracker translations to ui.common. * Created a new Trackers tab. * Added State to progressbar. * Translate State in piecesbar.
This commit is contained in:
parent
14776d86f5
commit
6496383e82
@ -39,10 +39,7 @@ all the interfaces.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import urlparse
|
||||
import locale
|
||||
from hashlib import sha1 as sha
|
||||
|
||||
from deluge import bencode
|
||||
@ -51,10 +48,12 @@ import deluge.configmanager
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Dummy tranlation dict so Torrent states text is available for Translators
|
||||
# All entries in deluge.common.TORRENT_STATE should be here. It does not need importing
|
||||
# as the string matches the translation text so using the _() function is enough.
|
||||
def _(message): return message
|
||||
def _(message):
|
||||
return message
|
||||
STATE_TRANSLATION = {
|
||||
"All": _("All"),
|
||||
"Active": _("Active"),
|
||||
@ -67,8 +66,16 @@ STATE_TRANSLATION = {
|
||||
"Queued": _("Queued"),
|
||||
"Error": _("Error"),
|
||||
}
|
||||
|
||||
TRACKER_STATUS_TRANSLATION = {
|
||||
"Error": _("Error"),
|
||||
"Warning": _("Warning"),
|
||||
"Announce OK": _("Announce OK"),
|
||||
"Announce Sent": _("Announce Sent")
|
||||
}
|
||||
del _
|
||||
|
||||
|
||||
class TorrentInfo(object):
|
||||
"""
|
||||
Collects information about a torrent file.
|
||||
|
@ -21,11 +21,11 @@ def fpeer_size_second(first, second):
|
||||
|
||||
|
||||
def fdate_blank(value):
|
||||
"""Display value as date, eg 05/05/08 or blank"""
|
||||
"""Display value as date, eg 05/05/08 or dash"""
|
||||
if value > 0.0:
|
||||
return fdate(value)
|
||||
else:
|
||||
return ""
|
||||
return "-"
|
||||
|
||||
|
||||
def str_yes_no(value):
|
||||
@ -59,25 +59,22 @@ class DetailsTab(Tab):
|
||||
(builder.get_object("summary_pieces"), fpeer_size_second, ("num_pieces", "piece_length")),
|
||||
]
|
||||
|
||||
self.status_keys = [status for widget in self.label_widgets for status in widget[2]]
|
||||
|
||||
def update(self):
|
||||
# Get the first selected torrent
|
||||
selected = component.get("TorrentView").get_selected_torrents()
|
||||
|
||||
# Only use the first torrent in the list or return if None selected
|
||||
if len(selected) != 0:
|
||||
if selected:
|
||||
selected = selected[0]
|
||||
else:
|
||||
# No torrent is selected in the torrentview
|
||||
self.clear()
|
||||
return
|
||||
|
||||
# Get the torrent status
|
||||
status_keys = ["name", "total_size", "num_files", "time_added", "completed_time",
|
||||
"download_location", "hash", "comment", "owner", "num_pieces", "piece_length",
|
||||
"shared", "private"]
|
||||
|
||||
session = component.get("SessionProxy")
|
||||
session.get_torrent_status(selected, status_keys).addCallback(self._on_get_torrent_status)
|
||||
session.get_torrent_status(selected, self.status_keys).addCallback(self._on_get_torrent_status)
|
||||
|
||||
def _on_get_torrent_status(self, status):
|
||||
# Check to see if we got valid data from the core
|
||||
@ -87,14 +84,11 @@ class DetailsTab(Tab):
|
||||
# Update all the label widgets
|
||||
for widget in self.label_widgets:
|
||||
if widget[1] is not None:
|
||||
args = []
|
||||
try:
|
||||
for key in widget[2]:
|
||||
args.append(status[key])
|
||||
except Exception, e:
|
||||
log.debug("Unable to get status value: %s", e)
|
||||
args = [status[key] for key in widget[2]]
|
||||
except KeyError, ex:
|
||||
log.debug("Unable to get status value: %s", ex)
|
||||
continue
|
||||
|
||||
txt = widget[1](*args)
|
||||
else:
|
||||
txt = status[widget[2][0]]
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -77,7 +77,6 @@ class OptionsTab(Tab):
|
||||
|
||||
component.get("MainWindow").connect_signals({
|
||||
"on_button_apply_clicked": self._on_button_apply_clicked,
|
||||
"on_button_edit_trackers_clicked": self._on_button_edit_trackers_clicked,
|
||||
"on_chk_move_completed_toggled": self._on_chk_move_completed_toggled,
|
||||
"on_chk_stop_at_ratio_toggled": self._on_chk_stop_at_ratio_toggled,
|
||||
"on_chk_toggled": self._on_chk_toggled,
|
||||
@ -102,7 +101,7 @@ class OptionsTab(Tab):
|
||||
torrent_id = component.get("TorrentView").get_selected_torrents()
|
||||
|
||||
# Only use the first torrent in the list or return if None selected
|
||||
if len(torrent_id) != 0:
|
||||
if torrent_id:
|
||||
torrent_id = torrent_id[0]
|
||||
self._child_widget.set_sensitive(True)
|
||||
else:
|
||||
@ -250,17 +249,6 @@ class OptionsTab(Tab):
|
||||
)
|
||||
self.button_apply.set_sensitive(False)
|
||||
|
||||
def _on_button_edit_trackers_clicked(self, button):
|
||||
from edittrackersdialog import EditTrackersDialog
|
||||
dialog = EditTrackersDialog(
|
||||
self.prev_torrent_id,
|
||||
component.get("MainWindow").window)
|
||||
|
||||
def on_response(result):
|
||||
if result:
|
||||
self.button_apply.set_sensitive(True)
|
||||
dialog.run().addCallback(on_response)
|
||||
|
||||
def _on_chk_move_completed_toggled(self, widget):
|
||||
value = self.chk_move_completed.get_active()
|
||||
self.move_completed_path_chooser.set_sensitive(value)
|
||||
|
@ -52,6 +52,7 @@ COLOR_STATES = {
|
||||
3: "completed"
|
||||
}
|
||||
|
||||
|
||||
class PiecesBar(gtk.DrawingArea):
|
||||
# Draw in response to an expose-event
|
||||
__gsignals__ = {"expose-event": "override"}
|
||||
@ -141,7 +142,7 @@ class PiecesBar(gtk.DrawingArea):
|
||||
|
||||
def __draw_pieces(self):
|
||||
if (self.__resized() or self.__pieces != self.__old_pieces or
|
||||
self.__pieces_overlay == None):
|
||||
self.__pieces_overlay is None):
|
||||
# Need to recreate the cache drawing
|
||||
self.__pieces_overlay = cairo.ImageSurface(
|
||||
cairo.FORMAT_ARGB32, self.__width, self.__height
|
||||
@ -194,8 +195,7 @@ class PiecesBar(gtk.DrawingArea):
|
||||
if not self.__state:
|
||||
# Nothing useful to draw, return now!
|
||||
return
|
||||
if (self.__resized() or self.__fraction != self.__old_fraction) or \
|
||||
self.__progress_overlay is None:
|
||||
if (self.__resized() or self.__fraction != self.__old_fraction) or self.__progress_overlay is None:
|
||||
# Need to recreate the cache drawing
|
||||
self.__progress_overlay = cairo.ImageSurface(
|
||||
cairo.FORMAT_ARGB32, self.__width, self.__height
|
||||
@ -230,7 +230,7 @@ class PiecesBar(gtk.DrawingArea):
|
||||
text += self.__text
|
||||
else:
|
||||
if self.__state:
|
||||
text += self.__state + " "
|
||||
text += _(self.__state) + " "
|
||||
if self.__fraction == 1.0:
|
||||
format = "%d%%"
|
||||
else:
|
||||
|
@ -55,8 +55,10 @@ def fratio(value):
|
||||
return "%.3f" % value
|
||||
|
||||
|
||||
def fpcnt(value):
|
||||
return "%.2f%%" % value
|
||||
def fpcnt(value, state):
|
||||
if state:
|
||||
state = _(state) + " "
|
||||
return "%s%.2f%%" % (state, value)
|
||||
|
||||
|
||||
def fspeed(value, max_value=-1):
|
||||
@ -102,40 +104,32 @@ class StatusTab(Tab):
|
||||
(builder.get_object("summary_peers"), deluge.common.fpeer, ("num_peers", "total_peers")),
|
||||
(builder.get_object("summary_eta"), deluge.common.ftime, ("eta",)),
|
||||
(builder.get_object("summary_share_ratio"), fratio, ("ratio",)),
|
||||
(builder.get_object("summary_tracker_status"), None, ("tracker_status",)),
|
||||
(builder.get_object("summary_next_announce"), deluge.common.ftime, ("next_announce",)),
|
||||
(builder.get_object("summary_active_time"), deluge.common.ftime, ("active_time",)),
|
||||
(builder.get_object("summary_seed_time"), deluge.common.ftime, ("seeding_time",)),
|
||||
(builder.get_object("summary_seed_rank"), str, ("seed_rank",)),
|
||||
(builder.get_object("summary_auto_managed"), str, ("is_auto_managed",)),
|
||||
(builder.get_object("progressbar"), fpcnt, ("progress",)),
|
||||
(builder.get_object("progressbar"), fpcnt, ("progress", "state")),
|
||||
(builder.get_object("summary_last_seen_complete"), fdate_or_never, ("last_seen_complete",)),
|
||||
(builder.get_object("summary_torrent_status"), str, ("message",)),
|
||||
(builder.get_object("summary_tracker"), None, ("tracker_host",)),
|
||||
]
|
||||
|
||||
self.status_keys = [status for widget in self.label_widgets for status in widget[2]]
|
||||
|
||||
def update(self):
|
||||
# Get the first selected torrent
|
||||
selected = component.get("TorrentView").get_selected_torrents()
|
||||
|
||||
# Only use the first torrent in the list or return if None selected
|
||||
if len(selected) != 0:
|
||||
if selected:
|
||||
selected = selected[0]
|
||||
else:
|
||||
# No torrent is selected in the torrentview
|
||||
self.clear()
|
||||
return
|
||||
|
||||
# Get the torrent status
|
||||
status_keys = [
|
||||
"distributed_copies", "all_time_download", "total_payload_download",
|
||||
"total_uploaded", "total_payload_upload", "download_payload_rate", "max_download_speed",
|
||||
"upload_payload_rate", "max_upload_speed", "num_peers", "num_seeds", "total_peers",
|
||||
"total_seeds", "eta", "ratio", "tracker_status", "next_announce", "active_time",
|
||||
"seeding_time", "seed_rank", "is_auto_managed", "progress", "last_seen_complete",
|
||||
"message", "tracker_host"
|
||||
]
|
||||
status_keys = self.status_keys
|
||||
if self.config['show_piecesbar']:
|
||||
status_keys.extend(["pieces", "state", "num_pieces"])
|
||||
status_keys = self.status_keys + ["pieces", "num_pieces"]
|
||||
|
||||
component.get("SessionProxy").get_torrent_status(
|
||||
selected, status_keys).addCallback(self._on_get_torrent_status)
|
||||
@ -145,33 +139,14 @@ class StatusTab(Tab):
|
||||
if status is None:
|
||||
return
|
||||
|
||||
if status["is_auto_managed"]:
|
||||
status["is_auto_managed"] = _("On")
|
||||
else:
|
||||
status["is_auto_managed"] = _("Off")
|
||||
|
||||
translate_tracker_status = {
|
||||
"Error": _("Error"),
|
||||
"Warning": _("Warning"),
|
||||
"Announce OK": _("Announce OK"),
|
||||
"Announce Sent": _("Announce Sent")
|
||||
}
|
||||
for key, value in translate_tracker_status.iteritems():
|
||||
if key in status["tracker_status"]:
|
||||
status["tracker_status"] = status["tracker_status"].replace(key, value, 1)
|
||||
break
|
||||
|
||||
# Update all the label widgets
|
||||
for widget in self.label_widgets:
|
||||
if widget[1] is not None:
|
||||
args = []
|
||||
try:
|
||||
for key in widget[2]:
|
||||
args.append(status[key])
|
||||
except Exception, e:
|
||||
log.debug("Unable to get status value: %s", e)
|
||||
args = [status[key] for key in widget[2]]
|
||||
except KeyError, ex:
|
||||
log.debug("Unable to get status value: %s", ex)
|
||||
continue
|
||||
|
||||
txt = widget[1](*args)
|
||||
else:
|
||||
txt = status[widget[2][0]]
|
||||
@ -202,7 +177,7 @@ class StatusTab(Tab):
|
||||
if show:
|
||||
self.piecesbar = PiecesBar()
|
||||
self.builder.get_object("status_progress_vbox").pack_start(
|
||||
self.piecesbar, False, False, 5
|
||||
self.piecesbar, False, False, 0
|
||||
)
|
||||
self.progressbar.hide()
|
||||
|
||||
|
@ -1,36 +1,10 @@
|
||||
#
|
||||
# torrentdetails.py
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007 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.
|
||||
#
|
||||
# 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.
|
||||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
|
||||
@ -45,6 +19,7 @@ from deluge.ui.gtkui.common import save_pickled_state_file, load_pickled_state_f
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Tab:
|
||||
def __init__(self):
|
||||
self.is_visible = True
|
||||
@ -69,6 +44,7 @@ class Tab:
|
||||
|
||||
return self._tab_label
|
||||
|
||||
|
||||
class TorrentDetails(component.Component):
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "TorrentDetails", interval=2)
|
||||
@ -91,13 +67,15 @@ class TorrentDetails(component.Component):
|
||||
from files_tab import FilesTab
|
||||
from peers_tab import PeersTab
|
||||
from options_tab import OptionsTab
|
||||
from trackers_tab import TrackersTab
|
||||
|
||||
default_tabs = {
|
||||
"Status": StatusTab,
|
||||
"Details": DetailsTab,
|
||||
"Files": FilesTab,
|
||||
"Peers": PeersTab,
|
||||
"Options": OptionsTab
|
||||
"Options": OptionsTab,
|
||||
"Trackers": TrackersTab
|
||||
}
|
||||
|
||||
# tab_name, visible
|
||||
@ -106,16 +84,18 @@ class TorrentDetails(component.Component):
|
||||
("Details", True),
|
||||
("Files", True),
|
||||
("Peers", True),
|
||||
("Options", True)
|
||||
("Options", True),
|
||||
("Trackers", True)
|
||||
]
|
||||
|
||||
self.translate_tabs = {
|
||||
"All" : _("_All"),
|
||||
"Status" : _("_Status"),
|
||||
"Details" : _("_Details"),
|
||||
"Files" : _("_Files"),
|
||||
"Peers" : _("_Peers"),
|
||||
"Options" : _("_Options")
|
||||
"All": _("_All"),
|
||||
"Status": _("_Status"),
|
||||
"Details": _("_Details"),
|
||||
"Files": _("_Files"),
|
||||
"Peers": _("_Peers"),
|
||||
"Options": _("_Options"),
|
||||
"Trackers": _("_Trackers")
|
||||
}
|
||||
|
||||
# Get the state from saved file
|
||||
@ -129,13 +109,12 @@ class TorrentDetails(component.Component):
|
||||
break
|
||||
|
||||
# The state is a list of tab_names in the order they should appear
|
||||
if state == None:
|
||||
if state is None:
|
||||
# Set the default order
|
||||
state = default_order
|
||||
|
||||
# We need to rename the tab in the state for backwards compat
|
||||
self.state = [(tab_name.replace("Statistics", "Status"), visible) for
|
||||
tab_name, visible in state]
|
||||
self.state = [(tab_name.replace("Statistics", "Status"), visible) for tab_name, visible in state]
|
||||
|
||||
for tab in default_tabs.itervalues():
|
||||
self.add_tab(tab(), generate_menu=False)
|
||||
@ -162,7 +141,6 @@ class TorrentDetails(component.Component):
|
||||
break
|
||||
return position
|
||||
|
||||
|
||||
def add_tab(self, tab, generate_menu=True, visible=None):
|
||||
name = tab.get_name()
|
||||
|
||||
@ -208,10 +186,8 @@ class TorrentDetails(component.Component):
|
||||
if generate_menu:
|
||||
self.generate_menu()
|
||||
|
||||
|
||||
def regenerate_positions(self):
|
||||
"""This will sync up the positions in the tab, with the position stored
|
||||
in the tab object"""
|
||||
"""Sync the positions in the tab, with the position stored in the tab object"""
|
||||
for tab in self.tabs:
|
||||
page_num = self.notebook.page_num(self.tabs[tab]._child_widget)
|
||||
if page_num > -1:
|
||||
@ -262,8 +238,7 @@ class TorrentDetails(component.Component):
|
||||
|
||||
def show_tab(self, tab_name, generate_menu=True):
|
||||
log.debug("%s\n%s\n%s", self.tabs[tab_name].get_child_widget(),
|
||||
self.tabs[tab_name].get_tab_label(),
|
||||
self.tabs[tab_name].position)
|
||||
self.tabs[tab_name].get_tab_label(), self.tabs[tab_name].position)
|
||||
|
||||
position = self.tab_insert_position(self.tabs[tab_name].weight)
|
||||
|
||||
@ -344,7 +319,6 @@ class TorrentDetails(component.Component):
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def shutdown(self):
|
||||
# Save the state of the tabs
|
||||
for tab in self.tabs:
|
||||
@ -362,7 +336,7 @@ class TorrentDetails(component.Component):
|
||||
self.clear()
|
||||
|
||||
if self.notebook.get_property("visible"):
|
||||
if page_num == None:
|
||||
if page_num is None:
|
||||
page_num = self.notebook.get_current_page()
|
||||
try:
|
||||
# Get the tab name
|
||||
|
96
deluge/ui/gtkui/trackers_tab.py
Normal file
96
deluge/ui/gtkui/trackers_tab.py
Normal file
@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2008 Andrew Resch <andrewresch@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.
|
||||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.common import ftime
|
||||
from deluge.ui.gtkui.torrentdetails import Tab
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def fcount(value):
|
||||
return "%s" % len(value)
|
||||
|
||||
|
||||
def ftranslate(text):
|
||||
if text:
|
||||
text = _(text)
|
||||
return text
|
||||
|
||||
|
||||
class TrackersTab(Tab):
|
||||
def __init__(self):
|
||||
Tab.__init__(self)
|
||||
# Get the labels we need to update.
|
||||
# widget name, modifier function, status keys
|
||||
builder = component.get("MainWindow").get_builder()
|
||||
|
||||
self._name = "Trackers"
|
||||
self._child_widget = builder.get_object("trackers_tab")
|
||||
self._tab_label = builder.get_object("trackers_tab_label")
|
||||
|
||||
self.label_widgets = [
|
||||
(builder.get_object("summary_next_announce"), ftime, ("next_announce",)),
|
||||
(builder.get_object("summary_tracker"), None, ("tracker_host",)),
|
||||
(builder.get_object("summary_tracker_status"), ftranslate, ("tracker_status",)),
|
||||
(builder.get_object("summary_tracker_total"), fcount, ("trackers",)),
|
||||
]
|
||||
|
||||
self.status_keys = [status for widget in self.label_widgets for status in widget[2]]
|
||||
|
||||
component.get("MainWindow").connect_signals({
|
||||
"on_button_edit_trackers_clicked": self._on_button_edit_trackers_clicked,
|
||||
})
|
||||
|
||||
def update(self):
|
||||
# Get the first selected torrent
|
||||
selected = component.get("TorrentView").get_selected_torrents()
|
||||
|
||||
# Only use the first torrent in the list or return if None selected
|
||||
if selected:
|
||||
selected = selected[0]
|
||||
else:
|
||||
self.clear()
|
||||
return
|
||||
|
||||
session = component.get("SessionProxy")
|
||||
session.get_torrent_status(selected, self.status_keys).addCallback(self._on_get_torrent_status)
|
||||
|
||||
def _on_get_torrent_status(self, status):
|
||||
# Check to see if we got valid data from the core
|
||||
if not status:
|
||||
return
|
||||
|
||||
# Update all the label widgets
|
||||
for widget in self.label_widgets:
|
||||
if widget[1] is None:
|
||||
txt = status[widget[2][0]]
|
||||
else:
|
||||
try:
|
||||
args = [status[key] for key in widget[2]]
|
||||
except KeyError, ex:
|
||||
log.debug("Unable to get status value: %s", ex)
|
||||
continue
|
||||
txt = widget[1](*args)
|
||||
|
||||
if widget[0].get_text() != txt:
|
||||
widget[0].set_text(txt)
|
||||
|
||||
def clear(self):
|
||||
for widget in self.label_widgets:
|
||||
widget[0].set_text("")
|
||||
|
||||
def _on_button_edit_trackers_clicked(self, button):
|
||||
torrent_id = component.get("TorrentView").get_selected_torrent()
|
||||
if torrent_id:
|
||||
from edittrackersdialog import EditTrackersDialog
|
||||
dialog = EditTrackersDialog(torrent_id, component.get("MainWindow").window)
|
||||
dialog.run()
|
Loading…
x
Reference in New Issue
Block a user