class ScheduleTable(Table): COLUMNS = [ Column('start_date', 80), Column('stop_date', 80), Column('repeat_type', 80), Column('interval', 50), Column('description', 110), Column('payee', 110), Column('checkno', 70), Column('from', 100), Column('to', 100), Column('amount', 97, alignment=Qt.AlignRight, painter=AMOUNT_PAINTER, resizeToFit=True), ] def __init__(self, model, view): Table.__init__(self, model, view) self.view.sortByColumn( 0, Qt.AscendingOrder) # sorted by start_date by default self.view.deletePressed.connect(self.model.delete) self.view.doubleClicked.connect(self.model.edit) # we have to prevent Return from initiating editing. self.view.editSelected = lambda: None
class PluginListTable(Table): COLUMNS = [ Column('enabled', 60), Column('name', 300), Column('type', 150), Column('author', 120), ] # --- Override def _getData(self, row, column, role): if column.name == 'enabled': if role == Qt.CheckStateRole: return Qt.Checked if row.enabled else Qt.Unchecked else: return None else: return Table._getData(self, row, column, role) def _getFlags(self, row, column): flags = Table._getFlags(self, row, column) if column.name == 'enabled': flags |= Qt.ItemIsUserCheckable return flags def _setData(self, row, column, value, role): if column.name == 'enabled': if role == Qt.CheckStateRole: row.enabled = not row.enabled return True else: return False else: return Table._setData(self, row, column, value, role)
class IgnoreListTable(Table): """ Ignore list model""" COLUMNS = [ Column("path1", defaultWidth=230), Column("path2", defaultWidth=230), ]
class ProblemTable(Table): COLUMNS = [ Column('path', defaultWidth=150), Column('msg', defaultWidth=150), ] def __init__(self, model, view, **kwargs): super().__init__(model, view, **kwargs)
class ProblemTable(Table): COLUMNS = [ Column('path', defaultWidth=150), Column('msg', defaultWidth=150), ] def __init__(self, model, view): Table.__init__(self, model, view)
class ProblemTable(Table): COLUMNS = [ Column("path", default_width=150), Column("msg", default_width=150), ] def __init__(self, model, view, **kwargs): super().__init__(model, view, **kwargs)
class ExcludeListTable(Table): """Model for exclude list""" COLUMNS = [ Column("marked", defaultWidth=15), Column("regex", defaultWidth=230) ] def __init__(self, app, view, **kwargs): model = app.model.exclude_list_dialog.exclude_list_table # pointer to GUITable super().__init__(model, view, **kwargs) font = view.font() font.setPointSize(app.prefs.tableFontSize) view.setFont(font) fm = QFontMetrics(font) view.verticalHeader().setDefaultSectionSize(fm.height() + 2) # app.willSavePrefs.connect(self.appWillSavePrefs) def _getData(self, row, column, role): if column.name == "marked": if role == Qt.CheckStateRole and row.markable: return Qt.Checked if row.marked else Qt.Unchecked if role == Qt.ToolTipRole and not row.markable: return tr("Compilation error: ") + row.get_cell_value("error") if role == Qt.DecorationRole and not row.markable: return QIcon.fromTheme("dialog-error", QIcon(":/error")) return None if role == Qt.DisplayRole: return row.data[column.name] elif role == Qt.FontRole: return QFont(self.view.font()) elif role == Qt.BackgroundRole and column.name == "regex": if row.highlight: return QColor(10, 200, 10) # green elif role == Qt.EditRole: if column.name == "regex": return row.data[column.name] return None def _getFlags(self, row, column): flags = Qt.ItemIsEnabled if column.name == "marked": if row.markable: flags |= Qt.ItemIsUserCheckable elif column.name == "regex": flags |= Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled return flags def _setData(self, row, column, value, role): if role == Qt.CheckStateRole: if column.name == "marked": row.marked = bool(value) return True elif role == Qt.EditRole: if column.name == "regex": return self.model.rename_selected(value) return False
class TransactionTable(TableWithTransactions): COLUMNS = [ Column('status', 25, cantTruncate=True), Column('date', 86, editor=DATE_EDIT, cantTruncate=True), Column('description', 230, editor=DESCRIPTION_EDIT), Column('payee', 150, editor=PAYEE_EDIT), Column('checkno', 80), Column('from', 120, editor=ACCOUNT_EDIT), Column('to', 120, editor=ACCOUNT_EDIT), Column('amount', 100, alignment=Qt.AlignRight, cantTruncate=True, painter=AMOUNT_PAINTER, resizeToFit=True), Column('mtime', 140), ] def __init__(self, model, view): TableWithTransactions.__init__(self, model, view) self.tableDelegate = TransactionTableDelegate(self.model) self.view.setItemDelegate(self.tableDelegate) self.view.sortByColumn(1, Qt.AscendingOrder) # sorted by date by default self.view.deletePressed.connect(self.model.delete)
class ResultsModel(ResultsModelBase): COLUMNS = [ Column("marked", default_width=30), Column("name", default_width=200), Column("folder_path", default_width=180), Column("size", default_width=60), Column("extension", default_width=40), Column("dimensions", default_width=100), Column("exif_timestamp", default_width=120), Column("mtime", default_width=120), Column("percentage", default_width=60), Column("dupe_count", default_width=80), ]
class ResultsModel(ResultsModelBase): COLUMNS = [ Column('marked', defaultWidth=30), Column('name', defaultWidth=200), Column('folder_path', defaultWidth=180), Column('size', defaultWidth=60), Column('extension', defaultWidth=40), Column('dimensions', defaultWidth=100), Column('exif_timestamp', defaultWidth=120), Column('mtime', defaultWidth=120), Column('percentage', defaultWidth=60), Column('dupe_count', defaultWidth=80), ]
class SplitTable(Table): COLUMNS = [ Column('account', 100, editor=ACCOUNT_EDIT), Column('memo', 70), Column('debit', 90, alignment=Qt.AlignRight), Column('credit', 90, alignment=Qt.AlignRight), ] INVALID_INDEX_FLAGS = Qt.ItemIsEnabled | Qt.ItemIsDropEnabled def __init__(self, model, view): Table.__init__(self, model, view) # XXX If we don't call that, our columns will all be very small. Find a more elegant way, # prefeably in the core, to avoid these situations. self.columns.setColumnsWidth(None) view.keyPressed.connect(self.keyPressed) def _getFlags(self, row, column): flags = Table._getFlags(self, row, column) return flags | Qt.ItemIsDragEnabled # --- Drag & Drop def dropMimeData(self, mimeData, action, row, column, parentIndex): if not mimeData.hasFormat(MIME_INDEX): return False # Since we only drop in between items, parentIndex must be invalid, and we use the row arg # to know where the drop took place. if parentIndex.isValid(): return False index = int(bytes(mimeData.data(MIME_INDEX)).decode()) self.model.move_split(index, row) return True def mimeData(self, indexes): data = str(indexes[0].row()) mimeData = QMimeData() mimeData.setData(MIME_INDEX, QByteArray(data.encode())) return mimeData def mimeTypes(self): return [MIME_INDEX] def supportedDropActions(self): return Qt.MoveAction # --- Event Handlers def keyPressed(self, event): # return if (event.key() == Qt.Key_Down) and (self.model.selected_index == len(self.model) - 1): event.ignore() self.model.add()
class ElementTable(Table): COLUMNS = [ Column('page', 50), Column('order', 50), Column('x', 50), Column('y', 50), Column('fontsize', 70), Column('text_length', 70), Column('state', 75), Column('text', 150), ] def __init__(self, model, view): Table.__init__(self, model, view) self._setupKeyBindings() def _setupKeyBindings(self): # we have to keep a reference to the shortcuts for them not to be freed right away self.shortcuts = [] for c in SHORTCUTKEY2FLAG: seq = QKeySequence(c) shortcut = QShortcut(seq, self.view, None, None, Qt.WidgetShortcut) shortcut.activated.connect(self.keyActivated) self.shortcuts.append(shortcut) #--- Event Handlers def keyActivated(self): shortcut = self.sender() key = shortcut.key().toString() self.model.press_key(key)
class GeneralLedgerTable(TableWithTransactions): COLUMNS = [ Column('status', 25, cantTruncate=True), Column('date', 86, editor=DATE_EDIT, cantTruncate=True), Column('reconciliation_date', 110, editor=DATE_EDIT, cantTruncate=True), Column('description', 150, editor=DESCRIPTION_EDIT), Column('payee', 150, editor=PAYEE_EDIT), Column('checkno', 100), Column('transfer', 120, editor=ACCOUNT_EDIT), Column('debit', 95, alignment=Qt.AlignRight, cantTruncate=True), Column('credit', 95, alignment=Qt.AlignRight, cantTruncate=True), Column('balance', 110, alignment=Qt.AlignRight, cantTruncate=True), ] def __init__(self, model, view): TableWithTransactions.__init__(self, model, view) self.view.deletePressed.connect(self.model.delete) # --- Override def _getData(self, row, column, role): is_account_row = self.model.is_account_row(row) if role == EXTRA_ROLE: flags = 0 if is_account_row: flags |= EXTRA_SPAN_ALL_COLUMNS return flags elif is_account_row: if role == Qt.DisplayRole: return row.account_name elif role == Qt.FontRole: font = QFont(self.view.font()) font.setBold(True) return font elif role == Qt.BackgroundRole: return QBrush(QColor(Qt.lightGray)) elif role == Qt.FontRole: font = QFont(self.view.font()) is_bold = self.model.is_bold_row(row) font.setBold(is_bold) return font else: return TableWithTransactions._getData(self, row, column, role) def reset(self): TableWithTransactions.reset(self) self.view.clearSpans() for index, row in enumerate(self.model): if self.model.is_account_row(row): self.view.setSpan(index, 0, 1, len(self.COLUMNS))
class NetWorthSheet(AccountSheet): COLUMNS = [ Column('name', 133), Column('account_number', 80), Column('end', 100, alignment=Qt.AlignRight), Column('start', 100, alignment=Qt.AlignRight), Column('delta', 100, alignment=Qt.AlignRight), Column('delta_perc', 100), Column('budgeted', 100, alignment=Qt.AlignRight), ] AMOUNT_ATTRS = {'end', 'start', 'delta', 'delta_perc', 'budgeted'} BOLD_ATTRS = {'end'}
class ExportAccountTable(Table): COLUMNS = [ Column('name', 80), Column('export', 60), ] def __init__(self, model, view): Table.__init__(self, model, view) view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Fixed) # --- Data methods override def _getData(self, row, column, role): if column.name == 'export': if role == Qt.CheckStateRole: return Qt.Checked if row.export else Qt.Unchecked else: return None else: return Table._getData(self, row, column, role) def _getFlags(self, row, column): flags = Table._getFlags(self, row, column) if column.name == 'export': flags |= Qt.ItemIsUserCheckable | Qt.ItemIsEditable return flags def _setData(self, row, column, value, role): if column.name == 'export': if role == Qt.CheckStateRole: row.export = value return True else: return False else: return Table._setData(self, row, column, value, role)
class ProfitSheet(AccountSheet): COLUMNS = [ Column('name', 133), Column('account_number', 80), Column('cash_flow', 100, alignment=Qt.AlignRight), Column('last_cash_flow', 100, alignment=Qt.AlignRight), Column('delta', 100, alignment=Qt.AlignRight), Column('delta_perc', 100), Column('budgeted', 100, alignment=Qt.AlignRight), ] AMOUNT_ATTRS = { 'cash_flow', 'last_cash_flow', 'delta', 'delta_perc', 'budgeted' } BOLD_ATTRS = { 'cash_flow', }
class ImportTable(Table): COLUMNS = [ Column('will_import', 20, editor=NO_EDIT), Column('date', 80), Column('description', 90), Column('amount', 90, alignment=Qt.AlignRight), Column('bound', 22), Column('date_import', 80), Column('description_import', 90), Column('payee_import', 90), Column('checkno_import', 57), Column('transfer_import', 90), Column('amount_import', 90, alignment=Qt.AlignRight), ] def __init__(self, model, view): Table.__init__(self, model, view) self.view.clicked.connect(self.cellClicked) self.view.spacePressed.connect(self.spacePressed) # --- Data methods override def _getData(self, row, column, role): if column.name == 'will_import': if role == Qt.CheckStateRole: return Qt.Checked if row.will_import else Qt.Unchecked else: return None elif column.name == 'bound': if role == Qt.DecorationRole: return QPixmap(':/lock_12') if row.bound else None else: return None else: return Table._getData(self, row, column, role) def _getFlags(self, row, column): flags = Table._getFlags(self, row, column) if column.name == 'will_import': flags |= Qt.ItemIsUserCheckable | Qt.ItemIsEditable if not row.can_edit_will_import: flags &= ~Qt.ItemIsEnabled if not row.bound: flags |= Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled return flags def _setData(self, row, column, value, role): if column.name == 'will_import': if role == Qt.CheckStateRole: row.will_import = value return True else: return False else: return Table._setData(self, row, column, value, role) # --- Drag & Drop def dropMimeData(self, mimeData, action, row, column, parentIndex): if not mimeData.hasFormat(MIME_INDEXES): return False if not parentIndex.isValid(): return False strMimeData = bytes(mimeData.data(MIME_INDEXES)).decode() indexes = list(map(int, strMimeData.split(','))) if len(indexes) != 1: return False index = indexes[0] if not self.model.can_bind(index, parentIndex.row()): return False # If we dont do the layoutChanged thing, we get a weird situation where the row below the # the dragged row has its will_import unchecked. self.layoutAboutToBeChanged.emit() self.model.bind(index, parentIndex.row()) self.layoutChanged.emit() return True def mimeData(self, indexes): rows = set(str(index.row()) for index in indexes) data = ','.join(rows) mimeData = QMimeData() mimeData.setData(MIME_INDEXES, QByteArray(data.encode())) return mimeData def mimeTypes(self): return [MIME_INDEXES] def supportedDropActions(self): return Qt.MoveAction # --- Private def _switchToOneSidedMode(self): h = self.view.horizontalHeader() if h.isSectionHidden(1): return # already in one sided mode for i in [1, 2, 3, 4]: h.hideSection(i) def _switchToTwoSidedMode(self): h = self.view.horizontalHeader() if not h.isSectionHidden(1): return # already in two sided mode for i in [1, 2, 3, 4]: h.showSection(i) # --- Public def updateColumnsVisibility(self): if self.model.is_two_sided: self._switchToTwoSidedMode() else: self._switchToOneSidedMode() # --- Event Handling def cellClicked(self, index): column = self.model.columns.column_by_index(index.column()) rowattr = column.name if rowattr == 'bound': self.model.unbind(index.row()) def spacePressed(self): selectedIndexes = self.view.selectionModel().selectedRows() for index in selectedIndexes: row = self.model[index.row()] row.will_import = not row.will_import self.refresh()
class IgnoreListTable(Table): COLUMNS = [ Column("path1", defaultWidth=230), Column("path2", defaultWidth=230), ]
class EntryTable(TableWithTransactions): COLUMNS = [ Column('status', 25, cantTruncate=True), Column('date', 86, editor=DATE_EDIT, cantTruncate=True), Column('reconciliation_date', 110, editor=DATE_EDIT, cantTruncate=True), Column('description', 150, editor=DESCRIPTION_EDIT), Column('payee', 150, editor=PAYEE_EDIT), Column('checkno', 100), Column('transfer', 120, editor=ACCOUNT_EDIT), Column('increase', 95, alignment=Qt.AlignRight, cantTruncate=True, painter=AMOUNT_PAINTER, resizeToFit=True), Column('decrease', 95, alignment=Qt.AlignRight, cantTruncate=True, painter=AMOUNT_PAINTER, resizeToFit=True), Column('debit', 95, alignment=Qt.AlignRight, cantTruncate=True), Column('credit', 95, alignment=Qt.AlignRight, cantTruncate=True), Column('balance', 110, alignment=Qt.AlignRight, cantTruncate=True), ] def __init__(self, model, view): TableWithTransactions.__init__(self, model, view) self.tableDelegate = EntryTableDelegate(self.model) self.view.setItemDelegate(self.tableDelegate) self.view.sortByColumn(1, Qt.AscendingOrder) # sorted by date by default self.view.spacePressed.connect(self.model.toggle_reconciled) self.view.deletePressed.connect(self.model.delete) # --- Data methods override def _getStatusData(self, row, role): # DecorationRole is handled in TableWithTransactions if role == Qt.CheckStateRole: if row.can_reconcile(): return Qt.Checked if row.reconciled else Qt.Unchecked else: return None else: return TableWithTransactions._getStatusData(self, row, role) def _getFlags(self, row, column): flags = TableWithTransactions._getFlags(self, row, column) if column.name == 'status': if row.can_reconcile(): flags |= Qt.ItemIsUserCheckable return flags def _setData(self, row, column, value, role): if column.name == 'status': if role == Qt.CheckStateRole: row.toggle_reconciled() self._overrideNextSelectionUpdate() return True else: return False else: return TableWithTransactions._setData(self, row, column, value, role)
class ResultsModel(ResultsModelBase): COLUMNS = [ Column("marked", default_width=30), Column("name", default_width=200), Column("folder_path", default_width=180), Column("size", default_width=60), Column("duration", default_width=60), Column("bitrate", default_width=50), Column("samplerate", default_width=60), Column("extension", default_width=40), Column("mtime", default_width=120), Column("title", default_width=120), Column("artist", default_width=120), Column("album", default_width=120), Column("genre", default_width=80), Column("year", default_width=40), Column("track", default_width=40), Column("comment", default_width=120), Column("percentage", default_width=60), Column("words", default_width=120), Column("dupe_count", default_width=80), ]
class IgnoreListTable(Table): COLUMNS = [ Column('path1', defaultWidth=230), Column('path2', defaultWidth=230), ]
class ResultsModel(ResultsModelBase): COLUMNS = [ Column('marked', defaultWidth=30), Column('name', defaultWidth=200), Column('folder_path', defaultWidth=180), Column('size', defaultWidth=60), Column('duration', defaultWidth=60), Column('bitrate', defaultWidth=50), Column('samplerate', defaultWidth=60), Column('extension', defaultWidth=40), Column('mtime', defaultWidth=120), Column('title', defaultWidth=120), Column('artist', defaultWidth=120), Column('album', defaultWidth=120), Column('genre', defaultWidth=80), Column('year', defaultWidth=40), Column('track', defaultWidth=40), Column('comment', defaultWidth=120), Column('percentage', defaultWidth=60), Column('words', defaultWidth=120), Column('dupe_count', defaultWidth=80), ]