使用PySide6/PyQt6实现Python跨平台表格数据分页打印预览处理

我曾经在前面使用WxPython开发跨平台应用程序的时候,写了一篇《WxPython跨平台开发框架之列表数据的通用打印处理》,介绍在WxPython下实现表格数据分页打印处理的过程,在Windows下和MacOS测试效果表现一致。然后在WxPython跨平台的基础上,我利用类似WxPhon的程序框架,使用PySide6/PyQt6实现了另一套跨平台的程序开发,功能上更是比WxPython的实现更加细致和完善了,本篇随笔介绍使用PySide6/PyQt6实现Python跨平台表格数据分页打印预览处理。

1、回顾WxPython的列表数据的通用打印处理

WxPython实现数据的表格预览和打印处理,主要是利用wx.PrintPreviewwx.Printerwx.Printout 等 wxPython 提供的用于打印功能的核心类来处理。

一般列表界面,如下所示。

我们打印模块的处理,需要把这些列表的记录显示在打印预览界面上,并决定是否继续打印即可。

打印预览入口,在列表界面上右键,弹出打印菜单,如下界面所示。

打印预览界面如下所示。


其打印的逻辑,主要就是调用MyPrintOut的自定义对象,然后调用PrintPreview进行打印预览窗体的显示即可。具体的逻辑还是在自定义的 MyPrintout 类里面。

2、使用PySide6/PyQt6实现Python跨平台表格数据分页打印处理

而使用PySide6/PyQt6实现Python跨平台表格数据分页打印预览处理,逻辑上有所不同,这里没有PrintOut对象来处理了。需要根据表格的TableModel对象来进行数据的分页打印。

在开始介绍实现逻辑前,我们先来看看PySide6/PyQt6实现打印预览的效果。

同样我们是在表格展示上给出通用的打印菜单入口,如上图所示,作为答应预览的统一入口。

而由于数据打印的时候,表格列字段可能有些多有些少,因此最好能够根据表格列选择那些可以打印,那些忽略。而选择后,可以进一步选择横向或者竖向等信息,因此在弹出打印预览前,我们让用户确认一下答应的信息,我订做了一个打印预览前的设置对话框,如下所示。

这样我们可以定制打印的相关信息,也方便我们对打印的格式精细化控制。

对于模型数据很多,这需要考虑到分页的处理,我们需要再实现打印预览的时候,实现分页显示的逻辑,分页打印预览的界面如下所示。

了解了PySide/PyQt的打印预览界面后,我们来分析下实现打印的逻辑处理。

打印预览和打印的时候,我们需要考虑显示器和实际打印设备之间的显示尺寸是不同的,有时候显示器设置显示为200%或其他偏大的数据,如果不注意尺寸的调整,很可能打印预览得到的是一个很小区域的显示内容。

在 PySide6 /PyQt6 中,如果你想实现 QTableView 打印功能并确保兼容不同操作系统(如 macOS 和 Windows)的尺寸变化,你可以使用 QPrinterQPrintDialog 来处理打印。要确保尺寸适应变化,你可以根据打印内容自动调整页面布局。

我编写了一个函数,用于计算缩放比例,如下函数所示。

复制代码
    def calculateScale(self, printer: QPrinter, painter: QPainter) -> float:
        """计算每毫米的逻辑单位"""

        # 获取打印机的 DPI
        ppiPrinterX = printer.resolution()
        ppiPrinterY = ppiPrinterX  # 假设 X 和 Y 方向的 DPI 相同

        # 获取屏幕的 DPI
        screen = QApplication.primaryScreen()
        ppiScreenX = screen.logicalDotsPerInchX()
        ppiScreenY = screen.logicalDotsPerInchY()

        # 计算缩放比例,
        self.logScale = logScale = float(ppiPrinterX) / float(ppiScreenX)

        # 可根据 DPI 比例调整字体大小
        print(f"缩放比例: {logScale}")

        # 获取页面大小和绘制区域大小
        pageRect = printer.pageRect(QPrinter.Unit.DevicePixel)  # 页面大小(像素)
        paintRect = painter.viewport()  # 绘制区域大小(像素)

        # 计算缩放比例
        scale = logScale * float(paintRect.width()) / float(pageRect.width())

        # 设置 QPainter 的缩放比例
        painter.scale(scale, scale)

        # 计算每毫米的逻辑单位
        logUnitsMM = float(ppiPrinterX) / (logScale * 25.4)
        print(f"每毫米的逻辑单位: {logUnitsMM}")
        return logUnitsMM

