From 08909f01832775aaabb6193ef79b0f9799c86751 Mon Sep 17 00:00:00 2001 From: Martin Baeuml Date: Tue, 14 Jun 2011 19:02:47 +0200 Subject: [PATCH] handle subcommands in labeltool object directly --- sloth/bin/sloth | 3 +- sloth/core/labeltool.py | 173 ++++++++++++++++++++++++++++++++-------- 2 files changed, 140 insertions(+), 36 deletions(-) diff --git a/sloth/bin/sloth b/sloth/bin/sloth index 8b1e022..7af8cf6 100755 --- a/sloth/bin/sloth +++ b/sloth/bin/sloth @@ -11,7 +11,8 @@ if __name__ == '__main__': app.setOrganizationDomain(ORGANIZATION_DOMAIN) app.setApplicationName(APP_NAME) - labeltool = LabelTool(sys.argv[1:]) + labeltool = LabelTool() + labeltool.execute_from_commandline(sys.argv) wnd = MainWindow(labeltool) wnd.show() diff --git a/sloth/core/labeltool.py b/sloth/core/labeltool.py index c470278..1780460 100755 --- a/sloth/core/labeltool.py +++ b/sloth/core/labeltool.py @@ -1,4 +1,10 @@ #!/usr/bin/python +""" +This is the core labeltool module. + + +""" + import sys, os import fnmatch from optparse import OptionParser @@ -7,53 +13,161 @@ from PyQt4.QtCore import * from sloth.annotations.model import * from sloth.annotations.container import AnnotationContainerFactory, AnnotationContainer from sloth.conf import config +from sloth.core.cli import LaxOptionParser, BaseCommand from sloth.core.utils import import_callable from sloth import VERSION +from sloth.core.commands import get_commands import okapy.videoio as okv class LabelTool(QObject): + """ + This is the main label tool object. It stores the state of the tool, i.e. + the current annotations, the containers responsible for loading and saving + etc. + + It is also responsible for parsing command line options, call respective + commands or start the gui. + """ + usage = "\n" + \ + " %prog [options] [filename]\n\n" + \ + " %prog subcommand [options] [args]\n" + + help_text = "Sloth can be started in two different ways. If the first argument\n" + \ + "is any of the following subcommands, this command is executed. Otherwise the\n" + \ + "sloth GUI is started and the optionally given label file is loaded.\n" + \ + "\n" + \ + "Type '%s help ' for help on a specific subcommand.\n\n" + # Signals statusMessage = pyqtSignal(QString) annotationsLoaded = pyqtSignal() pluginLoaded = pyqtSignal(QAction) currentIndexChanged = pyqtSignal(QModelIndex) - def __init__(self, argv, parent=None): + # TODO clean up --> prefix all members with _ + def __init__(self, parent=None): + """ + Constructor. Does nothing except resetting everything. + Initialize the labeltool with either:: + + execute_from_commandline() + + or:: + + init_from_config() + """ QObject.__init__(self, parent) - # Parse command line options - options, args = self.parseCommandLineOptions(argv) + self.container_factory_ = None + self.container_ = AnnotationContainer() + self.current_index_ = None + self._model = None + def main_help_text(self): + """ + Returns the labeltool's main help text, as a string. + + Includes a list of all available subcommands. + """ + usage = self.help_text % self.prog_name + usage += 'Available subcommands:\n' + commands = get_commands().keys() + commands.sort() + for cmd in commands: + usage += ' %s\n' % cmd + return usage + + def execute_from_commandline(self, argv=None): + """ + TODO + """ + self.argv = argv or sys.argv[:] + self.prog_name = os.path.basename(argv[0]) + + # Preprocess options to extract --settings and --pythonpath. + # These options could affect the commands that are available, so they + # must be processed early. + parser = LaxOptionParser(usage=self.usage, + version=VERSION, + option_list=BaseCommand.option_list) + try: + options, args = parser.parse_args(self.argv) + handle_default_options(options) + except: + pass # Ignore any option errors at this point. + + # Handle options common for all commands + # and initialize the labeltool object from + # the configuration (default config if not specified) + if options.pythonpath: + sys.path.insert(0, options.pythonpath) + self.init_from_config(options.config) + + # check for commands + try: + subcommand = self.argv[1] + except IndexError: + subcommand = None + + # handle commands + if subcommand == 'help' or subcommand == '-h' or subcommand == '--help': + if len(args) > 2: + self.fetch_command(args[2]).print_help(self.prog_name, args[2]) + sys.exit(0) + else: + sys.stdout.write(self.main_help_text() + '\n') + parser.print_lax_help() + sys.exit(1) + + elif subcommand in get_commands(): + self.fetch_command(subcommand).run_from_argv(self.argv) + sys.exit(0) + + else: + # no command is matching, then -- if not empty -- + # args must contain a labelfile filename to load + if len(args) > 1: + self.loadAnnotations(args[1]) + else: + self.clearAnnotations() + + def fetch_command(self, subcommand): + """ + Tries to fetch the given subcommand, printing a message with the + appropriate command called from the command line if it can't be found. + """ + try: + app_name = get_commands()[subcommand] + except KeyError: + sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \ + (subcommand, self.prog_name)) + sys.exit(1) + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + # TODO implement load_command_class + klass = load_command_class(app_name, subcommand) + return klass + + def init_from_config(self, config_module_path=""): + """ + Initializes the labeltool from the given configuration + at ``config_module_path``. If empty, the default configuration + is used. + """ # Load config - if options.config != "": - config.update(options.config) + if config_module_path: + config.update(config_module_path) # Instatiate container factory self.container_factory_ = AnnotationContainerFactory(config.CONTAINERS) - self.container_ = AnnotationContainer() - self.current_index_ = None - - # Load annotation file - if len(args) > 0: - self.loadInitialFile(args[0]) - else: - self.loadInitialFile() # Load plugins self.loadPlugins(config.PLUGINS) - def parseCommandLineOptions(self, argv): - usage = "Usage: %prog [-c config.py] [annotation_file]" - version = "%prog " + VERSION - - parser = OptionParser(usage=usage, version=version) - parser.add_option("-c", "--config", action="store", type="string", default="", help="Configuration file.") - - return parser.parse_args(argv) - def loadPlugins(self, plugins): - # TODO clean up, make configurable self.plugins_ = [] for plugin in plugins: if type(plugin) == str: @@ -80,6 +194,7 @@ class LabelTool(QObject): self._model.setBasedir("") except Exception, e: msg = "Error: Loading failed (%s)" % str(e) + self.statusMessage.emit(msg) self.annotationsLoaded.emit() @@ -105,18 +220,6 @@ class LabelTool(QObject): self.statusMessage.emit(msg) return success - def loadInitialFile(self, fname=None): - if fname is not None: - if QFile.exists(fname): - self.loadAnnotations(fname) - else: - settings = QSettings() - fname = settings.value("LastFile").toString() - if (not fname.isEmpty()) and QFile.exists(fname): - self.loadAnnotations(fname) - else: - self.clearAnnotations() - def clearAnnotations(self): self.container_.clear() self._model = AnnotationModel(self.container_.annotations())