Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Export QTableView to PDF



  • I have a QTableView associated with some QStandardItemModel. I want to export it to PDF. I know of two ways:

    1. I could write code to export the data to HTML a la https://stackoverflow.com/questions/3147030/qtableview-printing. I already have code to use that in a QWebEngineView and from there to save to PDF. I would be happy doing it this way if recommended.

    2. I read, say, https://forum.qt.io/topic/30728/how-to-turn-a-qtableview-to-a-pdf

    You write code to paint the data from the model underlying the table view to a QPrinter set to output PDF. How you access the data and format it is entirely up to you.

    but I don't really know how to do that, haven't looked at painting. I don't think I then now how to do the row/column layout that I see in the QTableView if I'm supposed to do the desired layout myself(?). And I certainly don't want to know anything about PDF!

    Is there some very simple way of doing #2, like somehow asking the QTableView to just output to PDF (printer?).

    Or, if I am happy to export my data to an HTML table which I can then throw at QWebEngineView to do the PDF export, is that best for me?

    P.S.
    In case anyone suggests: I came across, say, https://github.com/T0ny0/Qt-Table-Printer

    can print any QAbstractItemModel to pdf or postscript with QPrinter

    I don't know, this might be what approach #2 is all about. I am Python, and not prepared to use/write any C++ library. I don't fancy translating it into Python. Plus, this is my idea of "way too much code", if it isn't real short & sweet I think I'd prefer doing it via HTML myself...



  • Hello,

    You could use the QTextDocument API to reproduce the model content in a table, and then print it as a PDF using QPrinter. I think this code should also work if translated to Python.

     QTextDocument *doc = new QTextDocument;
    doc->setDocumentMargin(10);
    QTextCursor cursor(doc);
    
    cursor.movePosition(QTextCursor::Start);
    
    QTextTable *table = cursor.insertTable(properties.size() + 1, 2, tableFormat);
    QTextTableCell headerCell = table->cellAt(0, 0);
    QTextCursor headerCellCursor = headerCell.firstCursorPosition();
    headerCellCursor.insertText(QObject::tr("Name"), boldFormat);
    headerCell = table->cellAt(0, 1);
    headerCellCursor = headerCell.firstCursorPosition();
    headerCellCursor.insertText(QObject::tr("Value"), boldFormat);
    
    for(int i = 0; i < properties.size(); i++){
        QTextCharFormat cellFormat = i % 2 == 0 ? textFormat : alternateCellFormat;
        QTextTableCell cell = table->cellAt(i + 1, 0);
        cell.setFormat(cellFormat);
        QTextCursor cellCursor = cell.firstCursorPosition();
        cellCursor.insertText(properties.at(i)->name());
    
        cell = table->cellAt(i + 1, 1);
        cell.setFormat(cellFormat);
        cellCursor = cell.firstCursorPosition();
        cellCursor.insertText(properties.at(i)->value().toString() + " " + properties.at(i)->unit());
    }
    
    cursor.movePosition(QTextCursor::End);
    cursor.insertBlock();
    
    //Print to PDF
    QPrinter printer(QPrinter::HighResolution);
    printer.setOutputFormat(QPrinter::PdfFormat);
    printer.setOutputFileName(filename);
    doc->print(&printer);
    
    


  • @Gojir4
    Thank you, this is very interesting. (I'm a Qt noob, so didn't know about QTextDocument.)

    This approach is similar in principle to my #1, or https://stackoverflow.com/questions/3147030/qtableview-printing. However, instead of having to directly generate the HTML for my table myself, this is a "structured" document, which offers objects like QTextTable, QTextTableCell etc. which I can use to construct the desired structure. I can then print from that and (hopefully!) get a layout somewhat similar to the QTableView.

    I also note that there is even a http://doc.qt.io/qt-5/qtextdocument.html#toHtml method to get an HTML equivalent (which I presume will use <TABLE> etc.) Which is nice. If I want to, I could even doubtless poke that at QWebEnginePage to use its printToPdf(), which I already employ elsewhere :)

    So --- unless someone else wants to tell me about approach #2 instead --- I think I'll shortly give this a go and see how it comes out...!


  • Lifetime Qt Champion

    Hi
    I made a fast version of raw html output here
    https://forum.qt.io/topic/52652/solved-pdf-print-in-multiple-pages/22
    It fast becomes a bit messy if you want to use lots of formatting for good looks.

    Constructing a Text Document using its api should be much cleaner :)



  • @mrjj
    Thanks. Yep, that was one of the posts I looked at (the stackoverflow one, I mean).

    Using the QTextDocument looks cleaner, and relieves me of producing the HTML, so I'll give that a go. If you're saying it won't look as good as the hand-crafted HTML, I'll think again when I get there.

    Meanwhile neither of you is suggesting #2:

    I read, say, https://forum.qt.io/topic/30728/how-to-turn-a-qtableview-to-a-pdf

    You write code to paint the data from the model underlying the table view to a QPrinter set to output PDF. How you access the data and format it is entirely up to you.

    the "Paint" approach. Which is fine by me! Though now I'm curious as to how you actually do that, as I said I haven't gone near painting?


  • Lifetime Qt Champion

    @JonB
    Hi, im saying it will be easier to make it look better than handcrafted as
    you can use higher level classes like
    http://doc.qt.io/qt-5/qtexttableformat.html
    and CharFormat etc and its easier to scale/resize
    My main point is that constructing HTML was not super nice in terms of readability and
    reuse of html parts/styling etc. Just my feeling though. If you are master at html you might produce cleaner html than my run at it :)

    The pure paint way would to create a drawTable function and something to draw the cell text/style and
    set properties on QPainter for bold font etc. For a very plain table, its not very complex but
    for varying cell widths and extra formatting, you suddenly have to have a small structure to keep that info and
    it slowly becomes big(ger)
    Also, you would have to keep a YPos for newPage handling and other small details.

    For full blown printing, something like
    https://sourceforge.net/projects/qtrpt/
    is also very useful :)



  • @mrjj said in Export QTableView to PDF:

    The pure paint way would to create a drawTable function and something to draw the cell text/style and
    [...]

    I certainly would not want to do any styling, bolding, drawing at all! I thought the way the guy said that meant that you could somehow just tell QTableView to output to a QPrinter set to output PDF instead of to the screen, and it just handled all the drawing itself?



  • @JonB They are several advantages, at my opinion, to use QTextDocument:

    • You can preview/edit in a QTextEdit or QPlainTextEdit
    • You can export content to Open Document Format (Open Office Writer), HTML, PDF and plaintext of course.
    • You can customize everything (font, table, cells, etc..).

  • Lifetime Qt Champion

    @JonB
    Well you can use render() to make it draw it self to QPrinter
    This sample paint to pixmap but idea is the same.
    However, this is only nice if all rows are visible as it wont paint all of them. only how Widget looks on screen.
    Since QPrinter have much higher DPI/pixels, you can scale the widget to use all space but any rows
    not visible are not handled.
    Im not aware of anything else in terms of directly printing the TableView.

    void MainWindow::PrintWidget(QWidget* widget) {
    
      QPixmap pix(widget->size());
      QPainter painter(&pix);
      widget->render(&painter);
      painter.end();
      QPrinter printer(QPrinter::HighResolution);
      printer.setOrientation(QPrinter::Landscape);
      printer.setOutputFormat(QPrinter::PdfFormat);
      printer.setPaperSize(QPrinter::A4);
      printer.setOutputFileName("test.pdf"); // will be in build folder
    
      painter.begin(&printer);
      double xscale = printer.pageRect().width() / double(pix.width());
      double yscale = printer.pageRect().height() / double(pix.height());
      double scale = qMin(xscale, yscale);
      painter.translate(printer.paperRect().x() + printer.pageRect().width() / 2,
                        printer.paperRect().y() + printer.pageRect().height() / 2);
      painter.scale(scale, scale);
      painter.translate(-widget->width() / 2, -widget->height() / 2);
      painter.drawPixmap(0, 0, pix);
    
    QTextDocument doc;
    
    doc.setHtml("htmlcontent");
    doc.drawContents(&painter);
    
      painter.end();
    }
    
    


  • @mrjj
    Thanks. I think:

    Well you can use render() to make it draw it self to QPrinter

    is what I was trying to find, QTableView::render(&QPrinter). I understand your example too. Understand about "it wont paint all of them", have a look at https://stackoverflow.com/a/9784152/489865 for one guy's solution to that.

    I understand enough now to prefer to go down the QTextDocument route for my situation. I have a table of values here, I'm not tied to the physical QTableView visuals, and I'm already offering export to CSV file, so export to PDF via a structured document with a table is good. Plus I get text or HTML too if I want them :)


  • Lifetime Qt Champion

    @JonB
    Going QTextDocument also gives free page overflow handling or at least very easy so
    im sure you wont regret it.


Log in to reply