~ubuntu-branches/ubuntu/trusty/python-traitsui/trusty

« back to all changes in this revision

Viewing changes to traitsui/qt4/table_model.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-07-09 13:57:39 UTC
  • Revision ID: james.westby@ubuntu.com-20110709135739-x5u20q86huissmn1
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#------------------------------------------------------------------------------
 
2
# Copyright (c) 2008, Riverbank Computing Limited
 
3
# All rights reserved.
 
4
#
 
5
# This software is provided without warranty under the terms of the BSD license.
 
6
# However, when used with the GPL version of PyQt the additional terms described
 
7
# in the PyQt GPL exception also apply.
 
8
#
 
9
# Author: Riverbank Computing Limited
 
10
#------------------------------------------------------------------------------
 
11
 
 
12
""" Defines the table model used by the table editor.
 
13
"""
 
14
 
 
15
#-------------------------------------------------------------------------------
 
16
#  Imports:
 
17
#-------------------------------------------------------------------------------
 
18
 
 
19
from pyface.qt import QtCore, QtGui
 
20
 
 
21
from traitsui.ui_traits import SequenceTypes
 
22
 
 
23
#-------------------------------------------------------------------------------
 
24
#  Constants:
 
25
#-------------------------------------------------------------------------------
 
26
 
 
27
# Mapping for trait alignment values to qt4 horizontal alignment constants
 
28
h_alignment_map = {
 
29
    'left':   QtCore.Qt.AlignLeft,
 
30
    'center': QtCore.Qt.AlignHCenter,
 
31
    'right':  QtCore.Qt.AlignRight,
 
32
}
 
33
 
 
34
# Mapping for trait alignment values to qt4 vertical alignment constants
 
35
v_alignment_map = {
 
36
    'top':    QtCore.Qt.AlignTop,
 
37
    'center': QtCore.Qt.AlignVCenter,
 
38
    'bottom': QtCore.Qt.AlignBottom,
 
39
}
 
40
 
 
41
# MIME type for internal table drag/drop operations
 
42
mime_type = 'traits-ui-table-editor'
 
43
 
 
44
#-------------------------------------------------------------------------------
 
45
#  'TableModel' class:
 
46
#-------------------------------------------------------------------------------
 
47
 
 
48
class TableModel(QtCore.QAbstractTableModel):
 
49
    """The model for table data."""
 
50
 
 
51
    def __init__(self, editor, parent=None):
 
52
        """Initialise the object."""
 
53
 
 
54
        QtCore.QAbstractTableModel.__init__(self, parent)
 
55
 
 
56
        self._editor = editor
 
57
 
 
58
    #---------------------------------------------------------------------------
 
59
    #  QAbstractTableModel interface:
 
60
    #---------------------------------------------------------------------------
 
61
 
 
62
    def rowCount(self, mi):
 
63
        """Reimplemented to return the number of rows."""
 
64
 
 
65
        return len(self._editor.items())
 
66
 
 
67
    def columnCount(self, mi):
 
68
        """Reimplemented to return the number of columns."""
 
69
 
 
70
        return len(self._editor.columns)
 
71
 
 
72
    def data(self, mi, role):
 
73
        """Reimplemented to return the data."""
 
74
 
 
75
        obj = self._editor.items()[mi.row()]
 
76
        column = self._editor.columns[mi.column()]
 
77
 
 
78
        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
 
79
            text = column.get_value(obj)
 
80
            if text is not None:
 
81
                return text
 
82
 
 
83
        elif role == QtCore.Qt.ToolTipRole:
 
84
            tooltip = column.get_tooltip(obj)
 
85
            if tooltip:
 
86
                return tooltip
 
87
 
 
88
        elif role == QtCore.Qt.FontRole:
 
89
            font = column.get_text_font(obj)
 
90
            if font is not None:
 
91
                return QtGui.QFont(font)
 
92
 
 
93
        elif role == QtCore.Qt.TextAlignmentRole:
 
94
            string = column.get_horizontal_alignment(obj)
 
95
            h_alignment = h_alignment_map.get(string, QtCore.Qt.AlignLeft)
 
96
            string = column.get_vertical_alignment(obj)
 
97
            v_alignment = v_alignment_map.get(string, QtCore.Qt.AlignVCenter)
 
98
            return (h_alignment | v_alignment)
 
99
 
 
100
        elif role == QtCore.Qt.BackgroundRole:
 
101
            color = column.get_cell_color(obj)
 
102
            if color is not None:
 
103
                if isinstance(color, SequenceTypes):
 
104
                    q_color = QtGui.QColor(*color)
 
105
                else:
 
106
                    q_color = QtGui.QColor(color)
 
107
                return QtGui.QBrush(q_color)
 
108
 
 
109
        elif role == QtCore.Qt.ForegroundRole:
 
110
            color = column.get_text_color(obj)
 
111
            if color is not None:
 
112
                if isinstance(color, SequenceTypes):
 
113
                    q_color = QtGui.QColor(*color)
 
114
                else:
 
115
                    q_color = QtGui.QColor(color)
 
116
                return QtGui.QBrush(q_color)
 
117
 
 
118
        elif role == QtCore.Qt.UserRole:
 
119
            return obj
 
120
 
 
121
        return None
 
122
 
 
123
    def flags(self, mi):
 
124
        """Reimplemented to set editable and movable status."""
 
125
 
 
126
        flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
 
127
 
 
128
        if not mi.isValid():
 
129
            return flags
 
130
 
 
131
        editor = self._editor
 
132
        obj = editor.items()[mi.row()]
 
133
        column = editor.columns[mi.column()]
 
134
 
 
135
        if editor.factory.editable and column.is_editable(obj):
 
136
            flags |= QtCore.Qt.ItemIsEditable
 
137
 
 
138
        if editor.factory.reorderable:
 
139
            flags |= QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled
 
140
 
 
141
        return flags
 
142
 
 
143
    def headerData(self, section, orientation, role):
 
144
        """Reimplemented to return the header data."""
 
145
 
 
146
        if orientation == QtCore.Qt.Horizontal:
 
147
 
 
148
            editor = self._editor
 
149
            column = editor.columns[section]
 
150
 
 
151
            if role == QtCore.Qt.DisplayRole:
 
152
                return column.get_label()
 
153
 
 
154
        elif orientation == QtCore.Qt.Vertical:
 
155
 
 
156
            if role == QtCore.Qt.DisplayRole:
 
157
                return str(section + 1)
 
158
 
 
159
        return None
 
160
 
 
161
    def insertRow(self, row, parent=QtCore.QModelIndex(), obj=None):
 
162
        """Reimplemented to allow creation of new rows. Added an optional
 
163
        arg to allow the insertion of an existing row object."""
 
164
 
 
165
        editor = self._editor
 
166
        if obj is None:
 
167
            obj = editor.create_new_row()
 
168
 
 
169
        self.beginInsertRows(parent, row, row)
 
170
        editor.callx(editor.items().insert, row, obj)
 
171
        self.endInsertRows()
 
172
        return True
 
173
 
 
174
    def insertRows(self, row, count, parent=QtCore.QModelIndex()):
 
175
        """Reimplemented to allow creation of new rows."""
 
176
 
 
177
        editor = self._editor
 
178
        items = editor.items()
 
179
        self.beginInsertRows(parent, row, row + count - 1)
 
180
        for i in xrange(count):
 
181
            editor.callx(items.insert, row + i, editor.create_new_row())
 
182
        self.endInsertRows()
 
183
        return True
 
184
 
 
185
    def removeRows(self, row, count, parent=QtCore.QModelIndex()):
 
186
        """Reimplemented to allow row deletion, as well as reordering via drag
 
187
        and drop."""
 
188
 
 
189
        editor = self._editor
 
190
        items = editor.items()
 
191
        self.beginRemoveRows(parent, row, row + count - 1)
 
192
        for i in xrange(count):
 
193
            editor.callx(items.pop, row + i)
 
194
        self.endRemoveRows()
 
195
        return True
 
196
 
 
197
    def mimeTypes(self):
 
198
        """Reimplemented to expose our internal MIME type for drag and drop
 
199
        operations."""
 
200
 
 
201
        types = QtCore.QStringList()
 
202
        types.append(mime_type)
 
203
        return types
 
204
 
 
205
    def mimeData(self, indexes):
 
206
        """Reimplemented to generate MIME data containing the rows of the
 
207
        current selection."""
 
208
 
 
209
        mime_data = QtCore.QMimeData()
 
210
        rows = list(set([ index.row() for index in indexes ]))
 
211
        data = QtCore.QByteArray(str(rows[0]))
 
212
        for row in rows[1:]:
 
213
            data.append(' %i' % row)
 
214
        mime_data.setData(mime_type, data)
 
215
        return mime_data
 
216
 
 
217
    def dropMimeData(self, mime_data, action, row, column, parent):
 
218
        """Reimplemented to allow items to be moved."""
 
219
 
 
220
        if action == QtCore.Qt.IgnoreAction:
 
