Home > Net >  How to pass a parameter to createEditor functions of QStyledItemDelegate
How to pass a parameter to createEditor functions of QStyledItemDelegate

Time:05-29

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()

CodePudding user response:

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.

  • Related