[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pykde
Subject:    Re: [PyQt] Positioning autocompletion text after QLineEdit
From:       Maurizio Berti <maurizio.berti () gmail ! com>
Date:       2019-03-10 3:58:36
Message-ID: CAPn+-XRr2m-qG1eFXB7=UUTu_DhnWayii69qqXeJ-f_qau_gPA () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


Il giorno sab 9 mar 2019 alle ore 23:22 John F Sturtz <john@sturtz.org> ha
scritto:

> (*maurizio.berti@gmail.com <maurizio.berti@gmail.com>*, if you're reading
> this, you'll probably recognize it.  I finally decided to implement this
> using separate widgets, QLineEdit for the user input and QLabel for the
> auto-completion, instead of using a QTextEdit for all of it per your
> suggestion.  I realize there is a QCompleter class that can be used with
> a QLineEdit, but I'm intent on implementing it myself.)
> 

Hello again John :-)
I'm not a big fan of "reinventing the wheel", as I tried to do that a lot
of times, and those "lot of times" I realized that once you've deeper
knowledge and experience with a framework (in this case, Qt) using its
tools is usually better.
But. Sometimes having "control" over things *is* better, for various
reasons. In this particular scenario I may agree with you. I don't like the
QCompleter implementation a lot: even if it suits most user cases, its
customization is problematic, expecially for special cases where there is
the need for better UX response (like in your case, which involves some
things QLineEdit doesn't provide, as inline formatting).
Implementing the QTextEdit wouldn't be that much easier indeed, as it would
require *good* QSyntaxHighlighter programming and debugging, and probably
the same amount of coding (considering the effort of time and "mental
energy") you'll put in for font metrics issues and possible
character/cursor positioning.


The issue is that the QLabel doesn't position itself properly at the end of
> what the user has typed in.  I am using
> fontMetrics().horizontalAdvance(text) on the text the user has typed in,
> to determine where to position the Qlabel.  According to the
> documentation, that should be "the distance appropriate for drawing a
> subsequent character after *text*."  But it comes out too close to the
> preceding text.
> 

Unfortunately I'm still on Qt 5.7 (my main project is a bit critical,
upgrading PyQt5 now would demand a lot of dependency issues that would put
it at risk, as it's PyQt4 based), this means that I horizontalAdvance()
isn't available for me as it was introduced in Qt 5.11.
Nonetheless, by using the "simpler" QFontMetrics.width() I got almost the
same result, even if not probably "pixel-perfect" (it could depend on the
font rendering hints used for the current font we're using, some updates in
the rendering engine, png compression, etc).

That said, the issue here is that you didn't take into account the margins
applied to the QLineEdit contents, which requires some QStyle work.
The first thing to look for is the subElementRect of
QStyle.SE_LineEditContents, which returns the rectangle inside of which the
text rendering happens: while QLineEdit does not inherit from QFrame as
QLabels do, it still uses the QStyleOptionFrame (as it actually is some
sort of "frame", with its borders and margins).
After that, QLineEdit adds its own margin to the text, which seems to be
hardcoded and set to 2 (according to
https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qlineedit_p.cpp.html#QLineEditPrivate::horizontalMargin
 ).
Finally, while in most uses this could be enough, remember that you're
using two very different widgets that apply some margins to their contents
whenever they're drawn. Luckily, the QLabel doesn't seem to apply further
margins using default QStyle(s) on Linux, but it might be better to check
for the QLabel's option too. This may also be necessary in some cases where
the QLabel vertical positioning is different from QLineEdit, again
depending on the current QStyle (I think it might the case of the default
styles used on Windows and MacOS).

I've just added some lines to your code, and it seems to be all right also
while zooming in (even with QFontMetrics.width() instead of
horizontalAdvance):

        # Position display widget
        option = QtWidgets.QStyleOptionFrame()
        self.initStyleOption(option)
        rect =
self.style().subElementRect(QtWidgets.QStyle.SE_LineEditContents, option,
self)
        # Add the left position of the contents and the hardcoded
horizontalMargin of QLineEdit
        w.move(self.fontMetrics().width(user) + rect.left() + 2, 0)

As a side note, remember to be careful about variable and property naming:
you used self.style for the stylesheet, but style() is an important
property of QWidgets: I had to change it to have a direct reference to it,
otherwise I'd have to use super/QtWidgets.QLabel.style(self).


Cheers,
Maurizio

-- 
È difficile avere una convinzione precisa quando si parla delle ragioni del
cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net


[Attachment #5 (text/html)]

<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div \
dir="ltr">Il giorno sab 9 mar 2019 alle ore 23:22 John F Sturtz &lt;<a \
href="mailto:john@sturtz.org">john@sturtz.org</a>&gt; 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_-4362708302900851793__MailbirdStyleContent" \
style="font-size:12pt;font-family:Candara;color:rgb(0,0,0)"><div>(<i><a \
href="mailto:maurizio.berti@gmail.com" \
target="_blank">maurizio.berti@gmail.com</a></i>, if you&#39;re reading this, \
you&#39;ll probably recognize it.   I finally decided to implement this using \
separate widgets, <span style="font-family:&quot;Courier New&quot;">QLineEdit</span> \
for the user input and <span style="font-family:&quot;Courier \
New&quot;">QLabel</span> for the auto-completion, instead of using a <span \
style="font-family:&quot;Courier New&quot;">QTextEdit</span>  for all of it per your \
suggestion.   I realize there is a <span style="font-family:&quot;Courier \
New&quot;">QCompleter</span> class that can be used with a <span \
style="font-family:&quot;Courier New&quot;">QLineEdit</span>, but I&#39;m intent on \
implementing it myself.)</div></div></blockquote><div><br></div><div>Hello again John \
:-)<br>I&#39;m not a big fan of &quot;reinventing the wheel&quot;, as I tried to do \
that a lot of times, and those &quot;lot of times&quot; I realized that once \
you&#39;ve deeper knowledge and experience with a framework (in this case, Qt) using \
its tools is usually better.</div><div>But. Sometimes having &quot;control&quot; over \
things *is* better, for various reasons. In this particular scenario I may agree with \
you. I don&#39;t like the QCompleter implementation a lot: even if it suits most user \
cases, its customization is problematic, expecially for special cases where there is \
the need for better UX response (like in your case, which involves some things \
QLineEdit doesn&#39;t provide, as inline formatting).</div><div>Implementing the \
QTextEdit wouldn&#39;t be that much easier indeed, as it would require *good* \
QSyntaxHighlighter programming and debugging, and probably the same amount of coding \
(considering the effort of time and &quot;mental energy&quot;) you&#39;ll put in for \
font metrics issues and possible character/cursor \
positioning.</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_-4362708302900851793__MailbirdStyleContent" \
style="font-size:12pt;font-family:Candara;color:rgb(0,0,0)"><div><span \
style="font-size:12pt">The issue is that the </span><span \
style="font-size:12pt;font-family:&quot;Courier New&quot;">QLabel</span><span \
style="font-size:12pt"> doesn&#39;t position itself properly at the end of what the \
user has typed in.   I am using </span><span \
style="font-size:12pt;font-family:&quot;Courier \
New&quot;">fontMetrics().horizontalAdvance(text)</span><span style="font-size:12pt"> \
on the text the user has typed in, to determine where to position the </span><span \
style="font-size:12pt;font-family:&quot;Courier New&quot;">Qlabel</span><span \
style="font-size:12pt">.   According to the documentation, that should be \
&quot;</span><span style="font-size:12pt;line-height:1.5">the distance appropriate \
for drawing a subsequent character after </span><i \
style="font-size:12pt;line-height:1.5">text</i><span \
style="font-size:12pt;line-height:1.5">.&quot;   But it comes out too close to the \
preceding text.</span></div></div></blockquote><div><br></div><div>Unfortunately \
I&#39;m still on Qt 5.7 (my main project is a bit critical, upgrading PyQt5 now would \
demand a lot of dependency issues that would put it at risk, as it&#39;s PyQt4 \
based), this means that I horizontalAdvance() isn&#39;t available for me as it was \
introduced in Qt 5.11.</div><div>Nonetheless, by using the &quot;simpler&quot; \
QFontMetrics.width() I got almost the same result, even if not probably \
&quot;pixel-perfect&quot; (it could depend on the font rendering hints used for the \
current font we&#39;re using, some updates in the rendering engine, png compression, \
etc).</div><div><br></div><div>That said, the issue here is that you didn&#39;t take \
into account the margins applied to the QLineEdit contents, which requires some \
QStyle work.</div><div>The first thing to look for is the subElementRect of \
QStyle.SE_LineEditContents, which returns the rectangle inside of which the text \
rendering happens: while QLineEdit does not inherit from QFrame as QLabels do, it \
still uses the QStyleOptionFrame (as it actually is some sort of &quot;frame&quot;, \
with its borders and margins).</div><div>After that, QLineEdit adds its own margin to \
the text, which seems to be hardcoded and set to 2 (according to  <a \
href="https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qlineedit_p.cpp.html#QLine \
EditPrivate::horizontalMargin">https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qlineedit_p.cpp.html#QLineEditPrivate::horizontalMargin</a> \
).</div><div>Finally, while in most uses this could be enough, remember that \
you&#39;re using two very different widgets that apply some margins to their contents \
whenever they&#39;re drawn. Luckily, the QLabel doesn&#39;t seem to apply further \
margins using default QStyle(s) on Linux, but it might be better to check for the \
QLabel&#39;s option too. This may also be necessary in some cases where the QLabel \
vertical positioning is different from QLineEdit, again depending on the current \
QStyle (I think it might the case of the default styles used on Windows and \
MacOS).<br></div><div><br></div><div>I&#39;ve just added some lines to your code, and \
it seems to be all right also while zooming in (even with QFontMetrics.width() \
instead of horizontalAdvance):</div><div><br></div><div><div><font face="monospace, \
monospace">            # Position display widget</font></div><div><font \
face="monospace, monospace">            option = \
QtWidgets.QStyleOptionFrame()</font></div><div><font face="monospace, monospace">     \
self.initStyleOption(option)</font></div><div><font face="monospace, monospace">      \
rect = self.style().subElementRect(QtWidgets.QStyle.SE_LineEditContents, option, \
self)</font></div><div><font face="monospace, monospace">            # Add the left \
position of the contents and the hardcoded horizontalMargin of \
QLineEdit</font></div><div><font face="monospace, monospace">            \
w.move(self.fontMetrics().width(user) + rect.left() + 2, \
0)</font></div></div><div><br></div><div>As a side note, remember to be careful about \
variable and property naming: you used <font face="monospace, \
monospace">self.style</font> for the stylesheet, but <font face="monospace, \
monospace">style()</font>  is an important property of QWidgets: I had to change it \
to have a direct reference to it, otherwise I&#39;d have to use \
super/QtWidgets.QLabel.style(self).</div><div><br></div><div><br></div><div>Cheers,</div><div>Maurizio</div><div><br></div></div>-- \
<br><div dir="ltr" class="gmail_signature">È difficile avere una convinzione precisa \
quando si parla delle ragioni del cuore. - &quot;Sostiene Pereira&quot;, Antonio \
Tabucchi<br><a href="http://www.jidesk.net" \
target="_blank">http://www.jidesk.net</a></div></div></div></div></div></div>


[Attachment #6 (text/plain)]

_______________________________________________
PyQt mailing list    PyQt@riverbankcomputing.com
https://www.riverbankcomputing.com/mailman/listinfo/pyqt


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic