<div dir="ltr"><div dir="ltr">
<div>Wow, Maurizio, thank you very much, what an awesome and informative answer!</div><div><br></div><div>The example worked, but if you don't mind, I have some questions. Please see them inline.<br></div><div></div>
</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Apr 18, 2021 at 8:35 PM Maurizio Berti <<a href="mailto:maurizio.berti@gmail.com">maurizio.berti@gmail.com</a>> wrote:<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 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></div></div></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>This was great to learn! PyCharm indeed does not show the errors and I had been "flying blind" this whole time...</div><div> <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 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>Then, the problem is that you didn't implement the most important function for drop events in a model: dropMimeData().</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>Ah, I thought that was required for external drops only.</div><div> <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 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>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" target="_blank">https://doc.qt.io/qt-5/qabstractitemmodel.html#mimeData</a>), and the structure is the following:</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>Now, where did you learn about this format specification? I did not see it anywhere in the Qt documentation.<br></div><br><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 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>Here is a possible implementation:</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></blockquote><div><br></div><div>Thanks a lot for writing this! It's very useful. I had to make a couple of minor fixes, so I am posting the whole thing again in case someone will reuse it at some point.</div><div><br></div><div>I do have a question, though. Why do we need the condition
itemRow < <span style="color:rgb(148,85,141)">self</span>.rowCount(QModelIndex()) <span style="color:rgb(0,51,179)">and </span>itemCol < <span style="color:rgb(148,85,141)">self</span>.columnCount(QModelIndex())
? For an internal drop, itemRow and itemCol should always satisfy it. And for external drops, they could be larger than rowCount and colCount, but that should not matter as long as their different from startRow and startCol are small enough: (itemRow - startRow) < self.rowCount(QModelIndex()) and for itemCol analogously.<br></div><div><br></div><div>Going a bit beyond this particular example, what happens if the method mimeData has a list with multiple types? Method mimeData is supposed to return a single QMimeData, so there will be a single encoding of the data, and if so, for which of the multiple mime types?</div><div><br></div><div>Also, in case of multiple supported mime types, how can dropMimeData know the format in which 'data' is encoded? It does not seem QMimeData provides a method indicating its format.<br></div><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)">def </span><span style="color:rgb(0,0,0)">dropMimeData</span>(<span style="color:rgb(148,85,141)">self</span>, data, action, row, column, parent):<br> decoded = data.data(<span style="color:rgb(0,128,128);font-weight:bold">'application/x-qabstractitemmodeldatalist'</span>)<span style="color:rgb(140,140,140);font-style:italic"><br></span><span style="color:rgb(140,140,140);font-style:italic"> </span>stream = QDataStream(decoded, QIODevice.ReadOnly)<br> items = []<br> startRow = startCol = <span style="color:rgb(23,80,235)">65536<br></span><span style="color:rgb(23,80,235)"> </span><span style="color:rgb(0,51,179)">if </span>parent.isValid():<span style="color:rgb(140,140,140);font-style:italic"><br></span><span style="color:rgb(140,140,140);font-style:italic"> </span>row = parent.row()<br> column = parent.column()<br> <span style="color:rgb(0,51,179)">while not </span>stream.atEnd():<br> itemRow = stream.readInt()<br> itemCol = stream.readInt()<br> fieldCount = stream.readInt()<br> display = <span style="color:rgb(0,51,179)">None<br></span><span style="color:rgb(0,51,179)"> </span><span style="color:rgb(140,140,140);font-style:italic"># displayValid = True<br></span><span style="color:rgb(140,140,140);font-style:italic"> </span>displayValid = <span style="color:rgb(0,51,179)">False </span><span style="color:rgb(140,140,140);font-style:italic"># seems like you meant False here<br></span><span style="color:rgb(140,140,140);font-style:italic"> </span><span style="color:rgb(0,51,179)">while </span>fieldCount:<br> role = stream.readInt()<br> value = stream.readQVariant()<br> <span style="color:rgb(0,51,179)">if </span>role == Qt.DisplayRole:<br> displayValid = <span style="color:rgb(0,51,179)">True<br></span><span style="color:rgb(0,51,179)"> </span>display = value<br> fieldCount -= <span style="color:rgb(23,80,235)">1<br></span><span style="color:rgb(23,80,235)"> </span><span style="color:rgb(0,51,179)">if </span>displayValid <span style="color:rgb(0,51,179)">and </span>itemRow < <span style="color:rgb(148,85,141)">self</span>.rowCount(QModelIndex()) <span style="color:rgb(0,51,179)">and </span>itemCol < <span style="color:rgb(148,85,141)">self</span>.columnCount(QModelIndex()):<br> <span style="color:rgb(140,140,140);font-style:italic"># I had to add QModelIndex. It seems that is required in PyQt 5.9.2<br></span><span style="color:rgb(140,140,140);font-style:italic"> </span>startRow = <span style="color:rgb(0,0,128)">min</span>(startRow, itemRow)<br> startCol = <span style="color:rgb(0,0,128)">min</span>(startCol, itemCol)<br> items.append((itemRow, itemCol, display))<br> <span style="color:rgb(0,51,179)">if not </span>items:<br> <span style="color:rgb(0,51,179)">return False<br></span><span style="color:rgb(0,51,179)"><br></span> minRow = minCol = <span style="color:rgb(23,80,235)">65536<br></span><span style="color:rgb(23,80,235)"> </span>maxRow = maxCol = <span style="color:rgb(23,80,235)">0<br></span><span style="color:rgb(23,80,235)"> </span><span style="color:rgb(0,51,179)">for </span>itemRow, itemCol, value <span style="color:rgb(0,51,179)">in </span>items:<br> targetRow = row + (itemRow - startRow)<span style="color:rgb(140,140,140);font-style:italic"><br></span><span style="color:rgb(140,140,140);font-style:italic"> </span>targetCol = column + (itemCol - startCol)<span style="color:rgb(140,140,140);font-style:italic"><br></span><span style="color:rgb(140,140,140);font-style:italic"> </span>minRow = <span style="color:rgb(0,0,128)">min</span>(targetRow, minRow)<br> maxRow = <span style="color:rgb(0,0,128)">max</span>(targetRow, maxRow)<br> minCol = <span style="color:rgb(0,0,128)">min</span>(targetCol, minCol)<br> maxCol = <span style="color:rgb(0,0,128)">max</span>(targetCol, maxCol)<br><span style="color:rgb(0,51,179)"> if </span>targetRow < <span style="color:rgb(0,0,128)">len</span>(<span style="color:rgb(148,85,141)">self</span>.dataList) <span style="color:rgb(0,51,179)">and </span>targetCol < <span style="color:rgb(0,0,128)">len</span>(<span style="color:rgb(148,85,141)">self</span>.dataList[targetRow]): <span style="color:rgb(140,140,140);font-style:italic"> # minor fix<br></span><span style="color:rgb(140,140,140);font-style:italic"> </span> <span style="color:rgb(148,85,141)"> self</span>.dataList[targetRow][targetCol] = value<br> <span style="color:rgb(148,85,141)">self</span>.dataChanged.emit(<br> <span style="color:rgb(148,85,141)">self</span>.index(minRow, minCol),<br> <span style="color:rgb(148,85,141)">self</span>.index(maxRow, maxCol),<br> )<br> <span style="color:rgb(0,51,179)">return True<br></span></pre>Thank you!<br><br>Rodrigo<br></div></div>