From a2b1aacc72e2dda67737b824553de1dd3e58dc9e Mon Sep 17 00:00:00 2001 From: Pieter <@> Date: Tue, 3 Jan 2017 19:40:40 +0100 Subject: [PATCH 1/9] Create interactive command for curses interface --- todoman/cli.py | 16 ++ todoman/interactive.py | 603 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 619 insertions(+) create mode 100644 todoman/interactive.py diff --git a/todoman/cli.py b/todoman/cli.py index f2da1e6b..4576b276 100644 --- a/todoman/cli.py +++ b/todoman/cli.py @@ -1,5 +1,6 @@ import functools import glob +import logging from datetime import timedelta from os.path import expanduser, isdir @@ -7,9 +8,14 @@ from . import model from .configuration import ConfigurationException, load_config +from .interactive import TodomanInteractive from .model import Database, FileTodo from .ui import EditState, PorcelainFormatter, TodoEditor, TodoFormatter + +logging.basicConfig() +logger = logging.getLogger() + TODO_ID_MIN = 1 with_id_arg = click.argument('id', type=click.IntRange(min=TODO_ID_MIN)) @@ -188,6 +194,16 @@ def edit(ctx, id, todo_properties, interactive): ctx.exit(1) +@cli.command() +@click.pass_context +def interactive(ctx): + ''' + Provide an interactive, curses-based interface to Todoman. + ''' + TodomanInteractive(ctx.obj['db'], ctx.obj['formatter']) + ctx.exit(1) + + @cli.command() @click.pass_context @with_id_arg diff --git a/todoman/interactive.py b/todoman/interactive.py new file mode 100644 index 00000000..eeac9124 --- /dev/null +++ b/todoman/interactive.py @@ -0,0 +1,603 @@ +import logging + +import urwid + +from .model import Todo +from .ui import EditState, TodoEditor + + +logging.basicConfig() +logger = logging.getLogger() + + +class TodomanItem(urwid.CheckBox): + ''' + Class to contain a single todo item in a ListBox. + ''' + + def __init__(self, todo, database, labelmaker): + ''' + Create a TodomanItem instance based on the filename and the associated + Database that was provided. By providing the filename instead of the + Todo itself, we do not need to check if the provided todo is indeed in + in the given Database. The labelmaker is a function that turns a given + TodomanItem (i.e. self) into a string representation that is suitable + for its context. + + (TodomanItem, str, Database, function) -> None + ''' + self.database = database + if todo: + self.todo = todo + else: + self.todo = Todo() # A new todo is created + self.filename = self.todo.filename + # If the done status changes, save to database + urwid.connect_signal(self, 'change', self.save) + super().__init__(labelmaker(self), self.is_completed) + + @property + def is_completed(self): + ''' + Returns True iff the TodomanItem refers to a completed Todo. + + (TodomanItem) -> bool + ''' + return self.todo.is_completed + + @is_completed.setter + def is_completed(self, status): + ''' + Set the given status as the status of the Todo to which the TodomanItem + refers. + + (TodomanItem, bool) -> None + ''' + self.todo.is_completed = status + self.save() + + def save(self, *args): + ''' + Save the current state of the TodomanItem and the Todo to which it + refers in the associated Database. + + (TodomanItem, *args) -> None + ''' + # Todoman and Urwid have inverted notions of state. According to Urwid, + # the state of something that is done is 'False'. + if self.get_state(): + self.todo.is_completed = False + else: + self.todo.is_completed = True + self.database.save(self.todo) + + @property + def has_priority(self): + ''' + Returns True iff the TodomanItem refers to a Todo with a priority that + is set (i.e. not None and above zero). + + (self) -> bool + ''' + return self.todo.priority not in [None, 0] + + +class TodomanPage(urwid.Frame): + ''' + Abstract class. Inherit from this class and an appropriate Urwid Widget + class to create a new page type. + ''' + + def __init__(self, parent, callback): + ''' + Create an instance of TodomanPage. The parent is the TodomanInteractive + instance in which the page was created. + + Any subclass that calls this method should first set self.body to + the box widget that should be the body of the Frame. + + Callback is the function to call when the page is closed. This function + is responsible for passing information to the underlying TodomanPage. + It is usually a method of the underlying TodomanPage. If callback is + None, use the default. + + (TodomanPage, TodomanInteractive, function) -> None + ''' + self.parent = parent + if callback: + self.callback = callback + header = urwid.AttrMap(urwid.Text(" Todoman"), "statusbar") + statusbar = urwid.AttrMap(urwid.Text(""), "statusbar") + inputline = urwid.Edit() + footer = urwid.Pile([statusbar, inputline]) + super().__init__(self.body, header, footer) + + def open_page(self, page_to_open): + ''' + Open a page over the existing stack of pages. page_to_open is the + TodomanPage object to display. + + (TodomanPage, TodomanPage) -> None + ''' + self.parent._open_page(page_to_open) + + @property + def statusbar(self): + ''' + Returns the current contents of the statusbar of the Todomanpage. + + (TodomanPage) -> str + ''' + return self.footer.contents[0].original_widget.text + + @statusbar.setter + def statusbar(self, text): + ''' + Sets the given text as the current text in the statusbar of the + TodomanPage. + + (self, str) -> None + ''' + self.footer.contents + self.footer.contents[0][0].original_widget.set_text(text) + + def callback(self, **kwargs): + ''' + A default callback function to use when closing the previous page. + This callback function handles several keywords that are generic to + all callbacks. If a certain keyword is not set, the method does + nothing with it. + + This callback function supports the following keywords: + + statusbar (str): A text to set as the statusbar message + + (TodomanPage, **kwargs) -> None + ''' + for key, value in kwargs.items(): + if key == 'statusbar': + self.statusbar = value + self.reload() + + def reload(self): + ''' + Dummy method. TodomanPage subclasses that need a reload should + implement one under this name. + + (TodomanPage) -> None + ''' + return None + + +class TodomanItemListPage(TodomanPage): + ''' + Class to contain a ListBox filled with todo items, based on a given + Database. + ''' + + def __init__(self, parent, callback, database): + ''' + Create an instance of TodomanItemListPage. The parent is the + TodomanInteractive instance in which the page was created. + The database is the Database from which to display items. + + Callback is the function to call when the page is closed. This function + is responsible for passing information to the underlying TodomanPage. + It is usually a method of the underlying TodomanPage. + + (TodomanItemListPage, TodomanInteractive, function, Database) -> None + ''' + self.parent = parent + self.database = database + # By default, we hide the completed items + self.done_is_hidden = True + items = self.items_to_display() + self.body = urwid.ListBox(urwid.SimpleFocusListWalker(items)) + super().__init__(parent, callback) + + def items_to_display(self): + ''' + Create a list of TodomanItems to display, based on the associated + Database and the current done_is_hidden setting. + + (TodomanItemListPage) -> [TodomanItem] + ''' + items = [] + for t in self.database.todos(): + todo = TodomanItem(t, self.database, self.generate_label) + if not self.done_is_hidden or not todo.is_completed: + items.append(todo) + items.sort(key=lambda item: item.label.lower()) + return items + + def callback_move_to(self, **kwargs): + ''' + Move the TodomanItem in focus to Database database. + + (TodomanItemListPage, Database) -> None + ''' + for key, value in kwargs.items(): + if key == "database" and value != self.database: + value.save(self.body.focus.todo) + self.database.delete(self.body.focus.todo) + self.body.body.remove(self.body.focus) + self.statusbar = "Item moved to {0}.".format(value.name) + + def move_database_chooser(self): + ''' + Open a TodomanDatabasesPage from which to choose the destination of the + move operation for the selected item. + + (TodomanItemListPage) -> None + ''' + new_page = TodomanDatabasesPage(self.parent, self.callback_move_to) + self.open_page(new_page) + + def callback_copy_to(self, **kwargs): + ''' + Copy the TodomanItem in focus to Database database. + + (TodomanItemListPage, Database) -> None + ''' + for key, value in kwargs.items(): + if key == "database" and value != self.database: + value.save(self.body.focus.todo) + self.statusbar = "Item copied to {0}.".format(value.name) + + def copy_database_chooser(self): + ''' + Open a TodomanDatabasesPage from which to choose the destination of the + copy operation for the selected item. + + (TodomanItemListPage) -> None + ''' + new_page = TodomanDatabasesPage(self.parent, self.callback_copy_to) + self.open_page(new_page) + + def delete(self, item=None): + ''' + Delete the TodomanItem item from its database. By default, delete the + item in focus. + + (TodomanItemListPage, TodomanItem) -> None + ''' + if item is None: + item = self.body.focus + item.database.delete(item.todo) + self.body.body.remove(item) + + def generate_label(self, item): + ''' + Return a label for a given TodomanItem for display in the + TodomanItemListPage listing. + + (TodomanItemListPage, TodomanItem) -> str + ''' + return "{0} {1}".format( + '!' if item.has_priority else ' ', item.todo.summary + ) + + def keypress(self, size, key): + ''' + Make the different commands in the TodomanItemListPage view work. + + (TodomanItemListPage, int(?), str) -> str + ''' + if key == 'l': + self.list_chooser() + return None + if key == 'm': + self.move_database_chooser() + return None + if key == 'c': + self.copy_database_chooser() + return None + if key == 'd': + self.delete() + return None + if key == 'D': + self.delete_all_done() + return None + if key == 'esc': + self.parent.close_page(self) + return None + if key == 'h': + self.toggle_hide_completed_items() + return None + if key == 'e': + self.item_details() + return None + if key == 'n': + self.new_item() + return None + if key == 'j': + return super().keypress(size, 'down') + if key == 'k': + return super().keypress(size, 'up') + return super().keypress(size, key) + + def list_chooser(self): + ''' + Open a TodomanDatabasesPage from which to choose the + TodomanItemListPage to display. + + (TodomanItemListPage) -> None + ''' + new_page = TodomanDatabasesPage( + self.parent, + self.callback_open_other_database, + ) + self.open_page(new_page) + + def callback_open_other_database(self, **kwargs): + ''' + Callback function for the ListChooser option. Opens the Database that + the user selected in the TodomanDatabasesPage, and passes all arguments + to the default callback for further processing. The new TodomanPage + will be opened over the old one. + + This callback function handles the following keywords, in addition + to the keywords TodomanPage.callback handles: + + * database (Database): the Database to display in the new page. + + (TodomanItemListPage, **kwargs) -> None + ''' + for key, value in kwargs.items(): + if key == "database" and value != self.database: + new_page = TodomanItemListPage(self.parent, None, value) + self.open_page(new_page) + + def item_details(self): + ''' + Open a TodomanItemDetailsPage in which the user can edit the selected + TodomanItem. + + (TodomanItemListPage) -> None + ''' + new_page = TodomanItemDetailsPage( + self.parent, + self.callback, + self.body.focus, + ) + self.open_page(new_page) + + def new_item(self): + ''' + Create a new TodomanItem and open a TodomanItemDetailsPage in which + the user can edit the new item. + + (TodomanItemListPage) -> None + ''' + item = TodomanItem(None, self.database, self.generate_label) + new_page = TodomanItemDetailsPage(self.parent, self.callback, item) + self.open_page(new_page) + + def toggle_hide_completed_items(self): + ''' + Toggle whether completed items are still displayed in the TodomanPage. + + (TodomanItemListPage) -> None + ''' + self.done_is_hidden = not self.done_is_hidden + self.reload() + + def delete_all_done(self): + ''' + Delete all TodomanItems for which the associated Todos are completed. + + (TodomanItemListPage) -> None + ''' + to_delete = [] + for item in self.body.body: + if item.is_completed: + to_delete.append(item) + for item in to_delete: + self.delete(item) + + def reload(self): + ''' + Reload all TodomanItems in the ListBox from the underlying Database. + + (TodomanItemListPage) -> None + ''' + items = self.items_to_display() + self.body = urwid.ListBox(urwid.SimpleFocusListWalker(items)) + super().__init__(self.parent, self.callback) + + +class TodomanItemDetailsPage(TodomanPage): + ''' + Class to contain a TodoEditor filled with all the fields that a given + TodomanItem contains. Allows the user to view and edit all attributes of + the TodomanItem. + ''' + + def __init__(self, parent, callback, item): + ''' + Create an instance of TodomanItemDetailsPage. The parent is the + TodomanInteractive instance in which the page was created. + Item is the TodomanItem of which the details will be shown and + edited. + + Callback is the function to call when the page is closed. This function + is responsible for passing information to the underlying TodomanPage. + It is usually a method of the underlying TodomanPage. + + (TodomanItemDetailsPage, TodomanInteractive, function, TodomanItem) + -> None + ''' + self.parent = parent + self.item = item + self.editor = TodoEditor( + item.todo, + self.parent.databases, + self.parent.formatter, + ) + self.body = self.editor._ui + button = self.body.body.contents[-1].contents[0][0] + # Do not use regular callback, as that exits the MainLoop + urwid.disconnect_signal(button, 'click', self.editor._save) + urwid.connect_signal(button, 'click', self.close_page, True) + # Remove helper text, as the commands do not work the same way + self.body.body.contents[-1].contents[1][0].set_text("") + super().__init__(parent, callback) + + def close_page(self, dummy, should_save): + ''' + Callback for closing the TodomanItemDetailsPage. Will attempt to save + all associated data if shouldSave is True. Will discard all changes + otherwise. + + (TodomanItemDetailsPage, Button, bool) -> None + ''' + if should_save: + try: + self.editor._save_inner() + self.item.save() + except Exception as e: + self.message(('error', str(e))) + else: + self.editor.saved = EditState.saved + self.parent.close_page(self, statusbar="Item saved.") + else: + self.parent.close_page(self, statusbar="Item not saved.") + + def keypress(self, size, key): + ''' + Make the different commands in the TodomanItemDetailsPage view work. + + (TodomanItemDetailsPage, int(?), str) -> str + ''' + if key == 'esc': + self.close_page(None, False) + return None + return super().keypress(size, key) + + +class TodomanDatabasesPage(TodomanPage): + ''' + Class to contain a ListBox filled with all available Databases. The user + can choose a Database from the list, after which the TodomanDatabasesPage + closes again and the chosen Database is passed back to the callback. + ''' + + def __init__(self, parent, callback): + ''' + Create a TodomanDatabasesPage instance. + + Callback is the function to call when the page is closed. This function + is responsible for passing information to the underlying TodomanPage. + It is usually a method of the underlying TodomanPage. + + (TodomanDatabasesPage, TodomanInteractive, function) -> None + ''' + self.parent = parent + buttons = [] + for database in parent.databases: + button = urwid.Button("") + urwid.connect_signal(button, 'click', self.close_page, database) + button._w = urwid.AttrMap(urwid.SelectableIcon( + ["> ", database.name], 2), None, 'selected') + buttons.append(button) + self.body = urwid.ListBox(urwid.SimpleFocusListWalker(buttons)) + super().__init__(parent, callback) + + def close_page(self, button, database): + ''' + Close the current page, returning the selected database to the + underlying page. + + (TodomanDatabasesPage, Button, Database) -> None + ''' + self.parent.close_page(self, database=database) + + def keypress(self, size, key): + ''' + Make the different commands in the TodomanDatabasesPage view work. + + (TodomanDatabasesPage, int(?), str) -> str + ''' + if key == 'esc': + self.parent.close_page(self) + return None + if key == 'j': + return super().keypress(size, 'down') + if key == 'k': + return super().keypress(size, 'up') + return super().keypress(size, key) + + +class TodomanInteractive(object): + ''' + Class to run the interactive, curses-based interface to Todoman. + ''' + + palette = [ + ('statusbar', 'light gray', 'dark blue'), + ('reversed', 'standout', ''), + ('error', 'light red', '') + ] + + def __init__(self, databases, formatter): + ''' + Create a TodomanInteractive instance based on the Database objects that + the regular Todoman cli module passes. + + (TodomanInteractive, [Database], TodoFormatter) -> None + ''' + self.databases = databases + # self.databases.sort(key = lambda db: db.name) + self.formatter = formatter + top = urwid.Filler(urwid.Text("Press q to exit")) + self.loop = urwid.MainLoop( + top, + self.palette, + unhandled_input=self.unhandled_input, + ) + first_page = TodomanItemListPage(self, None, self.databases) + self.pageCounter = 0 + self._open_page(first_page) + + self.loop.run() + + def unhandled_input(self, key): + ''' + Handles all the key presses that are application-wide. + + (TodomanInteractive, str) -> None + ''' + if key in ('q', 'Q'): + raise urwid.ExitMainLoop() + + def _open_page(self, page_to_open): + ''' + Open a page over the existing stack of pages. page_to_open is the + TodomanPage object to display. + + (TodomanInteractive, TodomanPage) -> None + ''' + self.loop.widget = urwid.Overlay( + page_to_open, + self.loop.widget, + 'left', ('relative', 100), + 'middle', ('relative', 100)) + self.pageCounter += 1 + + def close_page(self, page, **kwargs): + ''' + Close the topmost open page, passing the given information to the + TodomanPage below via the callback that was provided when opening + the TodomanPage. + + Usually called from the page to be closed. + + (TodomanInteractive, TodomanPage, **kwargs) -> None + ''' + if self.pageCounter <= 1: + raise urwid.ExitMainLoop() + else: + self.loop.widget = self.loop.widget.contents[0][0] + self.pageCounter -= 1 + if page.callback: + page.callback(**kwargs) From 01d755647085c9c0020720a25cc74cd72dbfd980 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Sat, 18 Feb 2017 13:33:31 -0300 Subject: [PATCH 2/9] squashme: minor cleanups --- todoman/interactive.py | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/todoman/interactive.py b/todoman/interactive.py index eeac9124..70957d64 100644 --- a/todoman/interactive.py +++ b/todoman/interactive.py @@ -17,14 +17,14 @@ class TodomanItem(urwid.CheckBox): def __init__(self, todo, database, labelmaker): ''' - Create a TodomanItem instance based on the filename and the associated - Database that was provided. By providing the filename instead of the - Todo itself, we do not need to check if the provided todo is indeed in - in the given Database. The labelmaker is a function that turns a given - TodomanItem (i.e. self) into a string representation that is suitable - for its context. + Create a TodomanItem instance based on a todo and the associated + Database that was provided. - (TodomanItem, str, Database, function) -> None + :param todoman.model.Todo todo: The todo this entry will represent. + :param todoman.model.Database: The database from which persists tihs + todo. + :param func labelmake: A function that will create the string + representation for this item. ''' self.database = database if todo: @@ -38,21 +38,10 @@ def __init__(self, todo, database, labelmaker): @property def is_completed(self): - ''' - Returns True iff the TodomanItem refers to a completed Todo. - - (TodomanItem) -> bool - ''' return self.todo.is_completed @is_completed.setter def is_completed(self, status): - ''' - Set the given status as the status of the Todo to which the TodomanItem - refers. - - (TodomanItem, bool) -> None - ''' self.todo.is_completed = status self.save() @@ -65,10 +54,7 @@ def save(self, *args): ''' # Todoman and Urwid have inverted notions of state. According to Urwid, # the state of something that is done is 'False'. - if self.get_state(): - self.todo.is_completed = False - else: - self.todo.is_completed = True + self.todo.is_completed = not self.get_state() self.database.save(self.todo) @property From c5524a3cff1ab2b6d6eb8b252c1e294dee5a9a99 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 18 Feb 2017 18:43:22 +0100 Subject: [PATCH 3/9] Mass renaming --- todoman/interactive.py | 196 ++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/todoman/interactive.py b/todoman/interactive.py index 70957d64..36b43598 100644 --- a/todoman/interactive.py +++ b/todoman/interactive.py @@ -10,14 +10,14 @@ logger = logging.getLogger() -class TodomanItem(urwid.CheckBox): +class Item(urwid.CheckBox): ''' Class to contain a single todo item in a ListBox. ''' def __init__(self, todo, database, labelmaker): ''' - Create a TodomanItem instance based on a todo and the associated + Create a Item instance based on a todo and the associated Database that was provided. :param todoman.model.Todo todo: The todo this entry will represent. @@ -47,10 +47,10 @@ def is_completed(self, status): def save(self, *args): ''' - Save the current state of the TodomanItem and the Todo to which it + Save the current state of the Item and the Todo to which it refers in the associated Database. - (TodomanItem, *args) -> None + (Item, *args) -> None ''' # Todoman and Urwid have inverted notions of state. According to Urwid, # the state of something that is done is 'False'. @@ -60,7 +60,7 @@ def save(self, *args): @property def has_priority(self): ''' - Returns True iff the TodomanItem refers to a Todo with a priority that + Returns True iff the Item refers to a Todo with a priority that is set (i.e. not None and above zero). (self) -> bool @@ -68,7 +68,7 @@ def has_priority(self): return self.todo.priority not in [None, 0] -class TodomanPage(urwid.Frame): +class Page(urwid.Frame): ''' Abstract class. Inherit from this class and an appropriate Urwid Widget class to create a new page type. @@ -76,18 +76,18 @@ class to create a new page type. def __init__(self, parent, callback): ''' - Create an instance of TodomanPage. The parent is the TodomanInteractive + Create an instance of Page. The parent is the Main instance in which the page was created. Any subclass that calls this method should first set self.body to the box widget that should be the body of the Frame. Callback is the function to call when the page is closed. This function - is responsible for passing information to the underlying TodomanPage. - It is usually a method of the underlying TodomanPage. If callback is + is responsible for passing information to the underlying Page. + It is usually a method of the underlying Page. If callback is None, use the default. - (TodomanPage, TodomanInteractive, function) -> None + (Page, Main, function) -> None ''' self.parent = parent if callback: @@ -101,9 +101,9 @@ def __init__(self, parent, callback): def open_page(self, page_to_open): ''' Open a page over the existing stack of pages. page_to_open is the - TodomanPage object to display. + Page object to display. - (TodomanPage, TodomanPage) -> None + (Page, Page) -> None ''' self.parent._open_page(page_to_open) @@ -112,7 +112,7 @@ def statusbar(self): ''' Returns the current contents of the statusbar of the Todomanpage. - (TodomanPage) -> str + (Page) -> str ''' return self.footer.contents[0].original_widget.text @@ -120,7 +120,7 @@ def statusbar(self): def statusbar(self, text): ''' Sets the given text as the current text in the statusbar of the - TodomanPage. + Page. (self, str) -> None ''' @@ -138,7 +138,7 @@ def callback(self, **kwargs): statusbar (str): A text to set as the statusbar message - (TodomanPage, **kwargs) -> None + (Page, **kwargs) -> None ''' for key, value in kwargs.items(): if key == 'statusbar': @@ -147,15 +147,15 @@ def callback(self, **kwargs): def reload(self): ''' - Dummy method. TodomanPage subclasses that need a reload should + Dummy method. Page subclasses that need a reload should implement one under this name. - (TodomanPage) -> None + (Page) -> None ''' return None -class TodomanItemListPage(TodomanPage): +class ItemListPage(Page): ''' Class to contain a ListBox filled with todo items, based on a given Database. @@ -163,15 +163,15 @@ class TodomanItemListPage(TodomanPage): def __init__(self, parent, callback, database): ''' - Create an instance of TodomanItemListPage. The parent is the - TodomanInteractive instance in which the page was created. + Create an instance of ItemListPage. The parent is the + Main instance in which the page was created. The database is the Database from which to display items. Callback is the function to call when the page is closed. This function - is responsible for passing information to the underlying TodomanPage. - It is usually a method of the underlying TodomanPage. + is responsible for passing information to the underlying Page. + It is usually a method of the underlying Page. - (TodomanItemListPage, TodomanInteractive, function, Database) -> None + (ItemListPage, Main, function, Database) -> None ''' self.parent = parent self.database = database @@ -183,14 +183,14 @@ def __init__(self, parent, callback, database): def items_to_display(self): ''' - Create a list of TodomanItems to display, based on the associated + Create a list of Items to display, based on the associated Database and the current done_is_hidden setting. - (TodomanItemListPage) -> [TodomanItem] + (ItemListPage) -> [Item] ''' items = [] for t in self.database.todos(): - todo = TodomanItem(t, self.database, self.generate_label) + todo = Item(t, self.database, self.generate_label) if not self.done_is_hidden or not todo.is_completed: items.append(todo) items.sort(key=lambda item: item.label.lower()) @@ -198,9 +198,9 @@ def items_to_display(self): def callback_move_to(self, **kwargs): ''' - Move the TodomanItem in focus to Database database. + Move the Item in focus to Database database. - (TodomanItemListPage, Database) -> None + (ItemListPage, Database) -> None ''' for key, value in kwargs.items(): if key == "database" and value != self.database: @@ -211,19 +211,19 @@ def callback_move_to(self, **kwargs): def move_database_chooser(self): ''' - Open a TodomanDatabasesPage from which to choose the destination of the + Open a ListsPage from which to choose the destination of the move operation for the selected item. - (TodomanItemListPage) -> None + (ItemListPage) -> None ''' - new_page = TodomanDatabasesPage(self.parent, self.callback_move_to) + new_page = ListsPage(self.parent, self.callback_move_to) self.open_page(new_page) def callback_copy_to(self, **kwargs): ''' - Copy the TodomanItem in focus to Database database. + Copy the Item in focus to Database database. - (TodomanItemListPage, Database) -> None + (ItemListPage, Database) -> None ''' for key, value in kwargs.items(): if key == "database" and value != self.database: @@ -232,20 +232,20 @@ def callback_copy_to(self, **kwargs): def copy_database_chooser(self): ''' - Open a TodomanDatabasesPage from which to choose the destination of the + Open a ListsPage from which to choose the destination of the copy operation for the selected item. - (TodomanItemListPage) -> None + (ItemListPage) -> None ''' - new_page = TodomanDatabasesPage(self.parent, self.callback_copy_to) + new_page = ListsPage(self.parent, self.callback_copy_to) self.open_page(new_page) def delete(self, item=None): ''' - Delete the TodomanItem item from its database. By default, delete the + Delete the Item item from its database. By default, delete the item in focus. - (TodomanItemListPage, TodomanItem) -> None + (ItemListPage, Item) -> None ''' if item is None: item = self.body.focus @@ -254,10 +254,10 @@ def delete(self, item=None): def generate_label(self, item): ''' - Return a label for a given TodomanItem for display in the - TodomanItemListPage listing. + Return a label for a given Item for display in the + ItemListPage listing. - (TodomanItemListPage, TodomanItem) -> str + (ItemListPage, Item) -> str ''' return "{0} {1}".format( '!' if item.has_priority else ' ', item.todo.summary @@ -265,9 +265,9 @@ def generate_label(self, item): def keypress(self, size, key): ''' - Make the different commands in the TodomanItemListPage view work. + Make the different commands in the ItemListPage view work. - (TodomanItemListPage, int(?), str) -> str + (ItemListPage, int(?), str) -> str ''' if key == 'l': self.list_chooser() @@ -304,12 +304,12 @@ def keypress(self, size, key): def list_chooser(self): ''' - Open a TodomanDatabasesPage from which to choose the - TodomanItemListPage to display. + Open a ListsPage from which to choose the + ItemListPage to display. - (TodomanItemListPage) -> None + (ItemListPage) -> None ''' - new_page = TodomanDatabasesPage( + new_page = ListsPage( self.parent, self.callback_open_other_database, ) @@ -318,30 +318,30 @@ def list_chooser(self): def callback_open_other_database(self, **kwargs): ''' Callback function for the ListChooser option. Opens the Database that - the user selected in the TodomanDatabasesPage, and passes all arguments - to the default callback for further processing. The new TodomanPage + the user selected in the ListsPage, and passes all arguments + to the default callback for further processing. The new Page will be opened over the old one. This callback function handles the following keywords, in addition - to the keywords TodomanPage.callback handles: + to the keywords Page.callback handles: * database (Database): the Database to display in the new page. - (TodomanItemListPage, **kwargs) -> None + (ItemListPage, **kwargs) -> None ''' for key, value in kwargs.items(): if key == "database" and value != self.database: - new_page = TodomanItemListPage(self.parent, None, value) + new_page = ItemListPage(self.parent, None, value) self.open_page(new_page) def item_details(self): ''' - Open a TodomanItemDetailsPage in which the user can edit the selected - TodomanItem. + Open a ItemDetailsPage in which the user can edit the selected + Item. - (TodomanItemListPage) -> None + (ItemListPage) -> None ''' - new_page = TodomanItemDetailsPage( + new_page = ItemDetailsPage( self.parent, self.callback, self.body.focus, @@ -350,29 +350,29 @@ def item_details(self): def new_item(self): ''' - Create a new TodomanItem and open a TodomanItemDetailsPage in which + Create a new Item and open a ItemDetailsPage in which the user can edit the new item. - (TodomanItemListPage) -> None + (ItemListPage) -> None ''' - item = TodomanItem(None, self.database, self.generate_label) - new_page = TodomanItemDetailsPage(self.parent, self.callback, item) + item = Item(None, self.database, self.generate_label) + new_page = ItemDetailsPage(self.parent, self.callback, item) self.open_page(new_page) def toggle_hide_completed_items(self): ''' - Toggle whether completed items are still displayed in the TodomanPage. + Toggle whether completed items are still displayed in the Page. - (TodomanItemListPage) -> None + (ItemListPage) -> None ''' self.done_is_hidden = not self.done_is_hidden self.reload() def delete_all_done(self): ''' - Delete all TodomanItems for which the associated Todos are completed. + Delete all Items for which the associated Todos are completed. - (TodomanItemListPage) -> None + (ItemListPage) -> None ''' to_delete = [] for item in self.body.body: @@ -383,34 +383,34 @@ def delete_all_done(self): def reload(self): ''' - Reload all TodomanItems in the ListBox from the underlying Database. + Reload all Items in the ListBox from the underlying Database. - (TodomanItemListPage) -> None + (ItemListPage) -> None ''' items = self.items_to_display() self.body = urwid.ListBox(urwid.SimpleFocusListWalker(items)) super().__init__(self.parent, self.callback) -class TodomanItemDetailsPage(TodomanPage): +class ItemDetailsPage(Page): ''' Class to contain a TodoEditor filled with all the fields that a given - TodomanItem contains. Allows the user to view and edit all attributes of - the TodomanItem. + Item contains. Allows the user to view and edit all attributes of + the Item. ''' def __init__(self, parent, callback, item): ''' - Create an instance of TodomanItemDetailsPage. The parent is the - TodomanInteractive instance in which the page was created. - Item is the TodomanItem of which the details will be shown and + Create an instance of ItemDetailsPage. The parent is the + Main instance in which the page was created. + Item is the Item of which the details will be shown and edited. Callback is the function to call when the page is closed. This function - is responsible for passing information to the underlying TodomanPage. - It is usually a method of the underlying TodomanPage. + is responsible for passing information to the underlying Page. + It is usually a method of the underlying Page. - (TodomanItemDetailsPage, TodomanInteractive, function, TodomanItem) + (ItemDetailsPage, Main, function, Item) -> None ''' self.parent = parent @@ -431,11 +431,11 @@ def __init__(self, parent, callback, item): def close_page(self, dummy, should_save): ''' - Callback for closing the TodomanItemDetailsPage. Will attempt to save + Callback for closing the ItemDetailsPage. Will attempt to save all associated data if shouldSave is True. Will discard all changes otherwise. - (TodomanItemDetailsPage, Button, bool) -> None + (ItemDetailsPage, Button, bool) -> None ''' if should_save: try: @@ -451,9 +451,9 @@ def close_page(self, dummy, should_save): def keypress(self, size, key): ''' - Make the different commands in the TodomanItemDetailsPage view work. + Make the different commands in the ItemDetailsPage view work. - (TodomanItemDetailsPage, int(?), str) -> str + (ItemDetailsPage, int(?), str) -> str ''' if key == 'esc': self.close_page(None, False) @@ -461,22 +461,22 @@ def keypress(self, size, key): return super().keypress(size, key) -class TodomanDatabasesPage(TodomanPage): +class ListsPage(Page): ''' Class to contain a ListBox filled with all available Databases. The user - can choose a Database from the list, after which the TodomanDatabasesPage + can choose a Database from the list, after which the ListsPage closes again and the chosen Database is passed back to the callback. ''' def __init__(self, parent, callback): ''' - Create a TodomanDatabasesPage instance. + Create a ListsPage instance. Callback is the function to call when the page is closed. This function - is responsible for passing information to the underlying TodomanPage. - It is usually a method of the underlying TodomanPage. + is responsible for passing information to the underlying Page. + It is usually a method of the underlying Page. - (TodomanDatabasesPage, TodomanInteractive, function) -> None + (ListsPage, Main, function) -> None ''' self.parent = parent buttons = [] @@ -494,15 +494,15 @@ def close_page(self, button, database): Close the current page, returning the selected database to the underlying page. - (TodomanDatabasesPage, Button, Database) -> None + (ListsPage, Button, Database) -> None ''' self.parent.close_page(self, database=database) def keypress(self, size, key): ''' - Make the different commands in the TodomanDatabasesPage view work. + Make the different commands in the ListsPage view work. - (TodomanDatabasesPage, int(?), str) -> str + (ListsPage, int(?), str) -> str ''' if key == 'esc': self.parent.close_page(self) @@ -514,7 +514,7 @@ def keypress(self, size, key): return super().keypress(size, key) -class TodomanInteractive(object): +class Main(object): ''' Class to run the interactive, curses-based interface to Todoman. ''' @@ -527,10 +527,10 @@ class TodomanInteractive(object): def __init__(self, databases, formatter): ''' - Create a TodomanInteractive instance based on the Database objects that + Create a Main instance based on the Database objects that the regular Todoman cli module passes. - (TodomanInteractive, [Database], TodoFormatter) -> None + (Main, [Database], TodoFormatter) -> None ''' self.databases = databases # self.databases.sort(key = lambda db: db.name) @@ -541,7 +541,7 @@ def __init__(self, databases, formatter): self.palette, unhandled_input=self.unhandled_input, ) - first_page = TodomanItemListPage(self, None, self.databases) + first_page = ItemListPage(self, None, self.databases) self.pageCounter = 0 self._open_page(first_page) @@ -551,7 +551,7 @@ def unhandled_input(self, key): ''' Handles all the key presses that are application-wide. - (TodomanInteractive, str) -> None + (Main, str) -> None ''' if key in ('q', 'Q'): raise urwid.ExitMainLoop() @@ -559,9 +559,9 @@ def unhandled_input(self, key): def _open_page(self, page_to_open): ''' Open a page over the existing stack of pages. page_to_open is the - TodomanPage object to display. + Page object to display. - (TodomanInteractive, TodomanPage) -> None + (Main, Page) -> None ''' self.loop.widget = urwid.Overlay( page_to_open, @@ -573,12 +573,12 @@ def _open_page(self, page_to_open): def close_page(self, page, **kwargs): ''' Close the topmost open page, passing the given information to the - TodomanPage below via the callback that was provided when opening - the TodomanPage. + Page below via the callback that was provided when opening + the Page. Usually called from the page to be closed. - (TodomanInteractive, TodomanPage, **kwargs) -> None + (Main, Page, **kwargs) -> None ''' if self.pageCounter <= 1: raise urwid.ExitMainLoop() From ed4dc0131abec519b4fe0ed28905526dd5e03251 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 18 Feb 2017 18:44:24 +0100 Subject: [PATCH 4/9] Fix up docstring types --- todoman/interactive.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/todoman/interactive.py b/todoman/interactive.py index 36b43598..d4284847 100644 --- a/todoman/interactive.py +++ b/todoman/interactive.py @@ -530,7 +530,8 @@ def __init__(self, databases, formatter): Create a Main instance based on the Database objects that the regular Todoman cli module passes. - (Main, [Database], TodoFormatter) -> None + :type databases: list[Database] + :type formatter: TodoFormatter ''' self.databases = databases # self.databases.sort(key = lambda db: db.name) @@ -551,7 +552,7 @@ def unhandled_input(self, key): ''' Handles all the key presses that are application-wide. - (Main, str) -> None + :type key: str ''' if key in ('q', 'Q'): raise urwid.ExitMainLoop() @@ -561,7 +562,7 @@ def _open_page(self, page_to_open): Open a page over the existing stack of pages. page_to_open is the Page object to display. - (Main, Page) -> None + :type page_to_open: Page ''' self.loop.widget = urwid.Overlay( page_to_open, @@ -578,7 +579,7 @@ def close_page(self, page, **kwargs): Usually called from the page to be closed. - (Main, Page, **kwargs) -> None + :type page: Page ''' if self.pageCounter <= 1: raise urwid.ExitMainLoop() From 94f991e878edd68c3f71031ea0bf5ab4364aa2a8 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 18 Feb 2017 18:45:15 +0100 Subject: [PATCH 5/9] Remove superfluous exit --- todoman/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/todoman/cli.py b/todoman/cli.py index 4576b276..aeb14fea 100644 --- a/todoman/cli.py +++ b/todoman/cli.py @@ -201,7 +201,6 @@ def interactive(ctx): Provide an interactive, curses-based interface to Todoman. ''' TodomanInteractive(ctx.obj['db'], ctx.obj['formatter']) - ctx.exit(1) @cli.command() From 123461e0d4e5353f61e37e2f7c3bdfe7b5299f2b Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 18 Feb 2017 18:49:08 +0100 Subject: [PATCH 6/9] More code cleanup --- todoman/interactive.py | 54 ++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/todoman/interactive.py b/todoman/interactive.py index d4284847..7cb3508c 100644 --- a/todoman/interactive.py +++ b/todoman/interactive.py @@ -127,7 +127,7 @@ def statusbar(self, text): self.footer.contents self.footer.contents[0][0].original_widget.set_text(text) - def callback(self, **kwargs): + def callback(self, statusbar): ''' A default callback function to use when closing the previous page. This callback function handles several keywords that are generic to @@ -136,13 +136,9 @@ def callback(self, **kwargs): This callback function supports the following keywords: - statusbar (str): A text to set as the statusbar message - - (Page, **kwargs) -> None + :param statusbar: A text to set as the statusbar message ''' - for key, value in kwargs.items(): - if key == 'statusbar': - self.statusbar = value + self.statusbar = statusbar self.reload() def reload(self): @@ -196,46 +192,38 @@ def items_to_display(self): items.sort(key=lambda item: item.label.lower()) return items - def callback_move_to(self, **kwargs): + def callback_move_to(self, database): ''' Move the Item in focus to Database database. - - (ItemListPage, Database) -> None ''' - for key, value in kwargs.items(): - if key == "database" and value != self.database: - value.save(self.body.focus.todo) - self.database.delete(self.body.focus.todo) - self.body.body.remove(self.body.focus) - self.statusbar = "Item moved to {0}.".format(value.name) + if database != self.database: + database.save(self.body.focus.todo) + self.database.delete(self.body.focus.todo) + self.body.body.remove(self.body.focus) + self.statusbar = "Item moved to {0}.".format(database.name) def move_database_chooser(self): ''' Open a ListsPage from which to choose the destination of the move operation for the selected item. - - (ItemListPage) -> None ''' new_page = ListsPage(self.parent, self.callback_move_to) self.open_page(new_page) - def callback_copy_to(self, **kwargs): + def callback_copy_to(self, database): ''' Copy the Item in focus to Database database. (ItemListPage, Database) -> None ''' - for key, value in kwargs.items(): - if key == "database" and value != self.database: - value.save(self.body.focus.todo) - self.statusbar = "Item copied to {0}.".format(value.name) + if database != self.database: + database.save(self.body.focus.todo) + self.statusbar = "Item copied to {0}.".format(database.name) def copy_database_chooser(self): ''' Open a ListsPage from which to choose the destination of the copy operation for the selected item. - - (ItemListPage) -> None ''' new_page = ListsPage(self.parent, self.callback_copy_to) self.open_page(new_page) @@ -245,7 +233,7 @@ def delete(self, item=None): Delete the Item item from its database. By default, delete the item in focus. - (ItemListPage, Item) -> None + :type item: Item ''' if item is None: item = self.body.focus @@ -315,7 +303,7 @@ def list_chooser(self): ) self.open_page(new_page) - def callback_open_other_database(self, **kwargs): + def callback_open_other_database(self, database): ''' Callback function for the ListChooser option. Opens the Database that the user selected in the ListsPage, and passes all arguments @@ -325,14 +313,12 @@ def callback_open_other_database(self, **kwargs): This callback function handles the following keywords, in addition to the keywords Page.callback handles: - * database (Database): the Database to display in the new page. - - (ItemListPage, **kwargs) -> None + :param database: the Database to display in the new page. + :type database: Database ''' - for key, value in kwargs.items(): - if key == "database" and value != self.database: - new_page = ItemListPage(self.parent, None, value) - self.open_page(new_page) + if database != self.database: + new_page = ItemListPage(self.parent, None, database) + self.open_page(new_page) def item_details(self): ''' From 0a5d4384b272c09c81382dd753902ceef9feea85 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 18 Feb 2017 18:52:37 +0100 Subject: [PATCH 7/9] Replace list with tuple --- todoman/interactive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todoman/interactive.py b/todoman/interactive.py index 7cb3508c..06391b02 100644 --- a/todoman/interactive.py +++ b/todoman/interactive.py @@ -65,7 +65,7 @@ def has_priority(self): (self) -> bool ''' - return self.todo.priority not in [None, 0] + return self.todo.priority not in (None, 0) class Page(urwid.Frame): From 271bd437ab5ccb6ed2bec9142e3a44039cc876dc Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 18 Feb 2017 18:57:26 +0100 Subject: [PATCH 8/9] Replace leftover signatures --- todoman/interactive.py | 94 ++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 62 deletions(-) diff --git a/todoman/interactive.py b/todoman/interactive.py index 06391b02..a476a1c9 100644 --- a/todoman/interactive.py +++ b/todoman/interactive.py @@ -49,8 +49,6 @@ def save(self, *args): ''' Save the current state of the Item and the Todo to which it refers in the associated Database. - - (Item, *args) -> None ''' # Todoman and Urwid have inverted notions of state. According to Urwid, # the state of something that is done is 'False'. @@ -62,8 +60,6 @@ def has_priority(self): ''' Returns True iff the Item refers to a Todo with a priority that is set (i.e. not None and above zero). - - (self) -> bool ''' return self.todo.priority not in (None, 0) @@ -87,7 +83,8 @@ def __init__(self, parent, callback): It is usually a method of the underlying Page. If callback is None, use the default. - (Page, Main, function) -> None + :type parent: Main + :type callback: function ''' self.parent = parent if callback: @@ -102,8 +99,6 @@ def open_page(self, page_to_open): ''' Open a page over the existing stack of pages. page_to_open is the Page object to display. - - (Page, Page) -> None ''' self.parent._open_page(page_to_open) @@ -111,8 +106,6 @@ def open_page(self, page_to_open): def statusbar(self): ''' Returns the current contents of the statusbar of the Todomanpage. - - (Page) -> str ''' return self.footer.contents[0].original_widget.text @@ -121,8 +114,6 @@ def statusbar(self, text): ''' Sets the given text as the current text in the statusbar of the Page. - - (self, str) -> None ''' self.footer.contents self.footer.contents[0][0].original_widget.set_text(text) @@ -145,8 +136,6 @@ def reload(self): ''' Dummy method. Page subclasses that need a reload should implement one under this name. - - (Page) -> None ''' return None @@ -167,7 +156,9 @@ def __init__(self, parent, callback, database): is responsible for passing information to the underlying Page. It is usually a method of the underlying Page. - (ItemListPage, Main, function, Database) -> None + :param parent: Main + :param callback: function + :param database: Database ''' self.parent = parent self.database = database @@ -181,8 +172,6 @@ def items_to_display(self): ''' Create a list of Items to display, based on the associated Database and the current done_is_hidden setting. - - (ItemListPage) -> [Item] ''' items = [] for t in self.database.todos(): @@ -213,8 +202,6 @@ def move_database_chooser(self): def callback_copy_to(self, database): ''' Copy the Item in focus to Database database. - - (ItemListPage, Database) -> None ''' if database != self.database: database.save(self.body.focus.todo) @@ -244,8 +231,6 @@ def generate_label(self, item): ''' Return a label for a given Item for display in the ItemListPage listing. - - (ItemListPage, Item) -> str ''' return "{0} {1}".format( '!' if item.has_priority else ' ', item.todo.summary @@ -255,47 +240,39 @@ def keypress(self, size, key): ''' Make the different commands in the ItemListPage view work. - (ItemListPage, int(?), str) -> str + :type size: int + :type key: str + :rtype: str ''' if key == 'l': self.list_chooser() - return None - if key == 'm': + elif key == 'm': self.move_database_chooser() - return None - if key == 'c': + elif key == 'c': self.copy_database_chooser() - return None - if key == 'd': + elif key == 'd': self.delete() - return None - if key == 'D': + elif key == 'D': self.delete_all_done() - return None - if key == 'esc': + elif key == 'esc': self.parent.close_page(self) - return None - if key == 'h': + elif key == 'h': self.toggle_hide_completed_items() - return None - if key == 'e': + elif key == 'e': self.item_details() - return None - if key == 'n': + elif key == 'n': self.new_item() - return None - if key == 'j': + elif key == 'j': return super().keypress(size, 'down') - if key == 'k': + elif key == 'k': return super().keypress(size, 'up') - return super().keypress(size, key) + else: + return super().keypress(size, key) def list_chooser(self): ''' Open a ListsPage from which to choose the ItemListPage to display. - - (ItemListPage) -> None ''' new_page = ListsPage( self.parent, @@ -324,8 +301,6 @@ def item_details(self): ''' Open a ItemDetailsPage in which the user can edit the selected Item. - - (ItemListPage) -> None ''' new_page = ItemDetailsPage( self.parent, @@ -338,8 +313,6 @@ def new_item(self): ''' Create a new Item and open a ItemDetailsPage in which the user can edit the new item. - - (ItemListPage) -> None ''' item = Item(None, self.database, self.generate_label) new_page = ItemDetailsPage(self.parent, self.callback, item) @@ -348,8 +321,6 @@ def new_item(self): def toggle_hide_completed_items(self): ''' Toggle whether completed items are still displayed in the Page. - - (ItemListPage) -> None ''' self.done_is_hidden = not self.done_is_hidden self.reload() @@ -357,8 +328,6 @@ def toggle_hide_completed_items(self): def delete_all_done(self): ''' Delete all Items for which the associated Todos are completed. - - (ItemListPage) -> None ''' to_delete = [] for item in self.body.body: @@ -370,8 +339,6 @@ def delete_all_done(self): def reload(self): ''' Reload all Items in the ListBox from the underlying Database. - - (ItemListPage) -> None ''' items = self.items_to_display() self.body = urwid.ListBox(urwid.SimpleFocusListWalker(items)) @@ -396,8 +363,9 @@ def __init__(self, parent, callback, item): is responsible for passing information to the underlying Page. It is usually a method of the underlying Page. - (ItemDetailsPage, Main, function, Item) - -> None + :type parent: Main + :type callback: function + :type item: Item ''' self.parent = parent self.item = item @@ -415,13 +383,11 @@ def __init__(self, parent, callback, item): self.body.body.contents[-1].contents[1][0].set_text("") super().__init__(parent, callback) - def close_page(self, dummy, should_save): + def close_page(self, _, should_save): ''' Callback for closing the ItemDetailsPage. Will attempt to save all associated data if shouldSave is True. Will discard all changes otherwise. - - (ItemDetailsPage, Button, bool) -> None ''' if should_save: try: @@ -439,7 +405,8 @@ def keypress(self, size, key): ''' Make the different commands in the ItemDetailsPage view work. - (ItemDetailsPage, int(?), str) -> str + :type size: int + :type key: str ''' if key == 'esc': self.close_page(None, False) @@ -462,7 +429,8 @@ def __init__(self, parent, callback): is responsible for passing information to the underlying Page. It is usually a method of the underlying Page. - (ListsPage, Main, function) -> None + :type parent: Main + :type callback: function ''' self.parent = parent buttons = [] @@ -480,7 +448,8 @@ def close_page(self, button, database): Close the current page, returning the selected database to the underlying page. - (ListsPage, Button, Database) -> None + :type button: Button + :type database: Database ''' self.parent.close_page(self, database=database) @@ -488,7 +457,8 @@ def keypress(self, size, key): ''' Make the different commands in the ListsPage view work. - (ListsPage, int(?), str) -> str + :type size: int + :type key: str ''' if key == 'esc': self.parent.close_page(self) From a04aa5a4d2fcda5d977b6096648595ef7358309e Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 18 Feb 2017 19:00:37 +0100 Subject: [PATCH 9/9] Fixup --- todoman/cli.py | 4 ++-- todoman/interactive.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/todoman/cli.py b/todoman/cli.py index aeb14fea..75346c11 100644 --- a/todoman/cli.py +++ b/todoman/cli.py @@ -8,7 +8,7 @@ from . import model from .configuration import ConfigurationException, load_config -from .interactive import TodomanInteractive +from .interactive import Interactive from .model import Database, FileTodo from .ui import EditState, PorcelainFormatter, TodoEditor, TodoFormatter @@ -200,7 +200,7 @@ def interactive(ctx): ''' Provide an interactive, curses-based interface to Todoman. ''' - TodomanInteractive(ctx.obj['db'], ctx.obj['formatter']) + Interactive(ctx.obj['db'], ctx.obj['formatter']) @cli.command() diff --git a/todoman/interactive.py b/todoman/interactive.py index a476a1c9..db786fa9 100644 --- a/todoman/interactive.py +++ b/todoman/interactive.py @@ -460,17 +460,17 @@ def keypress(self, size, key): :type size: int :type key: str ''' - if key == 'esc': + if key in ('esc', 'q', 'Q'): self.parent.close_page(self) - return None - if key == 'j': + elif key == 'j': return super().keypress(size, 'down') - if key == 'k': + elif key == 'k': return super().keypress(size, 'up') - return super().keypress(size, key) + else: + return super().keypress(size, key) -class Main(object): +class Interactive(object): ''' Class to run the interactive, curses-based interface to Todoman. '''