[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! 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. 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). 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. 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. It so happens that the columns in my view that will have \
auto-completion <i style="font-family: Candara;">don't</i> 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! 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"> def __init__(self, *args, \
**kwargs):</span></div><div><span style="font-family: monospace, monospace"> \
QtGui.QSyntaxHighlighter.__init__(self, *args, \
**kwargs)</span></div><div><span style="font-family: monospace, monospace"> \
self.myFormat = QtGui.QTextCharFormat()</span></div><div><span \
style="font-family: monospace, monospace"> \
self.myFormat.setFontItalic(True)</span></div><div><span style="font-family: \
monospace, monospace"> \
self.myFormat.setForeground(QtCore.Qt.red)</span></div><div><span style="font-family: \
monospace, monospace"> <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"> def highlightBlock(self, \
text):</span></div><div><span style="font-family: monospace, monospace"> \
for match in self.re.finditer(text):</span></div><div><span \
style="font-family: monospace, monospace"> \
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"> submit = \
QtCore.pyqtSignal()</span></div><div><span style="font-family: monospace, \
monospace"><br></span></div><div><span style="font-family: monospace, \
monospace"> def __init__(self, *args, **kwargs):</span></div><div><span \
style="font-family: monospace, monospace"> \
QtWidgets.QTextEdit.__init__(self, *args, **kwargs)</span></div><div><span \
style="font-family:monospace,monospace"> \
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)</span><br></div><div><span \
style="font-family: monospace, monospace"> \
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)</span></div><div><span \
style="font-family: monospace, monospace"> \
self.setWordWrapMode(QtGui.QTextOption.NoWrap)</span></div><div><span \
style="font-family: monospace, monospace"> \
self.highlighter = MyHighlighter(self.document())</span></div><div><span \
style="font-family: monospace, monospace"><br></span></div><div><span \
style="font-family: monospace, monospace"> def keyPressEvent(self, \
event):</span></div><div><span style="font-family: monospace, monospace"> \
if event.key() in (QtCore.Qt.Key_Enter, \
QtCore.Qt.Key_Return):</span></div><div><span style="font-family: monospace, \
monospace"> \
self.submit.emit()</span></div><div><span style="font-family: monospace, \
monospace"> elif event.key() == \
QtCore.Qt.Key_Tab:</span></div><div><span style="font-family: monospace, \
monospace"> # Ignore tab by letting the \
parent(s) handle it, as you are </span></div><div><span style="font-family: \
monospace, monospace"> # probably not \
interested in the tab character.</span></div><div><span style="font-family: \
monospace, monospace"> # Since you also want \
to prevent leaving the cell editing </span></div><div><span style="font-family: \
monospace, monospace"> # if the content is \
not valid, here you can also choose to</span></div><div><span style="font-family: \
monospace, monospace"> # accept the event, \
inhibiting cell departure.</span></div><div><span style="font-family: monospace, \
monospace"> # Please note that the shift-tab \
is intercepted by the view.</span></div><div><span style="font-family: monospace, \
monospace"> \
event.ignore()</span></div><div><span style="font-family: monospace, \
monospace"> else:</span></div><div><span \
style="font-family: monospace, monospace"> \
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"> def createEditor(self, parent, option, \
index):</span></div><div><span style="font-family: monospace, monospace"> \
editor = MyEditor(parent)</span></div><div><span \
style="font-family: monospace, monospace"> \
editor.setPlainText(index.data())</span></div><div><span style="font-family: \
monospace, monospace"> editor.submit.connect(lambda: \
self.submit(editor, index))</span></div><div><span style="font-family: monospace, \
monospace"> return editor</span></div><div><span \
style="font-family: monospace, monospace"><br></span></div><div><span \
style="font-family: monospace, monospace"> def submit(self, editor, \
index):</span></div><div><span style="font-family: monospace, monospace"> \
# You can also check for contents before proceeding with \
this</span></div><div><span style="font-family: monospace, monospace"> \
self.setModelData(editor, index.model(), index)</span></div><div><span \
style="font-family: monospace, monospace"> \
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"> def setModelData(self, \
editor, model, index):</span></div><div><span style="font-family: monospace, \
monospace"> # This is required, otherwise you'll get the \
rich text contents as XHTML</span></div><div><span style="font-family: monospace, \
monospace"> 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