【问题标题】:Model/View QListView with complex widget representation具有复杂小部件表示的模型/视图 QListView
【发布时间】:2021-07-13 14:53:08
【问题描述】:

我正在尝试在 PyQT5 应用程序中显示 QListView 以显示一些数据。

我的目标是用复杂的表示来表示这些数据,而不仅仅是一行文本。

因此,我的 QListView 应该“实例化”从 Ui 文件加载的新 QWidget,并使用该 Ui 表示显示每个元素。

我的问题在于我的 Windows 布局。

我有一个主窗口,其中有一个 QSplitter,QListView 位于左侧,右侧有一些小部件(Label、textedit、...) QMainWindow 设计器:

我想要表示为这个小部件的 QListView 的每一行: 小部件设计器:

似乎如果 QListView 在这样的小部件容器内:

Arbo 对象检查器:

然后我有一个奇怪的行为,其中子小部件显示在彼此之上:

ListView 小部件堆叠:

但是如果我删除 QListView 的容器 OkWindow 设计器:

和检查员的观点

它们显示正确

按预期工作:

知道为什么吗?

我的代码和在模型/视图 QListView 中实现复杂小部件表示的方式有什么问题吗?

这是应用示例的代码:

import typing

from PyQt5 import QtCore, uic
from PyQt5.QtCore import QAbstractListModel, QModelIndex, Qt
from PyQt5.QtWidgets import (
    QApplication,
    QStyledItemDelegate,
    QWidget, QMainWindow, )


class Editor(QWidget):
    def __init__(self, index, parent=None):
        super().__init__(parent)
        uic.loadUi('step.ui', self)

        value = index.model().data(index, Qt.ItemDataRole)

        self.label.setText(value)


