QT_如何自行手绘绘制PDF报表(包括表头,表格,页眉,页脚,曲线图等)


前言

因项目需要,需要自行手绘pdf报表,网上已经有一些相关的方案实现了,但是没有一条龙服务,包括表头,表格,页眉,页脚,曲线图等,在这里我实现了以后,记录一下。(因为项目保密需要,没有图,反正肯定是能用的)
另外,由于大家情况各不相同,我不可能把我代码一股脑弄上来,不合理,所以就挑一些功能项摘出来介绍,大家灵活应用参考。
内容有点乱,没有做优化整理,不喜勿喷,右上角有个x,对,浏览器右上角那个,可以点。
友好意见,欢迎交流


一、初始化创建文件位置

这一步先做个弹窗,用于存放我们的pdf

//通过dialog来保存PDF路径
QString MyDialog::pdfSaveAs(QString fileName)
{
    QString file = QFileDialog::getSaveFileName(
        this,                               //父组件
        "另存为",                              //标题
        QCoreApplication::applicationDirPath() + QString::fromLocal8Bit("/data/Report/%1").arg(fileName + ".pdf"),
        //设置路径, .表示当前路径,./表示更目录
        QString::fromLocal8Bit("PDF文件(*.pdf)"),     //过滤器,保存的类型
        Q_NULLPTR,
        QFileDialog::ShowDirsOnly);

    return file;
}

调用办法

QString file_path = pdfSaveAs(QString::fromLocal8Bit("报告%1").arg(QDateTime::currentDateTime().toString("_yy_MM_dd_hh_mm")) + ".pdf"));

二、绘制表格

1.绘制表格函数(仅供参考,你们肯定写得更好)

//y:表单的Y坐标
//horzBorder:水平外边距,写0表示没有边距
//row,column:行数和列数
//unitHeight:单元高度
//font:字体大小
//list:要写入的单元数据
void MyDialog::pdfDrawForm(QPainter* paint, int y, int horzBorder, int row, int column, int unitHeight, QFont& font, QStringList& list)
{
    paint->setFont(font);
    paint->setPen(QPen(QBrush(QColor(0, 0, 0)), 2));
    int Width = paint->viewport().width() - (horzBorder << 1);
    int unitWidth = Width / column;
    for (int i = 0; i < row; i++)
    {
        int x = horzBorder;
        for (int j = 0; j < column; j++)
        {
            paint->drawText(QRect(x, y, unitWidth, unitHeight), Qt::AlignCenter, list.at(i * column + j));
            paint->drawRect(QRect(x, y, unitWidth, unitHeight));
            x += unitWidth;
        }
        y += unitHeight;
        paint->drawLine(0, y, Width, y);
        y++;
    }
}

2.基于QTableWidget绘制表格函数

//y:表单的Y坐标
//horzBorder:水平外边距,写0表示没有边距
//row,column:行数和列数
//unitHeight:单元高度
//lineweight:表格线粗
//font:字体大小
//table_get:要写入的表格
//返回值:返回最终的高度
int MyDialog::pdfDrawForm_from_table(QPainter* paint, int y, int horzBorder, int unitHeight, int lineweight, QFont& font, QTableWidget* table_get, QString unit)
{
    paint->setFont(font);
    paint->setPen(QPen(QBrush(QColor(0, 0, 0)), lineweight));
    const int Width = paint->viewport().width() - (horzBorder << 1);
    const int row = table_get->rowCount() + 1;
    const int column = table_get->columnCount() + 1;
    const int unitWidth = Width / column;
    int x_head_up = horzBorder;
    font.setBold(true);
    paint->setFont(font);
    paint->drawText(QRect(x_head_up, y, unitWidth, unitHeight), Qt::AlignCenter, QString::fromLocal8Bit("你想要放在左上角的信息").arg(unit));
    paint->drawRect(QRect(x_head_up, y, unitWidth, unitHeight));
    x_head_up += unitWidth;
    for (int j = 1; j < column; j++)
    {
        paint->drawText(QRect(x_head_up, y, unitWidth, unitHeight), Qt::AlignCenter, table_get->horizontalHeaderItem(j - 1)->text());
        paint->drawRect(QRect(x_head_up, y, unitWidth, unitHeight));
        x_head_up += unitWidth;
    }
    y += unitHeight;
    for (int i = 1; i < row; i++)
    {
        int x = horzBorder;
        font.setBold(true);
        paint->setFont(font);
        paint->drawText(QRect(x, y, unitWidth, unitHeight), Qt::AlignCenter, table_get->verticalHeaderItem(i - 1)->text());
        paint->drawRect(QRect(x, y, unitWidth, unitHeight));
        x += unitWidth;
        font.setBold(false);
        paint->setFont(font);
        for (int j = 1; j < column; j++)
        {
            paint->drawText(QRect(x, y, unitWidth, unitHeight), Qt::AlignVCenter | Qt::AlignRight, table_get->item(i - 1, j - 1)->text());
            paint->drawRect(QRect(x, y, unitWidth, unitHeight));
            x += unitWidth;
        }
        y += unitHeight;
    }
    return y;
}

