【问题标题】:QListView Complexe Delegate editor focus to parentQListView Complexe 将编辑器焦点委托给父级
【发布时间】:2021-07-20 23:53:22
【问题描述】:

我正在尝试制作一个 QListView,其中每行都表示有一个复杂的小部件。

我想要一个 QLabel 和一个 QTableView 代表一些数据。

到目前为止一切顺利。

问题是当我点击 QTableView(我禁用了焦点策略)时,它没有选择 QListView 的行。

但是当我点击 QLabel(这也是禁用焦点策略)时它正在工作

我做错了什么吗?我的 change_line 方法确实在单击“这是行”标签时被调用,但不是在它下面的表格上:(

我曾尝试在编辑器中使用 FocusPolicy 和 setFocusProxy,但到目前为止还不能正常工作。

感谢您的帮助

import sys
from typing import Any, Dict

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QAbstractListModel, QModelIndex, Qt, QAbstractTableModel, QSize
from PyQt5.QtMultimedia import QMediaMetaData
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QListView, QWidget, QStyledItemDelegate, \
    QTableView, QHeaderView, QAbstractScrollArea, QLabel


class TableModel(QAbstractTableModel):
    def __init__(self):
        super().__init__()
        self._data_list = []

    @property
    def data_list(self):
        return self._data_list

    @data_list.setter
    def data_list(self, data_list):
        self.beginResetModel()
        self._data_list = data_list
        self.endResetModel()

    def headerData(self, section: int, orientation: QMediaMetaData.Orientation, role: int = ...) -> Any:
        if role == Qt.DisplayRole:
            if section == 0:
                return "header"

    def data(self, index: QModelIndex, role: int = ...) -> Any:
        if index.column() == 0:
            if role == Qt.DisplayRole:
                return self.data_list[index.row()]

    def rowCount(self, parent: QModelIndex = ..., *args, **kwargs) -> int:
        return len(self.data_list)

    def columnCount(self, parent: QModelIndex = ..., *args, **kwargs) -> int:
        return 1


class ListModel(QAbstractListModel):
    def __init__(self):
        super().__init__()

    def rowCount(self, parent=QModelIndex(), *args, **kwargs) -> int:
        return 2

    def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> Any:
        if index.row() == 0:
            if role == Qt.DisplayRole:
                return ["Hello", "World!"]
        elif index.row() == 1:
            if role == Qt.DisplayRole:
                return ["One", "Two", "Three!"]

class ItemEditor(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.verticalLayout = QVBoxLayout(self)
        self.line_label = QLabel("This is a line", self)
        self.verticalLayout.addWidget(self.line_label)
        self.table_view = QTableView(self)
        self.table_view.setFocusPolicy(QtCore.Qt.NoFocus)
        self.table_view.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.table_view.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
        self.table_view.horizontalHeader().setVisible(False)
        self.table_view.verticalHeader().setVisible(False)
        self.verticalLayout.addWidget(self.table_view)


class StyledItemDelegate(QStyledItemDelegate):
    def __init__(self, parent):
        super(StyledItemDelegate, self).__init__(parent)
        self.editors: Dict[int, QTableView] = {}

    def sizeHint(self, option: 'QStyleOptionViewItem', index: QModelIndex) -> QSize:
        if index.row() in self.editors.keys():
            return QSize(min(self.parent().width(), self.editors[index.row()].sizeHint().width()),
                         self.editors[index.row()].sizeHint().height())
        else:
            return super(StyledItemDelegate, self).sizeHint(option, index)

    def createEditor(self, parent, option, index):
        editor = ItemEditor(parent)
        editor.setFocusProxy(parent)

        editor.table_view.setModel(TableModel())
        editor.table_view.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)

        header = editor.table_view.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.Stretch)

        return editor

    def setEditorData(self, editor: QTableView, index):
        editor.table_view.model().data_list = index.data()
        self.editors[index.row()] = editor

def change_line(index: QModelIndex):
    # FIXME Only working when clicking on the "editor" widget or the QLabel, not on the Table
    print(f"Changing to line {index.row()}")

if __name__ == "__main__":
    app = QApplication([])
    main_window = QMainWindow()

    centralwidget = QWidget(main_window)
    verticalLayout = QVBoxLayout(centralwidget)
    listView = QListView(centralwidget)
    listView.clicked.connect(change_line)

    verticalLayout.addWidget(listView)

    main_window.setCentralWidget(centralwidget)

    listModel = ListModel()
    listView.setModel(listModel)

    delegate = StyledItemDelegate(listView)
    listView.setItemDelegate(delegate)

    for i in range(listModel.rowCount()):
        index = listModel.index(i, 0)
        listView.openPersistentEditor(index)

    main_window.resize(600,400)
    main_window.show()
    sys.exit(app.exec_())

【问题讨论】:

  • 表格永远不会接受任何鼠标事件吗?
  • 确实,表格应该只允许单击以选择父行,仅此而已。这只是一个“只读”视图

标签: python model-view-controller pyqt5


【解决方案1】:

由于不需要鼠标交互,因此可以使表格对鼠标事件“透明”:

    self.table_view.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)

请注意,默认情况下,编辑器不会更改单元格的大小提示,而是相反,因此您应该在创建编辑器时发出 sizeHintChanged,并且应该将编辑器添加到 createEditor 的字典中,而不是setEditorData

    self.editors[index.row()] = editor
    QtCore.QTimer.singleShot(0, lambda: self.sizeHintChanged.emit(index))

    return editor

【讨论】:

  • 嘿,谢谢!这很好用:) 很好地抓住了编辑器 sizeHintChanged !我会在我的代码中更改它!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-14
  • 1970-01-01
  • 1970-01-01
  • 2018-11-28
  • 1970-01-01
相关资源
最近更新 更多