-
-
Notifications
You must be signed in to change notification settings - Fork 426
/
Copy pathtable.py
160 lines (134 loc) · 5.47 KB
/
table.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# Created By: Virgil Dupras
# Created On: 2009-11-01
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html
import typing
from PyQt5.QtCore import (
Qt,
QAbstractTableModel,
QModelIndex,
QItemSelectionModel,
QItemSelection,
)
from qt.column import Columns, Column
class Table(QAbstractTableModel):
# Flags you want when index.isValid() is False. In those cases, _getFlags() is never called.
INVALID_INDEX_FLAGS = Qt.ItemFlag.ItemIsEnabled
COLUMNS: typing.List[Column] = []
def __init__(self, model, view, **kwargs):
super().__init__(**kwargs)
self.model = model
self.view = view
self.view.setModel(self)
self.model.view = self
if hasattr(self.model, "_columns"):
self._columns = Columns(self.model._columns, self.COLUMNS, view.horizontalHeader())
self.view.selectionModel().selectionChanged[(QItemSelection, QItemSelection)].connect(self.selectionChanged)
def _updateModelSelection(self):
# Takes the selection on the view's side and update the model with it.
# an _updateViewSelection() call will normally result in an _updateModelSelection() call.
# to avoid infinite loops, we check that the selection will actually change before calling
# model.select()
new_indexes = [modelIndex.row() for modelIndex in self.view.selectionModel().selectedRows()]
if new_indexes != self.model.selected_indexes:
self.model.select(new_indexes)
def _updateViewSelection(self):
# Takes the selection on the model's side and update the view with it.
new_selection = QItemSelection()
column_count = self.columnCount(QModelIndex())
for index in self.model.selected_indexes:
new_selection.select(self.createIndex(index, 0), self.createIndex(index, column_count - 1))
self.view.selectionModel().select(new_selection, QItemSelectionModel.ClearAndSelect)
if len(new_selection.indexes()):
current_index = new_selection.indexes()[0]
self.view.selectionModel().setCurrentIndex(current_index, QItemSelectionModel.Current)
self.view.scrollTo(current_index)
# --- Data Model methods
# Virtual
def _getData(self, row, column, role):
if role in (Qt.DisplayRole, Qt.EditRole):
attrname = column.name
return row.get_cell_value(attrname)
elif role == Qt.TextAlignmentRole:
return column.alignment
return None
# Virtual
def _getFlags(self, row, column):
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if row.can_edit_cell(column.name):
flags |= Qt.ItemIsEditable
return flags
# Virtual
def _setData(self, row, column, value, role):
if role == Qt.EditRole:
attrname = column.name
if attrname == "from":
attrname = "from_"
setattr(row, attrname, value)
return True
return False
def columnCount(self, index):
return self.model._columns.columns_count()
def data(self, index, role):
if not index.isValid():
return None
row = self.model[index.row()]
column = self.model._columns.column_by_index(index.column())
return self._getData(row, column, role)
def flags(self, index):
if not index.isValid():
return self.INVALID_INDEX_FLAGS
row = self.model[index.row()]
column = self.model._columns.column_by_index(index.column())
return self._getFlags(row, column)
def headerData(self, section, orientation, role):
if orientation != Qt.Horizontal:
return None
if section >= self.model._columns.columns_count():
return None
column = self.model._columns.column_by_index(section)
if role == Qt.DisplayRole:
return column.display
elif role == Qt.TextAlignmentRole:
return column.alignment
else:
return None
def revert(self):
self.model.cancel_edits()
def rowCount(self, index):
if index.isValid():
return 0
return len(self.model)
def setData(self, index, value, role):
if not index.isValid():
return False
row = self.model[index.row()]
column = self.model._columns.column_by_index(index.column())
return self._setData(row, column, value, role)
def sort(self, section, order):
column = self.model._columns.column_by_index(section)
attrname = column.name
self.model.sort_by(attrname, desc=order == Qt.DescendingOrder)
def submit(self):
self.model.save_edits()
return True
# --- Events
def selectionChanged(self, selected, deselected):
self._updateModelSelection()
# --- model --> view
def refresh(self):
self.beginResetModel()
self.endResetModel()
self._updateViewSelection()
def show_selected_row(self):
if self.model.selected_index is not None:
self.view.showRow(self.model.selected_index)
def start_editing(self):
self.view.editSelected()
def stop_editing(self):
self.view.setFocus() # enough to stop editing
def update_selection(self):
self._updateViewSelection()