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

List:       pykde
Subject:    Re: [PyQt] Suppress departure from cell in QTableView
From:       "John F Sturtz" <john () sturtz ! org>
Date:       2019-02-23 18:01:40
Message-ID: 1d596405-0487-441f-9029-55db269f193a () getmailbird ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


Thanks again Maurizio!   This is very cool.

I played around with using QLineEdit, and that works quite well for most of the \
columns in my view.   The nice thing about that is that I can use a QRegExpValidator \
for validation, which is pretty slick.

Actually, the reason I was wanting to do special text formatting in some of the \
columns is to implement auto-completion (basically, what the user has typed in so far \
shows in one style, and the auto-completed portion in another).   It turns out using \
a QLineEdit, I can use a QCompleter for this.   I played around with that too, and it \
works pretty well.

On the other hand, I could implement my own auto-completion and use QTextEdit as you \
have done.   It so happens that the columns in my view that will have auto-completion \
don't  have much validation, so I wouldn't really miss the QRegExpValidator for those \
cells.

Thanks again!   You've given me lots of great ideas.

/John



Since you need inline text formatting, implementing QLineEdit can be an issue as \
you'd probably need to do all the painting by yourself.


I'd suggest another approach instead: use a QTextEdit. It's implementation is a bit \
harder to get, but allows a better way to do what you need, while giving the user a \
standard and much more comfortable text editing UX.

Here's a small example I made which also uses QSyntaxHighlighter to do the formatting \
(in this case, it makes all occurrences of the word "red" in italic red). It doesn't \
count in the "unallowed" text input you wrote about, but that can be done inside the \
keyPressEvent() method (but the release could need some checking too); you could also \
validate the input by connecting the textChanged() signal, to inhibit invalid \
clipboard pasting (like new line characters).