221
            return False
 
222
 
 
223
        data = mime_data.data(mime_type)
 
224
        if data.isNull():
 
225
            return False
 
226
 
 
227
        current_rows = map(int, str(data).split(' '))
 
228
        self.moveRows(current_rows, parent.row())
 
229
        return True
 
230
 
 
231
    def supportedDropActions(self):
 
232
        """Reimplemented to allow items to be moved."""
 
233
 
 
234
        return QtCore.Qt.MoveAction
 
235
 
 
236
    #---------------------------------------------------------------------------
 
237
    #  TableModel interface:
 
238
    #---------------------------------------------------------------------------
 
239
 
 
240
    def moveRow(self, old_row, new_row):
 
241
        """Convenience method to move a single row."""
 
242
 
 
243
        return self.moveRows([old_row], new_row)
 
244
 
 
245
    def moveRows(self, current_rows, new_row):
 
246
        """Moves a sequence of rows (provided as a list of row indexes) to a new
 
247
        row."""
 
248
 
 
249
        # Sort rows in descending order so they can be removed without
 
250
        # invalidating the indices.
 
251
        current_rows.sort()
 
252
        current_rows.reverse()
 
253
 
 
254
        # If the the highest selected row is lower than the destination, do an
 
255
        # insertion before rather than after the destination.
 
256
        if current_rows[-1] < new_row:
 
257
            new_row += 1
 
258
 
 
259
        # Remove selected rows...
 
260
        items = self._editor.items()
 
261
        objects = []
 
262
        for row in current_rows:
 
263
            if row <= new_row:
 
264
                new_row -= 1
 
265
            objects.insert(0, items[row])
 
266
            self.removeRow(row)
 
267
 
 
268
        # ...and add them at the new location.
 
269
        for i, obj in enumerate(objects):
 
270
            self.insertRow(new_row + i, obj=obj)
 
271
 
 
272
        # Update the selection for the new location.
 
273
        self._editor.set_selection(objects)
 
274
 
 
275
#-------------------------------------------------------------------------------
 
276
#  'SortFilterTableModel' class:
 
277
#-------------------------------------------------------------------------------
 
278
 
 
279
class SortFilterTableModel(QtGui.QSortFilterProxyModel):
 
280
    """A wrapper for the TableModel which provides sorting and filtering
 
281
    capability."""
 
282
 
 
283
    def __init__(self, editor, parent=None):
 
284
        """Initialise the object."""
 
285
 
 
286
        QtGui.QSortFilterProxyModel.__init__(self, parent)
 
287
 
 
288
        self._editor = editor
 
289
 
 
290
    #---------------------------------------------------------------------------
 
291
    #  QSortFilterProxyModel interface:
 
292
    #---------------------------------------------------------------------------
 
293
 
 
294
    def filterAcceptsRow(self, source_row, source_parent):
 
295
        """"Reimplemented to use a TableFilter for filtering rows."""
 
296
 
 
297
        if self._editor._filtered_cache is None:
 
298
            return True
 
299
        else:
 
300
            return self._editor._filtered_cache[source_row]
 
301
 
 
302
    def filterAcceptsColumn(self, source_column, source_parent):
 
303
        """Reimplemented to save time, because we always return True."""
 
304
 
 
305
        return True
 
306
 
 
307
    def lessThan(self, left_mi, right_mi):
 
308
        """Reimplemented to sort according to the 'cmp' method defined for
 
309
        TableColumn."""
 
310
 
 
311
        editor = self._editor
 
312
        column = editor.columns[left_mi.column()]
 
313
        items = editor.items()
 
314
        left, right = items[left_mi.row()], items[right_mi.row()]
 
315
 
 
316
        return column.cmp(left, right) < 0
 
317
 
 
318
    #---------------------------------------------------------------------------
 
319
    #  SortFilterTableModel interface:
 
320
    #---------------------------------------------------------------------------
 
321
 
 
322
    def moveRow(self, old_row, new_row):
 
323
        """Convenience method to move a single row."""
 
324
 
 
325
        return self.moveRows([old_row], new_row)
 
326
 
 
327
    def moveRows(self, current_rows, new_row):
 
328
        """Delegate to source model with mapped rows."""
 
329
 
 
330
        source = self.sourceModel()
 
331
        current_rows = [ self.mapToSource(self.index(row, 0)).row()
 
332
                         for row in current_rows ]
 
333
        new_row = self.mapToSource(self.index(new_row, 0)).row()
 
334
        source.moveRows(current_rows, new_row)