打印预览的处理,主要就是根据设置对话框,获得横向还是纵向,以及页面大小、标题等信息,然后实现QPreviewDialog里面的paintRequest事件即可,如下预览逻辑处理代码。

复制代码
    def print_preview(self, setting: PrintSetting) -> None:
        """打印预览"""

        # print(setting.__dict__)
        printer = QPrinter()  # QPrinter.PrinterMode.HighResolution

        # 打印的处理
        printer.setPageSize(setting.page_size)
        printer.setPageOrientation(setting.page_orientation)
        self.print_cols = setting.print_cols  # 打印指定列的索引列表
        self.print_title = setting.print_title  # 打印标题
        self.settings = setting  # 保存打印设置

        preview_dialog = QPrintPreviewDialog(printer)
        preview_dialog.paintRequested.connect(self.print_preview_paint)
        preview_dialog.exec()

打印预览的处理逻辑,主要就是需要根据缩放的尺寸获得对应的打印区域大小,并根据页面的大小和实现打印的内容显示,计算好尺寸,也就是一般按每页放置多少行,或者每行的高度来计算,如果需要分页,则标识一下即可。

复制代码
    def print_preview_paint(self, printer: QPrinter) -> None:
        """打印预览绘制"""
        self.painter = painter = QPainter(printer)

        # 计算每毫米的逻辑单位
        self.logUnitsMM = self.calculateScale(printer, painter)

        # 获取页面大小
        # unit 参数的可选值有:
        # QPrinter.Unit.Point:点(1 点 = 1/72 英寸)
        # QPrinter.Unit.Millimeter:毫米(常用)
        # QPrinter.Unit.Inch:英寸
        # QPrinter.Unit.Pixel:像素

        # 获取页面的可打印区域
        page_rect = printer.pageRect(QPrinter.Unit.Millimeter)
        self.page_height = page_height = page_rect.height() * self.logUnitsMM
        self.page_width = page_width = page_rect.width() * self.logUnitsMM

如果内容需要分页才能展示完,那么你要使用printer.newPage()来告诉打印机进行分页。

复制代码
        # 分页打印
        print(f"总行数: {total_rows}, 总页数: {total_pages}")
        for page in range(total_pages):
            if page > 0:  # 非第一页,需要换页
                printer.newPage()
            self.print_page(page, rows_per_page, self.print_cols)

        self.painter.end()

打印的时候,我们打印列头和每列内容,都是根据实际的列宽进行一定比例的处理,让它能够兼容打印最佳效果。

打印表头的时候,如下代码。

复制代码
        index = 0  # 用来计算递增的列数
        for col in range(self.columnCount()):
            if print_cols and col not in print_cols:
                continue
            header_text = self.GetColLabelValue(col)
            self.painter.drawText(
                int(x_offset + sum(col_widths[:index])),  # 累计不同的列宽
                y_offset,
                header_text,
            )
            index += 1

打印表格每列的内容,处理规则也是类似,如下代码所示

复制代码
       # 绘制表格内容
        for row in range(
            page * rows_per_page, min((page + 1) * rows_per_page, self.total_rows)
        ):
            y_offset += self.row_height
            index = 0  # 用来计算递增的列数
            for col in range(self.columnCount()):
                if print_cols and col not in print_cols:
                    continue
                # 获取单元格数据
                text = self.GetValue(row, col)
                # 绘制单元格内容
                self.painter.drawText(
                    int(x_offset + sum(col_widths[:index])),  # 累计不同的列宽
                    y_offset,
                    text,
                )
                index += 1

其他如标题,横线、页码等信息,根据显示规则绘制即可。

相关推荐
伍华聪7 小时前
使用PySide6/PyQt6实现Python跨平台通用列表页面的基类设计
python开发
伍华聪14 天前
使用PySide6/PyQt6实现Python跨平台GUI框架的开发
python开发
伍华聪2 个月前
一问一答学习PyQT6,对比WxPython和PyQt6的差异
python开发
伍华聪2 个月前
WxPython跨平台开发框架之使用PyInstaller 进行打包处理
python开发
伍华聪2 个月前
WxPython跨平台开发框架之模块字段权限的管理
python开发
伍华聪2 个月前
WxPython跨平台开发框架之动态菜单的管理和功能权限的控制
python开发
伍华聪2 个月前
WxPython跨平台开发框架之前后端结合实现附件信息的上传及管理
python开发
伍华聪2 个月前
WxPython跨平台开发框架之图标选择界面
python开发
伍华聪2 个月前
WxPython跨平台开发框架之列表数据的通用打印处理
python开发