--- a/TimeTracker/TimeTrackerWidget.py Thu Dec 30 12:38:13 2021 +0100 +++ b/TimeTracker/TimeTrackerWidget.py Tue Sep 20 19:10:20 2022 +0200 @@ -25,14 +25,15 @@ """ Class implementing the time tracker widget. """ + DurationColumn = 1 TaskColumn = 2 CommentColumn = 3 - + def __init__(self, tracker, parent=None): """ Constructor - + @param tracker reference to the time tracker @type TimeTracker @param parent reference to the parent widget @@ -40,15 +41,15 @@ """ super().__init__(parent) self.setupUi(self) - + self.__tracker = tracker - + @pyqtSlot(str) def on_taskCombo_editTextChanged(self, txt): """ Private slot handling changes of the task description of the current entry. - + @param txt new task description @type str """ @@ -56,16 +57,16 @@ if itm: itm.setText(self.TaskColumn, txt) self.entriesList.resizeColumnToContents(self.TaskColumn) - + entry = self.__tracker.getCurrentEntry() if entry: entry.setTask(txt) - + @pyqtSlot(str) def on_commentCombo_editTextChanged(self, txt): """ Private slot handling changes of the comment of the current entry. - + @param txt new comment @type str """ @@ -73,34 +74,33 @@ if itm: itm.setText(self.CommentColumn, txt) self.entriesList.resizeColumnToContents(self.CommentColumn) - + entry = self.__tracker.getCurrentEntry() if entry: entry.setComment(txt) - + @pyqtSlot(bool) def on_pauseButton_toggled(self, checked): """ Private slot to pause the current timing. - + @param checked flag indicating the checked status of the button @type bool """ if checked: self.__tracker.pauseTrackerEntry() - + entry = self.__tracker.getCurrentEntry() duration = entry.getDuration() - + itm = self.entriesList.topLevelItem(0) - itm.setText(self.DurationColumn, - self.tr("{0} min").format(duration)) + itm.setText(self.DurationColumn, self.tr("{0} min").format(duration)) self.entriesList.resizeColumnToContents(self.DurationColumn) - + self.durationSpinBox.setValue(duration) else: self.__tracker.continueTrackerEntry() - + @pyqtSlot() def on_newButton_clicked(self): """ @@ -110,68 +110,73 @@ eid, duration = self.__tracker.stopTrackerEntry() if eid > -1: itm = self.entriesList.topLevelItem(0) - itm.setText(self.DurationColumn, - self.tr("{0} min").format(duration)) + itm.setText(self.DurationColumn, self.tr("{0} min").format(duration)) itm.setData(0, Qt.ItemDataRole.UserRole, eid) else: itm = self.entriesList.takeTopLevelItem(0) del itm self.__resizeColumns() - + # start a new one self.__tracker.startTrackerEntry() - + @pyqtSlot(QPoint) def on_entriesList_customContextMenuRequested(self, pos): """ Private slot to create the context menu and show it. - + @param pos position the menu should be shown at @type QPoint """ menu = QMenu() - + act = menu.addAction(self.tr("Edit"), self.__editEntry) act.setEnabled( - len(self.entriesList.selectedItems()) == 1 and - self.entriesList.selectedItems()[0].data( - 0, Qt.ItemDataRole.UserRole) > -1 + len(self.entriesList.selectedItems()) == 1 + and self.entriesList.selectedItems()[0].data(0, Qt.ItemDataRole.UserRole) + > -1 ) menu.addAction(self.tr("Add"), self.__addEntry) act = menu.addAction(self.tr("Delete"), self.__deleteSelectedEntries) act.setEnabled( - (len(self.entriesList.selectedItems()) == 1 and - self.entriesList.selectedItems()[0].data( - 0, Qt.ItemDataRole.UserRole) > -1) or - len(self.entriesList.selectedItems()) > 1 + ( + len(self.entriesList.selectedItems()) == 1 + and self.entriesList.selectedItems()[0].data( + 0, Qt.ItemDataRole.UserRole + ) + > -1 + ) + or len(self.entriesList.selectedItems()) > 1 ) menu.addSeparator() menu.addAction(self.tr("Save"), self.__saveEntries) menu.addSeparator() menu.addAction(self.tr("Import"), self.__importEntries) - act = menu.addAction(self.tr("Export Selected"), - self.__exportSelectedEntries) + act = menu.addAction(self.tr("Export Selected"), self.__exportSelectedEntries) act.setEnabled(len(self.entriesList.selectedItems()) > 0) menu.addAction(self.tr("Export All"), self.__exportEntries) menu.addSeparator() menu.addAction(self.tr("Remove duplicates"), self.__removeDuplicates) menu.addAction(self.tr("Merge duplicates"), self.__mergeDuplicates) menu.exec(QCursor.pos()) - + def __addEntry(self): """ Private slot to manually add an entry. """ from .TimeTrackerEntryDialog import TimeTrackerEntryDialog - - tasks = [self.taskCombo.itemText(index) - for index in range(self.taskCombo.count())] - comments = [self.commentCombo.itemText(index) - for index in range(self.commentCombo.count())] + + tasks = [ + self.taskCombo.itemText(index) for index in range(self.taskCombo.count()) + ] + comments = [ + self.commentCombo.itemText(index) + for index in range(self.commentCombo.count()) + ] dlg = TimeTrackerEntryDialog(self.__tracker, None, tasks, comments) if dlg.exec() == QDialog.DialogCode.Accepted: self.__tracker.addTrackerEntry(*dlg.getData()) - + def __editEntry(self): """ Private slot to edit the selected tracker entry. @@ -183,31 +188,37 @@ entry = self.__tracker.getEntry(eid) if entry is not None: from .TimeTrackerEntryDialog import TimeTrackerEntryDialog - - tasks = [self.taskCombo.itemText(index) - for index in range(self.taskCombo.count())] - comments = [self.commentCombo.itemText(index) - for index in range(self.commentCombo.count())] - dlg = TimeTrackerEntryDialog( - self.__tracker, entry, tasks, comments) + + tasks = [ + self.taskCombo.itemText(index) + for index in range(self.taskCombo.count()) + ] + comments = [ + self.commentCombo.itemText(index) + for index in range(self.commentCombo.count()) + ] + dlg = TimeTrackerEntryDialog(self.__tracker, entry, tasks, comments) if dlg.exec() == QDialog.DialogCode.Accepted: start, duration, task, comment = dlg.getData() - + entry.setStartDateTime(start) entry.setDuration(duration) entry.setTask(task) entry.setComment(comment) self.__tracker.entryChanged() - + data = entry.getEntryData() - itm.setText(0, self.tr("{0}, {1}", "date, time") - .format(data["start_date"], - data["start_time"])) + itm.setText( + 0, + self.tr("{0}, {1}", "date, time").format( + data["start_date"], data["start_time"] + ), + ) itm.setText(1, self.tr("{0} min").format(data["duration"])) itm.setText(2, data["task"]) itm.setText(3, data["comment"]) self.__resizeColumns() - + def __deleteSelectedEntries(self): """ Private slot to delete the selected tracker entries. @@ -215,67 +226,64 @@ res = EricMessageBox.yesNo( self, self.tr("Delete Selected Entries"), - self.tr("""Do you really want to delete the selected""" - """ entries?""")) + self.tr("""Do you really want to delete the selected""" """ entries?"""), + ) if res: for item in self.entriesList.selectedItems(): eid = item.data(0, Qt.ItemDataRole.UserRole) if eid > -1: # the current entry must not be deleted self.entriesList.takeTopLevelItem( - self.entriesList.indexOfTopLevelItem(item)) + self.entriesList.indexOfTopLevelItem(item) + ) self.__tracker.deleteTrackerEntry(eid) del item - + def __saveEntries(self): """ Private slot to save the tracker entries. """ self.__tracker.saveTrackerEntries() - + def __importEntries(self): """ Private slot to import tracker entries. """ - path = ( - Preferences.getMultiProject("Workspace") or - Utilities.getHomeDir() - ) + path = Preferences.getMultiProject("Workspace") or Utilities.getHomeDir() fname = EricFileDialog.getOpenFileName( None, self.tr("Import Time Tracker Entries"), path, - self.tr("Time Tracker Files (*.ttj);;All Files (*)")) + self.tr("Time Tracker Files (*.ttj);;All Files (*)"), + ) if fname: fname = Utilities.toNativeSeparators(fname) if not os.path.exists(fname): EricMessageBox.critical( self, self.tr("Import Time Tracker Entries"), - self.tr("<p>The file <b>{0}</b> does not exist.</p>") - .format(fname)) + self.tr("<p>The file <b>{0}</b> does not exist.</p>").format(fname), + ) return - + self.__tracker.importTrackerEntries(fname) - + def __exportEntries(self, ids=None): """ Private method to export all or selected entries. - + @param ids list of IDs to export or all if empty @type list of int """ - path = ( - Preferences.getMultiProject("Workspace") or - Utilities.getHomeDir() - ) + path = Preferences.getMultiProject("Workspace") or Utilities.getHomeDir() fname, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( self, self.tr("Export Time Tracker Entries"), path, self.tr("Time Tracker Files (*.ttj);;All Files (*)"), None, - EricFileDialog.Options(EricFileDialog.DontConfirmOverwrite)) + EricFileDialog.Options(EricFileDialog.DontConfirmOverwrite), + ) if fname: ext = QFileInfo(fname).suffix() if not ext: @@ -286,14 +294,16 @@ res = EricMessageBox.yesNo( self, self.tr("Export Time Tracker Entries"), - self.tr("<p>The file <b>{0}</b> already exists." - " Overwrite it?</p>").format(fname), - icon=EricMessageBox.Warning) + self.tr( + "<p>The file <b>{0}</b> already exists." " Overwrite it?</p>" + ).format(fname), + icon=EricMessageBox.Warning, + ) if not res: return fname = Utilities.toNativeSeparators(fname) self.__tracker.saveTrackerEntries(filePath=fname, ids=ids) - + def __exportSelectedEntries(self): """ Private slot to export the selected tracker entries. @@ -303,10 +313,10 @@ eid = itm.data(0, Qt.ItemDataRole.UserRole) if eid > -1: ids.append(eid) - + if ids: self.__exportEntries(ids=ids) - + def __removeDuplicates(self): """ Private slot to remove duplicate entries. @@ -314,12 +324,15 @@ res = EricMessageBox.yesNo( self, self.tr("Remove Duplicate Tracker Entries"), - self.tr("""Are you sure you want to remove duplicate""" - """ tracker entries? Only the one with the longest""" - """ duration will be kept.""")) + self.tr( + """Are you sure you want to remove duplicate""" + """ tracker entries? Only the one with the longest""" + """ duration will be kept.""" + ), + ) if res: self.__tracker.removeDuplicateTrackerEntries() - + def __mergeDuplicates(self): """ Private slot to merge duplicate entries. @@ -327,16 +340,19 @@ res = EricMessageBox.yesNo( self, self.tr("Merge Duplicate Tracker Entries"), - self.tr("""Are you sure you want to merge duplicate""" - """ tracker entries? The durations of duplicate""" - """ ones will be added.""")) + self.tr( + """Are you sure you want to merge duplicate""" + """ tracker entries? The durations of duplicate""" + """ ones will be added.""" + ), + ) if res: self.__tracker.mergeDuplicateTrackerEntries() - + def __insertEntry(self, entry, index=-1): """ Private method to insert a tracker entry into the list. - + @param entry reference to the tracker entry @type TimeTrackEntry @param index index the entry is to be inserted; -1 for at the end @@ -344,11 +360,14 @@ """ data = entry.getEntryData() itm = QTreeWidgetItem( - [self.tr("{0}, {1}", "date, time").format(data["start_date"], - data["start_time"]), - self.tr("{0} min").format(data["duration"]), - data["task"], - data["comment"]] + [ + self.tr("{0}, {1}", "date, time").format( + data["start_date"], data["start_time"] + ), + self.tr("{0} min").format(data["duration"]), + data["task"], + data["comment"], + ] ) itm.setTextAlignment(1, Qt.AlignmentFlag.AlignRight) itm.setData(0, Qt.ItemDataRole.UserRole, data["id"]) @@ -356,27 +375,27 @@ self.entriesList.addTopLevelItem(itm) else: self.entriesList.insertTopLevelItem(index, itm) - + def __resizeColumns(self): """ Private slot to resize the columns of the entries list. """ for column in range(self.entriesList.columnCount()): self.entriesList.resizeColumnToContents(column) - + def showTrackerEntries(self, entries): """ Public method to show the tracker entries of the current project. - + @param entries list of tracker entries @type list of TimeTrackEntry """ self.taskCombo.addItem("") self.commentCombo.addItem("") - + tasks = [] comments = [] - + for entry in entries: self.__insertEntry(entry) task = entry.getTask() @@ -385,29 +404,29 @@ comment = entry.getComment() if comment and comment not in comments: comments.append(comment) - + self.__resizeColumns() if tasks: self.taskCombo.addItems(sorted(tasks)) if comments: self.commentCombo.addItems(sorted(comments)) - + def setCurrentEntry(self, entry): """ Public method to set the current entry. - + @param entry current entry @type TimeTrackEntry """ self.__insertEntry(entry, 0) self.__resizeColumns() - + data = entry.getEntryData() self.startDateTimeEdit.setDateTime(entry.getStartDateTime()) self.durationSpinBox.setValue(data["duration"]) self.taskCombo.setEditText(data["task"]) self.commentCombo.setEditText(data["comment"]) - + def clear(self): """ Public method to clear all the data.