python - 如何使 QTextBrowser 中的文本可选择?
问题描述
我希望能够以非自定义方式选择一段文本,就像您单击文本中间的网站链接一样(“你好,我的名字是 www.google.com”,www.google.com当你按下它时没有突出显示。当你按下它时,它会将你带到网站,就像我想要的文本一样。我想让它像“你好,我的名字是 Jeff,我住在伦敦,我吃每天土豆”我希望用户能够单独选择每个句子(“你好,我的名字是杰夫”)单独,(“我住在伦敦”)单独,所以当用户将鼠标光标移到一个句子突出显示(比如准备被选中),然后我想给它添加一些功能。
这是一个类似的项目,检查上面的文本,而不是下面的文本以及它对鼠标的反应。
解决方案
这是一个开始。在下面的代码中,文本中的可点击短语被翻译成文本光标的有序列表。当鼠标移动时(即当一个mouseMove
事件发生时),鼠标指针下的文本位置与这个文本光标列表进行比较。如果该位置落在列表中光标的范围内,则通过设置浏览器的额外选择来突出显示相应的短语。当单击鼠标按钮并且鼠标悬停在可单击的短语上时,会发出一个信号,其中包含与文本中所选短语相对应的文本光标。然后可以将此信号连接到另一个小部件的插槽以进行进一步的操作(例如打开消息框,如下例所示)。
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QTextBrowser, QApplication, QMessageBox
from PyQt5.QtGui import QFont, QSyntaxHighlighter, QTextCharFormat, QTextCursor
import bisect, re
class MyHighlighter(QSyntaxHighlighter):
def __init__(self, keywords, parent):
super().__init__(parent)
self.keywords = keywords
def highlightBlock(self, block):
if not self.keywords:
return
charFormat = QTextCharFormat()
charFormat.setFontWeight(QFont.Bold)
charFormat.setForeground(Qt.darkMagenta)
regex = re.compile('|'.join(self.keywords), re.IGNORECASE)
result = regex.search(block, 0)
while result:
self.setFormat(result.start(),result.end()-result.start(), charFormat)
result = regex.search(block, result.end())
class MyBrowser(QTextBrowser):
text_clicked = pyqtSignal("QTextCursor")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setMouseTracking(True)
self.setTextInteractionFlags(Qt.NoTextInteraction)
# self.phrases contains all phrases that should be clickable.
self.phrases = set()
self.cursors = []
# ExtraSelection object for highlighting phrases under the mouse cursor
self.selection = QTextBrowser.ExtraSelection()
self.selection.format.setBackground(Qt.blue)
self.selection.format.setForeground(Qt.white)
self.selected_cursor = None
# custom highlighter for highlighting all phrases
self.highlighter = MyHighlighter(self.phrases, self)
self.document().contentsChange.connect(self.text_has_changed)
@property
def selected_cursor(self):
return None if self.selection.cursor == QTextCursor() else self.selection.cursor
@selected_cursor.setter
def selected_cursor(self, cursor):
if cursor is None:
cursor = QTextCursor()
if self.selection.cursor != cursor:
self.selection.cursor = cursor
self.setExtraSelections([self.selection])
def mouseMoveEvent(self, event):
''' Update currently selected cursor '''
cursor = self.cursorForPosition(event.pos())
self.selected_cursor = self.find_selected_cursor(cursor)
def mouseReleaseEvent(self, event):
''' Emit self.selected_cursor signal when currently hovering over selectable phrase'''
if self.selected_cursor:
self.text_clicked.emit(self.selected_cursor)
self.selected_cursor = None
def add_phrase(self, phrase):
''' Add phrase to set of phrases and update list of text cursors'''
if phrase not in self.phrases:
self.phrases.add(phrase)
self.find_cursors(phrase)
self.highlighter.rehighlight()
def find_cursors(self, phrase):
''' Find all occurrences of phrase in the current document and add corresponding text cursor
to self.cursors '''
if not phrase:
return
self.moveCursor(self.textCursor().Start)
while self.find(phrase):
cursor = self.textCursor()
bisect.insort(self.cursors, cursor)
self.moveCursor(self.textCursor().Start)
def find_selected_cursor(self, cursor):
''' return text cursor corresponding to current mouse position or None if mouse not currently
over selectable phrase'''
position = cursor.position()
index = bisect.bisect(self.cursors, cursor)
if index < len(self.cursors) and self.cursors[index].anchor() <= position:
return self.cursors[index]
return None
def text_has_changed(self):
self.cursors.clear()
self.selected_cursor = None
for phrase in self.phrases:
self.find_cursors(phrase)
self.highlighter.rehighlight()
def text_message(widget):
def inner(cursor):
text = cursor.selectedText()
pos = cursor.selectionStart()
QMessageBox.information(widget, 'Information',
f'You have clicked on the phrase <b>{text}</b><br>'
f'which starts at position {pos} in the text')
return inner
if __name__=="__main__":
app = QApplication([])
window = MyBrowser()
window.resize(400,300)
information = text_message(window)
text = '''
<h1>Title</h1>
<p>This is a random text with. The following words are highlighted</p>
<ul>
<li>keyword1</li>
<li>keyword2</li>
</ul>
<p>Click on either keyword1 or keyword2 to get more info.
'''
window.add_phrase('keyword1')
window.add_phrase('keyword2')
window.setText(text)
window.text_clicked.connect(information)
window.show()
app.exec()
推荐阅读
- linux - 如何在 vscode 上打字更快?
- php - 通过 laravel 将多表行插入数据库
- sequelize.js - 在 hasMany 关联上使用 set 时出错
- reactjs - 将react hooks和context的reducer与类型安全结合起来
- docker - 列出 Docker Hub 上的存储库名称
- azure-devops - 在 Azure Devops 中,我希望我的 yaml 管道仅在同一管道的上一次执行完成后执行
- vue.js - 如何在 Azure DevOps YAML 中配置 ESLint 设置?
- firebase - 部署错误。服务器错误。连接 ETIMEDOUT
- server - 服务器无法正常启动。[2020-04-25T07:05:35.422Z] 错误 (hook:bookshelf) 加载时间过长 (hook:bookshelf) 加载时间过长
- regex - 正则表达式匹配具有重复序列的单词和数字 (FOO-123 / FOO-456 /...等)