user16613865
user16613865

Reputation: 105

How to pass a parameter to createEditor functions of QStyledItemDelegate

A software tool has a table filled with two columns: parameter and data. "Data" column have comboBox widget. How to pass the listData to the createEditor function as a parameter, that I can assign listCombo to the listData?

import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
import pandas as pd

class DataDelegate(QStyledItemDelegate):
    def createEditor(self, parent, opt, index):
        comboBox = QComboBox(parent)
        listCombo = []
        comboBox.addItems(listCombo)
        comboBox.setCurrentIndex(1)
        comboBox.currentTextChanged.connect(lambda: self.commitData.emit(comboBox))
        return comboBox

class Window(QWidget):
    singleton: 'Window' = None

    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle("Software tool")
        self.setGeometry(50, 50, 1800, 900)
        self.mainLayout=QHBoxLayout()
        self.setLayout(self.mainLayout)
        self.UI()
        self.table.itemChanged.connect(self._print)

    def UI(self):
        self.sublayouts = {}
        self.buttons = {}
        self._view()
        self._fillTableWidget()
        self.show()

    def _view(self):
        self.table = QTableWidget(0, 2)
        self.table.setHorizontalHeaderLabels(['Parameter', 'Data'])
        self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setEditTriggers(QTableWidget.NoEditTriggers)

        self.sublayouts['table'] = QGridLayout()
        self.sublayouts['table'].addWidget(self.table, 1, 0, 4, 4)
        self.sublayouts['table'].setRowStretch(4, 1)

        self.mainLayout.addLayout(self.sublayouts['table'])

        self.table.setItemDelegateForColumn(1, DataDelegate(self.table))

    def _fillTableWidget(self):
        listCol = {
            'Parameters': ['a', 'b', 'c', 'd', 'e'],
            'Data': ['data1', 'data2', 'data3', 'data4', 'data5']}
        self.df = pd.DataFrame(listCol)
        listData = self.df['Data'].to_list()
        print(listData)
        for parameter in self.df['Parameters']:
            rowPosition = self.table.rowCount()
            self.table.insertRow(rowPosition)
            self.table.setItem(rowPosition, 0, QTableWidgetItem(parameter))
            dataItem = QTableWidgetItem()
            self.table.setItem(rowPosition, 1, dataItem)
            self.table.openPersistentEditor(self.table.item(rowPosition, 1))

    def _tableCell(self, text):
        item = QTableWidgetItem()
        item.setText(text)
        return item

    def _print(self):
        print('Item changed:')

def main():
    App=QApplication(sys.argv)
    window =Window()
    sys.exit(App.exec_())

if __name__ == '__main__':
    main()

Upvotes: 0

Views: 284

Answers (1)

musicamante
musicamante

Reputation: 48399

A possible solution is to set a default list as an instance attribute of the delegate, use that list within createEditor() and overwrite it when the model is retrieved:

class DataDelegate(QStyledItemDelegate):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.items = []

    def setItems(self, items):
        self.items[:] = items

    def createEditor(self, parent, opt, index):
        comboBox = QComboBox(parent)
        comboBox.addItems(self.items)
        comboBox.setCurrentIndex(1)
        comboBox.currentTextChanged.connect(
            lambda: self.commitData.emit(comboBox))
        return comboBox

class Window(QWidget):
    # ...

    def _view(self):
        # ...
        self.dataDelegate = DataDelegate(self.table)
        self.table.setItemDelegateForColumn(1, self.dataDelegate)

    def _fillTableWidget(self):
        listCol = {
            'Parameters': ['a', 'b', 'c', 'd', 'e'],
            'Data': ['data1', 'data2', 'data3', 'data4', 'data5']}
        self.df = pd.DataFrame(listCol)
        listData = self.df['Data'].to_list()
        self.dataDelegate.setItems(listData)
        # ...

Note: the currentTextChanged signal should be used when you actually need it (which is normally required for editable comboboxes, and might not be fired in certain situations); while that signal is usually emitted for uneditable combo boxes even when the same text exists in different items, that behavior might change in the future, and the currentIndexChanged is usually preferred, especially for model editors, since the data should only be updated when the editor is actually submitted.

Upvotes: 1

Related Questions