在 PyQt6 中打印嵌套 HTML 表格

问题描述 投票:0回答:2

尝试在 PyQt6 应用程序中打印 QTableWidget 的内容时遇到问题。

它确实有效,但有一个小问题:我在主表中嵌入了表格,我希望这些表格完全填充父单元格(其宽度的 100%),但子表不会扩展为预计。

这是我的代码:

import sys
from PyQt6 import QtWidgets, QtPrintSupport
from PyQt6.QtGui import QTextDocument


class MyWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.table_widget = QtWidgets.QTableWidget()
        self.button = QtWidgets.QPushButton('Print TableWidget')
        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(self.table_widget)
        self.layout.addWidget(self.button)
        self.button.clicked.connect(self.print_table)

    def print_table(self):
        html_table = '''
            <table cellpadding="0">
                <tr><th>header1</th><th>header2</th><th>header3</th></tr>
                <tr>
                    <td>data1</td>
                    <td>data2</td>
                    <td><table>
                        <tr>
                            <th>header1</th><th>header2</th><th>header3</th>
                        </tr>
                        <tr>
                            <td>data3</td><td>data3</td><td>data3</td>
                        </tr>
                    </table></td>
                </tr>
                <tr>
                    <td>data1</td>
                    <td>data2</td>
                    <td><table>
                        <tr>
                            <th>hr1</th><th>hr2</th><th>hr3</th><th>hr4</th>
                        </tr>
                        <tr>
                            <td>d3</td><td>d3</td><td>d3</td><td>d3</td>
                        </tr>
                        <tr>
                            <td>d3</td><td>d3</td><td>d3</td><td>d3</td>
                        </tr>
                    </table></td>
                </tr>
            </table>
        '''

        style_sheet = '''
            table {
                border-collapse: collapse; 
                width: 100%;
            }
            th {
                background-color: lightblue; 
                border: 1px solid gray; 
                height: 1em;
            }
            td {
                border: 1px solid gray; 
                padding: 0; 
                vertical-align: top;
            }
        '''
        text_doc = QTextDocument()
        text_doc.setDefaultStyleSheet(style_sheet)
        text_doc.setHtml(html_table)
        prev_dialog = QtPrintSupport.QPrintPreviewDialog()
        prev_dialog.paintRequested.connect(text_doc.print)
        prev_dialog.exec()

if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    widget = MyWidget()
    widget.resize(640,480)
    widget.show()
    sys.exit(app.exec())

这就是我得到的:

Screenshot of the result

但这就是我想要的:

Expected result

我将不胜感激有关此问题的任何建议,因为我不知道如何解决它。

python html printing pyqt6 qtextdocument
2个回答
1
投票

Qt 中的富文本仅提供 HTML 和 CSS 的有限子集。更具体地说,它只提供 QTextDocument 允许的功能,因此 HTML 解析完全基于 QTextDocument 功能。

在常见的 Web 浏览器中使用标准 HTML 和 CSS 可能更容易实现的复杂布局,在 Qt 中可能会很困难(如果不是不可能),并且必须仔细检查文档。

具体来说,

width
属性列在CSS属性列表中。
尽管如此,许多标签都支持它作为 HTML 属性,包括
table
及其单元格。

注意:可能会出现“按预期”工作的标签/CSS行为,即使没有记录,但你不应该依赖它们,除非发现它在多个和以前的Qt版本中是一致的,并且可能仔细检查源代码的一致性,包括解析器和文档布局结构。

只需从 CSS 中删除该属性,并将其用作属性,例如:

<table width="100%"> ... <tr><td width="75%"> <table width="100%"> ... </table> </td></tr> </table>
上述将使父表格占据整个视口(或页面)宽度,包含嵌套表格的列占该宽度的 75%,并且该表格水平地完全占据单元格。

更新

正如

ekhumoro 的评论所正确指出的那样,有一个错误实际上阻止了设置 widthany

 数值,其结果是,无论如何,表格将始终占据几乎整个宽度细胞。

如果您希望嵌套表格仅占据“父单元格”的一定百分比,解决方法是为其每一列设置百分比。

考虑第二个嵌套表的以下标头语法:

<tr> <th width=20%>hr1</th> <th width=20%>hr2</th> <th width=20%>hr3</th> <th width=20%>hr4</th> </tr>
与上面的第一个代码片段一起,将生成一个正确占据单元格宽度 80% 的表格:

enter image description here

您可能会注意到表格左侧和顶部有一个小边距,这可能是由

