具有QTableView和QSelectionModel的日历样式选择

问题描述 投票:0回答:1

我正在使用QTableView构建自定义日历视图,并希望有一个QItemSelectionModel可以按日和周连续选择单元格。由于选择模型不会与视图交互,因此不确定从哪里开始。视图的onCurrentChange方法提供当前索引,在selectionModel中不起作用。

该视图通常连接到更复杂的日历模型;此处的表模型用于说明。

from PyQt5.QtCore import QModelIndex, QDate
from PyQt5.QtCore import Qt, QAbstractTableModel, QItemSelectionModel

from PyQt5.QtWidgets import QTableView
import typing


class TableModel(QAbstractTableModel):
    def __init__(self):
        super(TableModel, self).__init__()

    def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any:
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QDate.longDayName(section + 1)

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return index.row() * 7 + index.column() + 1

    def rowCount(self, index):
        return 6

    def columnCount(self, index):
        return 7


class CalendarSelectionModel(QItemSelectionModel):

    def __init__(self, *args, **kwargs):
        super(CalendarSelectionModel, self).__init__(*args, *kwargs)

    def currentChanged(self, current: QModelIndex, previous: QModelIndex) -> None:
        print(current, previous)


class CalendarView(QTableView):

    def __init__(self):
        super(CalendarView, self).__init__()

    # def currentChanged(self, current: QModelIndex, previous: QModelIndex) -> None:
    #     print(current)


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication, QTableView

    app = QApplication(sys.argv)

    model = TableModel()

    cal = CalendarView()
    cal.setModel(model)
    sel = CalendarSelectionModel(model)
    cal.setSelectionModel(sel)
    cal.show()

    cal.resize(860, 640)

    sys.exit(app.exec_())

python pyqt pyqt5 qtableview
1个回答
0
投票

这里是日历样式选择的解决方案。在表上设置QAbstractItemView.NoSelection并将选择逻辑放在currentChanged中是方法。

import typing

from PyQt5.QtCore import QDate
from PyQt5.QtCore import Qt, QAbstractTableModel, QItemSelectionModel, QItemSelection, QModelIndex
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QTableView, QAbstractItemView


class TableModel(QAbstractTableModel):
    def __init__(self):
        super(TableModel, self).__init__()

    def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any:
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QDate.longDayName(section + 1)

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return index.row() * 7 + index.column() + 1

    def rowCount(self, index):
        return 6

    def columnCount(self, index):
        return 7


class CalendarView(QTableView):

    def __init__(self):
        super(CalendarView, self).__init__()
        self.setSelectionMode(QAbstractItemView.NoSelection)
        self._start: typing.Union[QModelIndex, None] = None

    def mousePressEvent(self, e: QMouseEvent) -> None:
        self._start = self.indexAt(e.pos())
        self.clearSelection()
        self.selectionModel().select(self._start, QItemSelectionModel.Select)
        super(CalendarView, self).mousePressEvent(e)

    def index(self, row, col):
        return self.model().index(row, col)

    def currentChanged(self, current: QModelIndex, previous: QModelIndex) -> None:
        if self.state() == QAbstractItemView.DragSelectingState:
            self.clearSelection()
            if current.row() == self._start.row():  # we only have one row, select from start to current
                selection = QItemSelection(self._start, self.model().index(self._start.row(), current.column()))
            else:  # more than one row selected make the other 2 selections
                selection = QItemSelection(self._start, current)
                if current.row() < self._start.row():  # stencil out diagonal
                    right = QItemSelection(current, self.index(self._start.row() - 1, 6))
                    left = QItemSelection(self.index(current.row() + 1, 0), self._start)
                    selection.merge(right, QItemSelectionModel.Select)
                    selection.merge(left, QItemSelectionModel.Select)
                    if current.column() > self._start.column():
                        stencil = QItemSelection(self.index(current.row(), current.column() - 1),
                                                 self.index(current.row(), 0))
                        stencil2 = QItemSelection(self.index(self._start.row(), self._start.column() + 1),
                                                  self.index(self._start.row(), 6))
                        selection.merge(stencil, QItemSelectionModel.Deselect)
                        selection.merge(stencil2, QItemSelectionModel.Deselect)
                else:
                    right = QItemSelection(self._start, self.index(current.row() - 1, 6))
                    left = QItemSelection(self.index(self._start.row() + 1, 0), current)
                    selection.merge(right, QItemSelectionModel.Select)
                    selection.merge(left, QItemSelectionModel.Select)
                    if current.column() < self._start.column():  # stencil out diagonal
                        stencil = QItemSelection(self.index(self._start.row(), self._start.column() - 1),
                                                 self.index(self._start.row(), 0))
                        stencil2 = QItemSelection(self.index(current.row(), current.column() + 1),
                                                  self.index(current.row(), 6))
                        selection.merge(stencil, QItemSelectionModel.Deselect)
                        selection.merge(stencil2, QItemSelectionModel.Deselect)
            self.selectionModel().select(selection, QItemSelectionModel.Select)


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication, QTableView

    app = QApplication(sys.argv)

    model = TableModel()

    cal = CalendarView()
    cal.setModel(model)
    cal.show()

    cal.resize(860, 640)

    sys.exit(app.exec_())

© www.soinside.com 2019 - 2024. All rights reserved.