尝试在 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())
这就是我得到的:
但这就是我想要的:
我将不胜感激有关此问题的任何建议,因为我不知道如何解决它。
Qt 中的富文本仅提供 HTML 和 CSS 的有限子集。更具体地说,它只提供 QTextDocument 允许的功能,因此 HTML 解析完全基于 QTextDocument 功能。
在常见的 Web 浏览器中使用标准 HTML 和 CSS 可能更容易实现的复杂布局,在 Qt 中可能会很困难(如果不是不可能),并且必须仔细检查文档。
具体来说,
width
属性未列在CSS属性列表中。table
及其单元格。
注意:可能会出现“按预期”工作的标签/CSS行为,即使没有记录,但你不应该依赖它们,除非发现它在多个和以前的Qt版本中是一致的,并且可能仔细检查源代码的一致性,包括解析器和文档布局结构。
只需从 CSS 中删除该属性,并将其用作属性,例如:
<table width="100%">
...
<tr><td width="75%">
<table width="100%">
...
</table>
</td></tr>
</table>
上述将使父表格占据整个视口(或页面)宽度,包含嵌套表格的列占该宽度的 75%,并且该表格水平地完全占据单元格。
更新
正如ekhumoro 的评论所正确指出的那样,有一个错误实际上阻止了设置 width
的 any
数值,其结果是,无论如何,表格将始终占据几乎整个宽度细胞。如果您希望嵌套表格仅占据“父单元格”的一定百分比,解决方法是为其每一列设置百分比。
考虑第二个嵌套表的以下标头语法:
<tr>
<th width=20%>hr1</th>
<th width=20%>hr2</th>
<th width=20%>hr3</th>
<th width=20%>hr4</th>
</tr>
与上面的第一个代码片段一起,将生成一个正确占据单元格宽度 80% 的表格:您可能会注意到表格左侧和顶部有一个小边距,这可能是由
border-collapse
属性引起的,也是 Qt 富文本引擎局限性的另一个演示,该引擎并非旨在实现像素完美渲染并最初以更“灵活”(非设计导向)的超文本显示意图实现。一个可能的解决方法可能是完全避免边框折叠,并使用
table 属性 cellspacing=0
代替;它可能并不总是合适的,但它适用于您的具体情况,并且最终可以通过基于类选择器的更仔细编写的 CSS 来修复。例如,通过为表格设置一个类并显式声明单元格的规则(例如:
table.nested td { ... }
)。如前所述,如果您想要对布局/外观进行更多控制以及对现代 HTML 和 CSS 标准更兼容的支持,唯一的选择是使用 QtWebEngine 相关类。但请注意,该模块本身的大小就超过 150MB,因此请仔细考虑该要求。
HTML/CSS 的有限子集 的一些问题。主要问题是 border-collapse
CSS 属性的错误实现,这会在表格边距周围留下难看的间隙。因此,最好避免使用此属性并改为实现边框的自定义处理。这是我通过使用这种方法实现的目标:
如您所见,没有间隙或双重边框。
要实现此目的,需要设置一些无法通过 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())