border-collapse

 属性引起的,也是 Qt 富文本引擎局限性的另一个演示,该引擎并非旨在实现像素完美渲染并最初以更“灵活”(非设计导向)的超文本显示意图实现。

一个可能的解决方法可能是完全避免边框折叠,并使用

table 属性 cellspacing=0

 代替;它可能并不总是合适的,但它适用于您的具体情况,并且最终可以通过基于类选择器的更仔细编写的 CSS 来修复。
例如,通过为表格设置一个类并显式声明单元格的规则(例如:
table.nested td { ... }
)。

如前所述,如果您想要对布局/外观进行更多控制以及对现代 HTML 和 CSS 标准更兼容的支持,唯一的选择是使用 QtWebEngine 相关类。但请注意,该模块本身的大小就超过 150MB,因此请仔细考虑该要求。


0
投票
QTextDocument 可以提供完整的解决方案,但需要非常仔细的处理才能解决 Qt 目前支持的

HTML/CSS 的有限子集 的一些问题。主要问题是 border-collapse

 CSS 属性的错误实现,这会在表格边距周围留下难看的间隙。因此,最好避免使用此属性并改为实现边框的自定义处理。这是我通过使用这种方法实现的目标:

screenshot

如您所见,没有间隙或双重边框。

要实现此目的,需要设置一些无法通过 CSS 获得的 HTML 属性。所有表格必须有

cellspacing="0"

,嵌入表格必须有 
width="100%"
。必须通过在外表的 
width
 标签上设置 
tr
 属性来调整主列。但是,您应该注意,有一个小错误可能会导致某些边框被剪切,具体取决于所使用的确切百分比值。 (我发现将值总和为 
99%
 会有所帮助,但可能需要进行一些实验才能获得完美的结果)。

需要使用 CSS 显式设置边框。这必须使用相邻兄弟选择器 (

+

) 来完成,因为 Qt 不支持像 
:first-child
 这样的伪类。最后,外表必须嵌入到 
div
 中以允许使用子选择器 (
>
)。

这是一个基于原始示例的完整演示:

import sys from PyQt6 import QtWidgets, QtPrintSupport from PyQt6.QtGui import QTextDocument class MyWidget(QtWidgets.QWidget): def __init__(self): super().__init__() self.table_widget = QtWidgets.QTableWidget() self.button = QtWidgets.QPushButton('Print TableWidget') self.layout = QtWidgets.QVBoxLayout(self) self.layout.addWidget(self.table_widget) self.layout.addWidget(self.button) self.button.clicked.connect(self.print_table) def print_table(self): html_table = ''' <div> <table cellspacing="0"> <tr> <th width="18%">header1</th> <th width="18%">header2</th> <th width="63%">header3</th> </tr> <tr> <td>data1</td> <td>data2</td> <td> <table width="100%" cellspacing="0"> <tr> <th>header1</th> <th>header2</th> <th>header3</th> </tr> <tr> <td>data3</td> <td>data3</td> <td>data3</td> </tr> </table> </td> </tr> <tr> <td>data1</td> <td>data2</td> <td> <table width="100%" cellspacing="0"> <tr> <th>hr1</th> <th>hr2</th> <th>hr3</th> <th>hr4</th> </tr> <tr> <td>d3</td> <td>d3</td> <td>d3</td> <td>d3</td> </tr> <tr> <td>d3</td> <td>d3</td> <td>d3</td> <td>d3</td> </tr> </table> </td> </tr> </table> </div> ''' style_sheet = ''' th { background-color: lightblue; height: 1em; } td { padding: 0; vertical-align: top; text-align: center; } div > table th { border: 1px solid gray; } div > table td { border: 1px solid gray; border-top: 0px; } div > table td + td { border-left: 0px; } div > table th + th { border-left: 0px; } table table th + th { border-left: 1px solid gray; } table table td { border-top: 1px solid gray; } table table td + td { border-left: 1px solid gray; } ''' text_doc = QTextDocument() text_doc.setDefaultStyleSheet(style_sheet) text_doc.setHtml(html_table) prev_dialog = QtPrintSupport.QPrintPreviewDialog() prev_dialog.paintRequested.connect(text_doc.print) prev_dialog.exec() if __name__ == '__main__': app = QtWidgets.QApplication([]) widget = MyWidget() widget.resize(640, 480) widget.show() sys.exit(app.exec())
    
© www.soinside.com 2019 - 2024. All rights reserved.