I have a QTreeWidget with 3 columns, only the 3rd column should have a double validator. My issue is that it somewhat works; The QLineEdit does not stop at 100 it just keeps going.
what I am missing; to make it where it doesn't let user input anything higher than 100 while still editing the lineEdit?
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Delegate(QStyledItemDelegate):
'Changes number of decimal places in gas analysis self.chosen table'
def __init__(self, decimals, parent=None):
super().__init__(parent)
self.nDecimals = decimals
def createEditor(self, parent, option, index):
editor = QLineEdit(parent)
editor.setValidator(QDoubleValidator(0,100, 15))
return editor
def setEditorData(self, editor, index):
if index.column() == 2 and index.data() is not None:
editor.setText(str(float(index.data())))
class Widget(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent=None)
self.tree_widget = QTreeWidget()
self.tree_widget.setItemDelegate(Delegate(self.tree_widget))
self.tree_widget.setHeaderLabels(["Value1", "Value2", "Value3"])
self.setCentralWidget(self.tree_widget)
for vals in [("h", "20.0", "40.0"), ("k", "25.0", "50.0")]:
it = QTreeWidgetItem(vals)
it.setFlags(it.flags()| Qt.ItemIsEditable)
self.tree_widget.addTopLevelItem(it)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
The QDoubleValidator only tries to validate the text entered by the user, if it does not meet the requirement then the information will not be saved and that is what is observed in your case. If you want to define what values you want the user to use in your specific case, it is better to use the QDoubleSpinBox as editor:
class Delegate(QStyledItemDelegate):
def __init__(self, decimals, parent=None):
super().__init__(parent)
self.nDecimals = decimals
def createEditor(self, parent, option, index):
editor = QDoubleSpinBox(
parent, minimum=0, maximum=100.0, decimals=self.nDecimals
)
return editor
def setEditorData(self, editor, index):
if index.data() is not None:
try:
value = float(index.data())
except Exception as e:
print("error", e)
else:
editor.setValue(value)
class Widget(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent=None)
self.tree_widget = QTreeWidget()
self.tree_widget.setHeaderLabels(["Value1", "Value2", "Value3"])
self.tree_widget.setItemDelegateForColumn(2, Delegate(15, self.tree_widget))
self.setCentralWidget(self.tree_widget)
for vals in [("h", "20.0", "40.0"), ("k", "25.0", "50.0")]:
it = QTreeWidgetItem(vals)
it.setFlags(it.flags() | Qt.ItemIsEditable)
self.tree_widget.addTopLevelItem(it)
Note: I have modified the column selection since with the initial implementation of the OP the same editor is used in all the columns and that clearly contradicts its same requirement, so instead of using setItemDelegate() I have used setItemDelegateForColumn().
Related
I'm having problem to show the Editor Widget when Delegate is applied with the Proxy situation.
-> self.table.setModel(self.proxy)
If the Delegate is applied to the View/Model structure, then there is no problem at all.
-> #self.table.setModel(self.model)
Refer to: https://www.pythonfixing.com/2021/10/fixed-adding-row-to-qtableview-with.html
See the code below:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Delegate(QItemDelegate):
def __init__(self):
QItemDelegate.__init__(self)
self.type_items = ["1", "2", "3"]
def createEditor(self, parent, option, index):
if index.column() == 0:
comboBox = QComboBox(parent)
comboBox.addItems(self.type_items)
return comboBox
# no need to check for the other columns, as Qt automatically creates a
# QLineEdit for string values and QTimeEdit for QTime values;
return super().createEditor(parent, option, index)
class TableModel(QAbstractTableModel):
def __init__(self, data):
super(TableModel, self).__init__()
self._data = data
def appendRowData(self, data):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self._data.append(data)
self.endInsertRows()
def data(self, index, role=Qt.DisplayRole):
if role in (Qt.DisplayRole, Qt.EditRole):
return self._data[index.row()][index.column()]
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
self._data[index.row()][index.column()] = value
self.dataChanged.emit(index, index)
return True
return False
def rowCount(self, index=None):
return len(self._data)
def columnCount(self, index=None):
return len(self._data[0])
def flags(self, index):
# allow editing of the index
return super().flags(index) | Qt.ItemIsEditable
class CustomProxyModel(QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
self._filters = dict()
#property
def filters(self):
return self._filters
def setFilter(self, expresion, column):
if expresion:
self.filters[column] = expresion
elif column in self.filters:
del self.filters[column]
self.invalidateFilter()
def filterAcceptsRow(self, source_row, source_parent):
for column, expresion in self.filters.items():
text = self.sourceModel().index(source_row, column, source_parent).data()
regex = QRegExp(
expresion, Qt.CaseInsensitive, QRegExp.RegExp
)
if regex.indexIn(text) == -1:
return False
return True
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
localWidget = QWidget()
self.table = QTableView(localWidget)
data = [["1", "Hi", QTime(2, 1)], ["2", "Hello", QTime(3, 0)]]
self.model = TableModel(data)
self.proxy = CustomProxyModel() # Customized Filter
self.proxy.setSourceModel(self.model)
#self.table.setModel(self.model) # Original code, for View/Model
self.table.setModel(self.proxy) # Revised code, for View/Proxy/Model
self.table.setItemDelegate(Delegate())
self.add_row = QPushButton("Add Row", localWidget)
self.add_row.clicked.connect(self.addRow)
for row in range(self.model.rowCount()):
for column in range(self.model.columnCount()):
index = self.model.index(row, column)
self.table.openPersistentEditor(index) # openPersistentEditor for createEditor
layout_v = QVBoxLayout()
layout_v.addWidget(self.table)
layout_v.addWidget(self.add_row)
localWidget.setLayout(layout_v)
self.setCentralWidget(localWidget)
self.show()
def addRow(self):
row = self.model.rowCount()
new_row_data = ["3", "Howdy", QTime(9, 0)]
self.model.appendRowData(new_row_data)
for i in range(self.model.columnCount()):
index = self.model.index(row, i)
self.table.openPersistentEditor(index) # openPersistentEditor for createEditor
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Test with View/Model, Widget Editor display.
Test with View/Proxy/Model, Widget Editor not display.
Any attempt to access the view indexes must use the view's model.
Your code doesn't work because the index you are providing belongs to another model, so the editor cannot be created because the view doesn't recognize the model of the index as its own: the view uses the proxy model, while you're trying to open an editor for the source model.
While in this case the simplest solution would be to use self.proxy.index(), the proper solution is to always refer to the view's model.
Change both self.model.index(...) to self.table.model().index(...).
Yes, thanks you very much.
Revised the code as below, now the Widget Editor display accordingly.
for row in range(self.model.rowCount()):
for column in range(self.model.columnCount()):
#index = self.model.index(row, column) # original code, which get the wrong index from the model
index = self.proxy.index(row, column) # revised code, get the correct index from the proxy
self.table.openPersistentEditor(index) # openPersistentEditor for createEditor
Hi, I am trying to run a GUI based on wx. I made checkboxes in a drop-down list. But the checkboxes are not working. I want the checkboxes to work and save the selected input option names somewhere. Here is the code. With these selected input option names (one or more than one) I will filter a big string later. Thank you in advance :)
import wx
import wx.stc
from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):
def __init__(self, parent):
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT |
wx.SUNKEN_BORDER)
CheckListCtrlMixin.__init__(self)
ListCtrlAutoWidthMixin.__init__(self)
self.SetSize(-1, -1, -1, 50)
def OnCheckItem(self, index, flag):
item = self.GetItem(index)
if flag:
what = "checked"
else:
what = "unchecked"
print(f'{item.GetText()} - {what}')
class ListViewComboPopup(wx.ComboPopup):
def __init__(self):
wx.ComboPopup.__init__(self)
self.lc = None
def AddItem(self, txt):
self.lc.InsertItem(0, txt)
def Init(self):
self.value = -1
self.curitem = -1
def Create(self, parent):
self.lc = CheckListCtrl(parent)
self.lc.InsertColumn(0, '', width=90)
return True
def GetControl(self):
return self.lc
def OnPopup(self):
wx.ComboPopup.OnPopup(self)
def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
return wx.ComboPopup.GetAdjustedSize(
self, minWidth, 110, maxHeight)
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Popup Menu Tutorial")
panel = wx.Panel(self)
comboCtrl = wx.ComboCtrl(panel, wx.ID_ANY, "Select filter")
popupCtrl = ListViewComboPopup()
comboCtrl.SetPopupControl(popupCtrl)
popupCtrl.AddItem("mango")
popupCtrl.AddItem("cat")
popupCtrl.AddItem("dog")
popupCtrl.AddItem("tiger")
popupCtrl.AddItem("three")
popupCtrl.AddItem("hat")
popupCtrl.AddItem("hot")
popupCtrl.AddItem("sweden")
popupCtrl.AddItem("kth")
You can saev them as variables in a .txt file with pickle:
pickle.dump(your_variable, open("path/to/your/file.txt", "wb"))
and to access them:
your_variable = pickle.load(open("path/to/your/file.txt", "rb"))
In my application im using a QTableView, QStandardItemModel and a QSortFilterProxyModel in between for filtering.
The content is updated via a method for columns 1 & 2, and I want there to be a 3rd column for user to select options. I would prefer to use a QComboBox.
I've got everything pretty much working, except that when I select the item from the QComboBox in any of the cells in column 3, it doesn't populate. Does it have something to do with my setModelData() method?
I also have a clear button that I would like to reset all of the QComboBoxes to the first item which is an empty entry. I am not sure how to tackle this, i've found such things as using deleteLater() or setting the QTableView's setItemDelegateForColumn() to None and re-apply.
Obviously these are not the most efficient. What am I missing?
Working example:
import win32com.client
from PyQt5 import QtCore, QtGui, QtWidgets
outApp = win32com.client.gencache.EnsureDispatch("Outlook.Application")
outGAL = outApp.Session.GetGlobalAddressList()
entries = outGAL.AddressEntries
class ComboDelegate(QtWidgets.QItemDelegate):
def __init__(self,parent=None):
super().__init__(parent)
self.items = ['','To', 'CC']
def createEditor(self, widget, option, index):
editor = QtWidgets.QComboBox(widget)
editor.addItems(self.items)
return editor
def setEditorData(self, editor, index):
if index.column() == 2:
editor.blockSignals(True)
text = index.model().data(index, QtCore.Qt.EditRole)
try:
i = self.items.index(text)
except ValueError:
i = 0
editor.setCurrentIndex(i)
editor.blockSignals(False)
else:
QtWidgets.QItemDelegate.setModelData(editor,model,index)
def setModelData(self, editor, model, index):
if index.column() == 2:
model.setData(index, editor.currentText())
else:
QtWidgets.QItemDelegate.setModelData(editor,model,index)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
def paint(self, painter, option, index):
QtWidgets.QApplication.style().drawControl(QtWidgets.QStyle.CE_ItemViewItem, option, painter)
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
"""This method creates our GUI"""
self.centralwidget = QtWidgets.QWidget()
self.setCentralWidget(self.centralwidget)
self.lay = QtWidgets.QVBoxLayout(self.centralwidget)
self.filterEdit = QtWidgets.QLineEdit()
self.filterEdit.setPlaceholderText("Type to filter name.")
self.label = QtWidgets.QLabel("Select an option for each person:")
self.button = QtWidgets.QPushButton("Test Button")
self.button.clicked.connect(self.runButton)
self.resetbutton = QtWidgets.QPushButton("Clear")
self.resetbutton.clicked.connect(self.clear)
self.lay.addWidget(self.filterEdit)
self.lay.addWidget(self.label)
self.tableview=QtWidgets.QTableView(self.centralwidget)
self.model=QtGui.QStandardItemModel()
self.model.setHorizontalHeaderLabels(['Name','Address','Option'])
self.tableview.verticalHeader().hide()
self.tableview.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
self.tableview.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
self.proxyModel = QtCore.QSortFilterProxyModel(self)
self.proxyModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.proxyModel.setSourceModel(self.model)
self.proxyModel.sort(0,QtCore.Qt.AscendingOrder)
self.proxyModel.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.tableview.setModel(self.proxyModel)
self.model.insertRow(self.model.rowCount(QtCore.QModelIndex()))
#self.fillModel(self.model) #uncomment if you have outlook
self.tableview.resizeColumnsToContents()
self.tableview.verticalHeader().setDefaultSectionSize(10)
self.filterEdit.textChanged.connect(self.onTextChanged)
self.lay.addWidget(self.tableview)
self.delegate = ComboDelegate()
self.tableview.setItemDelegateForColumn(2, self.delegate)
self.lay.addWidget(self.button)
self.lay.addWidget(self.resetbutton)
self.setMinimumSize(450, 200)
self.setMaximumSize(1500, 200)
self.setWindowTitle('Application')
def clear(self):
###clear tableview comboboxes in column 3
print("clear")
def runButton(self,index):
print("Do stuff")
def fillModel(self,model):
"""Fills model from outlook address book """
nameList = []
addressList = []
for row,entry in enumerate(entries):
if entry.Type == "EX":
user = entry.GetExchangeUser()
if user is not None:
if len(user.FirstName) > 0 and len(user.LastName) > 0:
nameItem = QtGui.QStandardItem(str(user.Name))
emailItem = QtGui.QStandardItem(str(user.PrimarySmtpAddress))
nameItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
emailItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
model.appendRow([nameItem,emailItem])
#QtCore.pyqtSlot(str)
def onTextChanged(self, text):
self.proxyModel.setFilterRegExp(text)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = App()
w.show()
sys.exit(app.exec_())
The problem is that you override the paint method unnecessarily since you don't want to customize anything. Before override I recommend you understand what it does and for this you can use the docs or the source code. But to summarize, in the case of the QItemDelegate the paint method establishes the information of the roles in the "option" and then just paints, and within that information is the text. But in your case it is not necessary so there is no need to override. On the other hand, if your delegate has the sole function of establishing a QComboBox then you don't have to verify the columns. Considering all of the above, I have simplified your delegate to:
class ComboDelegate(QtWidgets.QItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
self.items = ["", "To", "CC"]
def createEditor(self, widget, option, index):
editor = QtWidgets.QComboBox(widget)
editor.addItems(self.items)
return editor
def setEditorData(self, editor, index):
editor.blockSignals(True)
text = index.model().data(index, QtCore.Qt.EditRole)
try:
i = self.items.index(text)
except ValueError:
i = 0
editor.setCurrentIndex(i)
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.currentText())
On the other hand, the QItemEditorFactory uses the qproperty user as the parameter for the update, and in the case of the QComboBox it is the "currentText", so it can be further simplified using that information:
class ComboDelegate(QtWidgets.QItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
self.items = ["", "To", "CC"]
def createEditor(self, widget, option, index):
editor = QtWidgets.QComboBox(widget)
editor.addItems(self.items)
return editor
For the clear method is simple: Iterate over all the rows of the third column and set the empty text:
def clear(self):
# clear tableview comboboxes in column 3
for i in range(self.model.rowCount()):
index = self.model.index(i, 2)
self.model.setData(index, "")
learning PyQt5 recently, I've tried to drag a QPushButton learning this tutorial Drag & drop a button widget, and made some improvements to place the button more accurate, so I add
mime = e.mimeData().text()
x, y = mime.split(',')
according to #Avaris for this question, but I found e.mimeData().text() returned nothing which supposed to be the coordinate of local position of the cursor with respect to the button, i tried to print(mime), and got a blank line with nothing, then i print(mime.split(',')) and got ['']。
here's the code:
import sys
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication, QLabel
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag
from PyQt5 import QtCore
class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
def mouseMoveEvent(self, e):
if e.buttons() != Qt.RightButton:
return
mimeData = QMimeData()
drag = QDrag(self)
drag.setMimeData(mimeData)
dropAction = drag.exec_(Qt.MoveAction)
def mousePressEvent(self, e):
QPushButton.mousePressEvent(self, e)
if e.button() == Qt.LeftButton:
print('press')
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
self.button = Button('Button', self)
self.button.move(100, 65)
self.setWindowTitle('Click or Move')
self.setGeometry(300, 300, 280, 150)
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
position = e.pos()
mime = e.mimeData().text()
x, y = mime.split(',')
#print(mime.split(','))
self.button.move(position - QtCore.QPoint(int(x), int(y)))
e.setDropAction(Qt.MoveAction)
e.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()
In the answer of #Avaris, you will notice they set the mimedata with the button position in the mouseMoveEvent:
mimeData = QtCore.QMimeData()
# simple string with 'x,y'
mimeData.setText('%d,%d' % (e.x(), e.y()))
The mimedata does not contain anything by default. You have to set everything yourself! Have a look at the documentation for QMimeData to see what else you can do (other than setting arbitrary text)
Drag and Drop in Camera View
def dragEnterEvent(self, event): # Drag lines
mimeData = QtCore.QMimeData()
if mimeData.hasText:
event.accept()
else:
event.ignore()
def dropEvent(self, event): # Drop lines
mimeData = QtCore.QMimeData()
format = 'application/x-qabstractitemmodeldatalist'
data=event.mimeData().data(format) # Drag Drop get data's name
name_str = codecs.decode(data,'utf-8') # Convert byte to string
mimeData.setText(name_str)
# print(name_str[26:].replace('\x00','').strip("")) # remove white space
if mimeData.hasText:
print(name_str)
# write what you will do
I need to swap the rows and columns of a QSqlRelationalTableModel. After a lot of searching, I wrote a little proxymodel to flip the rows and the columns.
It is partly working. The relations in the table are resolved and showed but the dropboxes to choose them gets lost. Also, how do I get them to update?
Here is a little self-contained script that reproduces the behavior.
Where is my error? I have strong suspicion that it has to do with the signals and slots of the models but I haven't found any hint which ones and how to reimplement them.
Is there another easier way to swap rows and columns?
EDIT: to clarify the delegational model isn't completely not working its just partial working.
from PySide import QtCore, QtGui, QtSql
from PySide.QtCore import Qt, QModelIndex
from PySide.QtGui import QAbstractProxyModel, QWidget, QHBoxLayout, QTableView
from PySide.QtSql import QSqlRelationalDelegate
class FlipProxyModel(QAbstractProxyModel):
def __init__(self, parent=None):
super(FlipProxyModel, self).__init__(parent)
def mapFromSource(self, index):
return self.createIndex(index.column(), index.row())
def mapToSource(self, index):
return self.sourceModel().index(index.column(), index.row(), QModelIndex())
def columnCount(self, parent):
return self.sourceModel().rowCount(QModelIndex())
def rowCount(self, parent):
return self.sourceModel().columnCount(QModelIndex())
def index(self, row, column, parent):
return self.createIndex(row, column)
def parent(self, index):
# tables have no parent object so return empty
return QModelIndex()
def data(self, index, role):
return self.sourceModel().data(self.mapToSource(index), role)
def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal:
return self.sourceModel().headerData(section, Qt.Vertical, role)
if orientation == Qt.Vertical:
return self.sourceModel().headerData(section, Qt.Horizontal, role)
def createConnection():
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(":memory:")
if not db.open():
print 'fatal'
return False
return True
def createView(title, model):
view = QtGui.QTableView()
view.setModel(model)
view.setItemDelegate(QtSql.QSqlRelationalDelegate(view))
view.setWindowTitle(title)
return view
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
if not createConnection():
sys.exit(1)
# createRelationalTables()
query = QtSql.QSqlQuery()
query.exec_("create table employee(id int, name varchar(20), city int, country int)")
query.exec_("insert into employee values(1, 'Espen', 5000, 47)")
query.exec_("insert into employee values(2, 'Harald', 80000, 49)")
query.exec_("insert into employee values(3, 'Sam', 100, 41)")
query.exec_("create table city(id int, name varchar(20))")
query.exec_("insert into city values(100, 'San Jose')")
query.exec_("insert into city values(5000, 'Oslo')")
query.exec_("insert into city values(80000, 'Munich')")
query.exec_("create table country(id int, name varchar(20))")
query.exec_("insert into country values(41, 'USA')")
query.exec_("insert into country values(47, 'Norway')")
query.exec_("insert into country values(49, 'Germany')")
model = QtSql.QSqlRelationalTableModel()
model.setTable("employee")
model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
model.setRelation(2, QtSql.QSqlRelation('city', 'id', 'name'))
model.setRelation(3, QtSql.QSqlRelation('country', 'id', 'name'))
model.setHeaderData(0, QtCore.Qt.Horizontal, "ID")
model.setHeaderData(1, QtCore.Qt.Horizontal, "Name")
model.setHeaderData(2, QtCore.Qt.Horizontal, "City")
model.setHeaderData(3, QtCore.Qt.Horizontal, "Country")
model.select()
proxy = FlipProxyModel()
proxy.setSourceModel(model)
w = QWidget()
layout = QHBoxLayout(w)
view = QTableView()
view.setModel(model)
view.setItemDelegate(QSqlRelationalDelegate(view))
layout.addWidget(view)
view2 = QTableView()
view2.setModel(proxy)
view2.setItemDelegate(QSqlRelationalDelegate(view2))
layout.addWidget(view2)
w.show()
sys.exit(app.exec_())
big thanks to some friendly stranger on the #pyqt irc. this is solved.
the answer is that the delegates also need an proxymodel class.
the class looks like:
class FlipProxyDelegate(QSqlRelationalDelegate):
def createEditor(self, parent, option, index):
proxy = index.model()
base_index = proxy.mapToSource(index)
return super(FlipProxyDelegate, self).createEditor(parent, option, base_index)
def setEditorData(self, editor, index):
proxy = index.model()
base_index = proxy.mapToSource(index)
return super(FlipProxyDelegate, self).setEditorData(editor, base_index)
def setModelData(self, editor, model, index):
base_model = model.sourceModel()
base_index = model.mapToSource(index)
return super(FlipProxyDelegate, self).setModelData(editor, base_model, base_index)
its used then replacing the delegates as:
view.setItemDelegate(FlipProxyDelegate(self.tableView))
I'd propose an extended version of #logsoft solution in order to handle the delegates whether a proxy is there or not.
class ExtendedRelationalDelegate(QSqlRelationalDelegate):
"""This customization allows to handle also sql models behind Proxy"""
#staticmethod
def base_model(model: QAbstractItemModel):
if isinstance(model, QAbstractProxyModel):
return model.sourceModel()
return model
#staticmethod
def base_index(index: QModelIndex):
if isinstance(index.model(), QAbstractProxyModel):
return index.model().mapToSource(index)
return index
def createEditor(self, parent, option, index):
return QSqlRelationalDelegate.createEditor(self, parent, option, self.base_index(index))
def setEditorData(self, editor, index: QModelIndex):
return QSqlRelationalDelegate.setEditorData(self, editor, self.base_index(index))
def setModelData(self, editor, model, index):
return QSqlRelationalDelegate.setModelData(self, editor, self.base_model(model), self.base_index(index))