<div dir="ltr"><br><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_extra"><br><div class="gmail_quote">On 3 May 2018 at 14:54, J Barchan <span dir="ltr"><<a href="mailto:jnbarchan@gmail.com" target="_blank">jnbarchan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div style="font-family:tahoma,sans-serif">Ah ha! I read in <a href="https://www.riverbankcomputing.com/pipermail/pyqt/2016-April/037326.html" target="_blank">https://www.<wbr>riverbankcomputing.com/<wbr>pipermail/pyqt/2016-April/<wbr>037326.html</a>:<br></div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif"><pre>>>>><i> I am currently updating QGIS to PyQt5 (and Qt5 and Python3)<br>>>>> Since this update, NULL QVariant strings are converted to empty strings '' and all numbers to 0 when converted from C++ to python objects.</i></pre>And that is exactly what I am experiencing: the string-NULL is giving me empty string and the int-NULL is giving me 0. So isn't this a PyQt issue I am seeing after all??<br></div></div><div class="gmail_extra"><div><div class="h5"><br><div class="gmail_quote">On 3 May 2018 at 14:50, J Barchan <span dir="ltr"><<a href="mailto:jnbarchan@gmail.com" target="_blank">jnbarchan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div style="font-family:tahoma,sans-serif">Hang on. This is an area I do not understand, doubtless you do.</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">The C++ <span style="font-family:monospace,monospace">QSqlQueryModel::data() </span>method is supposed to return a <span style="font-family:monospace,monospace">QVariant</span>. Am I maybe losing the <span style="font-family:monospace,monospace">QVariant</span>-ness when I write my own PyQt overload which returns the base method's result, because Python-esque conversion is going on?</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">I'm trying to understand <a href="http://pyqt.sourceforge.net/Docs/PyQt5/pyqt_qvariant.html" target="_blank">http://pyqt.sourceforge.net/Do<wbr>cs/PyQt5/pyqt_qvariant.html</a>, but I don't really. I do note:</div><div style="font-family:tahoma,sans-serif">> There is no obvious way to represent a null
<a class="m_3896359766521038193m_-3831465382182197263gmail-reference m_3896359766521038193m_-3831465382182197263external" href="http://pyqt.sourceforge.net/Docs/PyQt5/api/QtCore/qvariant.html#PyQt5-QtCore-QVariant" target="_blank"><code class="m_3896359766521038193m_-3831465382182197263gmail-xref m_3896359766521038193m_-3831465382182197263gmail-sip m_3896359766521038193m_-3831465382182197263gmail-sip-class m_3896359766521038193m_-3831465382182197263gmail-docutils m_3896359766521038193m_-3831465382182197263gmail-literal m_3896359766521038193m_-3831465382182197263gmail-notranslate"><span class="m_3896359766521038193m_-3831465382182197263gmail-pre">QVariant</span></code></a> as a standard Python object.<br></div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">Is there a connection between this and the fact that it goes wrong when the value it should be returning is the <span style="font-family:monospace,monospace">NULL</span> returned from the SQL query?<br></div></div><div class="gmail_extra"><div><div class="m_3896359766521038193h5"><br><div class="gmail_quote">On 3 May 2018 at 13:55, J Barchan <span dir="ltr"><<a href="mailto:jnbarchan@gmail.com" target="_blank">jnbarchan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div class="m_3896359766521038193m_-3831465382182197263h5"><div style="font-family:tahoma,sans-serif"><br></div><div class="gmail_extra"><br><div class="gmail_quote">On 3 May 2018 at 13:05, Phil Thompson <span dir="ltr"><<a href="mailto:phil@riverbankcomputing.com" target="_blank">phil@riverbankcomputing.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="m_3896359766521038193m_-3831465382182197263m_-4013603089682017903gmail-">On 3 May 2018, at 12:25 pm, J Barchan <<a href="mailto:jnbarchan@gmail.com" target="_blank">jnbarchan@gmail.com</a>> wrote:<br>
> <br>
> <br>
> I am finding (in PyQt 5.7 at least) that sub-classing QSqlQueryModel and overriding its data() method produces an incorrect result when the value retrieved from a MySQL database is NULL.<br>
> <br>
> Full details are in <a href="https://forum.qt.io/topic/90363/inexplicable-qsqlquerymodel-handling-of-null-value" rel="noreferrer" target="_blank">https://forum.qt.io/topic/9036<wbr>3/inexplicable-qsqlquerymodel-<wbr>handling-of-null-value</a>, and particularly post # <a href="https://forum.qt.io/topic/90363/inexplicable-qsqlquerymodel-handling-of-null-value" rel="noreferrer" target="_blank">https://forum.qt.io/topic/9036<wbr>3/inexplicable-qsqlquerymodel-<wbr>handling-of-null-value</a>. Nobody has tried it in C++ for me to date to verify, but I'm suspecting this might be a PyQt bug?<br>
> <br>
> Briefly:<br>
> My SELECT query returns a column which is NULLable, and has NULL as its value. Where I expect "blank" as the end value, I actually get, for example, 0 if the column type is int or '' if the type is string, etc.<br>
> <br>
> This is when I sub-class QSqlQueryModel. If all I have is:<br>
> class DBQueryModel(QtSql.QSqlQueryMo<wbr>del):<br>
> def __init__(self, parent=None):<br>
> super().__init__(parent)<br>
> I get the "NULL"/"blank". However, as soon as I add just:<br>
> def data(self, index: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole) -> typing.Any:<br>
> return super().data(index, role)<br>
> I get those values instead of NULL.<br>
> <br>
> Note that my override is based on the Qt definition of the method at <a href="http://doc.qt.io/qt-5/qsqlquerymodel.html#data" rel="noreferrer" target="_blank">http://doc.qt.io/qt-5/qsqlquer<wbr>ymodel.html#data</a>:<br>
> QVariant QSqlQueryModel::data(const QModelIndex &item, int role = Qt::DisplayRole) const<br>
> <br>
> Note that the default for role is Qt::DisplayRole. However, in QtSql.py I see:<br>
> def data(self, QModelIndex, role=None): # real signature unknown; restored from __doc__<br>
> """ data(self, QModelIndex, role: int = Qt.DisplayRole) -> Any """<br>
> pass<br>
> You will notice that the comment shows the default should be Qt.DisplayRole, but the declaration defaults it to None instead.<br>
<br>
</span>What is QtSql.py?<br>
<br>
If you want to know the signature of a method pss it to help().<br>
<span class="m_3896359766521038193m_-3831465382182197263m_-4013603089682017903gmail-"><br>
> I don't know enough to be sure, but would that be the underlying cause of the unexpected behaviour?<br>
> <br>
> FWIW, I have tried making my override be:<br>
> def data(self, index: QtCore.QModelIndex, role=None)<br>
> instead, but same bad behaviour.<br>
> <br>
> 1. Is this indeed a bug in PyQt, and the cause of my issue?<br>
<br>
</span>No and no.<br>
<div class="m_3896359766521038193m_-3831465382182197263m_-4013603089682017903gmail-HOEnZb"><div class="m_3896359766521038193m_-3831465382182197263m_-4013603089682017903gmail-h5"><br>
> 2. If so, I presume you (Phil!) will be kind enough to fix. However, for my part I am stuck with PyQt 5.7 for the foreseeable future. If the fix is indeed to change code in the latest/next release, is there anything I can do in existing code (my override) to make it work in 5.7, as a workaround? (in real code I need the override, as I do other processing)<br>
> <br>
> My coding has come to halt as I cannot proceed without a fix. So I should be obliged for any early response as to whether this is the cause of my woes. I do realise PyQt support/fixes are quite voluntary, and so thank whoever in advance!<br>
<br>
</div></div><span class="m_3896359766521038193m_-3831465382182197263m_-4013603089682017903gmail-HOEnZb"><font color="#888888">Phil</font></span></blockquote></div></div><div class="gmail_extra"><br></div></div></div><div class="gmail_extra"><div>For <span style="font-family:monospace,monospace">QtSql.py</span>:</div></div><div class="gmail_extra"><div><br></div><div>Hmm, I had not realised. I use PyCharm as my IDE. From there, while I am coding, I can click on anything PyQt and ask for "Go to definition/declaration". The editor then 9in this case) opens me up into a file named <span style="font-family:monospace,monospace">QtSql.py</span>, showing me in this case [extract]:</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><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(0,0,128);font-weight:bold">class </span>QSqlQueryModel(__PyQt5_QtCore.<wbr>QAbstractTableModel):</pre>...</div><div><br></div><div><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(0,0,128);font-weight:bold">def </span>columnCount(<span style="color:rgb(148,85,141)">self</span>, parent=<span style="color:rgb(0,0,128);font-weight:bold">None</span>, *args, **kwargs): <span style="color:rgb(128,128,128);font-style:italic"># real signature unknown; NOTE: unreliably restored from __doc__ <br></span><span style="color:rgb(128,128,128);font-style:italic"> """ columnCount(self, parent: QModelIndex = QModelIndex()) -> int """<br></span><span style="color:rgb(128,128,128);font-style:italic"> </span><span style="color:rgb(0,0,128);font-weight:bold">pass<br></span><span><span style="color:rgb(0,0,128);font-weight:bold"><br></span><span style="color:rgb(0,0,128);font-weight:bold">def </span>data(<span style="color:rgb(148,85,141)">self</span>, QModelIndex, role=<span style="color:rgb(0,0,128);font-weight:bold">None</span>): <span style="color:rgb(128,128,128);font-style:italic"># real signature unknown; restored from __doc__<br></span><span style="color:rgb(128,128,128);font-style:italic"> """ data(self, QModelIndex, role: int = Qt.DisplayRole) -> Any """<br></span><span style="color:rgb(128,128,128);font-style:italic"> </span><span style="color:rgb(0,0,128);font-weight:bold">pass<br></span><span style="color:rgb(0,0,128);font-weight:bold"><br></span></span><span style="color:rgb(0,0,128);font-weight:bold">def </span>endInsertColumns(<span style="color:rgb(148,85,141)">self</span>): <span style="color:rgb(128,128,128);font-style:italic"># real signature unknown; restored from __doc__<br></span><span style="color:rgb(128,128,128);font-style:italic"> """ endInsertColumns(self) """<br></span><span style="color:rgb(128,128,128);font-style:italic"> </span><span style="color:rgb(0,0,128);font-weight:bold">pass<br></span></pre></div></blockquote><div><br></div><div>etc. I had <i>assumed</i> this was a file supplied with PyQt. I guess now it's "generated on the fly" by PyCharm (I see its path is in a PyCharm temporary directory). At other times, it might open, say, <span style="font-family:monospace,monospace">/usr/lib/python3/dist-packages<wbr>/PyQt5/QtCore.pyi</span>, which I think is a file you supply. Oh, at the head of this <span style="font-family:monospace,monospace">QtSql.py</span> I see:</div><div><br></div><div><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(128,128,128);font-style:italic"># encoding: utf-8<br></span><span style="color:rgb(128,128,128);font-style:italic"># module PyQt5.QtSql<br></span><span style="color:rgb(128,128,128);font-style:italic"># from /usr/lib/python3/dist-packages<wbr>/PyQt5/<a href="http://QtSql.cpython-35m-x86_64-linux-gnu.so" target="_blank">QtSql.cpython-35m-x86_6<wbr>4-linux-gnu.so</a><br></span><span style="color:rgb(128,128,128);font-style:italic"># by generator 1.145<br></span><span style="color:rgb(128,128,128);font-style:italic"># no doc</span></pre><br></div><div>You'll probably understand all this better than I!</div><br></div><div class="gmail_extra"><div><br></div><div><br></div><div>For my problem:</div><div><br></div><div>I think I now understand better why it's not a PyQt method definition issue.</div><div><br></div><div>However, from the linked Qt forum discussion, I'm stuck between a rock & a hard place, because the only help I'm getting is that it might be a PyQt issue. I do not have C++ to try that out. So, I wonder if I might ask you if you can make any suggestion as to the cause, even if it is not a PyQt issue, given that you are familiar with Qt at least?</div><div><br></div><div>To summarise my problem as briefly as possible:</div><div><br></div><div>1.<br></div><div>I start with:</div><div><br></div><div><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt">model = QtSql.QSqlQueryModel(<span style="color:rgb(148,85,141)">self</span>)<br>model.setQuery(<span style="color:rgb(0,128,128);font-weight:bold">"SELECT LandlordNo, SMTPAccountId </span><span style="color:rgb(0,128,128);font-weight:bold">FROM landlords WHERE SMTPAccountId IS NULL")</span><br></pre><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"># or plain "SELECT NULL AS SMTPAccountId", to eliminate anything about the column definition being an issue<br></pre><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt">rowCount = model.rowCount()<br><span style="color:rgb(0,0,128);font-weight:bold">if </span>rowCount > <span style="color:rgb(0,0,255)">0</span>:<br> rec = model.record(<span style="color:rgb(0,0,255)">0</span>)<br> field = rec.field(<span style="color:rgb(0,128,128);font-weight:bold">"SMTPAccountId"</span>)<br> <span style="color:rgb(128,128,128)">isn </span>= field.isNull()<br> </pre><span style="font-family:monospace,monospace">SMTPAccointId</span> returns <span style="font-family:monospace,monospace">NULL</span> from MySQL. <i>At this point <span style="font-family:monospace,monospace">field.isNull()</span> correctly returns <span style="font-family:monospace,monospace">True</span>.</i><br></div><div><br></div><div>2.</div><div>I sub-class QSqlQueryModel, and use that, with quite simply, exactly:</div><div><br></div><div><span><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(0,0,128);font-weight:bold">class </span>DBQueryModel(QtSql.QSqlQueryMo<wbr>del):<br> <span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__init__</span>(<span style="color:rgb(148,85,141)">self</span>, parent=<span style="color:rgb(0,0,128);font-weight:bold">None</span>):<br> <span style="color:rgb(0,0,128)">super</span>().<span style="color:rgb(178,0,178)">__init__</span>(parent)</pre></span>and use that sub-class in place of <span style="font-family:monospace,monospace">QSqlQueryModel</span>: <span style="font-family:monospace,monospace">model = DBQueryModel(<span style="color:rgb(148,85,141)">self</span>)</span><br></div><div><i>And it this point point <span style="font-family:monospace,monospace">field.isNull()</span> </i>still<i> correctly returns <span style="font-family:monospace,monospace">True</span>.</i></div><div><br></div><div>3.</div><div>Then I add <i>just exactly this</i> to my sub-class:</div><div><br></div><div><span><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(0,0,128);font-weight:bold">def </span>data(<span style="color:rgb(148,85,141)">self</span>, index: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole) -> typing.Any:<br> <span style="color:rgb(0,0,128);font-weight:bold">return </span><span style="color:rgb(0,0,128)">super</span>().data(index, role)</pre></span>(I've also tried <span style="font-family:monospace,monospace">role=None</span>) You can see that simply calls the base class method. <i>But now <span style="font-family:monospace,monospace">field.isNull()</span> returns <span style="font-family:monospace,monospace">False</span>!!</i> The application sees <span style="font-family:monospace,monospace">0</span> instead of NULL for the value (<span style="font-family:monospace,monospace">SMTPAccountId</span> is declared <span style="font-family:monospace,monospace">INT NULL</span>), or <span style="font-family:monospace,monospace">''</span> if I use <span style="font-family:monospace,monospace">SELECT NULL AS SMTPAccountId</span> so it counts as string.<br></div><div><br></div><div><br></div><div>This leaves me completely stumped. I have no idea where the problem is (there shouldn't be a problem!). I have to sub-class the <span style="font-family:monospace,monospace">data()</span> method for other purposes, but then it handles NULL (only) incorrectly.</div><div><br></div><div>Would you have any idea what is going on here? My thanks in advance.<span class="m_3896359766521038193m_-3831465382182197263HOEnZb"><font color="#888888"><br></font></span></div><span class="m_3896359766521038193m_-3831465382182197263HOEnZb"><font color="#888888"><br clear="all"><br>-- <br><div class="m_3896359766521038193m_-3831465382182197263m_-4013603089682017903gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><span style="font-family:tahoma,sans-serif">Kindest,</span></div><div><span style="font-family:tahoma,sans-serif">Jonathan</span></div></div></div></div></div>
</font></span></div></div>
</blockquote></div><br><br clear="all"><br></div></div><span class="m_3896359766521038193HOEnZb"><font color="#888888">-- <br><div class="m_3896359766521038193m_-3831465382182197263gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><span style="font-family:tahoma,sans-serif">Kindest,</span></div><div><span style="font-family:tahoma,sans-serif">Jonathan</span></div></div></div></div></div>
</font></span></div>
</blockquote></div><br><br clear="all"><br></div></div><span class="HOEnZb"><font color="#888888">-- <br><div class="m_3896359766521038193gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><span style="font-family:tahoma,sans-serif">Kindest,</span></div><div><span style="font-family:tahoma,sans-serif">Jonathan</span></div></div></div></div></div>
</font></span></div>
</blockquote></div><br><br clear="all"><br><div style="font-family:tahoma,sans-serif;display:inline" class="gmail_default"></div><div class="gmail_default" style="font-family:tahoma,sans-serif">Hey Phil,</div><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_default" style="font-family:tahoma,sans-serif">Assuming
I am not barking up the wrong tree, I see you yourself were discussing
this issue in
<a href="http://python.6.x6.nabble.com/PyQt5-NULL-QVariant-tp5188782p5190811.html">http://python.6.x6.nabble.com/PyQt5-NULL-QVariant-tp5188782p5190811.html</a>.
It includes:</div><div class="gmail_default" style="font-family:tahoma,sans-serif"><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 class="gmail_default" style="font-family:tahoma,sans-serif">> Can you confirm that returning empty string and 0 for NULL is expected
<br>> behavior and not seen as a bug?
<br><br>Yes. If you want to distinguish null <span style="font-family:monospace,monospace">QVariant</span>s then use
<span style="font-family:monospace,monospace">sip.enableautoconversion()</span>. The problem is that you are not willing to
do that. </div></blockquote><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_default" style="font-family:tahoma,sans-serif">So I'm bumbling around trying to put a <span style="font-family:monospace,monospace">sip.enableautoconversion()</span>
in! Trouble is, I don't understand the syntax, and I don't know where
to put it (I'm not finding any examples, and you two obviously knew more
than me about it :) )</div><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_default" style="font-family:tahoma,sans-serif">1. I'm <i>assuming</i> I want to suppress converting <span style="font-family:monospace,monospace">QVariant</span>s. I'm trying <br><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt">sip.enableautoconversion(QVariant, <span style="color:rgb(0,0,128);font-weight:bold">False</span>)</pre>but it doesn't know what <span style="font-family:monospace,monospace">QVariant</span> is, and I don't know if it's supposed to or my syntax or imports or whatever?</div><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_default" style="font-family:tahoma,sans-serif">2. I don't know <i>where</i> I'm supposed to disable & re-enable the autoconversion? My method is an override called implicitly by Qt.</div><div class="gmail_default" style="font-family:tahoma,sans-serif">* Is this a "directive" that's supposed to be used e.g. as a Python method is read in, and so belongs around the whole function?</div><div class="gmail_default" style="font-family:tahoma,sans-serif">*
Is this a "run-time* that I just need to put inside my method overload
around where it calls the base class method? Or by the time it has hit
my overload is it too late because some conversion has already happened?</div><div class="gmail_default" style="font-family:tahoma,sans-serif">* Do I actually need to put it around the whole of my top-level call to, say, <span style="font-family:monospace,monospace">QSqlQueryModel.rowCount()</span>, which is what (I understand to be) invoking calls to the overridden <span style="font-family:monospace,monospace">QSqlQueryModel.data()</span> method?</div><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_default" style="font-family:tahoma,sans-serif">Many, many thanks for your time & patience.<br></div>
</div></div>