三、pdf换页逻辑器

1.判断当前y值是否超出了某一界限,若超出了,则pdf翻页,y设定为10 否则y直接改为设定值(勉强可以用,希望各位大神有更好的解决办法)

这个破函数是我自己造的,用来更新y的值,可以比较方便的判断是否需要换页,并更新y值

//写一个函数,判断当前y值是否超出了某一界限,若超出了,则pdf翻页,y设定为10 否则y直接改为设定值
void MyDialog::setYvalue_with_if(int& y_now, int y_target, int set_limit, int& page_now, QPdfWriter* get_writer, QPainter* paint, QImage get_logo_img_tmp, QString file_path, bool show_logo_flag)
{
    if (y_target > set_limit)
    {
        get_writer->newPage();
        page_now++;

        int nPDFWidth = paint->viewport().width();
        int nPDFHeight = paint->viewport().height();
        const int nPDFWidth_1_4 = nPDFWidth >> 2;
        const int top_gap = 140;
        const int bottom_gap = 25;
        //绘制页脚
        paint->setPen(QPen(QBrush(QColor(0, 0, 0)), 5));
        paint->drawLine(0, nPDFHeight - bottom_gap, nPDFWidth, nPDFHeight - bottom_gap);
        QFont font_tmp = QFont(QString::fromLocal8Bit("黑体"), 26, QFont::Normal);
        font_tmp.setPixelSize(25);
        paint->setFont(font_tmp);
        paint->drawText(QRect(0, nPDFHeight - bottom_gap, nPDFWidth, bottom_gap), Qt::AlignLeft,
            QString::fromLocal8Bit("文件名:          %1").arg(file_path));

        QImage show_logo = get_logo_img_tmp.scaled(QSize(top_gap * 2, top_gap), Qt::KeepAspectRatio, Qt::SmoothTransformation);
        if (show_logo_flag)
        {
            paint->drawImage(QPoint(0, 0), show_logo);
        }
        paint->drawText(QRect(nPDFWidth - 200, nPDFHeight - 25, bottom_gap, 25), Qt::AlignRight,
            QString::fromLocal8Bit("页码:%1").arg(QString::number(page_now)));

        y_now = top_gap;

        paint->setPen(QPen(QBrush(QColor(0, 0, 0)), 5));
        paint->drawLine(0, y_now, nPDFWidth, y_now);

        y_now = top_gap + 5;
    }
    else
    {
        y_now = y_target;
    }
}

四、第二类标题生成单元(可举一反三,我就不反了)

1.一个指定位置,背景之类的标题生成器,勉强可用


//第二类标题生成单元
void MyDialog::pdf_title_2_set(QString title_text, int title_width, int title_height, int first_background, QFont font_title, QPdfWriter* get_writer, QPainter* paint, int& y, int set_limit, int& page_now, QImage get_logo_img_tmp, QString file_path, bool show_logo_flag)
{
    if (y + title_height > set_limit)
    {
        setYvalue_with_if(y, y + title_height, set_limit, page_now, get_writer, paint, get_logo_img_tmp, file_path);
    }
    paint->fillRect(QRect(0, y, title_width, title_height), QGradient::Preset(first_background));
    paint->setFont(font_title);
    paint->drawText(QRect(10, y, title_width, title_height), Qt::AlignVCenter | Qt::AlignLeft, title_text);

    setYvalue_with_if(y, y + title_height, set_limit, page_now, get_writer, paint, get_logo_img_tmp, file_path);
    paint->setPen(QPen(QBrush(QColor(0, 0, 0)), 3));
    paint->drawLine(0, y, title_width, y);

    setYvalue_with_if(y, y + 5, set_limit, page_now, get_writer, paint, get_logo_img_tmp, file_path);
}

