From f06f9fe45f3e4c2c800cc0db1ec8873fa6fa4667 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 16 Apr 2009 08:46:31 -0500 Subject: [PATCH] * Fixed a bug when resetting the task list introduced when breaking the item list into a seperate class * Added user displaying error handling all over the place * Improved the look of the error box * Removed the popup calendar usage and embedded the calendar straight in edit * Fixed some enable/disable bugs in Edit --- src/doneit.glade | 26 ++++----- src/doneit_glade.py | 2 +- src/gtk_toolbox.py | 81 ++++++++++++++++------------ src/rtm_backend.py | 6 +-- src/rtm_view.py | 147 +++++++++++++++++++++++++++++++-------------------- 5 files changed, 151 insertions(+), 111 deletions(-) diff --git a/src/doneit.glade b/src/doneit.glade index 9b9f908..2957e48 100644 --- a/src/doneit.glade +++ b/src/doneit.glade @@ -1,6 +1,6 @@ - + 800 @@ -131,6 +131,8 @@ True True + PANGO_ELLIPSIZE_END + True 1 @@ -454,25 +456,19 @@ True - + True - False + True + 2009 + 3 + 16 + False True - - True - True - True - gtk-properties - True - 0 - - - True True @@ -481,12 +477,10 @@ True 0 - - 1 - + False 1 diff --git a/src/doneit_glade.py b/src/doneit_glade.py index 995f884..3e3a121 100755 --- a/src/doneit_glade.py +++ b/src/doneit_glade.py @@ -155,7 +155,7 @@ class DoneIt(object): # Setup costly backends import rtm_view with gtk_toolbox.gtk_lock(): - rtmView = rtm_view.GtkRtMilk(self._widgetTree) + rtmView = rtm_view.GtkRtMilk(self._widgetTree, self.__errorDisplay) self._todoUIs[rtmView.name()] = rtmView self._defaultUIName = rtmView.name() diff --git a/src/gtk_toolbox.py b/src/gtk_toolbox.py index fada4ed..3afede5 100644 --- a/src/gtk_toolbox.py +++ b/src/gtk_toolbox.py @@ -7,9 +7,11 @@ Short/long version with long including tags colored specially """ -import warnings -import contextlib +import sys +import traceback import datetime +import contextlib +import warnings import gobject import gtk @@ -240,9 +242,15 @@ class ErrorDisplay(object): else: self.__show_message(message) - def push_exception(self, exception): - self.push_message(exception.message) - warnings.warn(exception, stacklevel=3) + def push_exception(self, exception = None): + if exception is None: + userMessage = sys.exc_info()[1].message + warningMessage = traceback.format_exc() + else: + userMessage = exception.message + warningMessage = exception + self.push_message(userMessage) + warnings.warn(warningMessage, stacklevel=3) def pop_message(self): if 0 < len(self.__messages): @@ -339,28 +347,31 @@ class EditTaskDialog(object): self._taskName = widgetTree.get_widget("edit-taskNameEntry") self._pasteTaskNameButton = widgetTree.get_widget("edit-pasteTaskNameButton") self._priorityChoiceCombo = widgetTree.get_widget("edit-priorityChoiceCombo") - self._dueDateDisplay = widgetTree.get_widget("edit-dueDateDisplay") - self._dueDateProperties = widgetTree.get_widget("edit-dueDateProperties") + self._dueDateDisplay = widgetTree.get_widget("edit-dueDateCalendar") self._clearDueDate = widgetTree.get_widget("edit-clearDueDate") self._addButton = widgetTree.get_widget("edit-commitEditTaskButton") self._cancelButton = widgetTree.get_widget("edit-cancelEditTaskButton") - self._popupCalendar = PopupCalendar(self._dialog, coroutines.func_sink(self._update_duedate)) + self._onPasteTaskId = None + self._onClearDueDateId = None + self._onAddId = None + self._onCancelId = None def enable(self, todoManager): self._populate_projects(todoManager) - self._pasteTaskNameButton.connect("clicked", self._on_name_paste) - self._dueDateProperties.connect("clicked", self._on_choose_duedate) - self._clearDueDate.connect("clicked", self._on_clear_duedate) - self._addButton.connect("clicked", self._on_add_clicked) - self._cancelButton.connect("clicked", self._on_cancel_clicked) + self._onPasteTaskId = self._pasteTaskNameButton.connect("clicked", self._on_name_paste) + self._onClearDueDateId = self._clearDueDate.connect("clicked", self._on_clear_duedate) + self._onAddId = self._addButton.connect("clicked", self._on_add_clicked) + self._onCancelId = self._cancelButton.connect("clicked", self._on_cancel_clicked) def disable(self): - self._pasteTaskNameButton.disconnect("clicked", self._on_name_paste) - self._dueDateProperties.disconnect("clicked", self._on_choose_duedate) - self._clearDueDate.disconnect("clicked", self._on_clear_duedate) + self._pasteTaskNameButton.disconnect(self._onPasteTaskId) + self._clearDueDate.disconnect(self._onClearDueDateId) + self._addButton.disconnect(self._onAddId) + self._cancelButton.disconnect(self._onAddId) + self._projectsList.clear() self._projectCombo.set_model(None) @@ -374,15 +385,22 @@ class EditTaskDialog(object): originalName = taskDetails["name"] originalPriority = str(taskDetails["priority"].get_nothrow(0)) if taskDetails["dueDate"].is_good(): - originalDue = taskDetails["dueDate"].get().strftime("%Y-%m-%d %H:%M:%S") + originalDue = taskDetails["dueDate"].get() else: - originalDue = "" + originalDue = None self._dialog.set_default_response(gtk.RESPONSE_OK) self._taskName.set_text(originalName) self._set_active_proj(originalProjectName) self._priorityChoiceCombo.set_active(int(originalPriority)) - self._dueDateDisplay.set_text(originalDue) + if originalDue is not None: + # Months are 0 indexed + self._dueDateDisplay.select_month(originalDue.month - 1, originalDue.year) + self._dueDateDisplay.select_day(originalDue.day) + else: + now = datetime.datetime.now() + self._dueDateDisplay.select_month(now.month, now.year) + self._dueDateDisplay.select_day(0) try: response = self._dialog.run() @@ -394,7 +412,14 @@ class EditTaskDialog(object): newProjectName = self._get_project(todoManager) newName = self._taskName.get_text() newPriority = self._get_priority() - newDueDate = self._dueDateDisplay.get_text() + year, month, day = self._dueDateDisplay.get_date() + if day != 0: + # Months are 0 indexed + date = datetime.date(year, month + 1, day) + time = datetime.time() + newDueDate = datetime.datetime.combine(date, time) + else: + newDueDate = None isProjDifferent = newProjectName != originalProjectName isNameDifferent = newName != originalName @@ -417,8 +442,7 @@ class EditTaskDialog(object): print "PRIO CHANGE" if isDueDifferent: if newDueDate: - due = datetime.datetime.strptime(newDueDate, "%Y-%m-%d %H:%M:%S") - due = toolbox.Optional(due) + due = toolbox.Optional(newDueDate) else: due = toolbox.Optional() @@ -462,25 +486,14 @@ class EditTaskDialog(object): else: return str(index) - def _update_duedate(self, eventData): - widget, date = eventData - time = datetime.time() - dueDate = datetime.datetime.combine(date, time) - - formttedDate = dueDate.strftime("%Y-%m-%d %H:%M:%S") - self._dueDateDisplay.set_text(formttedDate) - def _on_name_paste(self, *args): clipboard = gtk.clipboard_get() contents = clipboard.wait_for_text() if contents is not None: self._taskName.set_text(contents) - def _on_choose_duedate(self, *args): - self._popupCalendar.run() - def _on_clear_duedate(self, *args): - self._dueDateDisplay.set_text("") + self._dueDateDisplay.select_day(0) def _on_add_clicked(self, *args): self._dialog.response(gtk.RESPONSE_OK) diff --git a/src/rtm_backend.py b/src/rtm_backend.py index 76c4ded..be908e7 100644 --- a/src/rtm_backend.py +++ b/src/rtm_backend.py @@ -48,9 +48,9 @@ class RtMilkManager(object): yield list def get_project(self, projId): - proj = [proj for proj in self.get_projects() if projId == proj["id"]] - assert len(proj) == 1, "%r / %r" % (proj, self._lists) - return proj[0] + projs = [proj for proj in self.get_projects() if projId == proj["id"]] + assert len(projs) == 1, "%r: %r / %r" % (projId, projs, self._lists) + return projs[0] def get_project_names(self): return (list["name"] for list in self.get_projects) diff --git a/src/rtm_view.py b/src/rtm_view.py index 22364f1..6a0ab36 100644 --- a/src/rtm_view.py +++ b/src/rtm_view.py @@ -129,7 +129,8 @@ class ItemListView(object): LINK_IDX = 6 NOTES_IDX = 7 - def __init__(self, widgetTree): + def __init__(self, widgetTree, errorDisplay): + self._errorDisplay = errorDisplay self._manager = None self._projId = None self._showCompleted = False @@ -195,7 +196,10 @@ class ItemListView(object): self._todoItemTree.append_column(self._dueColumn) self._todoItemTree.append_column(self._linkColumn) self._todoItemTree.append_column(self._notesColumn) - self.reset_task_list(self._projId) + try: + self.reset_task_list(self._projId) + except StandardError, e: + self._errorDisplay.push_exception() self._todoItemTree.set_headers_visible(False) self._nameColumn.set_expand(True) @@ -260,39 +264,46 @@ class ItemListView(object): self._todoItemTree.set_model(self._itemList) def _on_item_select(self, treeView, path, viewColumn): - # @todo See if there is a way to use the new gtk_toolbox.ContextHandler - taskId = self._itemList[path[0]][self.ID_IDX] - - if viewColumn is self._priorityColumn: - pass - elif viewColumn is self._nameColumn: - self._editDialog.enable(self._manager) - try: - self._editDialog.request_task(self._manager, taskId) - finally: - self._editDialog.disable() - self.reset_task_list(self._projId) - elif viewColumn is self._dueColumn: - self._editDialog.enable(self._manager) - try: - self._editDialog.request_task(self._manager, taskId) - finally: - self._editDialog.disable() - self.reset_task_list(self._projId) - elif viewColumn is self._linkColumn: - webbrowser.open(self._manager.get_task_details(taskId)["url"]) - elif viewColumn is self._notesColumn: - pass + try: + # @todo See if there is a way to use the new gtk_toolbox.ContextHandler + taskId = self._itemList[path[0]][self.ID_IDX] + + if viewColumn is self._priorityColumn: + pass + elif viewColumn is self._nameColumn: + self._editDialog.enable(self._manager) + try: + self._editDialog.request_task(self._manager, taskId) + finally: + self._editDialog.disable() + self.reset_task_list(self._projId) + elif viewColumn is self._dueColumn: + self._editDialog.enable(self._manager) + try: + self._editDialog.request_task(self._manager, taskId) + finally: + self._editDialog.disable() + self.reset_task_list(self._projId) + elif viewColumn is self._linkColumn: + webbrowser.open(self._manager.get_task_details(taskId)["url"]) + elif viewColumn is self._notesColumn: + pass + except StandardError, e: + self._errorDisplay.push_exception() def _on_completion_change(self, cell, path): - taskId = self._itemList[path[0]][self.ID_IDX] - self._manager.complete_task(taskId) - self.reset_task_list(self._projId) + try: + taskId = self._itemList[path[0]][self.ID_IDX] + self._manager.complete_task(taskId) + self.reset_task_list(self._projId) + except StandardError, e: + self._errorDisplay.push_exception() class QuickAddView(object): - def __init__(self, widgetTree, addSink): + def __init__(self, widgetTree, errorDisplay, addSink): + self._errorDisplay = errorDisplay self._manager = None self._projId = None self._addSink = addSink @@ -338,53 +349,72 @@ class QuickAddView(object): self._addTaskButton.set_sensitive(not isMeta) def _on_add(self, *args): - name = self._taskNameEntry.get_text() + try: + name = self._taskNameEntry.get_text() - projId = self._projId - taskId = self._manager.add_task(projId, name) + projId = self._projId + taskId = self._manager.add_task(projId, name) - self._taskNameEntry.set_text("") - self._addSink.send((projId, taskId)) + self._taskNameEntry.set_text("") + self._addSink.send((projId, taskId)) + except StandardError, e: + self._errorDisplay.push_exception() def _on_add_edit(self, *args): - name = self._taskNameEntry.get_text() + try: + name = self._taskNameEntry.get_text() - projId = self._projId - taskId = self._manager.add_task(projId, name) + projId = self._projId + taskId = self._manager.add_task(projId, name) - try: - self._editDialog.enable(self._manager) try: - self._editDialog.request_task(self._manager, taskId) + self._editDialog.enable(self._manager) + try: + self._editDialog.request_task(self._manager, taskId) + finally: + self._editDialog.disable() finally: - self._editDialog.disable() - finally: - self._taskNameEntry.set_text("") - self._addSink.send((projId, taskId)) + self._taskNameEntry.set_text("") + self._addSink.send((projId, taskId)) + except StandardError, e: + self._errorDisplay.push_exception() def _on_add_pressed(self, widget): - self._addToEditTimerId = gobject.timeout_add(1000, self._on_add_edit) + try: + self._addToEditTimerId = gobject.timeout_add(1000, self._on_add_edit) + except StandardError, e: + self._errorDisplay.push_exception() def _on_add_released(self, widget): - if self._addToEditTimerId is not None: - gobject.source_remove(self._addToEditTimerId) - self._addToEditTimerId = None + try: + if self._addToEditTimerId is not None: + gobject.source_remove(self._addToEditTimerId) + self._addToEditTimerId = None + except StandardError, e: + self._errorDisplay.push_exception() def _on_paste(self, *args): - entry = self._taskNameEntry.get_text() - entry += self._clipboard.wait_for_text() - self._taskNameEntry.set_text(entry) + try: + entry = self._taskNameEntry.get_text() + entry += self._clipboard.wait_for_text() + self._taskNameEntry.set_text(entry) + except StandardError, e: + self._errorDisplay.push_exception() def _on_clear(self, *args): - self._taskNameEntry.set_text("") + try: + self._taskNameEntry.set_text("") + except StandardError, e: + self._errorDisplay.push_exception() class GtkRtMilk(object): - def __init__(self, widgetTree): + def __init__(self, widgetTree, errorDisplay): """ @note Thread agnostic """ + self._errorDisplay = errorDisplay self._manager = None self._credentials = "", "", "" @@ -392,9 +422,9 @@ class GtkRtMilk(object): self._projectsCombo = widgetTree.get_widget("projectsCombo") self._onListActivateId = 0 - self._itemView = ItemListView(widgetTree) + self._itemView = ItemListView(widgetTree, self._errorDisplay) addSink = coroutines.func_sink(lambda eventData: self._itemView.reset_task_list(eventData[0])) - self._addView = QuickAddView(widgetTree, addSink) + self._addView = QuickAddView(widgetTree, self._errorDisplay, addSink) self._credentialsDialog = gtk_toolbox.LoginWindow(widgetTree) @staticmethod @@ -492,7 +522,7 @@ class GtkRtMilk(object): def _reset_task_list(self): projectName = self._get_project() - projId = self._manager.lookup_project(projectName) + projId = self._manager.lookup_project(projectName)["id"] self._addView.reset_task_list(projId) self._itemView.reset_task_list(projId) @@ -501,4 +531,7 @@ class GtkRtMilk(object): return currentProjectName def _on_list_activate(self, *args): - self._reset_task_list() + try: + self._reset_task_list() + except StandardError, e: + self._errorDisplay.push_exception() -- 1.7.9.5