mirror of
https://github.com/codex-storage/deluge.git
synced 2025-01-28 04:04:47 +00:00
Add some missing files
This commit is contained in:
parent
2542745c16
commit
99e981dbf8
267
deluge/ui/console/screen.py
Normal file
267
deluge/ui/console/screen.py
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
#
|
||||||
|
# screen.py
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
import curses
|
||||||
|
import colors
|
||||||
|
from deluge.log import LOG as log
|
||||||
|
from twisted.internet import reactor
|
||||||
|
|
||||||
|
class CursesStdIO(object):
|
||||||
|
"""fake fd to be registered as a reader with the twisted reactor.
|
||||||
|
Curses classes needing input should extend this"""
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
""" We want to select on FD 0 """
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def doRead(self):
|
||||||
|
"""called when input is ready"""
|
||||||
|
pass
|
||||||
|
def logPrefix(self): return 'CursesClient'
|
||||||
|
|
||||||
|
LINES_BUFFER_SIZE = 5000
|
||||||
|
INPUT_HISTORY_SIZE = 500
|
||||||
|
|
||||||
|
class Screen(CursesStdIO):
|
||||||
|
def __init__(self, stdscr, command_parser):
|
||||||
|
"""
|
||||||
|
A curses screen designed to run as a reader in a twisted reactor.
|
||||||
|
|
||||||
|
:param command_parser: a function that will be passed a string when the
|
||||||
|
user hits enter
|
||||||
|
"""
|
||||||
|
log.debug("Screen init!")
|
||||||
|
# Function to be called with commands
|
||||||
|
self.command_parser = command_parser
|
||||||
|
self.stdscr = stdscr
|
||||||
|
# Make the input calls non-blocking
|
||||||
|
self.stdscr.nodelay(1)
|
||||||
|
|
||||||
|
# Holds the user input and is cleared on 'enter'
|
||||||
|
self.input = ""
|
||||||
|
self.input_incomplete = ""
|
||||||
|
# Keep track of where the cursor is
|
||||||
|
self.input_cursor = 0
|
||||||
|
# Keep a history of inputs
|
||||||
|
self.input_history = []
|
||||||
|
self.input_history_index = 0
|
||||||
|
|
||||||
|
# Strings for the 2 status bars
|
||||||
|
self.topbar = ""
|
||||||
|
self.bottombar = ""
|
||||||
|
|
||||||
|
self.rows, self.cols = self.stdscr.getmaxyx()
|
||||||
|
|
||||||
|
# A list of strings to be displayed based on the offset (scroll)
|
||||||
|
self.lines = []
|
||||||
|
# The offset to display lines
|
||||||
|
self.display_lines_offset = 0
|
||||||
|
|
||||||
|
# Create some color pairs, should probably be moved to colors.py
|
||||||
|
# Regular text
|
||||||
|
#curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||||
|
# Status bar
|
||||||
|
#curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLUE)
|
||||||
|
|
||||||
|
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
|
def connectionLost(self, reason):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def add_line(self, text):
|
||||||
|
"""
|
||||||
|
Add a line to the screen. This will be showed between the two bars.
|
||||||
|
The text can be formatted with color using the following format:
|
||||||
|
|
||||||
|
"{{fg, bg, attributes, ...}}"
|
||||||
|
|
||||||
|
See: http://docs.python.org/library/curses.html#constants for attributes.
|
||||||
|
|
||||||
|
Alternatively, it can use some built-in scheme for coloring.
|
||||||
|
See colors.py for built-in schemes.
|
||||||
|
|
||||||
|
"{{scheme}}"
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
"{{blue, black, bold}}My Text is {{white, black}}cool"
|
||||||
|
"{{info}}I am some info text!"
|
||||||
|
"{{error}}Uh oh!"
|
||||||
|
|
||||||
|
:param text: str, the text to show
|
||||||
|
"""
|
||||||
|
log.debug("adding line: %s", text)
|
||||||
|
|
||||||
|
self.lines.extend(text.splitlines())
|
||||||
|
while len(self.lines) > LINES_BUFFER_SIZE:
|
||||||
|
# Remove the oldest line if the max buffer size has been reached
|
||||||
|
self.lines.remove(0)
|
||||||
|
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
|
def add_string(self, row, string):
|
||||||
|
"""
|
||||||
|
Adds a string to the desired `:param:row`.
|
||||||
|
|
||||||
|
:param row: int, the row number to write the string
|
||||||
|
|
||||||
|
"""
|
||||||
|
col = 0
|
||||||
|
parsed = colors.parse_color_string(string)
|
||||||
|
for index, (color, s) in enumerate(parsed):
|
||||||
|
if index + 1 == len(parsed):
|
||||||
|
# This is the last string so lets append some " " to it
|
||||||
|
s += " " * (self.cols - (col + len(s)) - 1)
|
||||||
|
self.stdscr.addstr(row, col, s, color)
|
||||||
|
col += len(s)
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
"""
|
||||||
|
Refreshes the screen.
|
||||||
|
Updates the lines based on the`:attr:lines` based on the `:attr:display_lines_offset`
|
||||||
|
attribute and the status bars.
|
||||||
|
"""
|
||||||
|
self.stdscr.clear()
|
||||||
|
|
||||||
|
# Update the status bars
|
||||||
|
self.add_string(0, self.topbar)
|
||||||
|
self.add_string(self.rows - 2, self.bottombar)
|
||||||
|
|
||||||
|
# The number of rows minus the status bars and the input line
|
||||||
|
available_lines = self.rows - 3
|
||||||
|
# If the amount of lines exceeds the number of rows, we need to figure out
|
||||||
|
# which ones to display based on the offset
|
||||||
|
if len(self.lines) > available_lines:
|
||||||
|
# Get the lines to display based on the offset
|
||||||
|
offset = len(self.lines) - self.display_lines_offset
|
||||||
|
lines = self.lines[-(available_lines - offset):offset]
|
||||||
|
elif len(self.lines) == available_lines:
|
||||||
|
lines = self.lines
|
||||||
|
else:
|
||||||
|
lines = [""] * (available_lines - len(self.lines))
|
||||||
|
lines.extend(self.lines)
|
||||||
|
|
||||||
|
# Add the lines to the screen
|
||||||
|
for index, line in enumerate(lines):
|
||||||
|
self.add_string(index + 1, line)
|
||||||
|
|
||||||
|
# Move the cursor
|
||||||
|
self.stdscr.move(self.rows - 1, self.input_cursor)
|
||||||
|
self.stdscr.refresh()
|
||||||
|
|
||||||
|
def doRead(self):
|
||||||
|
"""
|
||||||
|
Called when there is data to be read, ie, input from the keyboard.
|
||||||
|
"""
|
||||||
|
# We wrap this function to catch exceptions and shutdown the mainloop
|
||||||
|
try:
|
||||||
|
self._doRead()
|
||||||
|
except Exception, e:
|
||||||
|
log.exception(e)
|
||||||
|
reactor.stop()
|
||||||
|
|
||||||
|
def _doRead(self):
|
||||||
|
# Read the character
|
||||||
|
c = self.stdscr.getch()
|
||||||
|
|
||||||
|
# We clear the input string and send it to the command parser on ENTER
|
||||||
|
if c == curses.KEY_ENTER or c == 10:
|
||||||
|
if self.input:
|
||||||
|
self.add_line(">>> " + self.input)
|
||||||
|
self.command_parser(self.input)
|
||||||
|
if len(self.input_history) == INPUT_HISTORY_SIZE:
|
||||||
|
# Remove the oldest input history if the max history size
|
||||||
|
# is reached.
|
||||||
|
self.input_history.remove(0)
|
||||||
|
self.input_history.append(self.input)
|
||||||
|
self.input_history_index = len(self.input_history)
|
||||||
|
self.input = ""
|
||||||
|
self.input_incomplete = ""
|
||||||
|
self.input_cursor = 0
|
||||||
|
self.stdscr.refresh()
|
||||||
|
|
||||||
|
# We use the UP and DOWN keys to cycle through input history
|
||||||
|
elif c == curses.KEY_UP:
|
||||||
|
if self.input_history_index - 1 >= 0:
|
||||||
|
if self.input_history_index == len(self.input_history):
|
||||||
|
# We're moving from non-complete input so save it just incase
|
||||||
|
# we move back down to it.
|
||||||
|
self.input_incomplete = self.input
|
||||||
|
# Going back in the history
|
||||||
|
self.input_history_index -= 1
|
||||||
|
self.input = self.input_history[self.input_history_index]
|
||||||
|
elif c == curses.KEY_DOWN:
|
||||||
|
if self.input_history_index + 1 < len(self.input_history):
|
||||||
|
# Going forward in the history
|
||||||
|
self.input_history_index += 1
|
||||||
|
self.input = self.input_history[self.input_history_index]
|
||||||
|
elif self.input_history_index + 1 == len(self.input_history):
|
||||||
|
# We're moving back down to an incomplete input
|
||||||
|
self.input_history_index += 1
|
||||||
|
self.input = self.input_incomplete
|
||||||
|
|
||||||
|
# Cursor movement
|
||||||
|
elif c == curses.KEY_LEFT:
|
||||||
|
if self.input_cursor:
|
||||||
|
self.input_cursor -= 1
|
||||||
|
elif c == curses.KEY_RIGHT:
|
||||||
|
if self.input_cursor < len(self.input):
|
||||||
|
self.input_cursor += 1
|
||||||
|
elif c == curses.KEY_HOME:
|
||||||
|
self.input_cursor = 0
|
||||||
|
elif c == curses.KEY_END:
|
||||||
|
self.input_cursor = len(self.input)
|
||||||
|
|
||||||
|
# Delete a character in the input string based on cursor position
|
||||||
|
if c == curses.KEY_BACKSPACE or c == 127:
|
||||||
|
if self.input and self.input_cursor > 0:
|
||||||
|
self.input = self.input[:self.input_cursor - 1] + self.input[self.input_cursor:]
|
||||||
|
self.input_cursor -= 1
|
||||||
|
|
||||||
|
# A key to add to the input string
|
||||||
|
else:
|
||||||
|
if c > 31 and c < 127:
|
||||||
|
if self.input_cursor == len(self.input):
|
||||||
|
self.input += chr(c)
|
||||||
|
else:
|
||||||
|
# Insert into string
|
||||||
|
self.input = self.input[:self.input_cursor] + chr(c) + self.input[self.input_cursor:]
|
||||||
|
# Move the cursor forward
|
||||||
|
self.input_cursor += 1
|
||||||
|
|
||||||
|
# Update the input string on the screen
|
||||||
|
#self.stdscr.addstr(self.rows - 1, 0, self.input + " " * (self.cols - len(self.input) - 2), curses.color_pair(1))
|
||||||
|
self.add_string(self.rows - 1, self.input)
|
||||||
|
self.stdscr.move(self.rows - 1, self.input_cursor)
|
||||||
|
self.stdscr.refresh()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""
|
||||||
|
Clean up the curses stuff on exit.
|
||||||
|
"""
|
||||||
|
curses.nocbreak()
|
||||||
|
self.stdscr.keypad(0)
|
||||||
|
curses.echo()
|
||||||
|
curses.endwin()
|
102
deluge/ui/console/statusbars.py
Normal file
102
deluge/ui/console/statusbars.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#
|
||||||
|
# statusbars.py
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
import deluge.component as component
|
||||||
|
import deluge.common
|
||||||
|
from deluge.ui.client import client
|
||||||
|
|
||||||
|
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
|
||||||
|
self.download = ""
|
||||||
|
self.upload = ""
|
||||||
|
self.dht = 0
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
def on_coreconfig_ready(result):
|
||||||
|
self.__core_config_ready = True
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
self.__core_config_ready = False
|
||||||
|
# We need to add a callback to wait for the CoreConfig to be ready
|
||||||
|
self.config.start_defer.addCallback(on_coreconfig_ready)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if not self.__core_config_ready:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.config["dht"]:
|
||||||
|
def on_get_dht_nodes(result):
|
||||||
|
self.dht = result
|
||||||
|
client.core.get_dht_nodes().addCallback(on_get_dht_nodes)
|
||||||
|
|
||||||
|
def on_get_num_connections(result):
|
||||||
|
self.connections = result
|
||||||
|
client.core.get_num_connections().addCallback(on_get_num_connections)
|
||||||
|
|
||||||
|
def on_get_session_status(status):
|
||||||
|
self.upload = deluge.common.fsize(status["payload_upload_rate"])
|
||||||
|
self.download = deluge.common.fsize(status["payload_download_rate"])
|
||||||
|
self.update_statusbars()
|
||||||
|
|
||||||
|
client.core.get_session_status([
|
||||||
|
"payload_upload_rate",
|
||||||
|
"payload_download_rate"]).addCallback(on_get_session_status)
|
||||||
|
|
||||||
|
|
||||||
|
def update_statusbars(self):
|
||||||
|
# Update the topbar string
|
||||||
|
self.screen.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])
|
||||||
|
else:
|
||||||
|
self.screen.topbar += "Not Connected"
|
||||||
|
|
||||||
|
# Update the bottombar string
|
||||||
|
self.screen.bottombar = "{{status}}C: %s" % self.connections
|
||||||
|
|
||||||
|
if self.config["max_connections_global"] > -1:
|
||||||
|
self.screen.bottombar += " (%s)" % self.config["max_connections_global"]
|
||||||
|
|
||||||
|
self.screen.bottombar += " D: %s/s" % self.download
|
||||||
|
|
||||||
|
if self.config["max_download_speed"] > -1:
|
||||||
|
self.screen.bottombar += " (%s/s)" % deluge.common.fsize(self.config["max_download_speed"])
|
||||||
|
|
||||||
|
self.screen.bottombar += " U: %s/s" % self.upload
|
||||||
|
|
||||||
|
if self.config["max_upload_speed"] > -1:
|
||||||
|
self.screen.bottombar += " (%s/s)" % deluge.common.fsize(self.config["max_upload_speed"])
|
||||||
|
|
||||||
|
if self.config["dht"]:
|
||||||
|
self.screen.bottombar += " DHT: %s" % self.dht
|
||||||
|
|
||||||
|
self.screen.refresh()
|
Loading…
x
Reference in New Issue
Block a user