五、根据路径创建pdf文件

1.记录路径创建pdf生成器

其实到这一步,会的人就都会了,反正变成QPainter了,剩下的不是随便你们搞么,可发挥的地方就多了。
代码如下:

//判断路径是否为空
if (file_path.isEmpty())
    return;
//初始化pdf文件
QFile pdfFile(file_path);
pdfFile.open(QIODevice::WriteOnly);
//初始化pdf信息
QPdfWriter* pWriter = new QPdfWriter(&pdfFile);
pWriter->setPageSize(QPagedPaintDevice::A4);
pWriter->setResolution(300);    //设置dpi 每个平方英寸像素为300
pWriter->setPageMargins(QMarginsF(30, 30, 30, 30));//上下左右间隔,这个就不废话了

//初始化绘制层
QPainter* pPainter = new QPainter(pWriter);

//中间填写你要绘制的东西

//绘制完毕
delete pPainter;
delete pWriter;
pdfFile.close();

2.初始化字体样式

代码如下:

    //初始化字体样式
    QFont font[6] = { QFont(QString::fromLocal8Bit("黑体"),26,QFont::Bold),
        QFont(QString::fromLocal8Bit("黑体"),26,QFont::Normal),
        QFont(QString::fromLocal8Bit("黑体"),26,QFont::Bold),
        QFont(QString::fromLocal8Bit("黑体"),26,QFont::Normal),
        QFont(QString::fromLocal8Bit("黑体"),26,QFont::Normal),
        QFont(QString::fromLocal8Bit("黑体"),26,QFont::Normal)
    };

    font[0].setPixelSize(86);
    font[1].setPixelSize(55);
    font[2].setPixelSize(35);
    font[3].setPixelSize(42);
    font[4].setPixelSize(54);
    font[5].setPixelSize(25);

3.绘制pdf内容(初始化)

代码如下:

	//Painter PDF
    qDebug() << pPainter->viewport();
    int nPDFWidth = pPainter->viewport().width();
    int nPDFHeight = pPainter->viewport().height();
    const int nPDFWidth_1_4 = nPDFWidth >> 2;
    const int nPDFWidth_1_8 = nPDFWidth >> 3;
    int page_now = 1;
    const int top_gap = 140;
    const int bottom_gap = 25;
    const int title_1_height = 100;//标题一高度
    const int title_2_height = 65;//标题二高度
    const int chart_height = 880;
    const int unitHeight = 39;//表格单行高度
    const int line_head = 50;//文字缩进

    int y = 0;//比较重要,贯穿始终,指向当前绘制的高度
    

4.绘制页脚(仅供参考)

代码如下:

    //绘制页脚
    pPainter->setPen(QPen(QBrush(QColor(0, 0, 0)), 5));
    pPainter->drawLine(0, nPDFHeight - bottom_gap, nPDFWidth, nPDFHeight - bottom_gap);
    pPainter->setFont(font[5]);
    pPainter->drawText(QRect(0, nPDFHeight - bottom_gap, nPDFWidth, bottom_gap), Qt::AlignLeft,
        QString::fromLocal8Bit("文件名:          %1").arg(file_path));

    pPainter->drawText(QRect(nPDFWidth - 200, nPDFHeight - 25, bottom_gap, 25), Qt::AlignRight,
        QString::fromLocal8Bit("页码:%1").arg(QString::number(page_now)));

5.绘制logo图(仅供参考)