class MyHighlighter(QtGui.QSyntaxHighlighter):
      def __init__(self, *args, **kwargs):
            QtGui.QSyntaxHighlighter.__init__(self, *args, **kwargs)
            self.myFormat = QtGui.QTextCharFormat()
            self.myFormat.setFontItalic(True)
            self.myFormat.setForeground(QtCore.Qt.red)
            self.re [http://self.re] = re.compile(r'(red)+')

      def highlightBlock(self, text):
            for match in self.re.finditer(text):
                  self.setFormat(match.start(), match.end() - match.start(), \
self.myFormat)


class MyEditor(QtWidgets.QTextEdit):
      submit = QtCore.pyqtSignal()

      def __init__(self, *args, **kwargs):
            QtWidgets.QTextEdit.__init__(self, *args, **kwargs)
            self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

            self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
            self.setWordWrapMode(QtGui.QTextOption.NoWrap)
            self.highlighter = MyHighlighter(self.document())

      def keyPressEvent(self, event):
            if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
                  self.submit.emit()
            elif event.key() == QtCore.Qt.Key_Tab:
                  # Ignore tab by letting the parent(s) handle it, as you are  
                  # probably not interested in the tab character.
                  # Since you also want to prevent leaving the cell editing  
                  # if the content is not valid, here you can also choose to
                  # accept the event, inhibiting cell departure.
                  # Please note that the shift-tab is intercepted by the view.
                  event.ignore()
            else:
                  QtWidgets.QTextEdit.keyPressEvent(self, event)


class MyDelegate(QtWidgets.QStyledItemDelegate):
      def createEditor(self, parent, option, index):
            editor = MyEditor(parent)
            editor.setPlainText(index.data())
            editor.submit.connect(lambda: self.submit(editor, index))
            return editor

      def submit(self, editor, index):
            # You can also check for contents before proceeding with this
            self.setModelData(editor, index.model(), index)
            self.closeEditor.emit(editor, self.EditNextItem)

      def setModelData(self, editor, model, index):
            # This is required, otherwise you'll get the rich text contents as XHTML
            model.setData(index, editor.toPlainText(), QtCore.Qt.DisplayRole)


Now you only have to setItemDelegate() on your view, and eventually set the \
highlighting [validation] by checking the index.column() argument in the \
createEditor() method according to your needs (or just create different delegates for \
each column).

Cheers!

Maurizio


[Attachment #5 (text/html)]

<div id="__MailbirdStyleContent" style="font-size: 12pt;font-family: Candara;color: \
#000000"><blockquote class="history_container" type="cite" style="border-left-style: \
solid;border-width: 1px;margin-top: 20px;margin-left: 0px;padding-left: \
10px;min-width: 500px"><div style="font-family:Arial,Helvetica,sans-serif"><div \
dir="ltr"><div dir="ltr"><div \
class="gmail_quote"><div></div></div></div></div></div></blockquote><div><span \
style="font-size: 12pt;font-family: Candara;color: #000000"><b><span style="color: \
#3B5998"></span></b><span style="color: #3B5998">Thanks again Maurizio! &nbsp;This is \
very cool.</span></span></div><div><span style="font-size: 12pt;font-family: \
Candara;color: #000000"><span style="color: \
#3B5998"><br></span></span></div><div><span style="font-size: 12pt;color: rgb(0, 0, \
0)"><span style="color: rgb(59, 89, 152);">I played around with using <span \
style="font-family: Courier New">QLineEdit</span>, and that works quite well for most \
of the columns in my view. &nbsp;The nice thing about that is that I can use a <span \
style="font-family: Courier New">QRegExpValidator</span> for validation, which is \
pretty slick.</span></span></div><div><span style="font-size: 12pt;font-family: \
Candara;color: #000000"><span style="color: \
#3B5998"><br></span></span></div><div><span style="font-size: 12pt;color: rgb(0, 0, \
0)"><span style="color: rgb(59, 89, 152);">Actually, the reason I was wanting to do \
special text formatting in some of the columns is to implement auto-completion \
(basically, what the user has typed in so far shows in one style, and the \
auto-completed portion in another). &nbsp;It turns out using a <span \
style="font-family: Courier New">QLineEdit</span>, I can use a <span \
style="font-family: Courier New">QCompleter</span> for this. &nbsp;I played around \
with that too, and it works pretty well.</span></span></div><div><span \
style="font-size: 12pt;font-family: Candara;color: #000000"><span style="color: \
#3B5998"><br></span></span></div><div><span style="font-size: 12pt;color: rgb(0, 0, \
0)"><span style="color: rgb(59, 89, 152);">On the other hand, I could implement my \
own auto-completion and use <span style="font-family: Courier New">QTextEdit</span> \
as you have done. &nbsp;It so happens that the columns in my view that will have \
auto-completion <i style="font-family: Candara;">don't</i>&nbsp;have much validation, \
so I wouldn't really miss the <span style="font-family: Courier \
New">QRegExpValidator</span> for those cells.</span></span></div><div><span \
style="font-size: 12pt;font-family: Candara;color: #000000"><span style="color: \
#3B5998"><br></span></span></div><div><span style="font-size: 12pt;font-family: \
Candara;color: #000000"><span style="color: #3B5998">Thanks again! &nbsp;You've given \
me lots of great ideas.</span></span></div><div><span style="font-size: \
12pt;font-family: Candara;color: #000000"><span style="color: \
#3B5998"><br></span></span></div><div><span style="font-size: 12pt;font-family: \
Candara;color: #000000"><span style="color: \
#3B5998">/John<br></span><br></span></div><blockquote class="history_container" \
type="cite" style="border-left-style: solid;border-width: 1px;margin-top: \
20px;margin-left: 0px;padding-left: 10px;min-width: 500px"><div \
style="font-family:Arial,Helvetica,sans-serif"><div dir="ltr"><div dir="ltr"><div \
class="gmail_quote"><div><br></div><div></div></div><div>Since you need inline text \
formatting, implementing QLineEdit can be an issue as you'd probably need to do all \
the painting by yourself.<br><br></div><div>I'd suggest another approach instead: use \
a QTextEdit. It's implementation is a bit harder to get, but allows a better way to \
do what you need, while giving the user a standard and much more comfortable text \
editing UX.</div><div><br>Here's a small example I made which also uses \
QSyntaxHighlighter to do the formatting (in this case, it makes all occurrences of \
the word "red" in italic red).</div><div>It doesn't count in the "unallowed" text \
input you wrote about, but that can be done inside the keyPressEvent() method (but \
the release could need some checking too); you could also validate the input by \
connecting the textChanged() signal, to inhibit invalid clipboard pasting (like new \
line characters).</div><div><br></div><div><br></div><div><div><span \
style="font-family: monospace, monospace">class \
MyHighlighter(QtGui.QSyntaxHighlighter):</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; def __init__(self, *args, \
**kwargs):</span></div><div><span style="font-family: monospace, monospace">&nbsp; \
&nbsp; &nbsp; &nbsp; QtGui.QSyntaxHighlighter.__init__(self, *args, \
**kwargs)</span></div><div><span style="font-family: monospace, monospace">&nbsp; \
&nbsp; &nbsp; &nbsp; self.myFormat = QtGui.QTextCharFormat()</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
self.myFormat.setFontItalic(True)</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
self.myFormat.setForeground(QtCore.Qt.red)</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; <a \
href="http://self.re">self.re</a> = re.compile(r'(red)+')</span></div><div><span \
style="font-family: monospace, monospace"><br></span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; def highlightBlock(self, \
text):</span></div><div><span style="font-family: monospace, monospace">&nbsp; &nbsp; \
&nbsp; &nbsp; for match in self.re.finditer(text):</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
self.setFormat(match.start(), match.end() - match.start(), \
self.myFormat)</span></div><div><span style="font-family: monospace, \
monospace"><br></span></div><div><span style="font-family: monospace, \
monospace"><br></span></div><div><span style="font-family: monospace, \
monospace">class MyEditor(QtWidgets.QTextEdit):</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; submit = \
QtCore.pyqtSignal()</span></div><div><span style="font-family: monospace, \
monospace"><br></span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; def __init__(self, *args, **kwargs):</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
QtWidgets.QTextEdit.__init__(self, *args, **kwargs)</span></div><div><span \
style="font-family:monospace,monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)</span><br></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
self.setWordWrapMode(QtGui.QTextOption.NoWrap)</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
self.highlighter = MyHighlighter(self.document())</span></div><div><span \
style="font-family: monospace, monospace"><br></span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; def keyPressEvent(self, \
event):</span></div><div><span style="font-family: monospace, monospace">&nbsp; \
&nbsp; &nbsp; &nbsp; if event.key() in (QtCore.Qt.Key_Enter, \
QtCore.Qt.Key_Return):</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
self.submit.emit()</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; elif event.key() == \
QtCore.Qt.Key_Tab:</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Ignore tab by letting the \
parent(s) handle it, as you are&nbsp;</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # probably not \
interested in the tab character.</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Since you also want \
to prevent leaving the cell editing&nbsp;</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # if the content is \
not valid, here you can also choose to</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # accept the event, \
inhibiting cell departure.</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Please note that the shift-tab \
is intercepted by the view.</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
event.ignore()</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; else:</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
QtWidgets.QTextEdit.keyPressEvent(self, event)</span></div><div><span \
style="font-family: monospace, monospace"><br></span></div><div><span \
style="font-family: monospace, monospace"><br></span></div><div><span \
style="font-family: monospace, monospace">class \
MyDelegate(QtWidgets.QStyledItemDelegate):</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; def createEditor(self, parent, option, \
index):</span></div><div><span style="font-family: monospace, monospace">&nbsp; \
&nbsp; &nbsp; &nbsp; editor = MyEditor(parent)</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
editor.setPlainText(index.data())</span></div><div><span style="font-family: \
monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; editor.submit.connect(lambda: \
self.submit(editor, index))</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; return editor</span></div><div><span \
style="font-family: monospace, monospace"><br></span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; def submit(self, editor, \
index):</span></div><div><span style="font-family: monospace, monospace">&nbsp; \
&nbsp; &nbsp; &nbsp; # You can also check for contents before proceeding with \
this</span></div><div><span style="font-family: monospace, monospace">&nbsp; &nbsp; \
&nbsp; &nbsp; self.setModelData(editor, index.model(), index)</span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; &nbsp; &nbsp; \
self.closeEditor.emit(editor, self.EditNextItem)</span></div><div><span \
style="font-family: monospace, monospace"><br></span></div><div><span \
style="font-family: monospace, monospace">&nbsp; &nbsp; def setModelData(self, \
editor, model, index):</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; # This is required, otherwise you'll get the \
rich text contents as XHTML</span></div><div><span style="font-family: monospace, \
monospace">&nbsp; &nbsp; &nbsp; &nbsp; model.setData(index, editor.toPlainText(), \
QtCore.Qt.DisplayRole)</span></div><br><br></div><div>Now you only have to \
setItemDelegate() on your view, and eventually set the highlighting [validation] by \
checking the index.column() argument in the createEditor() method according to your \
needs (or just create different delegates for each \
column).</div><div><br></div><div>Cheers!</div><div><br></div><div>Maurizio</div></div></div>
 </div></blockquote></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