add torrentdetails state, allow state switching, move some formating to format_utils
This commit is contained in:
parent
007dd67ea1
commit
5d46d2aee5
|
@ -48,6 +48,7 @@ import deluge.component as component
|
|||
from deluge.ui.client import client
|
||||
import deluge.common
|
||||
from deluge.ui.coreconfig import CoreConfig
|
||||
from deluge.ui.sessionproxy import SessionProxy
|
||||
from deluge.ui.console.statusbars import StatusBars
|
||||
from deluge.ui.console.eventlog import EventLog
|
||||
import screen
|
||||
|
@ -156,7 +157,10 @@ class ConsoleUI(component.Component):
|
|||
|
||||
log.debug("Using encoding: %s", self.encoding)
|
||||
# Load all the commands
|
||||
self._commands = load_commands(os.path.join(UI_PATH, 'commands'))
|
||||
#self._commands = load_commands(os.path.join(UI_PATH, 'commands'))
|
||||
|
||||
# start up the session proxy
|
||||
self.sessionproxy = SessionProxy()
|
||||
|
||||
client.set_disconnect_callback(self.on_client_disconnect)
|
||||
|
||||
|
@ -213,9 +217,9 @@ class ConsoleUI(component.Component):
|
|||
# We want to do an interactive session, so start up the curses screen and
|
||||
# pass it the function that handles commands
|
||||
colors.init_colors()
|
||||
self.statusbars = StatusBars()
|
||||
from modes.alltorrents import AllTorrents
|
||||
self.screen = AllTorrents(stdscr, self.coreconfig, self.encoding)
|
||||
self.statusbars = StatusBars()
|
||||
self.eventlog = EventLog()
|
||||
|
||||
self.screen.topbar = "{!status!}Deluge " + deluge.common.get_version() + " Console"
|
||||
|
@ -264,6 +268,12 @@ class ConsoleUI(component.Component):
|
|||
if not batch and self.interactive:
|
||||
self.screen.refresh()
|
||||
|
||||
def set_mode(self, mode):
|
||||
reactor.removeReader(self.screen)
|
||||
self.screen = mode
|
||||
self.statusbars.screen = self.screen
|
||||
reactor.addReader(self.screen)
|
||||
|
||||
def write(self, line):
|
||||
"""
|
||||
Writes a line out depending on if we're in interactive mode or not.
|
||||
|
|
|
@ -46,7 +46,9 @@ from deluge.ui.sessionproxy import SessionProxy
|
|||
from popup import Popup,SelectablePopup,MessagePopup
|
||||
from add_util import add_torrent
|
||||
from input_popup import InputPopup
|
||||
from torrentdetail import TorrentDetail
|
||||
|
||||
import format_utils
|
||||
|
||||
try:
|
||||
import curses
|
||||
|
@ -146,7 +148,7 @@ class StateUpdater(component.Component):
|
|||
self._status_cb(state,refresh)
|
||||
|
||||
|
||||
class AllTorrents(BaseMode, component.Component):
|
||||
class AllTorrents(BaseMode):
|
||||
def __init__(self, stdscr, coreconfig, encoding=None):
|
||||
self.formatted_rows = None
|
||||
self.cursel = 1
|
||||
|
@ -166,7 +168,6 @@ class AllTorrents(BaseMode, component.Component):
|
|||
BaseMode.__init__(self, stdscr, encoding)
|
||||
curses.curs_set(0)
|
||||
self.stdscr.notimeout(0)
|
||||
self.sessionproxy = SessionProxy()
|
||||
|
||||
self._status_fields = ["queue","name","total_wanted","state","progress","num_seeds","total_seeds",
|
||||
"num_peers","total_peers","download_payload_rate", "upload_payload_rate"]
|
||||
|
@ -180,21 +181,21 @@ class AllTorrents(BaseMode, component.Component):
|
|||
self._info_fields = [
|
||||
("Name",None,("name",)),
|
||||
("State", None, ("state",)),
|
||||
("Down Speed", self._format_speed, ("download_payload_rate",)),
|
||||
("Up Speed", self._format_speed, ("upload_payload_rate",)),
|
||||
("Progress", self._format_progress, ("progress",)),
|
||||
("Down Speed", format_utils.format_speed, ("download_payload_rate",)),
|
||||
("Up Speed", format_utils.format_speed, ("upload_payload_rate",)),
|
||||
("Progress", format_utils.format_progress, ("progress",)),
|
||||
("ETA", deluge.common.ftime, ("eta",)),
|
||||
("Path", None, ("save_path",)),
|
||||
("Downloaded",deluge.common.fsize,("all_time_download",)),
|
||||
("Uploaded", deluge.common.fsize,("total_uploaded",)),
|
||||
("Share Ratio", lambda x:x < 0 and "∞" or "%.3f"%x, ("ratio",)),
|
||||
("Seeders",self._format_seeds_peers,("num_seeds","total_seeds")),
|
||||
("Peers",self._format_seeds_peers,("num_peers","total_peers")),
|
||||
("Seeders",format_utils.format_seeds_peers,("num_seeds","total_seeds")),
|
||||
("Peers",format_utils.format_seeds_peers,("num_peers","total_peers")),
|
||||
("Active Time",deluge.common.ftime,("active_time",)),
|
||||
("Seeding Time",deluge.common.ftime,("seeding_time",)),
|
||||
("Date Added",deluge.common.fdate,("time_added",)),
|
||||
("Availability", lambda x:x < 0 and "∞" or "%.3f"%x, ("distributed_copies",)),
|
||||
("Pieces", self._format_pieces, ("num_pieces","piece_length")),
|
||||
("Pieces", format_utils.format_pieces, ("num_pieces","piece_length")),
|
||||
]
|
||||
|
||||
self._status_keys = ["name","state","download_payload_rate","upload_payload_rate",
|
||||
|
@ -203,6 +204,11 @@ class AllTorrents(BaseMode, component.Component):
|
|||
"seeding_time","time_added","distributed_copies", "num_pieces",
|
||||
"piece_length","save_path"]
|
||||
|
||||
def resume(self):
|
||||
component.start(["AllTorrentsStateUpdater"])
|
||||
self.refresh()
|
||||
|
||||
|
||||
def _update_columns(self):
|
||||
self.column_widths = [5,-1,15,13,10,10,10,15,15]
|
||||
req = sum(filter(lambda x:x >= 0,self.column_widths))
|
||||
|
@ -220,18 +226,6 @@ class AllTorrents(BaseMode, component.Component):
|
|||
|
||||
self.column_string = "{!header!}%s"%("".join(["%s%s"%(self.column_names[i]," "*(self.column_widths[i]-len(self.column_names[i]))) for i in range(0,len(self.column_names))]))
|
||||
|
||||
def _trim_string(self, string, w):
|
||||
return "%s... "%(string[0:w-4])
|
||||
|
||||
def _format_column(self, col, lim):
|
||||
size = len(col)
|
||||
if (size >= lim - 1):
|
||||
return self._trim_string(col,lim)
|
||||
else:
|
||||
return "%s%s"%(col," "*(lim-size))
|
||||
|
||||
def _format_row(self, row):
|
||||
return "".join([self._format_column(row[i],self.column_widths[i]) for i in range(0,len(row))])
|
||||
|
||||
def set_state(self, state, refresh):
|
||||
self.curstate = state # cache in case we change sort order
|
||||
|
@ -239,16 +233,16 @@ class AllTorrents(BaseMode, component.Component):
|
|||
self._sorted_ids = self._sort_torrents(self.curstate)
|
||||
for torrent_id in self._sorted_ids:
|
||||
ts = self.curstate[torrent_id]
|
||||
newrows.append((self._format_row([self._format_queue(ts["queue"]),
|
||||
ts["name"],
|
||||
"%s"%deluge.common.fsize(ts["total_wanted"]),
|
||||
ts["state"],
|
||||
self._format_progress(ts["progress"]),
|
||||
self._format_seeds_peers(ts["num_seeds"],ts["total_seeds"]),
|
||||
self._format_seeds_peers(ts["num_peers"],ts["total_peers"]),
|
||||
self._format_speed(ts["download_payload_rate"]),
|
||||
self._format_speed(ts["upload_payload_rate"])
|
||||
]),ts["state"]))
|
||||
newrows.append((format_utils.format_row([self._format_queue(ts["queue"]),
|
||||
ts["name"],
|
||||
"%s"%deluge.common.fsize(ts["total_wanted"]),
|
||||
ts["state"],
|
||||
format_utils.format_progress(ts["progress"]),
|
||||
format_utils.format_seeds_peers(ts["num_seeds"],ts["total_seeds"]),
|
||||
format_utils.format_seeds_peers(ts["num_peers"],ts["total_peers"]),
|
||||
format_utils.format_speed(ts["download_payload_rate"]),
|
||||
format_utils.format_speed(ts["upload_payload_rate"])
|
||||
],self.column_widths),ts["state"]))
|
||||
self.numtorrents = len(state)
|
||||
self.formatted_rows = newrows
|
||||
if refresh:
|
||||
|
@ -328,27 +322,12 @@ class AllTorrents(BaseMode, component.Component):
|
|||
"sorts by queue #"
|
||||
return sorted(state,cmp=self._queue_sort,key=lambda s:state.get(s)["queue"])
|
||||
|
||||
def _format_speed(self, speed):
|
||||
if (speed > 0):
|
||||
return deluge.common.fspeed(speed)
|
||||
else:
|
||||
return "-"
|
||||
|
||||
def _format_queue(self, qnum):
|
||||
if (qnum >= 0):
|
||||
return "%d"%(qnum+1)
|
||||
else:
|
||||
return ""
|
||||
|
||||
def _format_seeds_peers(self, num, total):
|
||||
return "%d (%d)"%(num,total)
|
||||
|
||||
def _format_pieces(self, num, size):
|
||||
return "%d (%s)"%(num,deluge.common.fsize(size))
|
||||
|
||||
def _format_progress(self, perc):
|
||||
return "%.2f%%"%perc
|
||||
|
||||
def _action_error(self, error):
|
||||
rerr = error.value
|
||||
self.report_message("An Error Occurred","%s got error %s: %s"%(rerr.method,rerr.exception_type,rerr.exception_msg))
|
||||
|
@ -475,6 +454,9 @@ class AllTorrents(BaseMode, component.Component):
|
|||
self.messages.append((title,message))
|
||||
|
||||
def refresh(self,lines=None):
|
||||
#log.error("ref")
|
||||
#import traceback
|
||||
#traceback.print_stack()
|
||||
# Something has requested we scroll to the top of the list
|
||||
if self._go_top:
|
||||
self.cursel = 1
|
||||
|
@ -491,12 +473,12 @@ class AllTorrents(BaseMode, component.Component):
|
|||
|
||||
# Update the status bars
|
||||
if self._curr_filter == None:
|
||||
self.add_string(0,self.topbar)
|
||||
self.add_string(0,self.statusbars.topbar)
|
||||
else:
|
||||
self.add_string(0,"%s {!filterstatus!}Current filter: %s"%(self.topbar,self._curr_filter))
|
||||
self.add_string(0,"%s {!filterstatus!}Current filter: %s"%(self.statusbars.topbar,self._curr_filter))
|
||||
self.add_string(1,self.column_string)
|
||||
hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.bottombar) - 10))
|
||||
self.add_string(self.rows - 1, "%s%s"%(self.bottombar,hstr))
|
||||
hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.statusbars.bottombar) - 10))
|
||||
self.add_string(self.rows - 1, "%s%s"%(self.statusbars.bottombar,hstr))
|
||||
|
||||
# add all the torrents
|
||||
if self.formatted_rows == []:
|
||||
|
@ -621,6 +603,15 @@ class AllTorrents(BaseMode, component.Component):
|
|||
elif c == curses.KEY_NPAGE:
|
||||
self._scroll_down(int(self.rows/2))
|
||||
|
||||
elif c == curses.KEY_RIGHT:
|
||||
# We enter a new mode for the selected torrent here
|
||||
if not self.marked:
|
||||
component.stop(["AllTorrentsStateUpdater"])
|
||||
self.stdscr.clear()
|
||||
td = TorrentDetail(self,self._current_torrent_id(),self.stdscr,self.encoding)
|
||||
component.get("ConsoleUI").set_mode(td)
|
||||
return
|
||||
|
||||
# Enter Key
|
||||
elif c == curses.KEY_ENTER or c == 10:
|
||||
self.marked.append(self.cursel)
|
||||
|
|
|
@ -43,6 +43,7 @@ try:
|
|||
except ImportError:
|
||||
pass
|
||||
|
||||
import deluge.component as component
|
||||
import deluge.ui.console.colors as colors
|
||||
try:
|
||||
import signal
|
||||
|
@ -98,8 +99,7 @@ class BaseMode(CursesStdIO):
|
|||
self.stdscr.nodelay(1)
|
||||
|
||||
# Strings for the 2 status bars
|
||||
self.topbar = ""
|
||||
self.bottombar = ""
|
||||
self.statusbars = component.get("StatusBars")
|
||||
|
||||
# Keep track of the screen size
|
||||
self.rows, self.cols = self.stdscr.getmaxyx()
|
||||
|
@ -182,6 +182,10 @@ class BaseMode(CursesStdIO):
|
|||
screen.addstr(row, col, s, color)
|
||||
col += len(s)
|
||||
|
||||
def draw_statusbars(self):
|
||||
self.add_string(0, self.statusbars.topbar)
|
||||
self.add_string(self.rows - 1, self.statusbars.bottombar)
|
||||
|
||||
def refresh(self):
|
||||
"""
|
||||
Refreshes the screen.
|
||||
|
@ -189,10 +193,9 @@ class BaseMode(CursesStdIO):
|
|||
attribute and the status bars.
|
||||
"""
|
||||
self.stdscr.clear()
|
||||
|
||||
self.draw_statusbars()
|
||||
# Update the status bars
|
||||
self.add_string(0, self.topbar)
|
||||
self.add_string(self.rows - 1, self.bottombar)
|
||||
|
||||
self.add_string(1,"{!info!}Base Mode (or subclass hasn't overridden refresh)")
|
||||
|
||||
self.stdscr.redrawwin()
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# format_utils.py
|
||||
#
|
||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||
#
|
||||
# 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
|
||||
|
||||
def format_speed(speed):
|
||||
if (speed > 0):
|
||||
return deluge.common.fspeed(speed)
|
||||
else:
|
||||
return "-"
|
||||
|
||||
def format_seeds_peers(num, total):
|
||||
return "%d (%d)"%(num,total)
|
||||
|
||||
def format_progress(perc):
|
||||
return "%.2f%%"%perc
|
||||
|
||||
def format_pieces(num, size):
|
||||
return "%d (%s)"%(num,deluge.common.fsize(size))
|
||||
|
||||
def format_priority(prio):
|
||||
pstring = deluge.common.FILE_PRIORITY[prio]
|
||||
if prio > 0:
|
||||
return pstring[:pstring.index("Priority")-1]
|
||||
else:
|
||||
return pstring
|
||||
|
||||
def trim_string(string, w):
|
||||
return "%s... "%(string[0:w-4])
|
||||
|
||||
def format_column(col, lim):
|
||||
size = len(col)
|
||||
if (size >= lim - 1):
|
||||
return trim_string(col,lim)
|
||||
else:
|
||||
return "%s%s"%(col," "*(lim-size))
|
||||
|
||||
def format_row(row,column_widths):
|
||||
return "".join([format_column(row[i],column_widths[i]) for i in range(0,len(row))])
|
|
@ -0,0 +1,357 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# torrentdetail.py
|
||||
#
|
||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||
#
|
||||
# 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.component as component
|
||||
from basemode import BaseMode
|
||||
import deluge.common
|
||||
from deluge.ui.client import client
|
||||
|
||||
from sys import maxint
|
||||
|
||||
from deluge.ui.sessionproxy import SessionProxy
|
||||
|
||||
from popup import Popup,SelectablePopup,MessagePopup
|
||||
from add_util import add_torrent
|
||||
from input_popup import InputPopup
|
||||
import format_utils
|
||||
|
||||
|
||||
try:
|
||||
import curses
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TorrentDetail(BaseMode, component.Component):
|
||||
def __init__(self, alltorrentmode, torrentid, stdscr, encoding=None):
|
||||
self.alltorrentmode = alltorrentmode
|
||||
self.torrentid = torrentid
|
||||
self.torrent_state = None
|
||||
self._status_keys = ["files", "name","state","download_payload_rate","upload_payload_rate",
|
||||
"progress","eta","all_time_download","total_uploaded", "ratio",
|
||||
"num_seeds","total_seeds","num_peers","total_peers", "active_time",
|
||||
"seeding_time","time_added","distributed_copies", "num_pieces",
|
||||
"piece_length","save_path","file_progress","file_priorities"]
|
||||
self._info_fields = [
|
||||
("Name",None,("name",)),
|
||||
("State", None, ("state",)),
|
||||
("Down Speed", format_utils.format_speed, ("download_payload_rate",)),
|
||||
("Up Speed", format_utils.format_speed, ("upload_payload_rate",)),
|
||||
("Progress", format_utils.format_progress, ("progress",)),
|
||||
("ETA", deluge.common.ftime, ("eta",)),
|
||||
("Path", None, ("save_path",)),
|
||||
("Downloaded",deluge.common.fsize,("all_time_download",)),
|
||||
("Uploaded", deluge.common.fsize,("total_uploaded",)),
|
||||
("Share Ratio", lambda x:x < 0 and "∞" or "%.3f"%x, ("ratio",)),
|
||||
("Seeders",format_utils.format_seeds_peers,("num_seeds","total_seeds")),
|
||||
("Peers",format_utils.format_seeds_peers,("num_peers","total_peers")),
|
||||
("Active Time",deluge.common.ftime,("active_time",)),
|
||||
("Seeding Time",deluge.common.ftime,("seeding_time",)),
|
||||
("Date Added",deluge.common.fdate,("time_added",)),
|
||||
("Availability", lambda x:x < 0 and "∞" or "%.3f"%x, ("distributed_copies",)),
|
||||
("Pieces", format_utils.format_pieces, ("num_pieces","piece_length")),
|
||||
]
|
||||
self.file_list = None
|
||||
self.current_file = None
|
||||
self.current_file_idx = 0
|
||||
self.file_limit = maxint
|
||||
self.file_off = 0
|
||||
self.more_to_draw = False
|
||||
|
||||
self.column_string = ""
|
||||
|
||||
BaseMode.__init__(self, stdscr, encoding)
|
||||
component.Component.__init__(self, "TorrentDetail", 1, depend=["SessionProxy"])
|
||||
|
||||
self.column_names = ["Filename", "Size", "Progress", "Priority"]
|
||||
self._update_columns()
|
||||
|
||||
component.start(["TorrentDetail"])
|
||||
curses.curs_set(0)
|
||||
self.stdscr.notimeout(0)
|
||||
|
||||
# component start/update
|
||||
def start(self):
|
||||
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
|
||||
def update(self):
|
||||
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
|
||||
|
||||
def set_state(self, state):
|
||||
log.debug("got state")
|
||||
if not self.file_list:
|
||||
# don't keep getting the files once we've got them once
|
||||
self.file_list,self.file_dict = self.build_file_list(state["files"],state["file_progress"],state["file_priorities"])
|
||||
self._status_keys.remove("files")
|
||||
self._fill_progress(self.file_list,state["file_progress"])
|
||||
for i,prio in enumerate(state["file_priorities"]):
|
||||
self.file_dict[i][6] = format_utils.format_priority(prio)
|
||||
del state["file_progress"]
|
||||
del state["file_priorities"]
|
||||
self.torrent_state = state
|
||||
self.refresh()
|
||||
|
||||
# split file list into directory tree. this function assumes all files in a
|
||||
# particular directory are returned together. it won't work otherwise.
|
||||
# returned list is a list of lists of the form:
|
||||
# [file/dir_name,index,size,children,expanded,progress,priority]
|
||||
# for directories index will be -1, for files the value returned in the
|
||||
# state object for use with other libtorrent calls (i.e. setting prio)
|
||||
#
|
||||
# Also returns a dictionary that maps index values to the file leaves
|
||||
# for fast updating of progress and priorities
|
||||
def build_file_list(self, file_tuples,prog,prio):
|
||||
ret = []
|
||||
retdict = {}
|
||||
for f in file_tuples:
|
||||
cur = ret
|
||||
ps = f["path"].split("/")
|
||||
fin = ps[-1]
|
||||
for p in ps:
|
||||
if not cur or p != cur[-1][0]:
|
||||
cl = []
|
||||
if p == fin:
|
||||
ent = [p,f["index"],f["size"],cl,False,
|
||||
format_utils.format_progress(prog[f["index"]]*100),
|
||||
format_utils.format_priority(prio[f["index"]])]
|
||||
retdict[f["index"]] = ent
|
||||
else:
|
||||
ent = [p,-1,-1,cl,False,"-","-"]
|
||||
cur.append(ent)
|
||||
cur = cl
|
||||
else:
|
||||
cur = cur[-1][3]
|
||||
self._build_sizes(ret)
|
||||
self._fill_progress(ret,prog)
|
||||
return (ret,retdict)
|
||||
|
||||
# fill in the sizes of the directory entries based on their children
|
||||
def _build_sizes(self, fs):
|
||||
ret = 0
|
||||
for f in fs:
|
||||
if f[2] == -1:
|
||||
val = self._build_sizes(f[3])
|
||||
ret += val
|
||||
f[2] = val
|
||||
else:
|
||||
ret += f[2]
|
||||
return ret
|
||||
|
||||
# fills in progress fields in all entries based on progs
|
||||
# returns the # of bytes complete in all the children of fs
|
||||
def _fill_progress(self,fs,progs):
|
||||
tb = 0
|
||||
for f in fs:
|
||||
if f[3]: # dir, has some children
|
||||
bd = self._fill_progress(f[3],progs)
|
||||
f[5] = format_utils.format_progress((bd/f[2])*100)
|
||||
else: # file, update own prog and add to total
|
||||
bd = f[2]*progs[f[1]]
|
||||
f[5] = format_utils.format_progress(progs[f[1]]*100)
|
||||
tb += bd
|
||||
return tb
|
||||
|
||||
def _update_columns(self):
|
||||
self.column_widths = [-1,15,15,20]
|
||||
req = sum(filter(lambda x:x >= 0,self.column_widths))
|
||||
if (req > self.cols): # can't satisfy requests, just spread out evenly
|
||||
cw = int(self.cols/len(self.column_names))
|
||||
for i in range(0,len(self.column_widths)):
|
||||
self.column_widths[i] = cw
|
||||
else:
|
||||
rem = self.cols - req
|
||||
var_cols = len(filter(lambda x: x < 0,self.column_widths))
|
||||
vw = int(rem/var_cols)
|
||||
for i in range(0, len(self.column_widths)):
|
||||
if (self.column_widths[i] < 0):
|
||||
self.column_widths[i] = vw
|
||||
|
||||
self.column_string = "{!header!}%s"%("".join(["%s%s"%(self.column_names[i]," "*(self.column_widths[i]-len(self.column_names[i]))) for i in range(0,len(self.column_names))]))
|
||||
|
||||
|
||||
def draw_files(self,files,depth,off,idx):
|
||||
for fl in files:
|
||||
# kick out if we're going to draw too low on the screen
|
||||
if (off >= self.rows-1):
|
||||
self.more_to_draw = True
|
||||
return -1,-1
|
||||
|
||||
self.file_limit = idx
|
||||
|
||||
if idx >= self.file_off:
|
||||
# set fg/bg colors based on if we are selected or not
|
||||
if idx == self.current_file_idx:
|
||||
self.current_file = fl
|
||||
fc = "{!black,white!}"
|
||||
else:
|
||||
fc = "{!white,black!}"
|
||||
|
||||
#actually draw the dir/file string
|
||||
if fl[3] and fl[4]: # this is an expanded directory
|
||||
xchar = 'v'
|
||||
elif fl[3]: # collapsed directory
|
||||
xchar = '>'
|
||||
else: # file
|
||||
xchar = '-'
|
||||
|
||||
r = format_utils.format_row(["%s%s %s"%(" "*depth,xchar,fl[0]),
|
||||
deluge.common.fsize(fl[2]),fl[5],fl[6]],
|
||||
self.column_widths)
|
||||
|
||||
self.add_string(off,"%s%s"%(fc,r),trim=False)
|
||||
off += 1
|
||||
|
||||
if fl[3] and fl[4]:
|
||||
# recurse if we have children and are expanded
|
||||
off,idx = self.draw_files(fl[3],depth+1,off,idx+1)
|
||||
if off < 0: return (off,idx)
|
||||
else:
|
||||
idx += 1
|
||||
|
||||
return (off,idx)
|
||||
|
||||
def refresh(self,lines=None):
|
||||
# Update the status bars
|
||||
self.stdscr.clear()
|
||||
self.add_string(0,self.statusbars.topbar)
|
||||
hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.statusbars.bottombar) - 10))
|
||||
self.add_string(self.rows - 1, "%s%s"%(self.statusbars.bottombar,hstr))
|
||||
|
||||
self.stdscr.hline((self.rows/2)-1,0,"_",self.cols)
|
||||
|
||||
off = 1
|
||||
if self.torrent_state:
|
||||
for f in self._info_fields:
|
||||
if off >= (self.rows/2): break
|
||||
if f[1] != None:
|
||||
args = []
|
||||
try:
|
||||
for key in f[2]:
|
||||
args.append(self.torrent_state[key])
|
||||
except:
|
||||
log.debug("Could not get info field: %s",e)
|
||||
continue
|
||||
info = f[1](*args)
|
||||
else:
|
||||
info = self.torrent_state[f[2][0]]
|
||||
|
||||
self.add_string(off,"{!info!}%s: {!input!}%s"%(f[0],info))
|
||||
off += 1
|
||||
else:
|
||||
self.add_string(1, "Waiting for torrent state")
|
||||
|
||||
off = self.rows/2
|
||||
self.add_string(off,self.column_string)
|
||||
if self.file_list:
|
||||
off += 1
|
||||
self.more_to_draw = False
|
||||
self.draw_files(self.file_list,0,off,0)
|
||||
|
||||
#self.stdscr.redrawwin()
|
||||
self.stdscr.noutrefresh()
|
||||
|
||||
curses.doupdate()
|
||||
|
||||
# expand or collapse the current file
|
||||
def expcol_cur_file(self):
|
||||
self.current_file[4] = not self.current_file[4]
|
||||
self.refresh()
|
||||
|
||||
def file_list_down(self):
|
||||
if (self.current_file_idx + 1) > self.file_limit:
|
||||
if self.more_to_draw:
|
||||
self.current_file_idx += 1
|
||||
self.file_off += 1
|
||||
else:
|
||||
return
|
||||
else:
|
||||
self.current_file_idx += 1
|
||||
|
||||
self.refresh()
|
||||
|
||||
def file_list_up(self):
|
||||
self.current_file_idx = max(0,self.current_file_idx-1)
|
||||
self.file_off = min(self.file_off,self.current_file_idx)
|
||||
self.refresh()
|
||||
|
||||
def back_to_overview(self):
|
||||
component.stop(["TorrentDetail"])
|
||||
component.deregister("TorrentDetail")
|
||||
self.stdscr.clear()
|
||||
component.get("ConsoleUI").set_mode(self.alltorrentmode)
|
||||
self.alltorrentmode.resume()
|
||||
|
||||
def _doRead(self):
|
||||
c = self.stdscr.getch()
|
||||
|
||||
if c > 31 and c < 256:
|
||||
if chr(c) == 'Q':
|
||||
from twisted.internet import reactor
|
||||
if client.connected():
|
||||
def on_disconnect(result):
|
||||
reactor.stop()
|
||||
client.disconnect().addCallback(on_disconnect)
|
||||
else:
|
||||
reactor.stop()
|
||||
return
|
||||
elif chr(c) == 'q':
|
||||
self.back_to_overview()
|
||||
return
|
||||
|
||||
if c == 27:
|
||||
self.back_to_overview()
|
||||
return
|
||||
|
||||
# Navigate the torrent list
|
||||
if c == curses.KEY_UP:
|
||||
self.file_list_up()
|
||||
elif c == curses.KEY_PPAGE:
|
||||
pass
|
||||
elif c == curses.KEY_DOWN:
|
||||
self.file_list_down()
|
||||
elif c == curses.KEY_NPAGE:
|
||||
pass
|
||||
# Enter Key
|
||||
elif c == curses.KEY_ENTER or c == 10:
|
||||
pass
|
||||
|
||||
# space
|
||||
elif c == 32:
|
||||
self.expcol_cur_file()
|
||||
|
||||
self.refresh()
|
|
@ -41,7 +41,6 @@ class StatusBars(component.Component):
|
|||
def __init__(self):
|
||||
component.Component.__init__(self, "StatusBars", 2, depend=["CoreConfig"])
|
||||
self.config = component.get("CoreConfig")
|
||||
self.screen = component.get("ConsoleUI").screen
|
||||
|
||||
# Hold some values we get from the core
|
||||
self.connections = 0
|
||||
|
@ -49,6 +48,10 @@ class StatusBars(component.Component):
|
|||
self.upload = ""
|
||||
self.dht = 0
|
||||
|
||||
# Default values
|
||||
self.topbar = "{!status!}Deluge %s Console - " % deluge.common.get_version()
|
||||
self.bottombar = "{!status!}C: %s" % self.connections
|
||||
|
||||
def start(self):
|
||||
self.update()
|
||||
|
||||
|
@ -77,30 +80,28 @@ class StatusBars(component.Component):
|
|||
|
||||
def update_statusbars(self):
|
||||
# Update the topbar string
|
||||
self.screen.topbar = "{!status!}Deluge %s Console - " % deluge.common.get_version()
|
||||
self.topbar = "{!status!}Deluge %s Console - " % deluge.common.get_version()
|
||||
if client.connected():
|
||||
info = client.connection_info()
|
||||
self.screen.topbar += "%s@%s:%s" % (info[2], info[0], info[1])
|
||||
self.topbar += "%s@%s:%s" % (info[2], info[0], info[1])
|
||||
else:
|
||||
self.screen.topbar += "Not Connected"
|
||||
self.topbar += "Not Connected"
|
||||
|
||||
# Update the bottombar string
|
||||
self.screen.bottombar = "{!status!}C: %s" % self.connections
|
||||
self.bottombar = "{!status!}C: %s" % self.connections
|
||||
|
||||
if self.config["max_connections_global"] > -1:
|
||||
self.screen.bottombar += " (%s)" % self.config["max_connections_global"]
|
||||
self.bottombar += " (%s)" % self.config["max_connections_global"]
|
||||
|
||||
self.screen.bottombar += " D: %s/s" % self.download
|
||||
self.bottombar += " D: %s/s" % self.download
|
||||
|
||||
if self.config["max_download_speed"] > -1:
|
||||
self.screen.bottombar += " (%s KiB/s)" % self.config["max_download_speed"]
|
||||
self.bottombar += " (%s KiB/s)" % self.config["max_download_speed"]
|
||||
|
||||
self.screen.bottombar += " U: %s/s" % self.upload
|
||||
self.bottombar += " U: %s/s" % self.upload
|
||||
|
||||
if self.config["max_upload_speed"] > -1:
|
||||
self.screen.bottombar += " (%s KiB/s)" % self.config["max_upload_speed"]
|
||||
self.bottombar += " (%s KiB/s)" % self.config["max_upload_speed"]
|
||||
|
||||
if self.config["dht"]:
|
||||
self.screen.bottombar += " DHT: %s" % self.dht
|
||||
|
||||
self.screen.refresh()
|
||||
self.bottombar += " DHT: %s" % self.dht
|
||||
|
|
Loading…
Reference in New Issue