tags:

views:

41

answers:

1

So I have a model and one of the columns contains a country. However because I want to display a combo box to choose the country from a list of options, I don't store the country name in the model directly. Instead I store an index value into a list of allowable countries. This allows me to use a QComboBox in my form view as recommended in the Qt docs. The problem is I also have a table view, and the table view displays the index integer, not the country name. I have set up a QStyledItemDelegate and implemented createEditor so if you click in the world cell it does pop up the ComboBox, but when you're not editing the country you see the index value.

I'm part way to a solution. I have implemented a paint method to do the work, but it's displaying the value offset to it's proper position and I can't figure out how to get it to display correctly. I think option.rect.topLeft() in the render method is wrong, but I can't figure out how to set the drawing correctly. Any help appreciated, I do remember to up-tick helpful answers.

def paint(self, painter, option, index):
    if index.column() == COUNTRY:
        painter.save()
        countryRef, ok = inex.data().toInt()
        countryStr = country_list[countryRef]
        widget = QLineEdit()
        widget.setGeometry(option.rect)
        widget.setText(countryStr)
        widget.render(painter, option.rect.topLeft())
        painter.store()
    else:
        QStylyedItemDelegate.paint(self, painter, option, index)
+1  A: 

Models have different item data roles for different data. There's a Qt::DisplayRole, Qt::EditRole, and a Qt::UserRole among others. In this case, you want to display something different than your actual data, so add a new role, let's say Qt::UserRole+1 that's used for your index.

Then you want your delegate to set the appropriate data in setModelData:

def setModelData(self, editor, model, index):
    cbIndex = editor.currentIndex()
    model.setData(index, cbIndex, Qt.UserRole+1)
    # we want a nice displayable though
    model.setData(index, countryIndexToDisplayable(cbIndex), Qt.DisplayRole)

Of course, you'll retrieve the data to set in the editor in a similar fashion:

def setEditorData(self, widget, index):
    widget.setCurrentIndex(index.data(Qt.UserRole+1))

Depending on your model and view, you might be able to use Qt::EditRole which is pretty much intended for that purpose. If you then use a native type for the display role, you shouldn't need to do any custom painting, although you can if you like.

Kaleb Pederson
+1 Outstanding, thanks. I didn't know you could create user defined Roles. My feeling is that if you update the data using one role, the model should handle updating all the other roles for that data, but that would have been harder to show in the example.
Simon Hibbs
Minor point, in the delegate's setModelData method I had to convert the cbIndex into a QVariant as folows: model.setData(index, QVariant(cbIndex), Qt.UserRole+1). This is becasue I have a form that uses QDataWidgetMapper to access the data and it always sends QVariants to the model and I need to keep things consistent. Models should probably always return data and have data set in Qt native data types, not Python types?
Simon Hibbs
@Simon - My preference is to have a rich model, one that represents the vocabulary of the domain you're working with. So I'd hope to have a method called `setCountry` with a helper for converting the index to a country. I like to make a rich model that is not a `QAbstractItemModel`, but one that I can easily delegate to within the GUI model. If I don't do that I just create accessors on the `QAbstractItemModel` that provide a richer API. RE: QVariant(cbIndex), it's been a while since I have done any PyQt and I thought it would be automatically converted, like in C++.
Kaleb Pederson
I see your point, if you're extending the model with your own accessors then it makes sense to have them as convenient as possible. Not sure about automatic conversion, PyQt is usually fantastic at that, so don't know what heppened there. Maybe my mistake.
Simon Hibbs