最强大功能打印 代码有详实解疑 参数保姆级的注解 适合初学 学习类的使用
cpp
.h
#ifndef TABLEPRINTER_H
#define TABLEPRINTER_H
#include <QObject>
#include <QTableView>
#include <QPrinter>
#include <QPainter>
#include <QAbstractItemModel>
#include <QFont>
#include <QVector>
#include <QRect>
class TablePrinter
{
public:
// 在文件开头添加详细的注释说明:
/**
* TablePrinter 终极版
* 版本:1.0
* 最后修改:2024年
* 作者:hw完成
*
* 功能特点:
* 1. 支持指定列范围打印(0-10列等)
* 2. 支持自动缩放适应页面
* 3. 支持文本自动换行
* 4. 支持自定义字体大小和行高
* 5. 支持打印预览
* 6. 支持页眉页脚
* 7. 支持网格线和交替行背景色
*
* 使用示例:
* TablePrinter::PrintOptions options;
* options.title = "报表";
* options.contentFontSize = 10;
* options.rowHeightFactor = 12.0;
* TablePrinter::print(tableView, options, parent);
*/
struct PrintOptions {
// 构造函数,设置所有默认值
PrintOptions() :
// 打印范围控制
startColumn(0), // [范围: 0~列数-1] 开始列索引,0表示第一列
endColumn(-1), // [范围: -1~列数-1] 结束列索引,-1表示到最后一列
startRow(0), // [范围: 0~行数-1] 开始行索引,0表示第一行
endRow(-1), // [范围: -1~行数-1] 结束行索引,-1表示到最后一行
// 显示控制
showGrid(true), // [true/false] 是否显示网格线
showHeader(true), // [true/false] 是否显示表头
showFooter(true), // [true/false] 是否显示页脚
// 文本换行控制
textWrap(false), // [true/false] 是否启用文本自动换行
maxTextLines(3), // [范围: 1~10] 最大换行行数,防止文本过长
// 字体大小控制(单位:点/磅)
titleFontSize(25), // [建议: 20-30] 标题字体大小
headerFontSize(11), // [建议: 10-14] 表头字体大小
contentFontSize(10), // [建议: 8-12] 内容字体大小
footerFontSize(10), // [建议: 8-12] 页脚字体大小
// 高度因子控制(相对于字体高度的倍数)
rowHeightFactor(12.0), // [建议: 10-20] 行高因子,行高=内容字体高度×此值
headerHeightFactor(2.0), // [建议: 1.5-2.5] 表头高度因子
titleHeightFactor(5.0), // [建议: 4-6] 标题高度因子
footerHeightFactor(2.0), // [建议: 1.5-2.5] 页脚高度因子
// 布局控制
marginMM(15), // [建议: 10-20] 页面边距(毫米)
cellPadding(8), // [建议: 5-15] 单元格内边距(像素),解决文字太靠近边框
scaleToPage(true), // [true/false] 是否自动缩放以适应页面
stretchColumns(true), // [true/false] 是否拉伸列宽以适应页面宽度
// 标题文本
title("") // 打印标题文本
{}
// ==================== 参数详细说明 ====================
// 1. 打印范围参数
int startColumn; ///< 开始列索引:0表示第一列,通常设为0
int endColumn; ///< 结束列索引:-1表示自动到最后一列
int startRow; ///< 开始行索引:0表示第一行
int endRow; ///< 结束行索引:-1表示自动到最后一行
// 2. 显示控制参数
bool showGrid; ///< 网格线:true显示细网格线,false不显示(更简洁)
bool showHeader; ///< 表头:true显示列标题,false不显示
bool showFooter; ///< 页脚:true显示页码和时间,false不显示
// 3. 文本换行参数
bool textWrap; ///< 文本换行:true启用自动换行,false不换行(使用省略号)
int maxTextLines; ///< 最大行数:控制换行时最多显示几行,超出显示...
// 4. 字体大小参数(点/磅为单位)
int titleFontSize; ///< 标题字体:通常最大,用于突出显示
int headerFontSize; ///< 表头字体:通常比内容稍大且加粗
int contentFontSize; ///< 内容字体:主要数据的字体大小
int footerFontSize; ///< 页脚字体:通常与内容相同或稍小
// 5. 高度因子参数(关键参数,影响行高)
double rowHeightFactor; ///< 行高因子:最重要的参数!控制行的高度
///< 值越大行越高,建议10-20之间
///< 换行打印需要更大的值(15-25)
double headerHeightFactor;///< 表头高度因子:控制表头行的高度
double titleHeightFactor; ///< 标题高度因子:控制标题区域的高度
double footerHeightFactor;///< 页脚高度因子:控制页脚区域的高度
// 6. 布局参数
int marginMM; ///< 页面边距:控制纸张四周的空白,毫米为单位
int cellPadding; ///< 单元格内边距:控制文字与边框的距离
bool scaleToPage; ///< 自动缩放:true自动缩放表格以适应页面宽度
bool stretchColumns; ///< 拉伸列宽:true时如果表格宽度小于页面,会拉伸列宽填满
// 7. 其他
QString title; ///< 标题文本:显示在每页顶部的标题
};
/**
* @brief 打印表格
* @param tableView 要打印的表格视图
* @param options 打印选项配置
* @param parent 父窗口(用于显示对话框)
* @return 成功返回true,失败返回false
*/
static bool print(QTableView* tableView,
const PrintOptions& options = PrintOptions(),
QWidget* parent = nullptr);
/**
* @brief 打印预览
* @param tableView 要打印的表格视图
* @param options 打印选项配置
* @param parent 父窗口
* @return 成功返回true,失败返回false
*/
static bool printPreview(QTableView* tableView,
const PrintOptions& options = PrintOptions(),
QWidget* parent = nullptr);
private:
// 私有辅助函数
//void drawWrappedText(QPainter& painter, const QRectF& rect,
//const QString& text, const QFont& font);
/* @brief 绘制换行文本
*/
static void drawWrappedText(QPainter& painter, const QRectF& rect,
const QString& text, const QFont& font);
/**
* @brief 绘制换行文本(简单版本)
*/
static void drawWrappedTextSimple(QPainter& painter, const QRectF& rect,
const QString& text, const QFont& font);
/**
* @brief 实际执行打印操作
*/
static bool printToPrinter(QTableView* tableView, QPrinter& printer,
const PrintOptions& options);
/**
* @brief 毫米转换为像素
*/
static double mmToPx(double mm, double dpi);
/**
* @brief 获取页面内容区域(减去边距)
*/
static QRectF getPageContentRect(QPrinter& printer, const PrintOptions& options);
/**
* @brief 计算列宽(考虑拉伸选项)
*/
static QVector<double> calculateColumnWidths(QTableView* tableView,
const PrintOptions& options,
double availableWidth);
/**
* @brief 绘制表格内容(包括表头和数据行)
* @param cellPadding 单元格内边距,解决文字太靠近边框的问题
*/
static void drawTable(QPainter& painter,
QAbstractItemModel* model,
const PrintOptions& options,
const QVector<double>& colWidths,
double startX, double startY,
double rowHeight,
int startRow, int endRow);
/**
* @brief 绘制页眉(标题)
*/
static void drawPageHeader(QPainter& painter,
const QRectF& contentRect,
const PrintOptions& options,
double scaleFactor);
/**
* @brief 绘制页脚(页码、时间、标题)
*/
static void drawPageFooter(QPainter& painter,
const QRectF& contentRect,
const PrintOptions& options,
int pageNum, int totalPages,
double scaleFactor);
/**
* @brief 文本换行处理
* @return 换行后的文本
*/
static QString wrapText(const QString& text,
const QFontMetricsF& fm,
double maxWidth,
int maxLines);
};
#endif // TABLEPRINTER_H
.CPP
#include "tableprinter.h"
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QHeaderView>
#include <QDebug>
#include <QDateTime>
#include <QtAlgorithms>
#include <cmath>
#include <numeric>
/**
* @brief 将毫米转换为像素
* @param mm 毫米值
* @param dpi 打印机DPI
* @return 像素值
*/
double TablePrinter::mmToPx(double mm, double dpi)
{
// 转换公式:像素 = (毫米 / 25.4) × DPI
// 25.4是一英寸的毫米数
return (mm / 25.4) * dpi;
}
/**
* @brief 打印表格
*/
bool TablePrinter::print(QTableView* tableView, const PrintOptions& options, QWidget* parent)
{
// 参数检查
if (!tableView || !tableView->model()) {
qWarning() << "TablePrinter: 表格视图或数据模型为空";
return false;
}
// 创建打印机对象
QPrinter printer(QPrinter::HighResolution);
printer.setPageSize(QPageSize(QPageSize::A4));
// 显示打印对话框
QPrintDialog dialog(&printer, parent);
dialog.setWindowTitle("打印表格");
// 用户取消打印
if (dialog.exec() != QDialog::Accepted) {
return false;
}
// 执行打印
return printToPrinter(tableView, printer, options);
}
/**
* @brief 打印预览
*/
bool TablePrinter::printPreview(QTableView* tableView, const PrintOptions& options, QWidget* parent)
{
// 参数检查
if (!tableView || !tableView->model()) {
qWarning() << "TablePrinter: 表格视图或数据模型为空";
return false;
}
// 创建打印机对象
QPrinter printer(QPrinter::HighResolution);
printer.setPageSize(QPageSize(QPageSize::A4));
// 创建预览对话框
QPrintPreviewDialog preview(&printer, parent);
preview.setWindowTitle("打印预览");
preview.setMinimumSize(1000, 700);
// 连接绘制信号
QObject::connect(&preview, &QPrintPreviewDialog::paintRequested,
[=](QPrinter* previewPrinter) {
printToPrinter(tableView, *previewPrinter, options);
});
// 显示预览
preview.exec();
return true;
}
/**
* @brief 获取页面内容区域(减去边距后的可用区域)
*/
QRectF TablePrinter::getPageContentRect(QPrinter& printer, const PrintOptions& options)
{
double dpi = printer.resolution();
double marginPx = mmToPx(options.marginMM, dpi);
// 获取纸张矩形(像素单位)
QRectF pageRect = printer.paperRect(QPrinter::DevicePixel);
// 减去边距得到内容区域
return QRectF(pageRect.left() + marginPx,
pageRect.top() + marginPx,
pageRect.width() - 2 * marginPx,
pageRect.height() - 2 * marginPx);
}
/**
* @brief 实际执行打印操作
*/
bool TablePrinter::printToPrinter(QTableView* tableView, QPrinter& printer, const PrintOptions& options)
{
QPainter painter;
if (!painter.begin(&printer)) {
qWarning() << "TablePrinter: 无法开始绘制";
return false;
}
QAbstractItemModel* model = tableView->model();
double dpi = printer.resolution();
// 获取内容区域
QRectF contentRect = getPageContentRect(printer, options);
// 确定打印范围
int startCol = qMax(0, options.startColumn);
int endCol = (options.endColumn < 0) ? model->columnCount() - 1 :
qMin(options.endColumn, model->columnCount() - 1);
int startRow = qMax(0, options.startRow);
int endRow = (options.endRow < 0) ? model->rowCount() - 1 :
qMin(options.endRow, model->rowCount() - 1);
// 检查打印范围有效性
if (startCol > endCol || startRow > endRow) {
painter.end();
return false;
}
// ==================== 计算各种字体和高度 ====================
// 内容字体和高度
QFont contentFont;
contentFont.setPointSize(options.contentFontSize);
QFontMetricsF contentFontMetrics(contentFont);
double contentFontHeight = contentFontMetrics.height();
double rowHeight = contentFontHeight * options.rowHeightFactor;
// 表头字体和高度
QFont headerFont;
headerFont.setPointSize(options.headerFontSize);
headerFont.setBold(true);
QFontMetricsF headerFontMetrics(headerFont);
double headerHeight = headerFontMetrics.height() * options.headerHeightFactor;
// 标题字体和高度
QFont titleFont;
titleFont.setPointSize(options.titleFontSize);
titleFont.setBold(true);
QFontMetricsF titleFontMetrics(titleFont);
double titleHeight = titleFontMetrics.height() * options.titleHeightFactor;
// 页脚字体和高度
QFont footerFont;
footerFont.setPointSize(options.footerFontSize);
QFontMetricsF footerFontMetrics(footerFont);
double footerHeight = footerFontMetrics.height() * options.footerHeightFactor;
// ==================== 计算列宽和缩放因子 ====================
// 计算列宽
QVector<double> colWidths = calculateColumnWidths(tableView, options, contentRect.width());
double tableWidth = std::accumulate(colWidths.begin(), colWidths.end(), 0.0);
// 计算缩放因子
double scaleFactor = 1.0;
if (options.scaleToPage && tableWidth > 0) {
scaleFactor = contentRect.width() / tableWidth;
// 限制缩放范围在0.5到1.5之间
scaleFactor = qMax(0.5, qMin(1.5, scaleFactor));
}
// ==================== 计算分页 ====================
// 计算可用高度(减去标题和页脚)
double pageLogicalHeight = contentRect.height() / scaleFactor;
double reservedHeight = 0;
if (!options.title.isEmpty()) {
reservedHeight += titleHeight;
}
if (options.showFooter) {
reservedHeight += footerHeight;
}
double availableHeightForTable = pageLogicalHeight - reservedHeight;
// 计算每页行数
double rowsPerPageDouble = 0;
double currentHeight = 0;
if (options.showHeader) {
currentHeight += headerHeight;
}
for (int row = startRow; row <= endRow; row++) {
currentHeight += rowHeight;
if (currentHeight <= availableHeightForTable) {
rowsPerPageDouble++;
} else {
break;
}
}
if (rowsPerPageDouble < 1) rowsPerPageDouble = 1;
int rowsPerPage = static_cast<int>(rowsPerPageDouble);
// 计算总页数
int totalRows = endRow - startRow + 1;
int totalPages = static_cast<int>(ceil(static_cast<double>(totalRows) / rowsPerPage));
// 调试信息
qDebug() << "TablePrinter调试信息:";
qDebug() << " 页面逻辑高度:" << pageLogicalHeight;
qDebug() << " 表格宽度:" << tableWidth << "缩放因子:" << scaleFactor;
qDebug() << " 每页行数:" << rowsPerPage << "总页数:" << totalPages;
// ==================== 开始分页打印 ====================
// 应用缩放和平移
painter.save();
painter.translate(contentRect.left(), contentRect.top());
painter.scale(scaleFactor, scaleFactor);
int currentRow = startRow;
int pageNum = 1;
while (currentRow <= endRow) {
// 如果不是第一页,开始新页面
if (pageNum > 1) {
printer.newPage();
painter.restore();
painter.save();
painter.translate(contentRect.left(), contentRect.top());
painter.scale(scaleFactor, scaleFactor);
}
// 1. 绘制页眉(标题)
if (!options.title.isEmpty()) {
drawPageHeader(painter, contentRect, options, scaleFactor);
}
// 2. 绘制表格
double tableStartY = options.title.isEmpty() ? 0 : titleHeight;
// 计算当前页的结束行
int pageEndRow = currentRow + rowsPerPage - 1;
if (pageEndRow > endRow) {
pageEndRow = endRow;
}
// 绘制表格内容(使用修正后的内边距)
drawTable(painter, model, options, colWidths,
0, tableStartY,
rowHeight,
currentRow, pageEndRow);
// 3. 绘制页脚
if (options.showFooter) {
drawPageFooter(painter, contentRect, options, pageNum, totalPages, scaleFactor);
}
// 移动到下一页
currentRow = pageEndRow + 1;
pageNum++;
}
painter.restore();
painter.end();
return true;
}
/**
* @brief 计算列宽
* @param availableWidth 可用宽度
* @return 列宽数组
*/
QVector<double> TablePrinter::calculateColumnWidths(QTableView* tableView,
const PrintOptions& options,
double availableWidth)
{
QVector<double> colWidths;
QAbstractItemModel* model = tableView->model();
int startCol = qMax(0, options.startColumn);
int endCol = (options.endColumn < 0) ? model->columnCount() - 1 :
qMin(options.endColumn, model->columnCount() - 1);
// 计算原始列宽
double totalWidth = 0;
for (int col = startCol; col <= endCol; ++col) {
double width = tableView->columnWidth(col);
// 如果列宽太小,根据内容估算
if (width <= 10) {
QFontMetricsF fm(QFont("Arial", options.contentFontSize));
// 检查表头宽度
QString header = model->headerData(col, Qt::Horizontal).toString();
double headerWidth = fm.horizontalAdvance(header) + options.cellPadding * 2;
// 检查前几行数据宽度
double maxDataWidth = headerWidth;
int checkRows = qMin(20, model->rowCount());
for (int row = 0; row < checkRows; ++row) {
QString data = model->data(model->index(row, col)).toString();
double dataWidth = fm.horizontalAdvance(data) + options.cellPadding * 2;
maxDataWidth = qMax(maxDataWidth, dataWidth);
}
width = maxDataWidth;
}
colWidths.append(width);
totalWidth += width;
}
// 如果需要拉伸列宽以适应页面
if (options.stretchColumns && totalWidth > 0 && totalWidth < availableWidth) {
double stretchFactor = availableWidth / totalWidth;
for (int i = 0; i < colWidths.size(); ++i) {
colWidths[i] *= stretchFactor;
}
totalWidth = availableWidth;
}
return colWidths;
}
/**
* @brief 绘制页眉(标题)
*/
void TablePrinter::drawPageHeader(QPainter& painter,
const QRectF& contentRect,
const PrintOptions& options,
double scaleFactor)
{
painter.save();
// 设置标题样式
QFont titleFont;
titleFont.setPointSize(options.titleFontSize);
titleFont.setBold(true);
painter.setFont(titleFont);
painter.setPen(Qt::darkBlue);
// 绘制标题背景(浅蓝色)
painter.setBrush(QColor(220, 230, 240));
painter.setPen(Qt::NoPen);
double titleHeight = QFontMetricsF(titleFont).height() * options.titleHeightFactor;
painter.drawRect(QRectF(0, 0, contentRect.width() / scaleFactor, titleHeight));
// 绘制标题文本
painter.setPen(Qt::darkBlue);
QRectF titleRect(0, 0, contentRect.width() / scaleFactor, titleHeight);
painter.drawText(titleRect, Qt::AlignCenter | Qt::AlignVCenter, options.title);
painter.restore();
}
/**
* @brief 绘制页脚
*/
void TablePrinter::drawPageFooter(QPainter& painter,
const QRectF& contentRect,
const PrintOptions& options,
int pageNum, int totalPages,
double scaleFactor)
{
painter.save();
// 设置页脚字体
QFont footerFont;
footerFont.setPointSize(options.footerFontSize);
painter.setFont(footerFont);
painter.setPen(Qt::darkGray);
// 计算页脚位置(固定在页面底部)
double pageLogicalHeight = contentRect.height() / scaleFactor;
double footerFontHeight = QFontMetricsF(footerFont).height();
double footerHeight = footerFontHeight * options.footerHeightFactor;
double footerY = pageLogicalHeight - footerHeight;
// 给页脚足够的绘制区域(解决页脚文字显示不全的问题)
double footerRectHeight = 40; // 固定40像素高度,确保文字完整显示
// 绘制页脚分隔线(可选)
painter.setPen(QPen(QColor(180, 180, 180), 0.5));
painter.drawLine(0, footerY - 5, contentRect.width() / scaleFactor, footerY - 5);
// 恢复文本颜色
painter.setPen(Qt::darkGray);
// 绘制打印时间(左侧)
QString timeText = QDateTime::currentDateTime().toString("打印: yyyy-MM-dd hh:mm");
painter.drawText(QRectF(10, footerY,
contentRect.width() / scaleFactor ,
footerRectHeight* 2),
Qt::AlignLeft | Qt::AlignVCenter, timeText);
// 绘制页码(居中)- 给足够大的绘制区域
QString pageText = QString("第 %1 页 / 共 %2 页").arg(pageNum).arg(totalPages);
painter.drawText(QRectF(0, footerY,
contentRect.width() / scaleFactor,
footerRectHeight* 2),
Qt::AlignCenter | Qt::AlignVCenter, pageText);
// 绘制标题(右侧)- 如果标题太长则截断
if (!options.title.isEmpty()) {
QString displayTitle = options.title;
QFontMetricsF fm(footerFont);
double maxWidth = contentRect.width() / scaleFactor ;
if (fm.horizontalAdvance(displayTitle) > maxWidth) {
displayTitle = fm.elidedText(displayTitle, Qt::ElideRight, maxWidth);
}
painter.drawText(QRectF(0, footerY,
contentRect.width() / scaleFactor - 10,
footerRectHeight* 2),
Qt::AlignRight | Qt::AlignVCenter, displayTitle);
}
painter.restore();
}
/**
* @brief 文本换行处理
* @param text 原始文本
* @param fm 字体度量
* @param maxWidth 最大宽度
* @param maxLines 最大行数
* @return 换行后的文本
*/
QString TablePrinter::wrapText(const QString& text,
const QFontMetricsF& fm,
double maxWidth,
int maxLines)
{
if (text.isEmpty() || maxWidth <= 0) {
return text;
}
// 如果文本本来就不需要换行,直接返回
if (fm.horizontalAdvance(text) <= maxWidth) {
return text;
}
QStringList lines;
QString currentLine;
QStringList words = text.split(' ', Qt::SkipEmptyParts);
for (const QString& word : words) {
QString testLine = currentLine.isEmpty() ? word : currentLine + " " + word;
if (fm.horizontalAdvance(testLine) <= maxWidth) {
currentLine = testLine;
} else {
if (!currentLine.isEmpty()) {
lines.append(currentLine);
if (lines.size() >= maxLines) {
// 已达到最大行数
if (lines.size() > maxLines) {
lines = lines.mid(0, maxLines);
}
// 在最后一行添加省略号
QString lastLine = lines.last();
QString ellipsisLine = lastLine;
// 检查添加省略号后的宽度
QString testEllipsis = lastLine + "...";
if (fm.horizontalAdvance(testEllipsis) > maxWidth) {
// 需要截断
ellipsisLine = fm.elidedText(lastLine, Qt::ElideRight,
maxWidth - fm.horizontalAdvance("..."));
}
lines.last() = ellipsisLine + "...";
return lines.join("\n");
}
}
currentLine = word;
}
}
// 添加最后一行
if (!currentLine.isEmpty()) {
lines.append(currentLine);
}
// 如果行数超过最大行数
if (lines.size() > maxLines) {
lines = lines.mid(0, maxLines);
QString lastLine = lines.last();
QString ellipsisLine = lastLine;
QString testEllipsis = lastLine + "...";
if (fm.horizontalAdvance(testEllipsis) > maxWidth) {
ellipsisLine = fm.elidedText(lastLine, Qt::ElideRight,
maxWidth - fm.horizontalAdvance("..."));
}
lines.last() = ellipsisLine + "...";
}
return lines.join("\n");
}
void TablePrinter::drawTable(QPainter& painter,
QAbstractItemModel* model,
const PrintOptions& options,
const QVector<double>& colWidths,
double startX, double startY,
double rowHeight,
int startRow, int endRow)
{
int startCol = qMax(0, options.startColumn);
int endCol = (options.endColumn < 0) ? model->columnCount() - 1 :
qMin(options.endColumn, model->columnCount() - 1);
double currentX = startX;
double currentY = startY;
// ==================== 绘制表头 ====================
if (options.showHeader) {
painter.save();
// 设置表头样式 - 确保字体大小正确
QFont headerFont;
headerFont.setPointSize(options.headerFontSize); // 使用配置的字体大小
headerFont.setBold(true);
painter.setFont(headerFont);
// 表头背景色
painter.setBrush(QColor(240, 240, 240));
painter.setPen(QPen(Qt::black, 1));
// 绘制表头背景
double totalWidth = std::accumulate(colWidths.begin(), colWidths.end(), 0.0);
painter.drawRect(QRectF(currentX, currentY, totalWidth, rowHeight * 1.2));
// 绘制表头文本
double cellX = currentX;
for (int i = 0; i < colWidths.size(); ++i) {
int col = startCol + i;
QString header = model->headerData(col, Qt::Horizontal).toString();
QRectF cellRect(cellX, currentY, colWidths[i], rowHeight * 1.2);
QRectF textRect = cellRect.adjusted(options.cellPadding, 0, -options.cellPadding, 0);
// 绘制列分隔线
if (i < colWidths.size() - 1) {
painter.setPen(QPen(Qt::gray, 1));
painter.drawLine(cellX + colWidths[i], currentY,
cellX + colWidths[i], currentY + rowHeight * 1.2);
painter.setPen(QPen(Qt::black, 1));
}
// 绘制表头文本
painter.drawText(textRect, Qt::AlignCenter | Qt::AlignVCenter, header);
cellX += colWidths[i];
}
painter.restore();
currentY += rowHeight * 1.2;
}
// ==================== 绘制内容行 ====================
painter.save();
// 设置内容样式 - 关键:使用配置的字体大小
QFont contentFont;
contentFont.setPointSize(options.contentFontSize); // 这是关键!
painter.setFont(contentFont);
// 获取字体度量,用于换行计算
QFontMetricsF fm(contentFont);
double lineSpacing = fm.lineSpacing();
for (int row = startRow; row <= endRow; ++row) {
double cellX = currentX;
// 记录每个单元格需要的额外高度(用于换行)
QVector<double> cellExtraHeights(colWidths.size(), 0);
double maxExtraHeight = 0;
// 第一遍:计算需要换行的单元格的额外高度
if (options.textWrap) {
double tempX = currentX;
for (int i = 0; i < colWidths.size(); ++i) {
int col = startCol + i;
QModelIndex index = model->index(row, col);
QString text = model->data(index).toString();
if (!text.isEmpty()) {
QRectF cellRect(tempX, currentY, colWidths[i], rowHeight);
QRectF textRect = cellRect.adjusted(options.cellPadding, 3, -options.cellPadding, -3);
// 计算文本需要的行数
QString wrappedText = wrapText(text, fm, textRect.width(), options.maxTextLines);
int lineCount = wrappedText.count('\n') + 1;
// 计算文本总高度
double textHeight = lineSpacing * lineCount;
// 如果超过单元格高度,需要额外空间
if (textHeight > textRect.height()) {
double extraHeight = textHeight - textRect.height();
cellExtraHeights[i] = extraHeight;
maxExtraHeight = qMax(maxExtraHeight, extraHeight);
}
}
tempX += colWidths[i];
}
}
// 应用最大额外高度到这一行
double actualRowHeight = rowHeight + maxExtraHeight;
// 交替行背景色
if ((row - startRow) % 2 == 0) {
painter.save();
painter.setBrush(QColor(250, 250, 250));
painter.setPen(Qt::NoPen);
double totalWidth = std::accumulate(colWidths.begin(), colWidths.end(), 0.0);
painter.drawRect(QRectF(currentX, currentY, totalWidth, actualRowHeight));
painter.restore();
}
// 第二遍:实际绘制单元格
for (int i = 0; i < colWidths.size(); ++i) {
int col = startCol + i;
QModelIndex index = model->index(row, col);
QString text = model->data(index).toString();
QRectF cellRect(cellX, currentY, colWidths[i], actualRowHeight);
// 绘制单元格边框
if (options.showGrid) {
painter.setPen(QPen(QColor(200, 200, 200), 1));
painter.drawRect(cellRect);
}
// 设置文本颜色
painter.setPen(Qt::black);
// 计算文本区域
QRectF textRect = cellRect.adjusted(options.cellPadding, 3, -options.cellPadding, -3);
if (options.textWrap && !text.isEmpty()) {
// 处理换行文本
QString wrappedText = wrapText(text, fm, textRect.width(), options.maxTextLines);
// 绘制换行文本
drawWrappedText(painter, textRect, wrappedText, contentFont);
} else {
// 不换行,使用省略号
QString displayText = text;
if (fm.horizontalAdvance(text) > textRect.width()) {
displayText = fm.elidedText(text, Qt::ElideRight, textRect.width());
}
painter.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, displayText);
}
cellX += colWidths[i];
}
currentY += actualRowHeight;
}
painter.restore();
}
void TablePrinter::drawWrappedText(QPainter& painter, const QRectF& rect,
const QString& text, const QFont& font)
{
painter.save();
// 重要:保持原来的字体设置
painter.setFont(font);
// 设置文本选项
QTextOption textOption;
textOption.setWrapMode(QTextOption::WordWrap);
textOption.setAlignment(Qt::AlignLeft | Qt::AlignTop);
// 绘制文本
painter.drawText(rect, text, textOption);
painter.restore();
}
使用举例
// 打印指定列
void UnitManagerWindow::onPrintCustom()
{
TablePrinter::PrintOptions options;
// ==================== 1. 基本设置 ====================
options.title = "数据报表";// 报表标题,显示在每页顶部
options.startColumn = 0; // 从第1列开始
options.endColumn = 5; // 到第10列结束
// ==================== 2. 页面布局设置 ====================
options.marginMM = 15; // 页面边距:15毫米(约0.6英寸)// 值越小,可用打印区域越大
options.titleFontSize = 25; // 标题字体:25点(最大,突出显示)
options.headerFontSize = 11; // 表头字体:11点(比内容稍大)// 建议范围:10-14点// 表头会自动加粗显示
options.contentFontSize = 10; // 内容字体:10点(标准大小)// 建议范围:8-12点// 这是最重要的字体设置
options.footerFontSize = 10; // 页脚字体:10点(与内容相同)// 控制页码和时间的字体大小
// ==================== 4. 高度因子设置(关键!) ====================
options.rowHeightFactor = 12.0; // 行高因子:12.0(行高=字体高度×12) // 这是最重要的参数!// 普通打印:10-15
options.headerHeightFactor = 2.0; // 表头高度因子:2.0// 建议范围:1.5-2.5 // 控制表头行的高度
options.titleHeightFactor = 5.0; // 标题高度因子:5.0 // 建议范围:4-6// 控制标题区域的高度
options.footerHeightFactor = 2.0; // 页脚高度因子:2.0/ 建议范围:1.5-2.5 // 控制页脚区域的高度
// ==================== 5. 换行设置 ====================
options.textWrap = true; // 文本换行:false不换行(默认)// true:启用自动换行 // false:长文本显示省略号
options.maxTextLines = 2; // 最大换行行数:3行// 启用换行时有效// 建议范围:2-5行
// ==================== 6. 内边距设置 ====================
options.cellPadding = 15; // 单元格内边距:8像素// 解决文字太靠近边框的问题// 建议范围:5-15像素// 值越大,文字离边框越远
// ==================== 7. 缩放和拉伸设置 ====================
options.scaleToPage = true; // 自动缩放:true(默认)// true:自动缩放表格以适应页面宽度// false:按原始尺寸打印
options.stretchColumns = true; // 拉伸列宽:true(默认)// true:如果表格宽度小于页面,拉伸填满 // false:保持原始列宽比例
// ==================== 8. 显示控制设置 ====================
options.showGrid = true; // 显示网格线:true显示// true:显示浅灰色网格线// false:不显示网格线(更简洁)
options.showHeader = true; // 显示表头:true显示// true:显示列标题 // false:不显示表头
options.showFooter = true; // 显示页脚:true显示// true:显示页码、打印时间和标题 // false:不显示页脚
// ==================== 9. 打印执行 ====================
//TablePrinter::print(m_tableView, options, this);直接打印
TablePrinter::printPreview(m_tableView, options, this);//预览打印
}
