You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

266 lines
8.7 KiB

# coding: utf-8
from typing import List, Union
from PySide6.QtCore import Qt, QMargins, QModelIndex, QItemSelectionModel
from PySide6.QtGui import QPainter, QColor, QKeyEvent, QPalette
from PySide6.QtWidgets import (QStyledItemDelegate, QApplication, QStyleOptionViewItem,
QTableView, QTableWidget, QWidget, QTableWidgetItem)
from ...common.font import getFont
from ...common.style_sheet import isDarkTheme, FluentStyleSheet, themeColor
from .line_edit import LineEdit
from .scroll_bar import SmoothScrollDelegate
class TableItemDelegate(QStyledItemDelegate):
def __init__(self, parent: QTableView):
super().__init__(parent)
self.margin = 2
self.hoverRow = -1
self.pressedRow = -1
self.selectedRows = set()
def setHoverRow(self, row: int):
self.hoverRow = row
def setPressedRow(self, row: int):
self.pressedRow = row
def setSelectedRows(self, indexes: List[QModelIndex]):
self.selectedRows.clear()
for index in indexes:
self.selectedRows.add(index.row())
if index.row() == self.pressedRow:
self.pressedRow = -1
def sizeHint(self, option, index):
# increase original sizeHint to accommodate space needed for border
size = super().sizeHint(option, index)
size = size.grownBy(QMargins(0, self.margin, 0, self.margin))
return size
def createEditor(self, parent: QWidget, option: QStyleOptionViewItem, index: QModelIndex) -> QWidget:
lineEdit = LineEdit(parent)
lineEdit.setProperty("transparent", False)
lineEdit.setStyle(QApplication.style())
lineEdit.setText(option.text)
lineEdit.setClearButtonEnabled(True)
return lineEdit
def updateEditorGeometry(self, editor: QWidget, option: QStyleOptionViewItem, index: QModelIndex):
rect = option.rect
y = rect.y() + (rect.height() - editor.height()) // 2
x, w = max(8, rect.x()), rect.width()
if index.column() == 0:
w -= 8
editor.setGeometry(x, y, w, rect.height())
def _drawBackground(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
""" draw row background """
r = 5
if index.column() == 0:
rect = option.rect.adjusted(4, 0, r + 1, 0)
painter.drawRoundedRect(rect, r, r)
elif index.column() == index.model().columnCount(index.parent()) - 1:
rect = option.rect.adjusted(-r - 1, 0, -4, 0)
painter.drawRoundedRect(rect, r, r)
else:
rect = option.rect.adjusted(-1, 0, 1, 0)
painter.drawRect(rect)
def _drawIndicator(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
""" draw indicator """
y, h = option.rect.y(), option.rect.height()
ph = round(0.35*h if self.pressedRow == index.row() else 0.257*h)
painter.setBrush(themeColor())
painter.drawRoundedRect(4, ph + y, 3, h - 2*ph, 1.5, 1.5)
def initStyleOption(self, option: QStyleOptionViewItem, index: QModelIndex):
super().initStyleOption(option, index)
option.font = getFont(13)
if isDarkTheme():
option.palette.setColor(QPalette.Text, Qt.white)
option.palette.setColor(QPalette.HighlightedText, Qt.white)
else:
option.palette.setColor(QPalette.Text, Qt.black)
option.palette.setColor(QPalette.HighlightedText, Qt.black)
def paint(self, painter, option, index):
painter.save()
painter.setPen(Qt.NoPen)
painter.setRenderHint(QPainter.Antialiasing)
# set clipping rect of painter to avoid painting outside the borders
painter.setClipping(True)
painter.setClipRect(option.rect)
# call original paint method where option.rect is adjusted to account for border
option.rect.adjust(0, self.margin, 0, -self.margin)
# draw highlight background
isHover = self.hoverRow == index.row()
isPressed = self.pressedRow == index.row()
isAlternate = index.row() % 2 == 0 and self.parent().alternatingRowColors()
isDark = isDarkTheme()
c = 255 if isDark else 0
alpha = 0
if index.row() not in self.selectedRows:
if isPressed:
alpha = 9 if isDark else 6
elif isHover:
alpha = 12
elif isAlternate:
alpha = 5
else:
if isPressed:
alpha = 15 if isDark else 9
elif isHover:
alpha = 25
else:
alpha = 17
# draw indicator
if index.column() == 0 and self.parent().horizontalScrollBar().value() == 0:
self._drawIndicator(painter, option, index)
painter.setBrush(QColor(c, c, c, alpha))
self._drawBackground(painter, option, index)
painter.restore()
super().paint(painter, option, index)
class TableBase:
""" Table base class """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.delegate = TableItemDelegate(self)
self.scrollDelagate = SmoothScrollDelegate(self)
# set style sheet
FluentStyleSheet.TABLE_VIEW.apply(self)
self.setShowGrid(False)
self.setMouseTracking(True)
self.setAlternatingRowColors(True)
self.setItemDelegate(self.delegate)
self.setSelectionBehavior(TableWidget.SelectRows)
self.entered.connect(lambda i: self._setHoverRow(i.row()))
self.pressed.connect(lambda i: self._setPressedRow(i.row()))
self.verticalHeader().sectionClicked.connect(self.selectRow)
def showEvent(self, e):
QTableView.showEvent(self, e)
self.resizeRowsToContents()
def _setHoverRow(self, row: int):
""" set hovered row """
self.delegate.setHoverRow(row)
self.viewport().update()
def _setPressedRow(self, row: int):
""" set pressed row """
self.delegate.setPressedRow(row)
self.viewport().update()
def _setSelectedRows(self, indexes: List[QModelIndex]):
self.delegate.setSelectedRows(indexes)
self.viewport().update()
def leaveEvent(self, e):
QTableView.leaveEvent(self, e)
self._setHoverRow(-1)
def resizeEvent(self, e):
QTableView.resizeEvent(self, e)
self.viewport().update()
def keyPressEvent(self, e: QKeyEvent):
QTableView.keyPressEvent(self, e)
self._updateSelectedRows()
def mousePressEvent(self, e: QKeyEvent):
if e.button() == Qt.LeftButton:
QTableView.mousePressEvent(self, e)
else:
self._setPressedRow(self.indexAt(e.pos()).row())
def mouseReleaseEvent(self, e):
QTableView.mouseReleaseEvent(self, e)
row = self.indexAt(e.pos()).row()
if row >= 0 and e.button() != Qt.RightButton:
self._updateSelectedRows()
else:
self._setPressedRow(-1)
def setItemDelegate(self, delegate: TableItemDelegate):
self.delegate = delegate
super().setItemDelegate(delegate)
def selectAll(self):
QTableView.selectAll(self)
self._updateSelectedRows()
def selectRow(self, row: int):
QTableView.selectRow(self, row)
self._updateSelectedRows()
def setSelection(self, rect, command):
QTableView.setSelection(self, rect, command)
self._updateSelectedRows()
def clearSelection(self):
QTableView.clearSelection(self)
self._updateSelectedRows()
def setCurrentIndex(self, index: QModelIndex):
QTableView.setCurrentIndex(self, index)
self._updateSelectedRows()
def _updateSelectedRows(self):
self._setSelectedRows(self.selectedIndexes())
class TableWidget(TableBase, QTableWidget):
""" Table widget """
def __init__(self, parent=None):
super().__init__(parent)
def setCurrentCell(self, row: int, column: int, command=None):
self.setCurrentItem(self.item(row, column), command)
def setCurrentItem(self, item: QTableWidgetItem, command=None):
if not command:
super().setCurrentItem(item)
else:
super().setCurrentItem(item, command)
self._updateSelectedRows()
def setCurrentCell(self, row: int, column: int, command=None):
self.setCurrentItem(self.item(row, column), command)
def setCurrentItem(self, item: QTableWidgetItem, command=None):
if not command:
super().setCurrentItem(item)
else:
super().setCurrentItem(item, command)
self._updateSelectedRows()
class TableView(TableBase, QTableView):
""" Table view """
def __init__(self, parent=None):
super().__init__(parent)