mirror of
https://github.com/codex-storage/deluge.git
synced 2025-02-25 17:45:26 +00:00
[#1974] [UI] Decouple UI selection from core.
Add entry points into setup for each of the UIs and then use this information to determine which client UI to run. This ensures that custom UIs may be written and run without the need to modifify deluge source code.
This commit is contained in:
parent
6343f32d70
commit
aa82efd4f1
@ -20,6 +20,8 @@ import os
|
||||
import sys
|
||||
from logging import FileHandler, getLogger
|
||||
|
||||
import pkg_resources
|
||||
|
||||
import deluge.common
|
||||
import deluge.configmanager
|
||||
import deluge.error
|
||||
@ -38,11 +40,14 @@ def start_ui():
|
||||
parser = CommonOptionParser()
|
||||
group = optparse.OptionGroup(parser, _("Default Options"))
|
||||
|
||||
group.add_option("-u", "--ui", dest="ui",
|
||||
help="""The UI that you wish to launch. The UI choices are:\n
|
||||
\t gtk -- A GTK-based graphical user interface (default)\n
|
||||
\t web -- A web-based interface (http://localhost:8112)\n
|
||||
\t console -- A console or command-line interface""", action="store", type="str")
|
||||
ui_entrypoints = dict([(entrypoint.name, entrypoint.load())
|
||||
for entrypoint in pkg_resources.iter_entry_points('deluge.ui')])
|
||||
|
||||
cmd_help = ['The UI that you wish to launch. The UI choices are:']
|
||||
cmd_help.extend(["%s -- %s" % (k, getattr(v, 'cmdline', "")) for k, v in ui_entrypoints.iteritems()])
|
||||
|
||||
parser.add_option("-u", "--ui", dest="ui",
|
||||
choices=ui_entrypoints.keys(), help="\n\t ".join(cmd_help), action="store")
|
||||
group.add_option("-a", "--args", dest="args",
|
||||
help="Arguments to pass to UI, -a '--option args'", action="store", type="str")
|
||||
group.add_option("-s", "--set-default-ui", dest="default_ui",
|
||||
@ -79,22 +84,11 @@ def start_ui():
|
||||
client_args.extend(args)
|
||||
|
||||
try:
|
||||
if selected_ui == "gtk":
|
||||
log.info("Starting GtkUI..")
|
||||
from deluge.ui.gtkui.gtkui import Gtk
|
||||
ui = Gtk(skip_common=True)
|
||||
ui.start(client_args)
|
||||
elif selected_ui == "web":
|
||||
log.info("Starting WebUI..")
|
||||
from deluge.ui.web.web import Web
|
||||
ui = Web(skip_common=True)
|
||||
ui.start(client_args)
|
||||
elif selected_ui == "console":
|
||||
log.info("Starting ConsoleUI..")
|
||||
from deluge.ui.console.main import Console
|
||||
ui = Console(skip_common=True)
|
||||
ui.start(client_args)
|
||||
except ImportError, e:
|
||||
ui = ui_entrypoints[selected_ui](skip_common=True)
|
||||
except KeyError as ex:
|
||||
log.error("Unable to find the requested UI: '%s'. Please select a different UI with the '-u' option"
|
||||
" or alternatively use the '-s' option to select a different default UI.", selected_ui)
|
||||
except ImportError as ex:
|
||||
import sys
|
||||
import traceback
|
||||
error_type, error_value, tb = sys.exc_info()
|
||||
@ -104,10 +98,12 @@ def start_ui():
|
||||
log.error("Unable to find the requested UI: %s. Please select a different UI with the '-u' "
|
||||
"option or alternatively use the '-s' option to select a different default UI.", selected_ui)
|
||||
else:
|
||||
log.exception(e)
|
||||
log.exception(ex)
|
||||
log.error("There was an error whilst launching the request UI: %s", selected_ui)
|
||||
log.error("Look at the traceback above for more information.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
ui.start(client_args)
|
||||
|
||||
|
||||
def start_daemon(skip_start=False):
|
||||
|
@ -8,6 +8,9 @@
|
||||
#
|
||||
|
||||
UI_PATH = __path__[0]
|
||||
from deluge.ui.console.main import start # NOQA
|
||||
|
||||
assert start # silence pyflakes
|
||||
from deluge.ui.console.console import Console
|
||||
|
||||
|
||||
def start():
|
||||
Console().start()
|
||||
|
104
deluge/ui/console/console.py
Normal file
104
deluge/ui/console/console.py
Normal file
@ -0,0 +1,104 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||
# Copyright (C) 2009 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 optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from deluge.ui.console import UI_PATH
|
||||
from deluge.ui.ui import UI
|
||||
|
||||
|
||||
def load_commands(command_dir, exclude=[]):
|
||||
def get_command(name):
|
||||
return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')()
|
||||
|
||||
try:
|
||||
commands = []
|
||||
for filename in os.listdir(command_dir):
|
||||
if filename.split('.')[0] in exclude or filename.startswith('_'):
|
||||
continue
|
||||
if not (filename.endswith('.py') or filename.endswith('.pyc')):
|
||||
continue
|
||||
cmd = get_command(filename.split('.')[len(filename.split('.')) - 2])
|
||||
aliases = [filename.split('.')[len(filename.split('.')) - 2]]
|
||||
aliases.extend(cmd.aliases)
|
||||
for a in aliases:
|
||||
commands.append((a, cmd))
|
||||
return dict(commands)
|
||||
except OSError:
|
||||
return {}
|
||||
|
||||
|
||||
class Console(UI):
|
||||
|
||||
help = """Starts the Deluge console interface"""
|
||||
cmdline = """A console or command-line interface"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Console, self).__init__("console", *args, **kwargs)
|
||||
group = optparse.OptionGroup(self.parser, "Console Options", "These options control how "
|
||||
"the console connects to the daemon. These options will be "
|
||||
"used if you pass a command, or if you have autoconnect "
|
||||
"enabled for the console ui.")
|
||||
|
||||
group.add_option("-d", "--daemon", dest="daemon_addr",
|
||||
action="store", type="str", default="127.0.0.1",
|
||||
help="Set the address of the daemon to connect to. [default: %default]")
|
||||
group.add_option("-p", "--port", dest="daemon_port",
|
||||
help="Set the port to connect to the daemon on. [default: %default]",
|
||||
action="store", type="int", default=58846)
|
||||
group.add_option("-u", "--username", dest="daemon_user",
|
||||
help="Set the username to connect to the daemon with. [default: %default]",
|
||||
action="store", type="string")
|
||||
group.add_option("-P", "--password", dest="daemon_pass",
|
||||
help="Set the password to connect to the daemon with. [default: %default]",
|
||||
action="store", type="string")
|
||||
self.parser.add_option_group(group)
|
||||
|
||||
self.cmds = load_commands(os.path.join(UI_PATH, 'commands'))
|
||||
|
||||
class CommandOptionGroup(optparse.OptionGroup):
|
||||
def __init__(self, parser, title, description=None, cmds=None):
|
||||
optparse.OptionGroup.__init__(self, parser, title, description)
|
||||
self.cmds = cmds
|
||||
|
||||
def format_help(self, formatter):
|
||||
result = formatter.format_heading(self.title)
|
||||
formatter.indent()
|
||||
if self.description:
|
||||
result += "%s\n" % formatter.format_description(self.description)
|
||||
for cname in self.cmds:
|
||||
cmd = self.cmds[cname]
|
||||
if cmd.interactive_only or cname in cmd.aliases:
|
||||
continue
|
||||
allnames = [cname]
|
||||
allnames.extend(cmd.aliases)
|
||||
cname = "/".join(allnames)
|
||||
result += formatter.format_heading(" - ".join([cname, cmd.__doc__]))
|
||||
formatter.indent()
|
||||
result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage)
|
||||
formatter.dedent()
|
||||
formatter.dedent()
|
||||
return result
|
||||
cmd_group = CommandOptionGroup(self.parser, "Console Commands",
|
||||
description="The following commands can be issued at the "
|
||||
"command line. Commands should be quoted, so, for example, "
|
||||
"to pause torrent with id 'abc' you would run: '%s "
|
||||
"\"pause abc\"'" % os.path.basename(sys.argv[0]),
|
||||
cmds=self.cmds)
|
||||
self.parser.add_option_group(cmd_group)
|
||||
|
||||
def start(self, args=None):
|
||||
from main import ConsoleUI
|
||||
super(Console, self).start(args)
|
||||
ConsoleUI(self.args, self.cmds, (self.options.daemon_addr,
|
||||
self.options.daemon_port, self.options.daemon_user,
|
||||
self.options.daemon_pass))
|
@ -13,7 +13,6 @@ from __future__ import print_function
|
||||
import locale
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
@ -23,75 +22,15 @@ from twisted.internet import defer, reactor
|
||||
import deluge.common
|
||||
import deluge.component as component
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.console import UI_PATH, colors
|
||||
from deluge.ui.console import colors
|
||||
from deluge.ui.console.eventlog import EventLog
|
||||
from deluge.ui.console.statusbars import StatusBars
|
||||
from deluge.ui.coreconfig import CoreConfig
|
||||
from deluge.ui.sessionproxy import SessionProxy
|
||||
from deluge.ui.ui import _UI
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Console(_UI):
|
||||
|
||||
help = """Starts the Deluge console interface"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Console, self).__init__("console", *args, **kwargs)
|
||||
group = optparse.OptionGroup(self.parser, "Console Options", "These daemon connect options will be "
|
||||
"used for commands, or if console ui autoconnect is enabled.")
|
||||
group.add_option("-d", "--daemon", dest="daemon_addr")
|
||||
group.add_option("-p", "--port", dest="daemon_port", type="int")
|
||||
group.add_option("-u", "--username", dest="daemon_user")
|
||||
group.add_option("-P", "--password", dest="daemon_pass")
|
||||
|
||||
self.parser.add_option_group(group)
|
||||
self.parser.disable_interspersed_args()
|
||||
|
||||
self.console_cmds = load_commands(os.path.join(UI_PATH, "commands"))
|
||||
|
||||
class CommandOptionGroup(optparse.OptionGroup):
|
||||
def __init__(self, parser, title, description=None, cmds=None):
|
||||
optparse.OptionGroup.__init__(self, parser, title, description)
|
||||
self.cmds = cmds
|
||||
|
||||
def format_help(self, formatter):
|
||||
result = formatter.format_heading(self.title)
|
||||
formatter.indent()
|
||||
if self.description:
|
||||
result += "%s\n" % formatter.format_description(self.description)
|
||||
for cname in self.cmds:
|
||||
cmd = self.cmds[cname]
|
||||
if cmd.interactive_only or cname in cmd.aliases:
|
||||
continue
|
||||
allnames = [cname]
|
||||
allnames.extend(cmd.aliases)
|
||||
cname = "/".join(allnames)
|
||||
result += formatter.format_heading(" - ".join([cname, cmd.__doc__]))
|
||||
formatter.indent()
|
||||
result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage.split("\n")[0])
|
||||
formatter.dedent()
|
||||
formatter.dedent()
|
||||
return result
|
||||
cmd_group = CommandOptionGroup(self.parser, "Console Commands",
|
||||
description="""These commands can be issued from the command line.
|
||||
They require quoting and multiple commands separated by ';'
|
||||
e.g. Pause torrent with id 'abcd' and get information for id 'efgh':
|
||||
`%s \"pause abcd; info efgh\"`"""
|
||||
% os.path.basename(sys.argv[0]), cmds=self.console_cmds)
|
||||
self.parser.add_option_group(cmd_group)
|
||||
|
||||
def start(self, args=None):
|
||||
super(Console, self).start(args)
|
||||
ConsoleUI(self.args, self.console_cmds, (self.options.daemon_addr, self.options.daemon_port,
|
||||
self.options.daemon_user, self.options.daemon_pass))
|
||||
|
||||
|
||||
def start():
|
||||
Console().start()
|
||||
|
||||
|
||||
class DelugeHelpFormatter(optparse.IndentedHelpFormatter):
|
||||
"""
|
||||
Format help in a way suited to deluge Legacy mode - colors, format, indentation...
|
||||
@ -247,30 +186,6 @@ class BaseCommand(object):
|
||||
return OptionParser(prog=self.name, usage=self.usage, epilog=self.epilog, option_list=self.option_list)
|
||||
|
||||
|
||||
def load_commands(command_dir, exclude=None):
|
||||
if not exclude:
|
||||
exclude = []
|
||||
|
||||
def get_command(name):
|
||||
return getattr(__import__("deluge.ui.console.commands.%s" % name, {}, {}, ["Command"]), "Command")()
|
||||
|
||||
try:
|
||||
commands = []
|
||||
for filename in os.listdir(command_dir):
|
||||
if filename.split(".")[0] in exclude or filename.startswith("_"):
|
||||
continue
|
||||
if not (filename.endswith(".py") or filename.endswith(".pyc")):
|
||||
continue
|
||||
cmd = get_command(filename.split(".")[len(filename.split(".")) - 2])
|
||||
aliases = [filename.split(".")[len(filename.split(".")) - 2]]
|
||||
aliases.extend(cmd.aliases)
|
||||
for a in aliases:
|
||||
commands.append((a, cmd))
|
||||
return dict(commands)
|
||||
except OSError:
|
||||
return {}
|
||||
|
||||
|
||||
class ConsoleUI(component.Component):
|
||||
def __init__(self, args=None, cmds=None, daemon=None):
|
||||
component.Component.__init__(self, "ConsoleUI", 2)
|
||||
|
@ -1,3 +1,5 @@
|
||||
from deluge.ui.gtkui.gtkui import start
|
||||
from deluge.ui.gtkui.gtkui import Gtk
|
||||
|
||||
assert start # silence pyflakes
|
||||
|
||||
def start():
|
||||
Gtk().start()
|
||||
|
@ -52,7 +52,7 @@ from deluge.ui.gtkui.torrentdetails import TorrentDetails
|
||||
from deluge.ui.gtkui.torrentview import TorrentView
|
||||
from deluge.ui.sessionproxy import SessionProxy
|
||||
from deluge.ui.tracker_icons import TrackerIcons
|
||||
from deluge.ui.ui import _UI
|
||||
from deluge.ui.ui import UI
|
||||
|
||||
|
||||
gobject.set_prgname("deluge")
|
||||
@ -69,21 +69,6 @@ except ImportError:
|
||||
return
|
||||
|
||||
|
||||
class Gtk(_UI):
|
||||
|
||||
help = """Starts the Deluge GTK+ interface"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Gtk, self).__init__("gtk", *args, **kwargs)
|
||||
|
||||
def start(self, args=None):
|
||||
super(Gtk, self).start(args)
|
||||
GtkUI(self.args)
|
||||
|
||||
|
||||
def start():
|
||||
Gtk().start()
|
||||
|
||||
DEFAULT_PREFS = {
|
||||
"classic_mode": True,
|
||||
"interactive_add": True,
|
||||
@ -146,6 +131,25 @@ DEFAULT_PREFS = {
|
||||
}
|
||||
|
||||
|
||||
class Gtk(UI):
|
||||
|
||||
help = """Starts the Deluge GTK+ interface"""
|
||||
cmdline = """A GTK-based graphical user interface"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Gtk, self).__init__("gtk", *args, **kwargs)
|
||||
|
||||
group = self.parser.add_argument_group(_('GTK Options'))
|
||||
group.add_argument("torrents", metavar="<torrent>", nargs="*", default=None,
|
||||
help="Add one or more torrent files, torrent URLs or magnet URIs"
|
||||
" to a currently running Deluge GTK instance")
|
||||
|
||||
def start(self, args=None):
|
||||
from gtkui import GtkUI
|
||||
super(Gtk, self).start(args)
|
||||
GtkUI(self.options)
|
||||
|
||||
|
||||
class GtkUI(object):
|
||||
def __init__(self, args):
|
||||
# Setup gtkbuilder/glade translation
|
||||
|
@ -30,7 +30,7 @@ if 'dev' not in deluge.common.get_version():
|
||||
warnings.filterwarnings('ignore', category=DeprecationWarning, module='twisted')
|
||||
|
||||
|
||||
class _UI(object):
|
||||
class UI(object):
|
||||
|
||||
def __init__(self, name="gtk", skip_common=False):
|
||||
self.__name = name
|
||||
|
@ -1,3 +1,5 @@
|
||||
from deluge.ui.web.web import start
|
||||
from deluge.ui.web.web import Web
|
||||
|
||||
assert start # silence pyflakes
|
||||
|
||||
def start():
|
||||
Web().start()
|
||||
|
@ -14,7 +14,7 @@ from optparse import OptionGroup
|
||||
|
||||
from deluge.common import osx_check, windows_check
|
||||
from deluge.configmanager import get_config_dir
|
||||
from deluge.ui.ui import _UI
|
||||
from deluge.ui.ui import UI
|
||||
|
||||
|
||||
class WebUI(object):
|
||||
@ -24,9 +24,10 @@ class WebUI(object):
|
||||
deluge_web.start()
|
||||
|
||||
|
||||
class Web(_UI):
|
||||
class Web(UI):
|
||||
|
||||
help = """Starts the Deluge web interface"""
|
||||
cmdline = """A web-based interface (http://localhost:8112)"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Web, self).__init__("web", *args, **kwargs)
|
||||
|
Loading…
x
Reference in New Issue
Block a user