Issue
I'm trying to attach a hyperlink to the highlighted text. I found this example:
from PyQt5 import QtCore, QtGui, QtWidgets, Qt
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(320, 240)
self.verticalLayout = QtWidgets.QVBoxLayout(Form)
self.verticalLayout.setObjectName("verticalLayout")
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setObjectName("pushButton")
self.pushButton.clicked.connect(self.SetHyperLink)
self.verticalLayout.addWidget(self.pushButton)
self.textEdit = QtWidgets.QTextEdit(Form)
self.textEdit.setObjectName("textEdit")
self.verticalLayout.addWidget(self.textEdit)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.pushButton.setText(_translate("Form", "PushButton"))
def SetHyperLink(self):
cursor = self.textEdit.textCursor()
fmt = cursor.charFormat()
fmt.setForeground(Qt.QColor("blue"))
address = 'http://example.com'
fmt.setAnchor(True)
fmt.setAnchorHref(address)
fmt.setToolTip(address)
if cursor.hasSelection():
cursor.setCharFormat(fmt)
cursor.setCharFormat(self.textEdit.textCursor().charFormat())
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
In general it works, but there is one nuance. If the cursor in TextEdit is set in the block to which the link is attached, then all subsequent input will be added to the hyperlink text. This is logical because the cursor takes properties set to the text below it, but it is not so what I need, because if it is the last block, then everything I enter next becomes hyperlink text. What should I do to avoid this behavior? I would like to achieve the same behavior as in Word document, google docs, etc..
I've read the documentation, but I don't understand the logic behind the cursors yet((
Solution
The problem is that QTextCursor always considers the character format (QTextCharFormat) on the left of the cursor.
While this might not seem intuitive for "conventional" rich text editing, it's done for "dev-performance" reasons: the basic implementation should always follow the most common and simple usage (also considering performances), then it's up to the developer to create further customization of the behavior.
A possible solution is to verify the current text cursor position and eventually change the QTextCursor used by the editor.
This can be achieved by connecting to the cursorPositionChanged
and selectionChanged
signals.
Both signals are necessary in order to ensure that the editing after the cursor (especially when moving the cursor while a selection exists) will always use the default character.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class TextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.cursorPositionChanged.connect(self.checkAnchor)
self.selectionChanged.connect(self.checkAnchor)
def checkAnchor(self):
cursor = self.textCursor()
if cursor.hasSelection():
return
format = cursor.charFormat()
if not format.isAnchor() or not format.anchorHref():
return
if not cursor.atEnd():
tempCursor = QTextCursor(cursor)
tempCursor.movePosition(cursor.NextCharacter)
if tempCursor.charFormat() == format:
return
cursor.setCharFormat(QTextCharFormat())
self.setTextCursor(cursor)
def setHyperLink(self, url=None):
if isinstance(url, QUrl):
url = url.toString()
elif not isinstance(url, str):
url = 'https://example.com'
cursor = self.textCursor()
if not cursor.hasSelection():
return
format = cursor.charFormat()
format.setFontUnderline(True)
format.setForeground(self.palette().link())
format.setAnchor(True)
format.setAnchorHref(url)
cursor.setCharFormat(format)
class EditorWindow(QMainWindow):
def __init__(self):
super().__init__()
self.editor = TextEdit()
self.setCentralWidget(self.editor)
self.charToolBar = QToolBar()
self.addToolBar(self.charToolBar)
self.urlAction = self.charToolBar.addAction(
QIcon.fromTheme('insert-link'), 'Add link')
self.urlAction.triggered.connect(self.editor.setHyperLink)
def keyPressEvent(self, event):
if event.key() == Qt.Key_U and event.modifiers() == Qt.ControlModifier:
self.editor.setHyperLink()
else:
super().keyPressEvent(event)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
test = EditorWindow()
test.resize(app.primaryScreen().size() * 2 / 3)
editor = test.editor
editor.setText('The following is a link. Now type something.')
cursor = editor.textCursor()
cursor.movePosition(cursor.End)
cursor.movePosition(cursor.WordLeft, cursor.KeepAnchor, 4)
editor.setTextCursor(cursor)
editor.setHyperLink()
cursor.movePosition(cursor.End)
editor.setTextCursor(cursor)
test.show()
app.exec_()
Note: the example above always use the default char format (the system font used by Qt). The char format is always inherited, so there is no direct way (as far as I know) to get the "parent" character format and ignore what has been previously set by the anchor format. A possible solution is to cycle backwards from the current selection start and check for the previous text formats, excluding any format that is an anchor and has a link.
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.