I have a custom QListView:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from Diagnostics import Trace2 #writes to log file
import traceback
class ListOrderView(QListView):
itemMoved = pyqtSignal(int, int, QStandardItem) # Old index, new index, item
def __init__(self, parent=None):
try:
super(ListOrderView, self).__init__(parent)
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDefaultDropAction(Qt.MoveAction)
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.dragItem = None
self.dragRow = None
self.indexesMoved.connect(self.onIndexesMoved)
#self.installEventFilter(self)
except:
Trace2.WriteLine(str(traceback.format_exc()))
def onIndexesMoved(self, indexes):
Trace2.WriteLine("indexes were moved")
def dropEvent(self, event):
try:
super(ListOrderView, self).dropEvent(event)
self.selectionModel().setCurrentIndex(self.model().indexFromItem(self.dragItem), QItemSelectionModel.SelectCurrent)
Trace2.WriteLine("[LISTVIEW] item dropped")
Trace2.WriteLine("[LISTVIEW] current index is %d" %self.selectionModel().currentIndex().row())
Trace2.WriteLine("[LISTVIEW] current selection is %d" %self.selectionModel().selection().indexes()[0].row())
self.itemMoved.emit(self.dragRow, self.row(self.dragItem), self.dragItem)
self.dragItem = None
except:
Trace2.WriteLine(str(traceback.format_exc()))
def startDrag(self, supportedActions):
try:
self.dragItem = self.currentItem()
self.dragRow = self.row(self.dragItem)
super(ListOrderView, self).startDrag(Qt.MoveAction)
except:
Trace2.WriteLine(str(traceback.format_exc()))
def currentItem(self):
index = self.currentIndex()
item = self.model().itemFromIndex(index)
#Trace2.WriteLine("[LISTVIEW] currentItem = %s" % item.data(Qt.DisplayRole).toString())
return item
def row(self, item):
#index = self.model().indexFromItem(item)
index = self.selectedIndexes()[0]
row = index.row()
#Trace2.WriteLine("[LISTVIEW] row = %d" %row)
return row
And I really need to know where the item was dropped after a drag and drop operation so other things can be properly updated (I'm trying to put drag and drop into something never designed for it, big app, not my design). The selection model's current index and selection don't follow the dropped item, they stay behind effectively selecting a new item and screwing things up. Is there a way to make them move with the dropped item? The signal indexesMoved seems exactly like what I want, but it never fires. Am I using it wrong? Is there a different/better way?
I think you might need to actually have the model tell you where something was dropped, since it will ultimately handle the move:
class Model(QStandardItemModel):
rowDropped = pyqtSignal(int)
def dropMimeData(self, *args):
success = super(Model, self).dropMimeData(*args)
if success:
self.rowDropped.emit(args[2])
return success
This will emit, from the model, the row number on which the drop occurred. Your view already knows which item was dragged and dropped from its own events.
I am sure there are other ways in terms of tracking the object and then querying it again after the drop has completed.
Related
I'm trying to build an application with PyQT6 that allows users to browse through a list of images with thumbnails and display the selected image in an image viewer. The application can also add and delete images. Adding images seems to work fine, but when I delete an image from the model the row in the QListView suddenly displays the data from the next row in the list. After a random interval of anywhere between half a second and about five seconds the row will actually be removed, and the list will display the proper file ordering. The fact that this behavior occurs makes me think I'm not removing the item from the model properly, and ideally I'd like the deletion of a row to be instantaneous.
Here is my minimum reproducible example:
import PyQt6 as qt
import PyQt6.QtCore as QtCore
from PyQt6.QtCore import Qt, QAbstractListModel, QModelIndex
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
import os
import sys
import traceback
class ImageDataModel(QAbstractListModel):
def __init__(self, images=None):
super(ImageDataModel, self).__init__()
if images is None:
self.images = []
else:
self.images = images
self.thumbnails = []
for img_path in self.images:
icon = QPixmap(img_path).scaledToHeight(20)
self.thumbnails.append(icon)
def data(self, index, role):
if role == Qt.ItemDataRole.DisplayRole:
img_path = self.images[index.row()]
return img_path
if role == Qt.ItemDataRole.DecorationRole:
thumbnail = self.thumbnails[index.row()]
return thumbnail
def rowCount(self, index):
return len(self.images)
def removeRow(self, index):
self.images.pop(index)
self.thumbnails.pop(index)
class myListView(QListView):
def __init__(self, parent=None):
super().__init__()
self.parent = parent
self.setSelectionMode(QListView.SelectionMode.ExtendedSelection)
def currentChanged(self, current: QtCore.QModelIndex, previous: QtCore.QModelIndex) -> None:
if (current.row() >= 0):
self.parent.get_selection(current) # this method simply displays the selected image
return super().currentChanged(current, previous)
class MyMenu(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QHBoxLayout()
self.list = myListView(self)
try:
image_file_list = [x for x in os.listdir('path/to/image/directory') if x.lower().endswith(".png")]
except:
image_file_list = []
image_file_list.sort()
self.model = ImageDataModel(image_file_list)
self.list.setModel(self.model)
self.list.clicked.connect(self.get_selection) # this method simply displays the selected image
self.list.setCurrentIndex(self.model.index(0,0))
self.layout.addWidget(self.list)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
# Deletes the currently displayed image and annotation from the dataset
def delete_image(self):
# Determine what to set the new index to after deletion
if self.list.currentIndex().row() != 0:
new_index = self.list.currentIndex().row() - 1
else:
new_index = 0
# Attempt to remove the row and delete the file
try:
self.list.model().removeRow(self.list.currentIndex().row())
os.remove(self.img_path)
# Set index row to the image immediately preceding the deleted image
index = self.model.createIndex(new_index, 0)
self.list.setCurrentIndex(index)
except:
traceback.print_exc()
# Replaced display code for brevity
def get_selection(self, item):
print(item.row())
# Handles keypresses
def keyPressEvent(self, e) -> None:
global model_enabled
if (e.key() == Qt.Key.Key_Escape):
app.quit()
if (e.key() == Qt.Key.Key_Delete):
self.delete_image()
def main():
app = QApplication(sys.argv)
window = MyMenu()
window.show()
app.exec()
main()
Any change in the size, order/layout and data of a model should always be done using the proper function calls so that the views linked to the model get proper notifications about those changes.
For size and layout changes, it's important to always call the begin* and end* functions, which allows the view to be notified about the upcoming change, so they can keep a persistent list of the current items (including selection) and restore it when the change is completed.
Row removal is achieved using beginRemoveRows() and endRemoveRows().
In your case:
def removeRow(self, index):
self.beginRemoveRows(QModelIndex(), index, index)
self.images.pop(index)
self.thumbnails.pop(index)
self.endRemoveRows()
return True # <- the function expects a bool in return
Note that the correct way to implement row removal is done by implementing removeRows(), not removeRow() (singular), which internally calls removeRows anyway. So, you can leave the existing removeRow() call, do not override removeRow() and implement removeRows() instead.
def removeRows(self, row, count, parent=QModelIndex()):
if row + count >= len(self.images) or count < 1:
return False
self.beginRemoveRows(parent, row, row + count - 1)
del self.images[row:row+count]
del self.thumbnails[row:row+count]
self.endRemoveRows()
return True
A similar concept should always be done when adding new items after a view is linked to the model; in that case, implement insertRows() and there you'll call beginInsertRows() insert the new data and finally call endInsertRows().
Note that your code will throw an exception if the images is None, as it doesn't create the thumbnails object.
I'm building a GUI using PySide2 (Qt5) with a custom treeview widget (MyTreeView, inherited from QTreeView). The model is a QStandardItemModel object whereas the items are custom: MyStandardItem, inherited from QStandardItem.
The problem is: if I check the type of the moved item after a drag and drop action, it has become a QStandardItem but it should have been a MyStandardItem.
I believe that the problem is the MimeType, and after a lot of research I found out that the solution could be creating a custom model and overriding MIME related functions.
I tried to figure out how but I couldn't.
So, here are the questions:
Do I have to create a custom model or is there a simple solution?
If I have to create a custom model, which functions should I override and how should I override those functions?
For what it's worth, here is MyStandardItem implementation:
class MyStandardItem(QStandardItem):
def __init__(self, text, font, icon_path='', value='', num=0, check_state=None):
super().__init__()
self.setDragEnabled(True)
self.setDropEnabled(True)
self.setText(text)
self.setData({'value': (value, num)})
self.setToolTip(str(self.data()['value']))
self.setFont(font)
self.setIcon(QIcon(icon_path))
self.toggled = check_state
if check_state is not None:
self.setCheckable(True)
self.setCheckState(check_state)
def setCheckState(self, checkState):
super().setCheckState(checkState)
if checkState == Qt.Unchecked:
self.toggled = Qt.Unchecked
else:
self.toggled = Qt.Checked
I found a way to solve this problem without having to create a custom model.
In the MyTreeView.dropEvent function: I dont't call super().dropEvent() to complete the drag&drop action but I implement it by myself by copying item's data in a variable and creating a new MyStandardItem from those data. Then I call insertRow() to insert the new item in the given position and deleteRow() to delete the old item.
Clearly, the moved item has to be stored in a class attribute at the beginning of the action (DragEnterEvent()).
Everything works perfectly.
PS: I've already tried this way before but I always ended up by having an empty row. The difference here is that I create a new item instead of re-inserting the old one.
Here's some parts of my code to clarify what I mean. Please, note that these functions are MyTreeView's methods.
def dragEnterEvent(self, event: QDragEnterEvent):
if event.source() == self:
self.dragged_item = self.model.itemFromIndex(self.selectionModel().selectedIndexes()[0])
super().dragEnterEvent(event)
else:
...
def dropEvent(self, event: QDropEvent):
index = self.indexAt(event.pos())
if not index.isValid():
return
over_item = self.model.itemFromIndex(index)
over_value = over_item.data()['value'][0]
if event.source() == self:
item_was_moved = self._move_item(over_value, over_parent_value)
if not item_was_moved:
return
else:
...
def _move_item(self, over_value, over_parent_value):
over_value = self._check_indicator_position(over_value)
if over_value is None:
return False
dragged_parent_value = self.dragged_item.parent().data()['value'][0]
dragged_value = self.dragged_item.data()['value'][0]
row = self.dragged_item.row()
items = guifunc.copy_row_items(self.dragged_item, row)
over_value_num = int(over_value.strip('test'))
self.dragged_item.parent().insertRow(over_value_num, items)
if over_value_num < row:
row += 1
self.dragged_item.parent().removeRow(row)
return True
I'm using a QListWidget that simply displays a list of objects. I allow the user to reorder these items via internal drag and drop. Everything works, but I now need to add a check when the user tries to drop (reorder) and if the check fails, re-establish the original order programmatically. Here's what I've got:
class SequenceControl(QListWidget):
def __init__(self, parent = None):
super(SequenceControl, self).__init__(parent)
self.initialIndex = 0
self.selectedObject = None
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
def dragEnterEvent(self, event):
super(SequenceControl, self).dragEnterEvent(event)
self.selectedObject = self.currentItem()
self.initialIndex = self.currentRow()
def dropEvent(self, event):
super(SequenceControl, self).dropEvent(event)
# Some logic here (not shown) to see if the drop is not
# allowed. Assume it isn't:
warningDialog = MyWarningDialog(self.parent)
ProceedAnyway = warningDialog.exec_()
if ProceedAnyway:
# Do stuff...
else:
# Here's the problem. I need to place the item being dropped
# back in its initial pre-drag/drop location. The following
# naïve attempt doesn't work:
self.insertItem(self.initialIndex, self.selectedObject)
The above is definitely wrong (I believe) because it may duplicate the item. But aside from that, the problem is that it appears to have no effect. I think the drop event is overriding anything I'm doing in terms of reordering. But that's just a theory. Does anyone know the right way to do this?
To revert the change we must first remove the target item, for this we use takeItem() that besides removing it will return the item, then insert it in the source position with the help of the insertItem() function.
class SequenceControl(QListWidget):
def __init__(self, parent=None):
super(SequenceControl, self).__init__(parent)
self.fromPos = None
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
def dragEnterEvent(self, event):
super(SequenceControl, self).dragEnterEvent(event)
self.fromPos = self.currentRow()
def dropEvent(self, event):
super(SequenceControl, self).dropEvent(event)
reply = QMessageBox.question(None, "Revert to Drag and Drop",
"Do you want to keep the change?",
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
print("Do stuff...")
else:
currentItem = self.takeItem(self.currentRow())
self.insertItem(self.fromPos, currentItem)
To start, I've already looked at ekhumoro's code on a nearly similar subject Here. However, when I attempt to implement this code, I am getting a different result. Instead of copying and pasting all the I selected, it only copies the first cell of the selected row or rows respectively. I need users to be able to select multiple rows or columns and paste those values in either excel or notepad. Any ideas?
GUI:
Code:
from PyQt4 import QtCore, QtGui, QtSql
import sys
import sqlite3
import time
import csv
import Search # This file holds our MainWindow and all design related things
# it also keeps events etc that we defined in Qt Designer
import os
try:
from PyQt4.QtCore import QString
except ImportError:
QString = str
class TableEditor(QtGui.QMainWindow, Search.Search_MainWindow):
def __init__(self, tableName, parent=None):
# Explaining super is out of the scope of this article
# So please google it if you're not familar with it
# Simple reason why we use it here is that it allows us to
# access variables, methods etc in the design.py file
super(self.__class__, self).__init__()
self.setupUi(self) # This is defined in design.py file automatically
# It sets up layout and widgets that are defined
self.model = QtSql.QSqlTableModel(self)
self.model.setTable('CAUTI')
self.model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
self.model.select()
self.model.setHeaderData(0, QtCore.Qt.Horizontal, "MRN")
self.model.setHeaderData(1, QtCore.Qt.Horizontal, "Last Name")
self.model.setHeaderData(2, QtCore.Qt.Horizontal, "First Name")
self.model.setHeaderData(3, QtCore.Qt.Horizontal, "Date of Event")
self.model.setHeaderData(4, QtCore.Qt.Horizontal, "Facility")
self.model.setHeaderData(5, QtCore.Qt.Horizontal, "Unit")
self.model.setHeaderData(6, QtCore.Qt.Horizontal, "User")
#self.tableView.verticalHeader().setVisible(False)
self.tableView.setModel(self.model)
self.setWindowTitle("HAI Table")
self.tableView.setColumnWidth(0,100)
self.tableView.setColumnWidth(1,100)
self.tableView.setColumnWidth(2,100)
self.tableView.setColumnWidth(3,100)
self.tableView.setColumnWidth(4,100)
self.tableView.setColumnWidth(5,100)
self.tableView.setColumnWidth(6,83)
self.tableView.setSortingEnabled(True)
self.tableView.setDropIndicatorShown(True)
self.tableView.setAcceptDrops(True)
self.tableView.setDragEnabled(True)
self.tableView.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.horizontalHeader().setMovable(True)
self.tableView.horizontalHeader().setDragEnabled(True)
self.clip = QtGui.QApplication.clipboard()
## Note: ** When I unblock this line of code, I get an error.
#self.tableView.installEventFilters(self)
self.tableView.horizontalHeader().setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.verticalHeader().setMovable(True)
self.tableView.verticalHeader().setDragEnabled(True)
self.tableView.verticalHeader().setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.setSelectionBehavior(QtGui.QTableView.SelectRows)
self.submitButton.clicked.connect(self.submit)
self.revertButton.clicked.connect(self.model.revertAll)
self.quitButton.clicked.connect(self.close)
self.tableView.horizontalHeader().setSortIndicatorShown(True)
self.tableView.horizontalHeader().setClickable(True)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.KeyPress and
event.matches(QtGui.QKeySequence.Copy)):
self.copySelection()
return True
return super(Window, self).eventFilter(source, event)
def copySelection(self):
selection = self.tableView.selectedIndexes()
if selection:
rows = sorted(index.row() for index in selection)
columns = sorted(index.column() for index in selection)
rowcount = rows[-1] - rows[0] + 1
colcount = columns[-1] - columns[0] + 1
table = [[''] * colcount for _ in range(rowcount)]
for index in selection:
row = index.row() - rows[0]
column = index.column() - columns[0]
table[row][column] = index.data()
stream = io.StringIO()
csv.writer(stream).writerows(table)
QtGui.qApp.clipboard().setText(stream.getvalue())
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDropEnabled
def main():
app = QtGui.QApplication(sys.argv)
#app.setStyle( "Plastique" )
db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('HAI.db')
editor = TableEditor('CAUTI')
editor.show()
app.exec_()
if __name__ == '__main__':
main()
Your implementation is broken in several ways, so the code you copied never gets executed. As a result, the table's built-in copy function is used, which does not handle multiple selected items.
My original code for copying multiple table items is here. I think you should be able to get your example working by changing the following lines:
from PyQt4 import QtCore, QtGui, QtSql
# fix imports
import sys, io
...
class TableEditor(QtGui.QMainWindow, Search.Search_MainWindow):
def __init__(self, tableName, parent=None):
# do not ever use self.__class__ here
super(TableEditor, self).__init__()
...
# install event-filter properly
self.tableView.installEventFilter(self)
def eventFilter(self, source, event):
...
# call super properly
return super(TableEditor, self).eventFilter(source, event)
Obviously, I cannot run your actual example code, so there may be other errors that I haven't spotted.
This is much faster :
def copySelection(self):
clipboardString = StringIO()
selectedIndexes = self.ui.tableView.selectedIndexes()
if selectedIndexes:
countList = len(selectedIndexes)
for r in range(countList):
current = selectedIndexes[r]
displayText = current.data(QtCore.Qt.DisplayRole)
if r+1 < countList:
next_ = selectedIndexes[r+1]
if next_.row() != current.row():
displayText += ("\n")
else:
displayText += ("\t")
clipboardString.write(displayText)
pyperclip.copy(clipboardString.getvalue())
I get this behavior only on linux. On windows this problem does not happen.
I have a table using the model/view framework. One of the columns is editable, and when I enter it to change the data the old data is still visible in the background while I edit the data in the foreground.
I'm not sure what is causing this, I've tried flipping various settings, but I've been unable to change the behavior.
Maybe a simpler question that will still help me: I'm correct to be looking in the view code for this issue correct? Would the model possibly have anything to do with this? Do I need to set any currently editing flags in the model?
Assuming that the view is where the issue is here is most of the view logic I'm using. There are some caveats to the following code: I'm injecting common code used between QTableViews and QTreeViews, so there are a few functions this class has that are no explicitly listed as methods:
from __future__ import absolute_import, division, print_function
from guitool.__PYQT__ import QtCore, QtGui
from guitool import api_item_view
from guitool.guitool_decorators import signal_, slot_
API_VIEW_BASE = QtGui.QTableView
class APITableView(API_VIEW_BASE):
rows_updated = signal_(str, int)
contextMenuClicked = signal_(QtCore.QModelIndex, QtCore.QPoint)
API_VIEW_BASE = API_VIEW_BASE
def __init__(view, parent=None):
API_VIEW_BASE.__init__(view, parent)
api_item_view.injectviewinstance(view)
view._init_table_behavior()
view._init_header_behavior()
view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
view.customContextMenuRequested.connect(view.on_customMenuRequested)
#---------------
# Initialization
#---------------
def _init_table_behavior(view):
view.setCornerButtonEnabled(False)
view.setWordWrap(True)
view.setSortingEnabled(True)
view.setShowGrid(True)
# Selection behavior #view.setSelectionBehavior(QtGui.QAbstractItemView.SelectColumns)
view.setSelectionBehavior(QtGui.QAbstractItemView.SelectItems)
view._defaultEditTriggers = QtGui.QAbstractItemView.AllEditTriggers
view.setEditTriggers(view._defaultEditTriggers)
view.setIconSize(QtCore.QSize(64, 64))
def _init_header_behavior(view):
""" Header behavior """
# Row Headers
verticalHeader = view.verticalHeader()
verticalHeader.setVisible(True)
#verticalHeader.setSortIndicatorShown(True)
verticalHeader.setHighlightSections(True)
verticalHeader.setResizeMode(QtGui.QHeaderView.Interactive)
verticalHeader.setMovable(True)
# Column headers
horizontalHeader = view.horizontalHeader()
horizontalHeader.setVisible(True)
horizontalHeader.setStretchLastSection(True)
horizontalHeader.setSortIndicatorShown(True)
horizontalHeader.setHighlightSections(True)
# Column Sizes
# DO NOT USE ResizeToContents. IT MAKES THINGS VERY SLOW
#horizontalHeader.setResizeMode(QtGui.QHeaderView.ResizeToContents)
#horizontalHeader.setResizeMode(QtGui.QHeaderView.Stretch)
horizontalHeader.setResizeMode(QtGui.QHeaderView.Interactive)
#horizontalHeader.setCascadingSectionResizes(True)
# Columns moveable
horizontalHeader.setMovable(True)
#---------------
# Qt Overrides
#---------------
def setModel(view, model):
""" QtOverride: Returns item delegate for this index """
api_item_view.setModel(view, model)
def keyPressEvent(view, event):
assert isinstance(event, QtGui.QKeyEvent)
API_VIEW_BASE.keyPressEvent(view, event)
if event.matches(QtGui.QKeySequence.Copy):
#print('Received Ctrl+C in View')
view.copy_selection_to_clipboard()
#print ('[view] keyPressEvent: %s' % event.key())
def mouseMoveEvent(view, event):
assert isinstance(event, QtGui.QMouseEvent)
API_VIEW_BASE.mouseMoveEvent(view, event)
def mousePressEvent(view, event):
assert isinstance(event, QtGui.QMouseEvent)
API_VIEW_BASE.mousePressEvent(view, event)
#print('no editing')
view.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
def mouseReleaseEvent(view, event):
assert isinstance(event, QtGui.QMouseEvent)
#print('editing ok')
view.setEditTriggers(view._defaultEditTriggers)
API_VIEW_BASE.mouseReleaseEvent(view, event)
def clearSelection(view, *args, **kwargs):
print('[table_view] clear selection')
API_VIEW_BASE.clearSelection(view, *args, **kwargs)
#---------------
# Slots
#---------------
#slot_(str, int)
def on_rows_updated(view, tblname, num):
# re-emit the model signal
view.rows_updated.emit(tblname, num)
#slot_(QtCore.QPoint)
def on_customMenuRequested(view, pos):
index = view.indexAt(pos)
view.contextMenuClicked.emit(index, pos)
# ----
# Injected funcs from api_item_view
#register_view_method
def infer_delegates(view, **headers):
""" Infers which columns should be given item delegates """
get_thumb_size = headers.get('get_thumb_size', None)
col_type_list = headers.get('col_type_list', [])
num_cols = view.model().columnCount()
num_duplicates = int(num_cols / len(col_type_list))
col_type_list = col_type_list * num_duplicates
for colx, coltype in enumerate(col_type_list):
if coltype in qtype.QT_PIXMAP_TYPES:
if VERBOSE:
print('[view] colx=%r is a PIXMAP' % colx)
thumb_delegate = APIThumbDelegate(view, get_thumb_size)
view.setItemDelegateForColumn(colx, thumb_delegate)
elif coltype in qtype.QT_BUTTON_TYPES:
if VERBOSE:
print('[view] colx=%r is a BUTTON' % colx)
button_delegate = APIButtonDelegate(view)
view.setItemDelegateForColumn(colx, button_delegate)
else:
if VERBOSE:
print('[view] colx=%r does not have a delgate' % colx)
#register_view_method
def set_column_persistant_editor(view, column):
""" Set each row in a column as persistant """
num_rows = view.model.rowCount()
print('view.set_persistant: %r rows' % num_rows)
for row in range(num_rows):
index = view.model.index(row, column)
view.view.openPersistentEditor(index)
The item-delegate editor needs to paint its own background:
editor.setAutoFillBackground(True)
Obviously, this has to be fixed within the custom delegate classes (APIThumbDelegate and/or APIButtonDelegate) which create the editor widgets (i.e. via their createEditor functions).