使用多个小部件进行委托

Posted

技术标签:

【中文标题】使用多个小部件进行委托【英文标题】:Delegate with multiple widgets 【发布时间】:2017-08-01 16:54:31 【问题描述】:

基础模拟:

你好,

我有什么:

QAbstractListModel 包含我的项目。 QListView 将包含单个小部件或委托。 QListView 中的每个单独项目都需要在小部件中有 3 个可编辑区域。

前言:

我无法弄清楚如何让适当的分层小部件系统正常工作。如果您查看附加的图像,每个绿色框代表我模型中的一个“项目”,以及在我看来用户的一个单独项目,而每个绿色框内的“小部件”上的数据是来自并与模型中的该项目相关。

因此,如果有人在 Widget 1 中编辑 QLineEdit1,我希望模型中的数据将更新 Widget1 相关的项目。

问题:

我不知道如何让多个编辑器在同一个委托中工作。如果我点击项目(QLineEdit),索引当然会与主要的小部件项目(绿色框)相关 - 我找不到任何东西告诉我我点击了绘制的 QLineEdit,所以如果我只是在 createEditor 中返回一个可编辑的 QLineEdit,它只是一个通用的,放置在任何地方,我需要弄清楚它与哪个小部件相关,以便我可以正确更新模型,还可以让编辑器绘制我点击绘制的 QLineEdit 的位置。

我不知道如何让一个好的系统正常工作。我了解代表,我了解 MVC,这一切都很好。我只是不知道我正在尝试做的事情是否可行,或者我需要改变它并做其他事情。如果我确实需要尝试其他东西,我会很感激一些建议。

谢谢。

这里有一些基本代码(现在没有在小部件内绘制单个项目,但已经设置了基础):

import sys
from PySide import QtCore, QtGui


DELEGATE_DATA_ROLE = 37
DELEGATE_INDEX = 0


class ItemWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(ItemWidget, self).__init__(parent=parent)
        self.main_layout = QtGui.QVBoxLayout()
        self.setLayout(self.main_layout)


class ItemDelegate(QtGui.QStyledItemDelegate):
    def __init__(self, parent=None):
        super(ItemDelegate, self).__init__(parent=parent)

    def sizeHint(self, option, index):
        return QtCore.QSize(80, 80)

    def createEditor(self, parent, option, index):
        editor = QtGui.QLineEdit(parent)
        editor.setFixedWidth(200)
        editor.setFixedHeight(50)
        return editor

    def setEditorData(self, editor, index):
        item_str = index.data(QtCore.Qt.DisplayRole)
        description = item_str['description']
        editor.setValue(description)

    def setEditorData(self, editor, index):
        print 'running setEditorData'
        if index.column() == DELEGATE_INDEX:
            print 'delegate'
        else:
            QtGui.QStyledItemDelegate(self, editor, index)


class ItemModel(QtCore.QAbstractListModel):
    def __init__(self, parent=None):
        super(ItemModel, self).__init__(parent=parent)
        self.items = [
            'one': '1', 'name': 'one', 'thumbnail': 'charlie.jpg', 'description': 'aabb',
            'two': '2', 'name': 'two', 'thumbnail': 'charlie.jpg', 'description': 'aabb',
            'three': '3', 'name': 'three', 'thumbnail': 'charlie.jpg', 'description': 'aabb',
            'four': '4', 'name': 'four', 'thumbnail': 'charlie.jpg', 'description': 'aabb',
            'five': '5', 'name': 'five', 'thumbnail': 'charlie.jpg', 'description': 'aabb',
            'six': '6', 'name': 'six', 'thumbnail': 'charlie.jpg', 'description': 'aabb'
        ]

    def rowCount(self, index):
        return len(self.items)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None

        if role == QtCore.Qt.DisplayRole:
            if 0 <= index.row() < self.rowCount(index):
                return self.items[index.row()]
        if role == DELEGATE_DATA_ROLE:
            item = self.items[index.row()]
            return item, item.get('name'), item.get('description'), item.get('thumbnail')
        if role == QtCore.Qt.EditRole:
            return self.items[index.row()]

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if role == QtCore.Qt.EditRole:
            self.items[index.row()] = value
            self.dataChanged.emit(index, index)
            return True
        return False

    def flags(self, index):
        flag = super(ItemModel, self).flags(index)
        return flag | QtCore.Qt.ItemIsEditable


class ListView(QtGui.QListView):
    def __init__(self, parent=None):
        super(ListView, self).__init__(parent=parent)


class TestUI(QtGui.QDialog):
    def __init__(self, parent=None):
        super(TestUI, self).__init__(parent)
        self.main_layout = QtGui.QVBoxLayout()
        self.resize(700, 500)

        self.view = ListView()
        self.item_delegate = ItemDelegate()
        self.view.setItemDelegate(self.item_delegate)

        self.model = ItemModel()
        self.view.setModel(self.model)

        self.item_widget = ItemWidget()

        self.setLayout(self.main_layout)
        self.main_layout.addWidget(self.view)
        self.main_layout.addWidget(self.item_widget)


def start():
    app = QtGui.QApplication(sys.argv)
    ui = TestUI()
    ui.show()
    ui.setWindowState(QtCore.Qt.WindowActive)
    ui.raise_()
    app.exec_()


if __name__ == '__main__':
    start()

【问题讨论】:

你的模型适合你吗? 是的,我的模型工作正常 - 现在没有任何显示,直到您双击,因为这只是表明我无法弄清楚如何知道在委托中点击了什么。 role == QtCore.Qt.DisplayRole 中你必须返回一个字符串,但你返回的是一个字典,这对我来说似乎很奇怪。 我得到以下信息:imgur.com/a/18XX6 再一次,里面什么都没有显示,这只是一个非常基本的视图,没有填充委托绘制的项目 - 随意在角色部分返回一个字符串,但这不是问题所在参考。 【参考方案1】:

您的代码仅定义编辑器 - 即 Qt 在编辑项目时显示的内容。您需要提供一个绘制方法,然后 Qt 调用该方法为每个项目绘制。

这是一个在项目的右边缘绘制一个三点按钮的示例(在 C++ 中):

void ColorDelegate::paint(
    QPainter * a_Painter,
    const QStyleOptionViewItem & a_Option,
    const QModelIndex & a_Index
) const

    // Draw the button:
    QStyleOptionButton button;
    button.rect = buttonRectFromItemRect(a_Option.rect);
    button.text = "...";
    button.state = QStyle::State_Enabled;
    QApplication::style()->drawControl(QStyle::CE_PushButton, &button, a_Painter);

    // Draw the text, using the original delegate:
    QStyleOptionViewItem txt(a_Option);
    txt.rect.setWidth(txt.rect.width() - txt.rect.height() - 1);
    Super::paint(a_Painter, txt, a_Index);

(取自https://github.com/madmaxoft/SkauTan/blob/a40b94d8646c215ddde3efe422c466ca8b15cb88/src/UI/ColorDelegate.cpp#L20-L37)

【讨论】:

以上是关于使用多个小部件进行委托的主要内容,如果未能解决你的问题,请参考以下文章

小部件的多个实例仅更新最后一个小部件

如何在颤动的pageView中使用AnimatedCrossFade多个小部件?

DataCell 溢出带有多个文本小部件的列 [dataTable]

为啥我们必须从一个小部件更新多个小部件?

QGridLayout 限制中的多个小部件

如何在窗口小部件中放置多个字体