<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">Il giorno dom 17 feb 2019 alle ore 23:19 John F Sturtz <<a href="mailto:john@sturtz.org">john@sturtz.org</a>> ha scritto:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div id="gmail-m_-6085083566341669124__MailbirdStyleContent" style="font-size:12pt;font-family:Candara;color:rgb(0,0,0)"><div>[...]<span style="font-family:Arial,Helvetica,sans-serif;font-size:small;color:rgb(34,34,34)"> </span></div></div></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div id="gmail-m_-6085083566341669124__MailbirdStyleContent" style="font-size:12pt;font-family:Candara;color:rgb(0,0,0)"><div>There are still a few things I don't fully understand.</div><div><br></div><div>I actually did experiment with reimplementing <span style="font-family:"Courier New"">closeEditor()</span> awhile back.  But I reimplemented it in the <i>delegate</i>, not in the <i>view</i>.  I thought I understood <span style="font-family:"Courier New"">closeEditor()</span> to be a member of <span style="font-family:"Courier New"">QAbstractItemDelegate</span>, not <span style="font-family:"Courier New"">QTableView</span>.  I don't quite understand how it is possible to override this method in the <span style="font-family:"Courier New"">QTableView</span> definition.  (Still, empirically, it seems to work, so I must be wrong).</div></div></blockquote><div><br></div><div>Well, <font face="monospace, monospace">closeEditor</font> is a <b>signal</b> in delegates, which is automatically connected to the <font face="monospace, monospace">closeEditor</font> <i><b>slot</b></i> of the item view.</div><div>The signal is sent anyway from the delegate, whenever the focus is actually changed by the item view (or any other widget, including the widget's window losing focus), or when a "change cell" is internally requested for the delegate by the view (via tab navigation or any way of commit/ignore is supported by it): for example, if you use a QComboBox as an editor for the itemDelegate, the closeEditor won't be sent if the item of the combo has changed, <b>but</b> will be sent if the combo is editable and you "submit" any data or ignore it.</div><div><br></div><div>As explained in the QItemDelegate details ( <a href="https://doc.qt.io/archives/qt-4.8/qitemdelegate.html#details">https://doc.qt.io/archives/qt-4.8/qitemdelegate.html#details</a> ):<br></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"><span style="color:rgb(64,66,68);font-family:"Titillium Web",Arial,Helvetica,sans-serif;line-height:20px">The<span class="gmail-Apple-converted-space"> </span></span><a href="https://doc.qt.io/archives/qt-4.8/qabstractitemdelegate.html#closeEditor" style="margin:0px;padding:0px;border:0px;vertical-align:baseline;color:rgb(23,168,26);text-decoration-line:none;font-family:"Titillium Web",Arial,Helvetica,sans-serif;line-height:20px">closeEditor</a><span style="color:rgb(64,66,68);font-family:"Titillium Web",Arial,Helvetica,sans-serif;line-height:20px">() signal indicates that the user has completed editing the data, and that the editor widget can be destroyed.</span></blockquote><div><br></div><div>The stress is on the "<i>can</i> be destroyed".</div><div>The fact that the signal is emitted does not actually mean that the editor will actually be closed: it only means that the editing is theoretically finished, and it's a way of telling the view (and the delegate) that it *should* close the editor.</div><div>In fact, you can actually disconnect the signal of the delegate from the relative slot of the view:<br></div><div><br></div><div><font face="monospace, monospace">self.myTable.itemDelegate().closeEditor.disconnect(self.myTable.closeEditor)</font><br></div><div><br></div><div>This will result in an editor kept alive even when trying to commit (or ignore) the data by using keyboard events.</div><div>In spite of this, though, the view.closeEditor() slot will obviously be called anyway from the view the first time you click on any other cell, due to the currentChanged() slot called from the mousePressEvent(); note that if you return the closeEditor() slot as above - without actually destroying the editor -, the slot won't be called another time even if you change the current index again by clicking on another item or outside the table contents (I suppose that it's because the closeEditor signal has been already fired once and the delegate is still there), unless the focus is restored to the editor again first. Also, in the same scenario, changing the focus to another widget won't result in the closeEditor() slot called, but the signal will be fired anyway.</div><div><br></div><div>As a side note, be aware that if you implement the closeEditor slot, you'll need to properly set the slot signature to correctly allow the auto connection the view creates for the delegate to work and, eventually, disconnect from it.<br></div><div>In PyQt4, using PyQt objects was fine enough:</div><div><br></div><div><font face="monospace, monospace">@QtCore.Slot(QtWidgets.QWidget, QtWidgets.QAbstractItemDelegate.EndEditHint)</font></div><div><font face="monospace, monospace">def closeEditor(self, widget, hint):</font></div><div><font face="monospace, monospace">    [...]</font></div><div><br></div><div>With PyQt5 (at least on my old version, 5.7.1) it seems that the actual C++ string signature is required as reported from the official Qt docs (not PyQt/PySide), so it will need to be like this in order to allow the original signal disconnection to work, since the connection was not "created by Python":</div><div><div><br></div><div><font face="monospace, monospace">@QtCore.Slot('QWidget*', 'QAbstractItemDelegate::EndEditHint')</font></div><div><font face="monospace, monospace">def closeEditor(self, widget, hint):<br></font></div></div><div><font face="monospace, monospace">    [...]</font></div><div><br></div><div><br></div><div>That said, since the type of control you need can be required whenever any event type (tab navigation, editor commit/ignore, mouse events, etc) and source (the delegate, the view or the application) occurs, it's better to implement the closeEditor in the view itself, which is not only the "simplest" and most logic solution, but also the way Qt actually deals with item editing.<br></div><div><br></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 id="gmail-m_-6085083566341669124__MailbirdStyleContent" style="font-size:12pt;font-family:Candara;color:rgb(0,0,0)"><div><span style="font-size:12pt">One slight problem with your code as written:  I found that if a delegate editor is active and I mouse click on a different cell, the </span><span style="font-size:12pt;font-family:"Courier New"">mousePressEvent</span><span style="font-size:12pt"> actually registers before the </span><span style="font-size:12pt;font-family:"Courier New"">closeEditor()</span><span style="font-size:12pt"> method is called.  You assign </span><span style="font-size:12pt;font-family:"Courier New"">self.currentEditor</span><span style="font-size:12pt"> inside </span><span style="font-size:12pt;font-family:"Courier New"">closeEditor()</span><span style="font-size:12pt">, so on the first mouse click it isn't set yet.  However, if I assign </span><span style="font-size:12pt;font-family:"Courier New"">currentEditor</span><span style="font-size:12pt"> when the editor is </span><i style="font-size:12pt">created</i><span style="font-size:12pt"> (i.e., in the delegate's </span><span style="font-size:12pt;font-family:"Courier New"">createEditor()</span><span style="font-size:12pt"> method), then it seems to work.</span><br></div><div><span style="font-size:12pt;line-height:1.5"><br></span></div><div><span style="font-size:12pt;line-height:1.5">And with that, I find I don't seem to need to reimplement <span style="font-family:"Courier New"">closeEditor()</span> at all.  Just catching a <span style="font-family:"Courier New"">mousePressEvent</span>, checking that <span style="font-family:"Courier New"">self.state()</span> is <span style="font-family:"Courier New"">EditState</span> and calling <span style="font-family:"Courier New"">self.currentEditor.setFocus()</span> to set the focus back to the editor widget seems to be enough.</span></div></div></blockquote><div><br></div><div>It works because you've set the currentEditor in the createEditor() slot.<br>I didn't know you were already using a delegate, so your solution can work too.</div><div><br></div><div>Just a small "conceptual" note about this. Remember that, even if your code works fine and makes perfect sense, from the modularity point of view, that's not "theoretically" good, as item delegates can be used interactively and more than once. A much more "elegant" way to do so would be to request the actual view from the parent argument of createEditor(); keep in mind, though, that the parent of the editor is the viewport of the view (since an item view is a QAbstractScrollArea and its contents are in an embedded "scrollable" QWidget), so the reference to the view is the parent() of the parent argument of createEditor() method, actually.<br>Anyway, as said, that's not an actual "error", and in most cases your implementation is fine enough (I've done something similar lots of times myself).</div><div><br></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 id="gmail-m_-6085083566341669124__MailbirdStyleContent" style="font-size:12pt;font-family:Candara;color:rgb(0,0,0)"><div><span style="font-size:12pt">I will also need to similarly catch keystrokes that try to change to focus to a different cell.  But that is easier, because those events go to the delegate, so can be handled within the delegate's </span><span style="font-size:12pt;font-family:"Courier New"">eventFilter()</span><span style="font-size:12pt"> method.</span></div></div></blockquote><div><br></div><div>Luckily enough, Qt's implementation correctly handles QKeyEvents of delegates and views in the right "levels". Tab navigation and return/escape keys are catched by the view, so you can take care of everything in the closeEditor() slot.</div><div><br></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 id="gmail-m_-6085083566341669124__MailbirdStyleContent" style="font-size:12pt;font-family:Candara;color:rgb(0,0,0)"><div><span style="font-size:12pt">I </span><i style="font-size:12pt">still</i><span style="font-size:12pt"> don't really understand why it didn't work when I tried catching and suppressing the </span><span style="font-size:12pt;font-family:"Courier New"">FocusOut</span><span style="font-size:12pt"> event.  I feel like I followed the documentation properly.  But I guess it's somehow related to the fact that there are multiple levels of widgets that the events are passing through.</span></div></div></blockquote><div><br></div><div>As I mentioned, working with focus events in widgets as complex as views can be very hard to deal with.</div><div>You have an inherited QAbstractItemView which is inherited from QAbstractScrollArea, which might even have one or two QHeaderViews and QScrollBars (and other scrollbar additional widgets, optionally), an optional cornerWidget, and a QWidget that works as a viewport, containing the actual view contents; *then* you can have an editor, which can also be a complex widget like a combobox (with its internal widgets handling focus on their own, including the popup view). Moreover, a QTableView also has a cornerButton too.<br>So, you'll have to exactly understand the hierarchy of all those widget levels, remembering that any widget reacts differently according to it's level in the tree hierarchy and the event type, by accepting or ignoring the event (which doesn't mean that they won't deal with it) and eventually sending it "back" to the whole parent widget tree if required.</div><div>Finally, whenever an editor is active, it has a focusProxy set to the view's focusProxy, which means that that widget handles focus events of another one, thus reacting to those events in its place first.</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div id="gmail-m_-6085083566341669124__MailbirdStyleContent" style="font-size:12pt;font-family:Candara;color:rgb(0,0,0)"><div><span style="font-size:12pt">In any event, it looks like I have a workaround.  Thank you!</span></div></div></blockquote><div></div></div><div><br></div><div>Glad to be of any help! :-)<br><br>Cheers,</div><div>Maurizio<br><br>PS: in your code, you've been using model.setData() inside the setEditorData() method of the itemDelegate, which not only doesn't makes not much sense, but might also result in an infinite recursion.</div><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></div></div></div></div></div></div></div></div></div></div></div>