如何在QTextEdit中添加全宽水平线?

huangapple go评论73阅读模式
英文:

How do I add a full-width horizontal line in QTextEdit?

问题

def print_results(self, results):
    self.result_text_box.clear()
    self.cursor = self.result_text_box.textCursor()
    self.cursor.insertText(results[0])
    for result in results[1:]:
        # 在这里如何添加一个全宽的水平线?
        self.cursor.insertHtml("<hr/>")
        self.cursor.insertText(result)
英文:

I am trying to add a full-width horizontal line in QTextEdit from PyQt5.QtWidgets.

My function looks like this:

def print_results(self, results):
    self.result_text_box.clear()
    self.cursor = self.result_text_box.textCursor()
    self.cursor.insertText(results[0])
    for result in results[1:]:
        # how do I add a horizontal line here?
        self.cursor.insertText(result)

results is a list of strings (possibly with newline characters) and self.result_text_box is an instance of QTextEdit.

When I tried self.cursor.insertHtml(&quot;&lt;hr/&gt;&quot;) I got weird results. Starting with the second string, a horizontal line was added whenever there was a newline character. For whatever reason, self.cursor.insertHtml(&quot;&lt;p/&gt;&lt;hr/&gt;&lt;p/&gt;&quot;) solved that problem, but only on macOS, while on Windows it didn't. I tried using just insertHtml, for the text too, but that didn't do the trick.

I would appreciate your help with this. Thanks!

答案1

得分: 1

以下是您要翻译的内容:

支持合规性的HTML之间的区别-

应始终牢记的一件重要事情是,虽然Qt“支持”HTML,但它不是一个Web浏览器<sup>[见注]</sup>。

每当QtGui/QtWidget类或函数提供HTML功能时,它始终遵循有限的HTML子集支持(请参阅脚注)。

每当调用像setHtml()(或对于可选的富文本对象,如QLabel,setText())这样的函数时,给定的HTML将被解析并转换为QTextDocument:如果文本引擎解析器不支持某个HTML标签/属性或CSS语法,它将被完全忽略,并将恢复为以考虑上下文和HTML对象树显示给定内容的最可能的方式。

<hr>(又名“Horizontal Rule”)元素在HTML世界中也是一个特殊的元素。
在Qt中,它始终是当前 QTextBlock的一部分(就像<p>中的“段落”一样),并显示在其底部。

我要重申一下,这非常重要:<hr>线始终是上一个块的一部分,它不是一个单独的元素。

在元素之间添加线作为分隔符-

现在,虽然我们可以深入研究QTextDocument结构,但似乎你只想显示由水平线分隔的纯文本条目。

所以,让我们保持简单。

如果您只想在元素之间有线,那么很容易:

def print_results(self, results):
    self.result_text_box.setHtml(
        '&#39;<hr>'&#39;.join(results))

<sup>请注意,我删除了self.result_text_box.clear(),在使用setHtml()时,这是完全没有意义的,因为该函数始终会覆盖当前内容。</sup>

允许用户在其后插入的进一步行-

如果您想在最后一个条目的末尾添加进一步的行并允许插入进一步的元素,情况会变得有点棘手。

如上所述,水平线始终是QTextBlock的一部分。这意味着如果您尝试执行insertHtml(),插入将始终在文本光标的当前文本块内进行,由于插入新块始终继承当前块的块格式,因此您将得到两个或更多连续的行。

在末尾添加一行并允许插入新文本的正确方法如下:

  1. 执行上述操作,但还要添加一个进一步的<hr>元素;
  2. 获取文档的QTextCursor;
  3. 将其移动到末尾;
  4. 添加进一步的文本块;
  5. 删除新文本块的线(该线继承自上一个块);

以下是上述操作的可能实现:

def print_results(self, results):
    self.result_text_box.setHtml(
        '&#39;<hr>'&#39;.join(results) + '&#39;<hr>'&#39;) # &lt;-- 请注意添加的&lt;hr&gt;元素

    tc = self.result_text_box.textCursor()
    # 将光标移动到文档末尾
    tc.movePosition(tc.End)
    # 插入一个任意的QTextBlock,将继承上一个格式
    tc.insertBlock()
    # 获取块格式
    fmt = tc.blockFormat()
    # 从块中删除水平标尺属性
    fmt.clearProperty(fmt.BlockTrailingHorizontalRulerWidth)
    # 设置(不合并!)块格式
    tc.setBlockFormat(fmt)
    # 最终,应用光标以确保编辑实际上从末尾开始
    self.result_text_box.setTextCursor(tc)

<sup>请注意,我只是为QTextCursor使用了一个本地变量;为其创建一个实例属性不仅毫无意义(您可能会多次调用该函数,因此每次都会覆盖它),而且是错误的,因为cursor()是所有QWidgets的现有属性/函数,并且不应该被完全不相关于原始用途的东西覆盖,尤其是使用本地变量。</sup>

请注意,您在macOS和Windows之间看到的差异可能与两个不同方面有关:

  • 不同的Qt版本;
  • 可能在不同的操作系统和操作系统版本之间不同的字体/文本渲染引擎;

<hr>

<sup>如果您想要符合HTML的方式,在Qt中唯一的方法是通过Qt WebEngine API;在PyQt5中,大多数重要的类都在QtWebEngineQtWebEngineWidgetsQtWebEngineCore模块中,而在PyQt6中,QtWebEngine模块的所有类都已经移动到QtWebEngineCore模块中。</sup>

英文:

The difference between support and compliance of HTML

An important thing that should always be kept in mind is that, while Qt "supports" HTML, it is NOT a web browser <sup>[see note]</sup>.

Whenever a QtGui/QtWidget class or function provides HTML functionalities, it always follows a limited HTML subset support (see the foot note).

Whenever a function like setHtml() (or setText() for optional rich-text objects like QLabel) is called, the given HTML is parsed and converted into a QTextDocument: if the text engine parser doesn't support a certain HTML tag/attribute or CSS syntax, it will be completely ignored and will revert to the most possible way to show the given content considering the context and the HTML object tree.

The &lt;hr&gt; (aka, "Horizontal Rule") element is a peculiar one, even in the HTML world.
In Qt, it is an element that is always part of the current QTextBlock (a "paragraph", as in &lt;p&gt;...&lt;/p&gt;) and shown at its bottom.

I'll repeat this, as it is extremely important: the &lt;hr&gt; line is always part of a previous block, it is not a separate element.

Add lines as separators between elements

Now, while we could go extremely into deep of the QTextDocument structure, it seems clear that you just want to show plain text entries that are separated by a horizontal line.

So, let's keep this simple.

If you only want lines between elements, it's quite easy:

def print_results(self, results):
    self.result_text_box.setHtml(
        &#39;&lt;hr&gt;&#39;.join(results))

<sup>Note that I removed self.result_text_box.clear(), which is completely pointless when using setHtml(), because that function always overwrites the current contents.</sup>

A further line, allowing user insertion after it

In case you want to add a further line at the end of the last entry and also allow insertion of further elements, things get a bit trickier.

As said above, the horizontal line is always part of a QTextBlock. This means that if you try to do insertHtml() the insertion will always be within the current text block of the text cursor, and since insertion of new block always inherits the block format of the current block, you'll end up with two or more consecutive lines.

The proper way to add a line at the end and allow insertion of new text after that is the following:

  1. do the above, but also add a further &lt;hr&gt; element;
  2. get the QTextCursor of the document;
  3. move it to the end;
  4. add a further text block;
  5. remove the line of the new text block (which is inherited by the previous);

Here is a possible implementation of the above:

def print_results(self, results):
    self.result_text_box.setHtml(
        &#39;&lt;hr&gt;&#39;.join(results) + &#39;&lt;hr&gt;&#39;) # &lt;-- note the added &lt;hr&gt; element

    tc = self.result_text_box.textCursor()
    # move the cursor to the end of the document
    tc.movePosition(tc.End)
    # insert an arbitrary QTextBlock that will inherit the previous format
    tc.insertBlock()
    # get the block format
    fmt = tc.blockFormat()
    # remove the horizontal ruler property from the block
    fmt.clearProperty(fmt.BlockTrailingHorizontalRulerWidth)
    # set (not merge!) the block format
    tc.setBlockFormat(fmt)
    # eventually, apply the cursor so that editing actually starts at the end
    self.result_text_box.setTextCursor(tc)

<sup>Note that I just used a local variable for the QTextCursor; creating an instance attribute for it is not only pointless (you're probably going to call that function more than once, so you will also be overwriting it every time), but wrong, since cursor() is an existing property/function of all QWidgets and, as such, should never be overwritten, especially with something that is completely unrelated to the original purpose.</sup>

Be aware that the differences you've seen between macOS and Window might be related to two different aspects:

  • different Qt versions;
  • font/text rendering engines that might differ between different OSes and OS versions;

<hr>

<sup>If you want proper HTML compliance, the only way to do so in Qt is through the Qt WebEngine API; in PyQt5 most of the important classes are within the QtWebEngine, QtWebEngineWidgets and QtWebEngineCore modules, while the QtWebEngine module classes have all been moved to the QtWebEngineCore module in PyQt6.</sup>

huangapple
  • 本文由 发表于 2023年7月18日 16:26:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76710833.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定