<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr">An important thing to remember is that you can't always completely rely on IDEs for debugging: not only they will probably hide the actual exception depending on their implementations, but they normally don't show the full traceback. When in doubt, just run your program from terminal/prompt and check the output.</div><div dir="ltr"><div><br></div><div>In your case, the crash is caused by the fact that you didn't correctly implement setData(), which **always** expects a bool as the returned value.</div><div><br></div><div>Then, the problem is that you didn't implement the most important function for drop events in a model: dropMimeData().</div><div><br></div><div>Drag and drop always happens (or should happen) through QMimeData in Qt models. Qt views (and their models) use the "application/x-qabstractitemmodeldatalist" format to serialize data, which is created by calling QAbstractItemModel.mimeData (see <a href="https://doc.qt.io/qt-5/qabstractitemmodel.html#mimeData">https://doc.qt.io/qt-5/qabstractitemmodel.html#mimeData</a>), and the structure is the following:</div><div><br></div><div>- for each item:</div><div>    - write row number</div><div>    - write column number</div><div>    - write QMap (which in PyQt means the number of key/value pairs): for each key/value pairs in mapping:</div><div>        - write role (integer)</div><div>        - write value (QVariant, aka a serialization of a possibly python object)</div><div><br>Here is a possible implementation:</div><div><br></div><div><div><font face="monospace">    def dropMimeData(self, data, action, row, column, parent):</font></div><div><div><font face="monospace">        decoded = data.data('application/x-qabstractitemmodeldatalist')</font></div><div><font face="monospace">        stream = QDataStream(decoded, QIODevice.ReadOnly)</font></div><div><font face="monospace">        items = []</font></div><div><font face="monospace">        startRow = startCol = 65536</font></div><div><font face="monospace">        if parent.isValid():</font></div><div><font face="monospace">            row = parent.row()</font></div><div><font face="monospace">            column = parent.column()</font></div><div><font face="monospace">        while not stream.atEnd():</font></div><div><font face="monospace">            itemRow = stream.readInt()</font></div><div><font face="monospace">            itemCol = stream.readInt()</font></div><div><font face="monospace">            fieldCount = stream.readInt()</font></div><div><font face="monospace">            display = None</font></div><div><font face="monospace">            displayValid = True</font></div><div><font face="monospace">            while fieldCount:</font></div><div><font face="monospace">                role = stream.readInt()</font></div><div><font face="monospace">                value = stream.readQVariant()</font></div><div><font face="monospace">                if role == Qt.DisplayRole:</font></div><div><font face="monospace">                    displayValid = True</font></div><div><font face="monospace">                    display = value</font></div><div><font face="monospace">                fieldCount -= 1</font></div><div><font face="monospace">            if displayValid and itemRow < self.rowCount() and itemCol < self.columnCount():</font></div><div><font face="monospace">                startRow = min(startRow, itemRow)</font></div><div><font face="monospace">                startCol = min(startCol, itemCol)</font></div><div><font face="monospace">                items.append((itemRow, itemCol, display))</font></div><div><font face="monospace">        if not items:</font></div><div><font face="monospace">            return False</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        minRow = minCol = 65536</font></div><div><font face="monospace">        maxRow = maxCol = 0</font></div><div><font face="monospace">        for itemRow, itemCol, value in items:</font></div><div><font face="monospace">            targetRow = row + (itemRow - startRow)</font></div><div><font face="monospace">            targetCol = column + (itemCol - startCol)</font></div><div><font face="monospace">            minRow = min(targetRow, minRow)</font></div><div><font face="monospace">            maxRow = max(targetRow, maxRow)</font></div><div><font face="monospace">            minCol = min(targetCol, minCol)</font></div><div><font face="monospace">            maxCol = max(targetCol, maxCol)</font></div><div><font face="monospace">            self.dataList[targetRow][targetCol] = value</font></div><div><font face="monospace">        self.dataChanged.emit(</font></div><div><font face="monospace">            self.index(minRow, minCol), </font></div><div><font face="monospace">            self.index(maxRow, maxCol), </font></div><div><font face="monospace">        )</font></div><div><font face="monospace">        return True</font></div></div></div><div><br></div><div>Some important notes:</div><div><br></div><div>- the dropMimeData parent argument refers to the item highlighted by the drop indicator; this means that when you directly drop on an item (not above/below it) you have to get the row and column coordinates of the parent (that's the reason behind the first if statement in my code); for basic 2d models the above replacement of row/column is fine, for tree models it requires more attention;</div><div>- the above implementation is very basic: it only works intuitively for single items or *close* selections (selected items that are in the next/previous column or row of the others); you have to find your preferred implementation based on your needs; also, it just replaces the data, so there's no clearing for MoveAction, nor "switching" between items;</div><div>- reading through the *whole* stream is mandatory (even for roles you're not interested into), since reading is sequential; if you don't read all fields, you'll get unexpected behavior; remember that python evaluates objects recursively, so you can't do something like `fields[stream.readInt()] = stream.readQVariant()`, since the role comes first in the stream;</div><div></div><div>- the selection order is always respected in the QDataStream; if you use extended selection, select item(1, 1) and then item(0, 0), you'll get a datastream with that same order; note that this also means that there is no way (from the model) to know the item on which the drag has been started;</div><div>- in order to provide more advanced drag&drop features (such as providing correct item replacement and/or switching for complex selections, see the previous point), you also need to reimplement the view's startDrag() so that the order of item respects the item on which the drag was started on by modifying the order of selected indexes returned by the selection model;</div><div>- both rowCount and columnCount index argument should be optional (aka keyworded: for table models it's fine to use None, for tree structures QModelIndex() is usually preferred);</div><div></div><div>- the above respects the default implementation for both views and models; obviously, as long as d&d is kept within the same view, nothing stops you to implement your own mimeData() in order to provide simpler data content (for instance, just row/column pairs) with a custom mime format and then properly react to it in dropMimeData();</div><div><br></div><div>Cheers,</div><div>Maurizio</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Il giorno lun 19 apr 2021 alle ore 02:15 Rodrigo de Salvo Braz <<a href="mailto:rodrigobraz@gmail.com">rodrigobraz@gmail.com</a>> ha scritto:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>Hi,</div><div><br></div><div>I want to use drag and drop for a QAbstractItemModel. I followed the instructions at <a href="https://doc.qt.io/archives/qt-5.5/model-view-programming.html#using-drag-and-drop-with-item-views" target="_blank">Qt's documentation</a> quite carefully, but when I try a drag-and-drop, the program crashes.<br></div><div><br></div><div>I'm using PyQt 5.9.2 / Qt 5.9.7 installed with conda, on Windows 10.