代码如下:

	//左上角绘制logo图
    QImage show_logo = get_logo_img_tmp.scaled(QSize(top_gap * 2, top_gap), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    if (show_logo_flag)
    {
        pPainter->drawImage(QPoint(0, 0), show_logo);
    }

6.绘制大标题

代码如下:

	//在10%的头部居中显示
    //0.主标题信息展示
    pPainter->fillRect(QRect(0, y, nPDFWidth, title_1_height), QGradient::Preset(title_background));
    pPainter->setFont(font[0]);
    pPainter->drawText(QRect(0, y, nPDFWidth, title_1_height), Qt::AlignCenter,
        QString::fromLocal8Bit("什么什么检测报告"));

    y += 140;
    pPainter->setPen(QPen(QBrush(QColor(0, 0, 0)), 5));
    pPainter->drawLine(0, y, nPDFWidth, y);
    pPainter->drawLine(0, y + 18, nPDFWidth, y + 18);

    setYvalue_with_if(y, y + 20, nPDFHeight - bottom_gap, page_now, pWriter, pPainter, get_logo_img_tmp, file_path, show_logo_flag);

7.强制跳转到最后一页

代码如下:

	//强行跳转到最后一页
    setYvalue_with_if(y, y + nPDFHeight, nPDFHeight - bottom_gap, page_now, pWriter, pPainter, get_logo_img_tmp, file_path, show_logo_flag);

六.导入曲线图

1.QChart

可以通过QChartView把已经画好的QChart拉出来,然后变个尺寸扔进去,然后记得把尺寸变回来,清空一下之前放QChartView的地方(清空方法友情赠送,不谢),再把这个扔回去就行。这个方法如果用opengl的话容易出问题,建议不要弄,如果弄的话,记得重新把opengl打开(如果可以的话)。
基本原理就是把窗体扣下来变成QPixmap然后画到pdf里。你们可以举一反三一下。
代码如下:

		QChartView* chartView_tmp = new QChartView(&chart_A);
        chartView_tmp->setRenderHint(QPainter::Antialiasing);

        float old_w = chartView_tmp->width();
        float old_h = chartView_tmp->height();

        float show_w = nPDFWidth - (imageBorder << 1);
        float show_h = chart_height;

        chartView_tmp->resize(QSize(show_w, show_h));
        QPixmap pixmap = QPixmap::grabWidget(chartView_tmp, chartView_tmp->rect());
        chartView_tmp->resize(QSize(old_w, old_h));

        if (y + show_h > nPDFHeight - bottom_gap)
        {
            setYvalue_with_if(y, y + show_h, nPDFHeight - bottom_gap, page_now, pWriter, pPainter, get_logo_img_tmp, file_path, show_logo_flag);
        }

        pPainter->drawPixmap(imageBorder, y, pixmap);

        //清空verticalLayout_widget布局内的所有元素
        QLayoutItem* child;
        while ((child = ui.verticalLayout_widget->takeAt(0)) != 0)
        {
            //setParent为NULL,防止删除之后界面不消失
            if (child->widget())
            {
                child->widget()->setParent(NULL);
            }

            delete child;
        }
        ui.verticalLayout_widget->addWidget(chartView_tmp);

2.QCustomPlot

代码如下:

        QCustomPlot* show_plot = ui.mycustomplot;
        
        float show_w = nPDFWidth - (imageBorder << 1);
        float show_h = chart_height;

        QPixmap pixmap = show_plot->toPixmap(show_w, show_h);

所以说qcustomplot是真好用

七.通过其它PDF阅读器来打开PDF

代码如下(示例):

//通过其它PDF阅读器来打开PDF
QDesktopServices::openUrl(QUrl::fromLocalFile(file_path));

该处使用的url网络请求的数据。


总结

以上就是大概的内容了,基本上都有了。大家可以基于这个举一反三,最好有大神帮我封装一下,我以后就可以拿来用了(想得美)。或者大家有更好的插件、方案之类的,欢迎分享给我,我就把这个扔了。

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在C++和Qt中,绘制曲线图可以使用Qt中的QCustomPlot库来实现。QCustomPlot是一个强大的绘图库,提供了丰富的绘图功能,包括曲线图、柱状图、散点图等。 要绘制曲线图,首先需要创建一个QCustomPlot对象,并将其添加到QWidget或QMainWindow上。然后,我们可以使用QCustomPlot的函数来设置图表的标题、坐标轴标签和范围等属性。 接下来,可以创建一个QCPGraph对象,用于绘制曲线。通过设置QCPGraph的数据和颜色等属性,可以完成曲线的绘制。我们可以使用QCustomPlot的函数来添加和删除曲线。 在绘制曲线之前,需要准备好曲线的数据。通常,我们可以将曲线的数据存储在一个Qt容器(如QVector)中,然后将其传递给QCPGraph的setData函数。 一旦准备好曲线的数据和属性,即可调用QCustomPlot的replot函数来重新绘制曲线图曲线图会根据传递给setData函数的数据自动更新和调整。 除了基本的曲线绘制外,QCustomPlot还提供了许多其他功能,如坐标轴刻度的设置、网格线的显示、多曲线的绘制等。通过研究QCustomPlot的文档和示例代码,可以更深入地了解和使用它的功能。 总之,使用C++和Qt绘制曲线图是相对简单的。通过使用QCustomPlot库,我们可以方便地实现曲线的绘制和操控。 ### 回答2: 在C++中使用Qt绘制曲线图可以通过QCustomPlot库来实现。以下是实现步骤: 1. 安装QCustomPlot库:将QCustomPlot的源代码添加到项目中,并在项目文件的.pro文件中添加库的引用。 2. 创建曲线图窗口:使用QMainWindow或QWidget派生类创建一个窗口用于显示曲线图。 3. 初始化曲线图:在窗口的构造函数中,创建一个QCustomPlot对象,并设置图表的标题、坐标轴等属性。 4. 添加数据:使用QVector保存曲线图的数据点,并将其添加到QCustomPlot对象中。 5. 绘制曲线图:使用QCustomPlot的addGraph()方法创建曲线,并将数据点设置为曲线的坐标。 6. 设置曲线样式:使用QPen类设置曲线的颜色、线型等样式。 7. 显示曲线图:使用QCustomPlot的replot()方法将曲线图绘制在窗口上。 8. 添加交互功能:如果需要,可以通过QCustomPlot的信号和槽机制,实现交互功能,例如缩放、平移等。 9. 更新曲线图:如果需要在运行期间更新曲线图,可以通过重新设置数据点的方式,再次调用replot()方法进行更新。 10. 清除曲线图:如果需要清除曲线图,可以调用QCustomPlot的clearGraphs()方法清除所有曲线。 总结:使用Qt的QCustomPlot库可以方便地绘制曲线图。通过初始化、添加数据、绘制曲线、设置样式、显示曲线、添加交互功能、更新和清除曲线等步骤,可以实现自定义的曲线图绘制。 ### 回答3: C++和Qt是一种常用的编程语言和开发工具,可以用于绘制曲线图Qt是一个跨平台的开发框架,具有丰富的功能和易用的API,可以轻松地绘制各种图形,包括曲线图。 要使用Qt绘制曲线图,首先需要创建一个窗口或视图来展示图形。可以使用Qt的窗口类(如QMainWindow、QWidget)或图形视图类(如QGraphicsView、QChartView)来创建一个可显示图形的界面。 然后,需要创建一个曲线图对象(如QLineSeries、QSplineSeries),并且添加数据点到曲线上。Qt提供了数据序列类来存储数据,以便绘制曲线图。可以使用append方法向序列对象中添加数据点。 接下来,需要创建一个图表对象(如QChart),将曲线图对象添加到图表中,并设置图表的样式和属性,如标题、坐标轴等。Qt提供了一系列的图表类,来帮助我们创建具有各种样式的图表。 最后,将图表对象添加到窗口或视图中,并显示出来。可以使用布局管理器(如QVBoxLayout、QGridLayout)将图表放置在窗口或视图中的合适位置,然后调用show()方法显示出来。 在绘制曲线图时,还可以根据需要自定义曲线的样式和属性,如线条颜色、线宽等。可以使用Qt的绘图API来绘制曲线,如设置笔(QPen)的颜色和宽度。 绘制曲线图是一个需要一定编程知识和经验的任务,但使用Qt可以简化这个过程,提供了丰富的功能和易用的API,使曲线图绘制变得容易和灵活。通过学习和熟悉Qt的绘图功能,我们可以更加方便地创建出漂亮的曲线图

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迷失的walker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值