1

Base mock:

Hello,

What I have:

  • QAbstractListModel that contains my items.
  • QListView that will contain individual widgets or delegates.
  • Each individual item in the QListView needs to have 3 editable areas in the widget.

Preface:

I am having trouble figuring out how to get a proper layered widget system working. If you look at the attached image, each box in green represents an 'item' in my model, as well as an invidual item to the user in my view, while the data on the 'widgets' inside of each green box is data from and relating to that item in the model.

So if somebody edits QLineEdit1 in Widget 1, I expect that the data will be updated in the model for the item that Widget1 relates to.

Problem:

I can't figure out how to have multiple editors working inside the same delegate. If I click on the item (the QLineEdit), the index is of course going to relate to the main widget item (the green box) - I can't find anything to tell me that I've clicked on the painted QLineEdit, so if I just return an editable QLineEdit inside of createEditor, it's just going to be a generic one, placed wherever, and I need to figure out which widget it relates to so I can update the model correct, and can also make the editor draw in the position of where I clicked on the painted QLineEdit.

I can't figure out how to get a good system working. I understand delegates, I understand MVC, all of that is fine. I just don't know if what I'm trying to do is possible or I need to switch it up and do something else. If I do need to try something else, I'd appreciate some suggestions.

Thanks.

Here's some basic code (this doesn't have individual items painting inside the widgets right now, but has the base set up):

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()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Your model works for you? – eyllanesc Aug 01 '17 at 17:53
  • Yes, my model is working fine - there's nothing showing right now until you double click, because this is just showcasing that I'm having trouble figuring out how to know what was clicked on inside the delegate. – WhereAreTheseMangoes Aug 01 '17 at 17:55
  • It seems strange to me that in the `role == QtCore.Qt.DisplayRole` you must return a string, but you are returning a dictionary. – eyllanesc Aug 01 '17 at 17:57
  • I get the following: http://imgur.com/a/18XX6 – eyllanesc Aug 01 '17 at 18:01
  • Again, there's nothing showing in it, it's just a very basic view with nothing populating the items painted by the delegate - feel free to return a string in the role section, but that's not what the question is referring to. – WhereAreTheseMangoes Aug 01 '17 at 18:15
  • You can always put multiple widgets into another widget to have only one. – NoDataDumpNoContribution Aug 08 '17 at 11:32
  • Yeah, @Trilarion - that's what I'm going with, it looks like the only way to get what I want is widgets inside of other widgets and then querying the information. I was trying to stick a little too hard to MVC and it seems what I wanted with delegation and multiple editors isn't possible. Thanks. – WhereAreTheseMangoes Aug 09 '17 at 13:36

1 Answers1

1

Your code is only defining the Editor - that is, what Qt shows when editing the item. You need to provide a paint method that Qt then calls to draw for each item.

Here's an example (in C++) that draws a triple-dot button at the right edge of the item:

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);
}

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

Mattes D
  • 21
  • 2