class MyListModel(QAbstractListModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._data_list = []

    def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> typing.Any:
        if role == Qt.ItemDataRole:
            return self._data_list[index.row()]

    def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
        return len(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.copy()
        self.endResetModel()


class StyledItemDelegate(QStyledItemDelegate):

    def __init__(self, parent):
        QStyledItemDelegate.__init__(self, parent)
        self.editor = None

    def createEditor(self, parent, option, index):
        self.editor = Editor(index, parent)
        return self.editor

    def sizeHint(self, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> QtCore.QSize:
        if self.editor:
            return self.editor.sizeHint()
        else:
            return super().sizeHint(option, index)

data = ["Apple", "Strawberry", "Cherry"]


class MainWindow(QMainWindow):
    def __init__(self, qapp):
        """
        Init main window
        """
        super().__init__()

        self.qapp = qapp
        # Not working main_window ui file 
        # uic.loadUi('main_window.ui', self)

        # Working main_window ui file 
        uic.loadUi('main_window_ok.ui', self)

        self.load_data()

    def load_data(self):
        controller = MyCtrl(self)
        controller.load_datalist()

class MyCtrl:
    def __init__(self, parent: QMainWindow):
        self.parent = parent

    def load_datalist(self):
        self.model = MyListModel()
        self.model.data_list = data
        self.parent.listView.setModel(self.model)
        delegate = StyledItemDelegate(self.parent.listView)
        self.parent.listView.setItemDelegate(delegate)

        for i in range(self.model.rowCount()):
            index = self.model.index(i, 0)
            self.parent.listView.openPersistentEditor(index)


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    main = MainWindow(app)
    main.showMaximized()

    sys.exit(app.exec_())

main_window.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>642</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout_3">
    <item>
     <widget class="QSplitter" name="splitter">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
      <widget class="QWidget" name="widget" native="true">
       <layout class="QVBoxLayout" name="verticalLayout">
        <item>
         <widget class="QListView" name="listView"/>
        </item>
       </layout>
      </widget>
      <widget class="QWidget" name="widget_2" native="true">
       <layout class="QVBoxLayout" name="verticalLayout_2">
        <item>
         <widget class="QLabel" name="label">
          <property name="text">
           <string>TextLabel</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="lineEdit"/>
        </item>
        <item>
         <widget class="QSlider" name="horizontalSlider">
          <property name="orientation">
           <enum>Qt::Horizontal</enum>
          </property>
         </widget>
        </item>
        <item>
         <spacer name="verticalSpacer">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
       </layout>
      </widget>
     </widget>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="pushButton_2">
        <property name="text">
         <string>Cancel</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="pushButton">
        <property name="text">
         <string>Ok</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>26</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

main_window_ok.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>799</width>
    <height>642</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout_3">
    <item>
     <widget class="QSplitter" name="splitter">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
      <widget class="QListView" name="listView"/>
      <widget class="QWidget" name="widget_2" native="true">
       <layout class="QVBoxLayout" name="verticalLayout_2">
        <item>
         <widget class="QLabel" name="label">
          <property name="text">
           <string>TextLabel</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QLineEdit" name="lineEdit"/>
        </item>
        <item>
         <widget class="QSlider" name="horizontalSlider">
          <property name="orientation">
           <enum>Qt::Horizontal</enum>
          </property>
         </widget>
        </item>
        <item>
         <spacer name="verticalSpacer">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
       </layout>
      </widget>
     </widget>
    </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="pushButton_2">
        <property name="text">
         <string>Cancel</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="pushButton">
        <property name="text">
         <string>Ok</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>799</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

step.ui 文件

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>387</width>
    <height>173</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="label">
     <property name="text">
      <string>TextLabel</string>
     </property>
    </widget>
   </item>
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <widget class="QDial" name="dial"/>
     </item>
     <item>
      <widget class="QComboBox" name="comboBox"/>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

【问题讨论】:

    标签: python model-view-controller pyqt5


    【解决方案1】:

    创建一个存储 QWidget 的变量是无用且危险的,因为例如您将只有最后一个小部件,或者更糟的是访问已删除的小部件。相反,您必须使用角色来存储和获取大小,并且默认角色是 Qt::SizeHintRole。

    from PyQt5.uic import loadUi
    from PyQt5.QtCore import QAbstractListModel, QModelIndex, Qt, QSize
    from PyQt5.QtWidgets import (
        QApplication,
        QStyledItemDelegate,
        QWidget,
        QMainWindow,
    )
    
    
    class Editor(QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
            loadUi("step.ui", self)
    
    
    class MyListModel(QAbstractListModel):
        def __init__(self, parent=None):
            super().__init__(parent)
            self._data_list = list()
    
            self._size_hints = dict()
    
        def rowCount(self, parent=QModelIndex()):
            if parent.isValid():
                return 0
            return len(self._data_list)
    
        def data(self, index, role=Qt.DisplayRole):
            if role == Qt.DisplayRole:
                return self._data_list[index.row()]
            elif role == Qt.SizeHintRole:
                return self._size_hints.get(index.row(), QSize(100, 30))
    
        def setData(self, index, value, role=Qt.EditRole):
            if role == Qt.SizeHintRole:
                self._size_hints[index.row()] = value
                self.dataChanged.emit(index, index, (role,))
                return True
            return False
    
        @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.copy()
            self.endResetModel()
    
    
    class StyledItemDelegate(QStyledItemDelegate):
        def createEditor(self, parent, option, index):
            editor = Editor(parent)
            model = index.model()
            model.setData(index, editor.sizeHint(), Qt.SizeHintRole)
            return editor
    
        def setEditorData(self, editor, index):
            value = index.data()
            editor.label.setText(value)
    
    
    data = ["Apple", "Strawberry", "Cherry"]
    
    
    class MainWindow(QMainWindow):
        def __init__(self):
            """
            Init main window
            """
            super().__init__()
            loadUi("main_window.ui", self)
    
    
    class MyCtrl:
        def __init__(self, view):
            self.view = view
    
        def load_datalist(self):
            self.model = MyListModel()
            self.model.data_list = data
            self.view.listView.setModel(self.model)
            delegate = StyledItemDelegate(self.view.listView)
            self.view.listView.setItemDelegate(delegate)
    
            for i in range(self.model.rowCount()):
                index = self.model.index(i, 0)
                self.view.listView.openPersistentEditor(index)
    
    
    if __name__ == "__main__":
        import sys
    
        app = QApplication(sys.argv)
    
        main = MainWindow()
        main.showMaximized()
    
        controller = MyCtrl(main)
        controller.load_datalist()
    
        sys.exit(app.exec_())
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 2014-12-04
    • 2015-04-14
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多