Reputation: 39
I'm trying to display in a PyQT5 Application a QListView to display some data.
My goal is to represent those data with a complex representation, not just line of text.
So, my QListView should "instantiate" a new QWidget loaded from a Ui file, and display each elements with that Ui representation.
My issue is with the layout of my Windows.
I have a main window, in which there is a QSplitter, the QListView is on the left, and on the right there are some widgets (Label, textedit, ...)
QMainWindow Designer:
Each line of my QListView I want to represent as this small widget:
Mini Widget Designer:
It seems that IF the QListView is inside a Widget Container like this:
Arbo object inspectors:
Then I have a weird behavior where child widget are displayed on top of each others:
ListView widget stacking:
But if I remove the container of the QListView OkWindow Designer:
and the view of the inspector
They are properly displayed
Working as intented:
Any idea why?
Is there anything wrong with my code and the way to implement complex widget representation inside a Model/View QListView?
Here is the code of the application example:
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 file
<?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>
Upvotes: 0
Views: 1412
Reputation: 243887
Creating a variable that stores a QWidget is useless and dangerous since for example you would only have the last widget or worse would be to access a widget that has been removed. Instead you must use a role to store and get the size, and the default role is 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_())
Upvotes: 1