为啥我的 charFormat 样式仅适用于选择,并且仅适用于特定方向的选择?
Posted
技术标签:
【中文标题】为啥我的 charFormat 样式仅适用于选择,并且仅适用于特定方向的选择?【英文标题】:Why are my charFormat styles only working on selections, and only those made in a specific direction?为什么我的 charFormat 样式仅适用于选择,并且仅适用于特定方向的选择? 【发布时间】:2020-08-13 19:13:37 【问题描述】:我一直在尝试更明确地为文本编辑器分配字符格式,以便了解我可以根据我当前的技能范围自定义哪些内容。虽然我的格式方法的基本复制粘贴版本运行良好,但下面的版本继续运行,然后以令人沮丧的方式运行,需要帮助找出可能导致它的原因。
该编辑器最初的目的是成为一个所见即所得的编辑器,通过文档标签进行样式化。 Qt 对 html 的令人困惑的使用并不容易。
我的基本流程是提取当前格式的副本,检查其当前状态,反转它,然后将格式重新应用到提取它的位置或选择。
# textEdit is a QTextEdit with a loaded document.
# This function is one of several related pairs called by a switchboard.
# It's intent is to invert the italic state of the current position/selection.
def toggle_italic_text(textEdit):
# Get the cursor, and the format's state at its current selection/position.
cursor = textEdit.textCursor()
charFormat = cursor.charFormat()
currentState = charFormat.fontItalic()
# Invert the state within the format.
print(currentState)
charFormat.setFontItalic(not currentState)
print(charFormat.fontItalic())
# Reapply the format to the cursor's current selection/position.
cursor.mergeCharFormat(charFormat)
当我第一次实现它时,发现它很有效。现在,它只适用于选择,即使这样,它似乎也可以根据我选择的方向来识别错误的状态。在对其进行试验后,似乎如果我向右进行选择,它会正确反转。如果我在左侧进行选择,则不会。
当试图将其分配给一个位置时没有选择,打印的状态会从 False 变为 True,这是所需的,但在我键入时效果并不适用。如果我在原地重复运行它,它会继续从 False 变为 True,这意味着更改正在丢失。
该函数被一致地调用并完全运行。 charFormat 副本的存储状态确实发生了变化。
为什么这种模式停止工作了?我使用 charFormats 错了吗?为什么选择的方向会改变结果?
就我的最终变化而言,在需要通过针对小部件和 html 标记的 QFonts、QCharFormats、QPalette 和 CSS 样式表(以及 doc.defaultStylesheet)应用样式之后,我一直迷失在我的样式设计工作中。我迫切希望通过一种方法来控制我的风格,但无法弄清楚层次结构或找到一种足够广泛应用的方法。最后,我去掉了除了分配给窗口的样式表之外的所有内容。
如果代码本身没有问题,我真的希望能得到一些提示,说明什么可能会破坏事情。我花了一段时间才习惯这样的想法:光标和格式是要更改和重新应用的副本,而文档及其块才是真正的结构。
【问题讨论】:
“反转”应该如何工作?如果您有一个包含斜体和非斜体字符的选择,是否应该将它们全部反转?或者您想使用光标位置作为参考并将反转样式应用于整个选择,无论当前状态如何? 如果选择中有多个斜体状态,第一个切换应该将整个选择对齐到一个状态或另一个状态。随后的切换会来回反转整个选择。我没有考虑过究竟是什么规则会决定第一个行为。我曾假设 Qt 的内置函数以某种方式处理它(它们并不总是这样做是我打算编写完整、明确的模式的原因)。 确实如此,您只需要了解如何正确使用它们。看我的回答。以供将来参考,请尝试在您的下一个更简洁。过于广泛且提供过多细节(其中许多是不必要的)的问题往往会被忽略。 会的。我让太多的问题堆积在我身上,现在它们很难干净地分开。 请不要编辑您的问题(这已经太广泛了)以包含一个答案或引用现有答案的解释。它使问题更加混乱,并没有真正改善上下文。如果您有实际答案,请自行创建。否则,使用 cmets。 【参考方案1】:关于QTextCursor.charFormat()
必须考虑的重要一点是:
在光标position()之前返回字符的格式。
因此,这不仅不适用于包含多种字符格式的选择,而且您还必须考虑光标位置,这可能会在选择中发生变化:它可能在开头(所以它会返回字符的格式之前选择),或结束(返回选择中最后一个字符的格式)。
如果要根据当前光标位置反转状态(如果在开头,则使用第一个字符,如果在结尾,则使用最后一个字符),则可以使用以下内容:
def toggle_italic_text(self):
cursor = self.textEdit.textCursor()
if not cursor.hasSelection():
charFormat = cursor.charFormat()
charFormat.setFontItalic(not charFormat.fontItalic())
cursor.setCharFormat(charFormat)
# in this case, the cursor has to be applied to the textEdit to ensure
# that the following typed characters use the new format
self.textEdit.setTextCursor(cursor)
return
start = cursor.selectionStart()
end = cursor.selectionEnd()
newCursor = QtGui.QTextCursor(self.textEdit.document())
newCursor.setPosition(start)
if cursor.position() == start:
cursor.setPosition(start + 1)
charFormat = cursor.charFormat()
charFormat.setFontItalic(not charFormat.fontItalic())
newCursor.setPosition(end, cursor.KeepAnchor)
newCursor.mergeCharFormat(charFormat)
如果要反转选择中的所有状态,则需要循环遍历所有字符。 虽然您可以只更改每个字符的 char 格式,但这对于非常大的选择不是一件好事,因此解决方案是仅在 char 格式实际从以前的状态更改时应用斜体,和 在选择结束时。
def toggle_italic_text(self):
# ...
start = cursor.selectionStart()
end = cursor.selectionEnd()
newCursor = QtGui.QTextCursor(self.textEdit.document())
newCursor.setPosition(start)
cursor.setPosition(start)
prevState = cursor.charFormat().fontItalic()
while cursor.position() < end:
cursor.movePosition(cursor.Right)
charFormat = cursor.charFormat()
if charFormat.fontItalic() != prevState or cursor.position() >= end:
newPos = cursor.position()
if cursor.position() < end:
newPos -= 1
newCursor.setPosition(newPos, cursor.KeepAnchor)
charFormat.setFontItalic(not prevState)
newCursor.mergeCharFormat(charFormat)
prevState = not prevState
newCursor.setPosition(cursor.position() - 1)
【讨论】:
谢谢,我认为这正是我最终解决问题所需的洞察力(我会在有时间处理它时更新)。 Qt 通过大多数不同的文本类提供对格式和字体的访问,但很难知道哪些具有独特的功能,哪些只是调用较低的类,以及何时/使用哪些默认样式。光标存在于字符之间的负空间中,因此查看它如何采样、接收并将其自己的引用应用于 charFormat 是有帮助的。有意识地注意对哪个角色进行采样以及这样做的后果也很重要。 基本上,Qt 中所有与文本相关的小部件都以某种方式使用 QTextDocument,有些是显式的(如 QTextEdit 子类),有些是私下的(如 QLabel);理解它是如何工作的(连同 QTextCursor、QTextBlock 和 QTextFormat 子类)当然不是一件容易的事,但如果你做一些研究和实验,你就会开始了解它的要点。关于光标,您可以将其视为一种切片符号。 成功了!我包含了一个overrideState=None
参数,当给定一个布尔值时,它允许函数选择结果而不是切换。我认为也可能是 newCursor 的初始位置应该是结束位置(对最后一个字符进行采样)而不是开始位置(对第一个 -1 进行采样)。这样选项就在第一个和最后一个字符之间,而不是第一个和第一个字符之间。
我不确定我是否理解了你的最后一部分。 newCursor
的起始位置应始终是第一个字符的索引,结束位置(在保持锚点移动时)设置在 cursor.position() -1
的位置上(因为cursor
已经增加了一个字符权限来检查格式更改)。
我错了。我指的是第一个示例,而不是我尚未探索的完全反转版本,并且因为我使用我的 cursor
/newCursor
对略有不同,我误读了您的流程。我将现有选择保留在cursor
上,并专门使用newCursor
来采样第一个或最后一个字符。您交换了这些角色,并且必须在 newCursor
上重建选择。我将在我的原始帖子中更新我完成的代码(添加了覆盖)。以上是关于为啥我的 charFormat 样式仅适用于选择,并且仅适用于特定方向的选择?的主要内容,如果未能解决你的问题,请参考以下文章