Добавить виджет (QProgressbar) как childItem в QTreeView - PullRequest
2 голосов
/ 28 февраля 2020

Я сделал модель, которая добавляет данные в QTreeView. Я хочу добавить виджет (например, QProgressbar) в качестве дочернего элемента к каждой строке в этом QTreeView. Так что каждый раз, когда мы нажимаем на каждую строку, мы можем видеть индикатор выполнения в качестве дочернего элемента для этой строки

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. Базовая c структура 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)

1 Ответ

1 голос
/ 28 февраля 2020

Если структура имеет древовидный тип, то модель должна иметь эту структуру, чтобы можно было использовать модель, основанную на QAbstractItemModel, но для упрощения я реализовал ее с помощью класса QStandardItemModel, где я установил значения в соответствующих ролях, и так, что индикатор выполнения отображается в новом файле, поэтому вы должны быть дочерним элементом этого элемента. Чтобы показать индикатор выполнения, я не установил виджет, но он был нарисован с использованием 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_())

enter image description here

Другой Возможное решение - создать постоянный редактор через делегата:

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), тогда лучше использовать второй вариант.

...