</div><div><br></div><div>Any idea what I am doing wrong here? Thanks.</div><div><br></div><div><pre style="background-color:rgb(255,255,255);color:rgb(8,8,8);font-family:"JetBrains Mono",monospace;font-size:9.8pt"><span style="color:rgb(0,51,179)">import </span>sys<br><br><span style="color:rgb(0,51,179)">from </span>PyQt5 <span style="color:rgb(0,51,179)">import </span>QtCore, QtWidgets<br><span style="color:rgb(0,51,179)">from </span>PyQt5.QtCore <span style="color:rgb(0,51,179)">import </span>Qt<br><span style="color:rgb(0,51,179)">from </span>PyQt5.QtWidgets <span style="color:rgb(0,51,179)">import </span>QApplication, QMainWindow, QTableView, QAbstractItemView<br><br><span style="color:rgb(140,140,140);font-style:italic"># Attempting to implement drag-and-drop for an QAbstractTableModel as described in<br></span><span style="color:rgb(140,140,140);font-style:italic"># <a href="https://doc.qt.io/archives/qt-5.5/model-view-programming.html#using-drag-and-drop-with-item-views" target="_blank">https://doc.qt.io/archives/qt-5.5/model-view-programming.html#using-drag-and-drop-with-item-views</a><br></span><span style="color:rgb(140,140,140);font-style:italic"><br></span><span style="color:rgb(0,51,179)">class </span><span style="color:rgb(0,0,0)">TableModel</span>(QtCore.QAbstractTableModel):<br>    <span style="color:rgb(0,51,179)">def </span><span style="color:rgb(178,0,178)">__init__</span>(<span style="color:rgb(148,85,141)">self</span>):<br>        <span style="color:rgb(0,0,128)">super</span>().<span style="color:rgb(178,0,178)">__init__</span>()<br>        <span style="color:rgb(148,85,141)">self</span>.dataList = [[<span style="color:rgb(0,128,128);font-weight:bold">"Lion"</span>, <span style="color:rgb(0,128,128);font-weight:bold">"Tiger"</span>, <span style="color:rgb(0,128,128);font-weight:bold">"Bear"</span>], [<span style="color:rgb(0,128,128);font-weight:bold">"Gazelle"</span>, <span style="color:rgb(0,128,128);font-weight:bold">"Ox"</span>, <span style="color:rgb(0,128,128);font-weight:bold">"Pig"</span>], [<span style="color:rgb(0,128,128);font-weight:bold">"Mouse"</span>, <span style="color:rgb(0,128,128);font-weight:bold">"Cat"</span>, <span style="color:rgb(0,128,128);font-weight:bold">"Dog"</span>]]<br><br>    <span style="color:rgb(0,51,179)">def </span><span style="color:rgb(0,0,0)">data</span>(<span style="color:rgb(148,85,141)">self</span>, index, role=<span style="color:rgb(0,51,179)">None</span>):<br>        <span style="color:rgb(0,51,179)">if </span>role == Qt.DisplayRole:<br>            <span style="color:rgb(0,51,179)">return </span><span style="color:rgb(0,128,128);font-weight:bold">f"</span><span style="color:rgb(0,55,166)">{</span><span style="color:rgb(148,85,141)">self</span>.dataList[index.row()][index.column()]<span style="color:rgb(0,55,166)">}</span><span style="color:rgb(0,128,128);font-weight:bold">"<br></span><span style="color:rgb(0,128,128);font-weight:bold"><br></span><span style="color:rgb(0,128,128);font-weight:bold">    </span><span style="color:rgb(0,51,179)">def </span><span style="color:rgb(0,0,0)">setData</span>(<span style="color:rgb(148,85,141)">self</span>, index, value, role):<br>        <span style="color:rgb(0,51,179)">if </span>role == Qt.EditRole:<br>            <span style="color:rgb(148,85,141)">self</span>.dataList[index.row()][index.column()] = value<br>            <span style="color:rgb(0,51,179)">return True<br></span><span style="color:rgb(0,51,179)"><br></span><span style="color:rgb(0,51,179)">    def </span><span style="color:rgb(0,0,0)">rowCount</span>(<span style="color:rgb(148,85,141)">self</span>, index):<br>        <span style="color:rgb(0,51,179)">return </span><span style="color:rgb(0,0,128)">len</span>(<span style="color:rgb(148,85,141)">self</span>.dataList)<br><br>    <span style="color:rgb(0,51,179)">def </span><span style="color:rgb(0,0,0)">columnCount</span>(<span style="color:rgb(148,85,141)">self</span>, index):<br>        <span style="color:rgb(0,51,179)">return </span><span style="color:rgb(23,80,235)">3<br></span><span style="color:rgb(23,80,235)"><br></span><span style="color:rgb(23,80,235)">    </span><span style="color:rgb(0,51,179)">def </span><span style="color:rgb(0,0,0)">flags</span>(<span style="color:rgb(148,85,141)">self</span>, index):<br>        <span style="color:rgb(0,51,179)">return </span>Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled<br><br>    <span style="color:rgb(0,51,179)">def </span><span style="color:rgb(0,0,0)">supportedDropActions</span>(<span style="color:rgb(148,85,141)">self</span>):<br>        <span style="color:rgb(0,51,179)">return </span>Qt.CopyAction | Qt.MoveAction<br><br>    <span style="color:rgb(0,51,179)">def </span><span style="color:rgb(0,0,0)">insertRows</span>(<span style="color:rgb(148,85,141)">self</span>, row: <span style="color:rgb(0,0,128)">int</span>, count: <span style="color:rgb(0,0,128)">int</span>, parent) -> <span style="color:rgb(0,0,128)">bool</span>:<br>        <span style="color:rgb(148,85,141)">self</span>.beginInsertRows(parent, row, row + count)<br>        <span style="color:rgb(148,85,141)">self</span>.dataList[row : row + count] = [<span style="color:rgb(0,128,128);font-weight:bold">""</span>, <span style="color:rgb(0,128,128);font-weight:bold">""</span>, <span style="color:rgb(0,128,128);font-weight:bold">""</span>] * count<br>        <span style="color:rgb(148,85,141)">self</span>.endInsertRows()<br>        <span style="color:rgb(0,51,179)">return True<br></span><span style="color:rgb(0,51,179)"><br></span><span style="color:rgb(0,51,179)">    def </span><span style="color:rgb(0,0,0)">removeRows</span>(<span style="color:rgb(148,85,141)">self</span>, row: <span style="color:rgb(0,0,128)">int</span>, count: <span style="color:rgb(0,0,128)">int</span>, parent) -> <span style="color:rgb(0,0,128)">bool</span>:<br>        <span style="color:rgb(148,85,141)">self</span>.beginRemoveRows(parent, row, row + count)<br>        <span style="color:rgb(0,51,179)">del </span><span style="color:rgb(148,85,141)">self</span>.dataList[row : row + count]<br>        <span style="color:rgb(148,85,141)">self</span>.endRemoveRows()<br>        <span style="color:rgb(0,51,179)">return True<br></span><span style="color:rgb(0,51,179)"><br></span><span style="color:rgb(0,51,179)"><br></span><span style="color:rgb(0,51,179)">class </span><span style="color:rgb(0,0,0)">MainWindow</span>(QMainWindow):<br>    <span style="color:rgb(0,51,179)">def </span><span style="color:rgb(178,0,178)">__init__</span>(<span style="color:rgb(148,85,141)">self</span>):<br>        <span style="color:rgb(0,0,128)">super</span>().<span style="color:rgb(178,0,178)">__init__</span>()<br><br>        <span style="color:rgb(148,85,141)">self</span>.setWindowTitle(<span style="color:rgb(0,128,128);font-weight:bold">"A drag-and-drop table app"</span>)<br><br>        <span style="color:rgb(148,85,141)">self</span>.tableView = QTableView()<br><br>        <span style="color:rgb(148,85,141)">self</span>.model = TableModel()<br>        <span style="color:rgb(148,85,141)">self</span>.tableView.setModel(<span style="color:rgb(148,85,141)">self</span>.model)<br>        <span style="color:rgb(148,85,141)">self</span>.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)<br><br>        <span style="color:rgb(148,85,141)">self</span>.tableView.setSelectionMode(QAbstractItemView.ExtendedSelection)<br>        <span style="color:rgb(148,85,141)">self</span>.tableView.setDragEnabled(<span style="color:rgb(0,51,179)">True</span>)<br>        <span style="color:rgb(148,85,141)">self</span>.tableView.viewport().setAcceptDrops(<span style="color:rgb(0,51,179)">True</span>)<br>        <span style="color:rgb(148,85,141)">self</span>.tableView.setDropIndicatorShown(<span style="color:rgb(0,51,179)">True</span>)<br>        <span style="color:rgb(148,85,141)">self</span>.tableView.setDragDropMode(QAbstractItemView.InternalMove)<br><br>        <span style="color:rgb(148,85,141)">self</span>.setCentralWidget(<span style="color:rgb(148,85,141)">self</span>.tableView)<br><br><br>app = QApplication(sys.argv)<br>w = MainWindow()<br>w.show()<br>app.exec_()<br></pre></div><br></div>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature">È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi<br><a href="http://www.jidesk.net" target="_blank">http://www.jidesk.net</a></div>