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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
# -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 28, 2015
"""
from atom.api import Int, Typed
from enaml.application import timed_call
from qtpy.QtCore import QAbstractTableModel
from qtpy.QtWidgets import QTableView
from enamlx.qt.qt_abstract_item import (
RESIZE_MODES,
AbstractQtWidgetItem,
AbstractQtWidgetItemGroup,
)
from enamlx.qt.qt_abstract_item_view import QAbstractAtomItemModel, QtAbstractItemView
from enamlx.widgets.table_view import (
ProxyTableView,
ProxyTableViewColumn,
ProxyTableViewItem,
ProxyTableViewRow,
)
class QAtomTableModel(QAbstractAtomItemModel, QAbstractTableModel):
"""Model that pulls it's data from the TableViewItems
declaration.
"""
def rowCount(self, parent=None):
d = self.declaration
return len(d.vertical_headers if d.vertical_headers else d.items)
def columnCount(self, parent=None):
d = self.declaration
return len(d.horizontal_headers if d.horizontal_headers else d.items)
def itemAt(self, index):
if not index.isValid():
return None
d = self.declaration
try:
d.current_row = index.row()
d.current_column = index.column()
r = d.current_row - d.visible_row
c = d.current_column - d.visible_column
return d._items[r]._items[c].proxy
except IndexError:
return None
class QtTableView(QtAbstractItemView, ProxyTableView):
#: Proxy widget
widget = Typed(QTableView)
def create_widget(self):
self.widget = QTableView(self.parent_widget())
def init_widget(self):
super(QtTableView, self).init_widget()
d = self.declaration
self.set_show_grid(d.show_grid)
def init_model(self):
self.set_model(QAtomTableModel(parent=self.widget))
# -------------------------------------------------------------------------
# Widget settters
# -------------------------------------------------------------------------
def set_show_grid(self, show):
self.widget.setShowGrid(show)
def set_cell_padding(self, padding):
self.widget.setStyleSheet("QTableView::item { padding: %ipx }" % padding)
def set_vertical_minimum_section_size(self, size):
self.widget.verticalHeader().setMinimumSectionSize(size)
def set_horizontal_minimum_section_size(self, size):
self.widget.horizontalHeader().setMinimumSectionSize(size)
def set_horizontal_stretch(self, stretch):
self.widget.horizontalHeader().setStretchLastSection(stretch)
def set_vertical_stretch(self, stretch):
self.widget.verticalHeader().setStretchLastSection(stretch)
def set_resize_mode(self, mode):
header = self.widget.horizontalHeader()
# Custom is obsolete, use fixed instead.
mode = "fixed" if mode == "custom" else mode
header.setSectionResizeMode(RESIZE_MODES[mode])
def set_show_horizontal_header(self, show):
header = self.widget.horizontalHeader()
header.show() if show else header.hide()
def set_show_vertical_header(self, show):
header = self.widget.verticalHeader()
header.show() if show else header.hide()
# -------------------------------------------------------------------------
# View refresh handlers
# -------------------------------------------------------------------------
def _refresh_visible_column(self, value):
self._pending_column_refreshes -= 1
if self._pending_column_refreshes == 0:
d = self.declaration
cols = self.model.columnCount() - d.visible_columns
d.visible_column = max(0, min(value, cols))
def _refresh_visible_row(self, value):
self._pending_row_refreshes -= 1
if self._pending_row_refreshes == 0 and (self.declaration is not None):
d = self.declaration
rows = self.model.rowCount() - d.visible_rows
d.visible_row = max(0, min(value, rows))
def _refresh_visible_rows(self):
return
top = self.widget.rowAt(self.widget.rect().top())
bottom = self.widget.rowAt(self.widget.rect().bottom())
self.declaration.visible_rows = max(1, (bottom - top)) * 2 # 2x for safety
def _refresh_visible_columns(self):
return
left = self.widget.rowAt(self.widget.rect().left())
right = self.widget.rowAt(self.widget.rect().right())
self.declaration.visible_columns = max(1, (right - left)) * 2
class AbstractQtTableViewItemGroup(AbstractQtWidgetItemGroup):
def create_widget(self):
pass
@property
def widget(self):
return self.parent_widget()
class QtTableViewItem(AbstractQtWidgetItem, ProxyTableViewItem):
#: Pending refreshes when loading widgets
_refresh_count = Int(0)
#: Time to wait before loading widget in ms
_loading_interval = Int(100)
def _default_view(self):
return self.parent().parent()
def set_row(self, row):
self._update_index()
def set_column(self, column):
self._update_index()
def _update_index(self):
"""Update the reference to the index within the table"""
d = self.declaration
self.index = self.view.model.index(d.row, d.column)
if self.delegate:
self._refresh_count += 1
timed_call(self._loading_interval, self._update_delegate)
def _update_delegate(self):
"""Update the delegate cell widget. This is deferred so it
does not get called until the user is done scrolling.
"""
self._refresh_count -= 1
if self._refresh_count != 0:
return
try:
delegate = self.delegate
if not self.is_visible():
return
# The table destroys when it goes out of view
# so we always have to make a new one
delegate.create_widget()
delegate.init_widget()
# Set the index widget
self.view.widget.setIndexWidget(self.index, delegate.widget)
except RuntimeError:
pass # Since this is deferred, the table could be deleted already
def is_visible(self):
"""Check if this index is currently visible"""
return True
def data_changed(self, change):
"""Notify the model that data has changed in this cell!"""
index = self.index
if index:
self.view.model.dataChanged.emit(index, index)
class QtTableViewRow(AbstractQtTableViewItemGroup, ProxyTableViewRow):
pass
class QtTableViewColumn(AbstractQtTableViewItemGroup, ProxyTableViewColumn):
pass
|