在 QTreeView 中添加一个 Widget (QProgressbar) 作为 childItem
Add a Widget (QProgressbar) as a childItem within a QTreeView
我制作了一个向 QTreeView 添加数据的模型。我想添加一个小部件(例如:QProgressbar)作为该 QTreeView 中每一行的子元素。这样每次我们点击每一行时,我们都可以看到进度条作为该行的 childItem
main.py
import sys
from PySide2.QtWidgets import QApplication, QMainWindow, QHeaderView, QPushButton
from PySide2 import QtCore
from TreeViewUI import Ui_MainWindow
from custom_models.table_model import CustomTableModel
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, data):
QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.table_model = CustomTableModel(data)
self.treeView.setIconSize(QtCore.QSize(360 / 4, 640 / 4))
self.treeView.header().setSectionResizeMode(QHeaderView.ResizeToContents)
self.treeView.setModel(self.table_model)
def get_data():
content_list = [
{
"user_id": 101,
"user_name": "User_1",
"user_image": "images/demo.jpg",
"user_details": "details_1",
"user_progress": 45,
},
{
"user_id": 102,
"user_name": "User_2",
"user_image": "images/demo.jpg",
"user_details": "details_2",
"user_progress": 33,
},
{
"user_id": 103,
"user_name": "User_3",
"user_image": "images/demo.jpg",
"user_details": "details_3",
"user_progress": 80,
},
]
return content_list
if __name__ == '__main__':
app = QApplication([])
data = get_data()
main_window = MainWindow(data)
main_window.showMaximized()
sys.exit(app.exec_())
table_model.py
from PySide2.QtCore import Qt, QAbstractTableModel, QModelIndex
from PySide2.QtGui import QColor, QImage, QIcon, QPixmap
class CustomTableModel(QAbstractTableModel):
def __init__(self, data):
QAbstractTableModel.__init__(self)
self.content_id = []
self.content_names = []
self.content_url = []
self.content_variant = []
self.content_progress = []
for content_data in data:
self.content_id.append(content_data["user_id"])
self.content_names.append(content_data["user_name"])
self.content_url.append(content_data["user_image"])
self.content_variant.append(content_data["user_details"])
self.content_progress.append(content_data["user_progress"])
self.row_count = len(self.content_id)
self.column_count = 4
def rowCount(self, parent=QModelIndex()):
return self.row_count
def columnCount(self, parent=QModelIndex()):
return self.column_count
def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return ("ID", "NAME", "IMAGE PREVIEW", "DETAILS")[section]
def data(self, index, role=Qt.DisplayRole):
column = index.column()
row = index.row()
if role == Qt.DisplayRole:
if column == 0:
content_id = self.content_id[row]
return content_id
elif column == 1:
return self.content_names[row]
elif column == 3:
return self.content_variant[row]
elif role == Qt.DecorationRole:
if column == 2:
image_path = self.content_url[row]
image = QImage()
image.load(image_path)
icon = QIcon()
icon.addPixmap(QPixmap.fromImage(image))
return icon
elif role == Qt.TextAlignmentRole:
return Qt.AlignLeft
TreeViewUI.py
from PySide2.QtCore import (QCoreApplication, QMetaObject, QObject, QPoint,
QRect, QSize, QUrl, Qt)
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
QFontDatabase, QIcon, QLinearGradient, QPalette, QPainter, QPixmap,
QRadialGradient)
from PySide2.QtWidgets import *
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(817, 600)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.setObjectName(u"gridLayout")
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setObjectName(u"verticalLayout")
self.label = QLabel(self.centralwidget)
self.label.setObjectName(u"label")
self.verticalLayout.addWidget(self.label)
self.treeView = QTreeView(self.centralwidget)
self.treeView.setObjectName(u"treeView")
self.verticalLayout.addWidget(self.treeView)
self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
self.label.setText(QCoreApplication.translate("MainWindow", u"TreeView", None))
# retranslateUi
这些是我创建的文件,用于测试 QTreeView 的示例实现。
Treeview 的基本结构如下:
User_ID User_Name User_Image User_Details
101 ABC abc.jpg abc
QProgressBar widget for the above user(101)# QProgressbar as the childItem for each row.
102 DEF def.jpg def
QProgressBar widget for the above user(102)
103 GHI ghi.png ghi
QProgressBar widget for the above user(103)
如果结构是树型的,则模型必须具有该结构,以便可以使用基于 QAbstractItemModel 的模型,但为了使其更简单,我使用 QStandardItemModel class 实现了它,我已经建立了适当角色中的值,以便进度条显示在新文件中,因此您必须是该项目的子项。为了显示进度条,我没有设置小部件,但它已通过委托使用 QStyle 绘制:
from PySide2.QtCore import Qt
from PySide2.QtGui import QIcon, QStandardItem, QStandardItemModel
class UserModel(QStandardItemModel):
def __init__(self, parent=None):
super(UserModel, self).__init__(parent)
self.setColumnCount(4)
def appendUser(self, id_, name, image, details, progress):
items = []
row = self.rowCount()
for text, column in zip((id_, name, details), (0, 1, 3)):
it = QStandardItem()
it.setData(text, Qt.DisplayRole)
self.setItem(row, column, it)
items.append(it)
image_item = QStandardItem()
image_item.setData(QIcon(image), Qt.DecorationRole)
self.setItem(row, 2, image_item)
progress_item = QStandardItem()
progress_item.setData(progress, Qt.UserRole)
items[0].appendRow(progress_item)
import os
import sys
from PySide2.QtCore import QSize, Qt
from PySide2.QtWidgets import (
QApplication,
QMainWindow,
QHeaderView,
QStyledItemDelegate,
QStyleOptionProgressBar,
QStyle,
)
from TreeViewUI import Ui_MainWindow
from custom_models.table_model import UserModel
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class ProgressDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
if index.parent().isValid():
r = option.rect
r.setWidth(200)
progress = index.data(Qt.UserRole)
progress_option = QStyleOptionProgressBar()
progress_option.rect = r
progress_option.minimum = 0
progress_option.maximum = 100
progress_option.progress = progress
progress_option.text = "{}%".format(progress)
progress_option.textVisible = True
QApplication.style().drawControl(
QStyle.CE_ProgressBar, progress_option, painter
)
return
super(ProgressDelegate, self).paint(painter, option, index)
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, data):
super(MainWindow, self).__init__()
self.setupUi(self)
self.model = UserModel(self)
for content_data in data:
self.model.appendUser(
content_data["user_id"],
content_data["user_name"],
os.path.join(CURRENT_DIR, content_data["user_image"]),
content_data["user_details"],
content_data["user_progress"],
)
self.treeView.setIconSize(QSize(360 / 4, 640 / 4))
self.treeView.header().setSectionResizeMode(QHeaderView.ResizeToContents)
self.treeView.setModel(self.model)
delegate = ProgressDelegate(self.treeView)
self.treeView.setItemDelegate(delegate)
def get_data():
content_list = [
{
"user_id": 101,
"user_name": "User_1",
"user_image": "images/demo.jpg",
"user_details": "details_1",
"user_progress": 45,
},
{
"user_id": 102,
"user_name": "User_2",
"user_image": "images/demo.jpg",
"user_details": "details_2",
"user_progress": 33,
},
{
"user_id": 103,
"user_name": "User_3",
"user_image": "images/demo.jpg",
"user_details": "details_3",
"user_progress": 80,
},
]
return content_list
if __name__ == "__main__":
app = QApplication([])
data = get_data()
main_window = MainWindow(data)
main_window.showMaximized()
sys.exit(app.exec_())
另一种可能的解决方案是通过委托创建持久化编辑器:
class ProgressDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
if index.parent().isValid():
view = option.widget
if isinstance(view, QTreeView) and index.model() is view.model():
view.openPersistentEditor(index)
return
super(ProgressDelegate, self).paint(painter, option, index)
def createEditor(self, parent, option, index):
if index.parent().isValid():
editor = QProgressBar(parent)
editor.setFixedWidth(200)
editor.setContentsMargins(0, 0, 0, 0)
editor.setValue(index.data(Qt.UserRole))
return editor
super(ProgressDelegate, self).createEditor(parent, option, index)
注意: 像我在第一个委托中所做的那样绘制 QProgressBar 是一项微不足道的任务,但是如果你想放置更复杂的小部件(具有更多状态的小部件,如 QPushButton)然后最好使用第二个选项。
我制作了一个向 QTreeView 添加数据的模型。我想添加一个小部件(例如:QProgressbar)作为该 QTreeView 中每一行的子元素。这样每次我们点击每一行时,我们都可以看到进度条作为该行的 childItem
main.py
import sys
from PySide2.QtWidgets import QApplication, QMainWindow, QHeaderView, QPushButton
from PySide2 import QtCore
from TreeViewUI import Ui_MainWindow
from custom_models.table_model import CustomTableModel
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, data):
QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.table_model = CustomTableModel(data)
self.treeView.setIconSize(QtCore.QSize(360 / 4, 640 / 4))
self.treeView.header().setSectionResizeMode(QHeaderView.ResizeToContents)
self.treeView.setModel(self.table_model)
def get_data():
content_list = [
{
"user_id": 101,
"user_name": "User_1",
"user_image": "images/demo.jpg",
"user_details": "details_1",
"user_progress": 45,
},
{
"user_id": 102,
"user_name": "User_2",
"user_image": "images/demo.jpg",
"user_details": "details_2",
"user_progress": 33,
},
{
"user_id": 103,
"user_name": "User_3",
"user_image": "images/demo.jpg",
"user_details": "details_3",
"user_progress": 80,
},
]
return content_list
if __name__ == '__main__':
app = QApplication([])
data = get_data()
main_window = MainWindow(data)
main_window.showMaximized()
sys.exit(app.exec_())
table_model.py
from PySide2.QtCore import Qt, QAbstractTableModel, QModelIndex
from PySide2.QtGui import QColor, QImage, QIcon, QPixmap
class CustomTableModel(QAbstractTableModel):
def __init__(self, data):
QAbstractTableModel.__init__(self)
self.content_id = []
self.content_names = []
self.content_url = []
self.content_variant = []
self.content_progress = []
for content_data in data:
self.content_id.append(content_data["user_id"])
self.content_names.append(content_data["user_name"])
self.content_url.append(content_data["user_image"])
self.content_variant.append(content_data["user_details"])
self.content_progress.append(content_data["user_progress"])
self.row_count = len(self.content_id)
self.column_count = 4
def rowCount(self, parent=QModelIndex()):
return self.row_count
def columnCount(self, parent=QModelIndex()):
return self.column_count
def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return ("ID", "NAME", "IMAGE PREVIEW", "DETAILS")[section]
def data(self, index, role=Qt.DisplayRole):
column = index.column()
row = index.row()
if role == Qt.DisplayRole:
if column == 0:
content_id = self.content_id[row]
return content_id
elif column == 1:
return self.content_names[row]
elif column == 3:
return self.content_variant[row]
elif role == Qt.DecorationRole:
if column == 2:
image_path = self.content_url[row]
image = QImage()
image.load(image_path)
icon = QIcon()
icon.addPixmap(QPixmap.fromImage(image))
return icon
elif role == Qt.TextAlignmentRole:
return Qt.AlignLeft
TreeViewUI.py
from PySide2.QtCore import (QCoreApplication, QMetaObject, QObject, QPoint,
QRect, QSize, QUrl, Qt)
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
QFontDatabase, QIcon, QLinearGradient, QPalette, QPainter, QPixmap,
QRadialGradient)
from PySide2.QtWidgets import *
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(817, 600)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.setObjectName(u"gridLayout")
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setObjectName(u"verticalLayout")
self.label = QLabel(self.centralwidget)
self.label.setObjectName(u"label")
self.verticalLayout.addWidget(self.label)
self.treeView = QTreeView(self.centralwidget)
self.treeView.setObjectName(u"treeView")
self.verticalLayout.addWidget(self.treeView)
self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
self.label.setText(QCoreApplication.translate("MainWindow", u"TreeView", None))
# retranslateUi
这些是我创建的文件,用于测试 QTreeView 的示例实现。 Treeview 的基本结构如下:
User_ID User_Name User_Image User_Details
101 ABC abc.jpg abc
QProgressBar widget for the above user(101)# QProgressbar as the childItem for each row.
102 DEF def.jpg def
QProgressBar widget for the above user(102)
103 GHI ghi.png ghi
QProgressBar widget for the above user(103)
如果结构是树型的,则模型必须具有该结构,以便可以使用基于 QAbstractItemModel 的模型,但为了使其更简单,我使用 QStandardItemModel class 实现了它,我已经建立了适当角色中的值,以便进度条显示在新文件中,因此您必须是该项目的子项。为了显示进度条,我没有设置小部件,但它已通过委托使用 QStyle 绘制:
from PySide2.QtCore import Qt
from PySide2.QtGui import QIcon, QStandardItem, QStandardItemModel
class UserModel(QStandardItemModel):
def __init__(self, parent=None):
super(UserModel, self).__init__(parent)
self.setColumnCount(4)
def appendUser(self, id_, name, image, details, progress):
items = []
row = self.rowCount()
for text, column in zip((id_, name, details), (0, 1, 3)):
it = QStandardItem()
it.setData(text, Qt.DisplayRole)
self.setItem(row, column, it)
items.append(it)
image_item = QStandardItem()
image_item.setData(QIcon(image), Qt.DecorationRole)
self.setItem(row, 2, image_item)
progress_item = QStandardItem()
progress_item.setData(progress, Qt.UserRole)
items[0].appendRow(progress_item)
import os
import sys
from PySide2.QtCore import QSize, Qt
from PySide2.QtWidgets import (
QApplication,
QMainWindow,
QHeaderView,
QStyledItemDelegate,
QStyleOptionProgressBar,
QStyle,
)
from TreeViewUI import Ui_MainWindow
from custom_models.table_model import UserModel
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class ProgressDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
if index.parent().isValid():
r = option.rect
r.setWidth(200)
progress = index.data(Qt.UserRole)
progress_option = QStyleOptionProgressBar()
progress_option.rect = r
progress_option.minimum = 0
progress_option.maximum = 100
progress_option.progress = progress
progress_option.text = "{}%".format(progress)
progress_option.textVisible = True
QApplication.style().drawControl(
QStyle.CE_ProgressBar, progress_option, painter
)
return
super(ProgressDelegate, self).paint(painter, option, index)
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, data):
super(MainWindow, self).__init__()
self.setupUi(self)
self.model = UserModel(self)
for content_data in data:
self.model.appendUser(
content_data["user_id"],
content_data["user_name"],
os.path.join(CURRENT_DIR, content_data["user_image"]),
content_data["user_details"],
content_data["user_progress"],
)
self.treeView.setIconSize(QSize(360 / 4, 640 / 4))
self.treeView.header().setSectionResizeMode(QHeaderView.ResizeToContents)
self.treeView.setModel(self.model)
delegate = ProgressDelegate(self.treeView)
self.treeView.setItemDelegate(delegate)
def get_data():
content_list = [
{
"user_id": 101,
"user_name": "User_1",
"user_image": "images/demo.jpg",
"user_details": "details_1",
"user_progress": 45,
},
{
"user_id": 102,
"user_name": "User_2",
"user_image": "images/demo.jpg",
"user_details": "details_2",
"user_progress": 33,
},
{
"user_id": 103,
"user_name": "User_3",
"user_image": "images/demo.jpg",
"user_details": "details_3",
"user_progress": 80,
},
]
return content_list
if __name__ == "__main__":
app = QApplication([])
data = get_data()
main_window = MainWindow(data)
main_window.showMaximized()
sys.exit(app.exec_())
另一种可能的解决方案是通过委托创建持久化编辑器:
class ProgressDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
if index.parent().isValid():
view = option.widget
if isinstance(view, QTreeView) and index.model() is view.model():
view.openPersistentEditor(index)
return
super(ProgressDelegate, self).paint(painter, option, index)
def createEditor(self, parent, option, index):
if index.parent().isValid():
editor = QProgressBar(parent)
editor.setFixedWidth(200)
editor.setContentsMargins(0, 0, 0, 0)
editor.setValue(index.data(Qt.UserRole))
return editor
super(ProgressDelegate, self).createEditor(parent, option, index)
注意: 像我在第一个委托中所做的那样绘制 QProgressBar 是一项微不足道的任务,但是如果你想放置更复杂的小部件(具有更多状态的小部件,如 QPushButton)然后最好使用第二个选项。