0

A bit of background (not fully necessary to understand the problem):

In a PySide2 application I developed earlier this year, I have a QTableView that I'm using with a custom model. Some items in there can be selected in order to then click a button to perform a bulk action on the selected items. Some other items are there as a visual indication that the data exists, but the function that does the bulk action woudn't know how to handle them, so I made them non-selectable. I have another area in my program that can accept drops, either from external files or from the items in my TableView. In that case, every single one of the items in the table should be able to be dragged and then dropped in the other area.

The problem:

When an Item is not selectable, it seems to also not be draggable.

What I have tried:

Not much in terms of code, as I don't really know how to approach that one. I tried a lot of combinations of Flags with no success.

I have been googling and browsing StackOverflow for a few hours, but can't find anything relevant, I'm just inundated by posts of people asking how to re-order a table by drag and dropping.

Minimum reproduction code:

from PySide2 import QtCore, QtWidgets, QtGui


class MyWindow(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.setGeometry(300, 200, 570, 450)
        self.setWindowTitle("Drag Example")
        model = QtGui.QStandardItemModel(2, 1)
        table_view = QtWidgets.QTableView()

        selectable = QtGui.QStandardItem("I'm selectable and draggable")
        selectable.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDragEnabled)
        model.setItem(0, 0, selectable)

        non_selectable = QtGui.QStandardItem("I'm supposed to be draggable, but I'm not")
        non_selectable.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDragEnabled)
        model.setItem(1, 0, non_selectable)

        table_view.setModel(model)
        table_view.setDragDropMode(table_view.DragOnly)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(table_view)
        self.setLayout(layout)


app = QtWidgets.QApplication([])
win = MyWindow()
win.show()
app.exec_()

Does anyone have a suggestion as to how to make an Item drag-enabled but selection-disabled?

Thanks

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Erwan Leroy
  • 160
  • 10

1 Answers1

1

I found a workaround, though not necessarily a perfect answer, it does the trick for me.

Instead of making the item non selectable, I make it visually look like it doesn't select when selecting it, using a custom data role.

I use that same data flag in my bulk function to filter out the selection.

To make it look like the items are not selected even though they are, I used a custom styled item delegate:

class MyDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent=None):
        super(MyDelegate, self).__init__(parent)

    def paint(self, painter, option, index):

        if option.state & QtWidgets.QStyle.State_Selected:
            if index.data(Qt.UserRole) == 2:  # Some random flag i'm using
                text_brush = option.palette.brush(QtGui.QPalette.Text)
                if index.row() % 2 == 0:
                    bg_brush = option.palette.brush(QtGui.QPalette.Base)
                else:
                    bg_brush = option.palette.brush(QtGui.QPalette.AlternateBase)
                # print brush
                option.palette.setBrush(QtGui.QPalette.Highlight, bg_brush)
                option.palette.setBrush(QtGui.QPalette.HighlightedText, text_brush)

        super(MyDelegate, self).paint(painter, option, index)

and in the table I set:

delegate = MyDelegate()
table_view.setItemDelegate(delegate)

I adapted this solution from this post: QTreeView Item Hover/Selected background color based on current color

Erwan Leroy
  • 160
  • 10