Sun, 21 Oct 2012 17:03:22 +0200
Implemented the 'edit entry' action.
# -*- coding: utf-8 -*- # Copyright (c) 2012 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the time tracker widget. """ import os from PyQt4.QtCore import pyqtSlot, QPoint, Qt, QDate, QTime, QFileInfo from PyQt4.QtGui import QWidget, QMenu, QTreeWidgetItem, QCursor, QDialog from E5Gui import E5MessageBox, E5FileDialog from .Ui_TimeTrackerWidget import Ui_TimeTrackerWidget from .TimeTrackerEntryDialog import TimeTrackerEntryDialog import Preferences import Utilities class TimeTrackerWidget(QWidget, Ui_TimeTrackerWidget): """ 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 (TimeTracker) @param parent reference to the parent widget (QWidget) """ 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 (string) """ itm = self.entriesList.topLevelItem(0) itm.setText(self.TaskColumn, txt) self.entriesList.resizeColumnToContents(self.TaskColumn) entry = self.__tracker.getCurrentEntry() 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 (string) """ itm = self.entriesList.topLevelItem(0) itm.setText(self.CommentColumn, txt) self.entriesList.resizeColumnToContents(self.CommentColumn) entry = self.__tracker.getCurrentEntry() entry.setComment(txt) @pyqtSlot(bool) def on_pauseButton_toggled(self, checked): """ Private slot to pause the current timing. """ 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)) self.entriesList.resizeColumnToContents(self.CommentColumn) self.durationSpinBox.setValue(duration) else: self.__tracker.continueTrackerEntry() @pyqtSlot() def on_newButton_clicked(self): """ Private slot to end the current timer and start a new one. """ # stop the current tracker eid, duration = self.__tracker.stopTrackerEntry() if eid > -1: itm = self.entriesList.topLevelItem(0) itm.setText(self.DurationColumn, self.tr("{0} min").format(duration)) itm.setData(0, Qt.UserRole, eid) else: itm = self.entriesList.takeTopLevelItem(0) del itm self.entriesList.resizeColumnToContents(self.CommentColumn) # 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 (QPoint) """ menu = QMenu() menu.addAction(self.tr("Edit"), self.__editEntry).setEnabled( len(self.entriesList.selectedItems()) == 1) menu.addAction(self.tr("Add"), self.__addEntry) menu.addAction(self.tr("Delete"), self.__deleteSelectedEntries) menu.addSeparator() menu.addAction(self.tr("Save"), self.__saveEntries) menu.addSeparator() menu.addAction(self.tr("Import"), self.__importEntries) menu.addAction(self.tr("Export Selected"), self.__exportSelectedEntries)\ .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. """ # TODO: implement this def __editEntry(self): """ Private slot to edit the selected tracker entry. """ eid = self.entriesList.selectedItems()[0].data(0, Qt.UserRole) if eid > -1: # the current entry is edited via the elements of this widget entry = self.__tracker.getEntry(eid) if entry is not None: tasks = [] for index in range(self.taskCombo.count()): tasks.append(self.taskCombo.itemText(index)) comments = [] for index in range(self.commentCombo.count()): comments.append(self.commentCombo.itemText(index)) dlg = TimeTrackerEntryDialog(entry, tasks, comments) if dlg.exec_() == QDialog.Accepted: start, duration, task, comment = dlg.getData() entry.setStartDateTime(start) entry.setDuration(duration) entry.setTask(task) entry.setComment(comment) self.__tracker.entryChanged() def __deleteSelectedEntries(self): """ Private slot to delete the selected tracker entries. """ res = E5MessageBox.yesNo(self, self.trUtf8("Delete Selected Entries"), self.trUtf8("""Do you really want to delete the selected entries?""")) if res: for item in self.entriesList.selectedItems(): eid = item.data(0, Qt.UserRole) if eid > -1: # the current entry must not be deleted self.entriesList.takeTopLevelItem( 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() fname = E5FileDialog.getOpenFileName( None, self.trUtf8("Import Tracker Entries"), path, self.trUtf8("Text Files (*.txt);;All Files (*)")) if fname: fname = Utilities.toNativeSeparators(fname) if not os.path.exists(fname): E5MessageBox.critical(self, self.trUtf8("Import Tracker Entries"), self.trUtf8("<p>The file <b>{0}</b> does not exist.</p>").format( fname)) return self.__tracker.importTrackerEntries(fname) def __exportEntries(self, ids=[]): """ Private method to export all or selected entries. @keyparam ids list of IDs to export or all if empty (list of integer) """ path = Preferences.getMultiProject("Workspace") or Utilities.getHomeDir() fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( self, self.trUtf8("Export Tracker Entries"), path, self.tr("Text Files (*.txt);;All Files (*)"), None, E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) if fname: ext = QFileInfo(fname).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fname += ex if QFileInfo(fname).exists(): res = E5MessageBox.yesNo(self, self.trUtf8("Export Tracker Entries"), self.trUtf8("<p>The file <b>{0}</b> already exists." " Overwrite it?</p>").format(fname), icon=E5MessageBox.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. """ ids = [] for itm in self.entriesList.selectedItems(): eid = itm.data(0, Qt.UserRole) if eid > -1: ids.append(eid) if ids: self.__exportEntries(ids=ids) def __removeDuplicates(self): """ Private slot to remove duplicate entries. """ res = E5MessageBox.yesNo(self, self.trUtf8("Remove Duplicate Tracker Entries"), self.trUtf8("""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. """ res = E5MessageBox.yesNo(self, self.trUtf8("Merge Duplicate Tracker Entries"), self.trUtf8("""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 (TimeTrackEntry) @param index index the entry is to be inserted; -1 for at the end (integer) """ eid, date, time, duration, task, comment, paused = entry.getEntryData() itm = QTreeWidgetItem( [self.tr("{0}, {1}", "date, time").format(date, time), self.tr("{0} min").format(duration), task, comment]) itm.setTextAlignment(1, Qt.AlignRight) itm.setData(0, Qt.UserRole, eid) if index == -1: 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 (list of TimeTrackEntry) """ self.taskCombo.addItem("") self.commentCombo.addItem("") tasks = [] comments = [] for entry in entries: self.__insertEntry(entry) task = entry.getTask() if task and task not in tasks: tasks.append(task) 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 (TimeTrackEntry) """ self.__insertEntry(entry, 0) self.__resizeColumns() eid, date, time, duration, task, comment, paused = entry.getEntryData() self.startDateTimeEdit.setDateTime(entry.getStartDateTime()) self.durationSpinBox.setValue(duration) self.taskCombo.setEditText(task) self.commentCombo.setEditText(comment) def clear(self): """ Public method to clear all the data. """ self.entriesList.clear() self.startDateTimeEdit.setDate(QDate(2000, 1, 1)) self.startDateTimeEdit.setTime(QTime(0, 0, 0)) self.durationSpinBox.setValue(0) self.taskCombo.clear() self.commentCombo.clear()