1

Using Pyside6, I am trying to print an HTML Table. The HTML table format looks good in browser. However, when I open it up in print preview in Pyside6, the length and width of the table are not considered and the table collapses.

Devlopment Environment:

  1. Ubuntu 20.04
  2. Python 3.8.10 (using python virtual environment)
  3. PySide6 version 6.1.3

I also tried the same code in Windows with Python 3.9.7 and Pyside6 version 6.2, and got the same result.

Expected:

enter image description here

Actual:

enter image description here

main.py

from PySide6.QtGui import (
    QPageSize,
    QPageLayout,
    QTextBlockFormat,
    QTextCursor,
    QTextDocument,
    QTextFormat,
)
from PySide6.QtWidgets import QApplication
from PySide6.QtPrintSupport import QPrinter, QPrintPreviewDialog

app = QApplication()

dialog = QPrintPreviewDialog()


def handle_paint_requested(printer):
    document = QTextDocument()
   
    f = open("template.html", "r")
    billTemplate = f.read()

    document.setHtml(billTemplate)
    document.print_(printer)


dialog.paintRequested.connect(handle_paint_requested)
dialog.exec()

template.html

<p>&nbsp;</p>
<table style="border-collapse: collapse; width: 300px;" border="1">
    <tbody>
        <tr>
            <td style="width: 50%; height: 50px;">test1</td>
            <td style="width: 50%; height: 50px;">&nbsp;</td>
        </tr>
    </tbody>
</table>
Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
kumar
  • 8,207
  • 20
  • 85
  • 176
  • I wonder if this has to do with *pixels* not being respected or correctly interpreted in the context of printing. Have you tried any em- or measurement-based dimensions? (Also, what happens if you open print preview from the browser?) – CrazyChucky Oct 03 '21 at 04:24

1 Answers1

3

Since QTextDocument only supports a subset of HTML4 properties it causes the observed error.

One workaround is to use QtWebEngine which was reintroduced in Qt 6.2. Currently only the windows .whl is available in pypi (See here and here for more information) so we can install the Linux and MacOs package you must execute:

python -m pip install pyside6 \
   --index-url=http://download.qt.io/official_releases/QtForPython

So translating my previous answer to PySide6:

import os
from pathlib import Path

from PySide6.QtCore import (
    QCoreApplication,
    QEventLoop,
    QObject,
    QPointF,
    Qt,
    QUrl,
    Slot,
)
from PySide6.QtGui import QKeySequence, QPainter, QShortcut
from PySide6.QtPrintSupport import QPrintDialog, QPrinter, QPrintPreviewDialog
from PySide6.QtWebEngineWidgets import QWebEngineView
from PySide6.QtWidgets import (
    QApplication,
    QDialog,
    QProgressBar,
    QProgressDialog,
)

CURRENT_DIRECTORY = Path(__file__).resolve().parent


class PrintHandler(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.m_page = None
        self.m_inPrintPreview = False

    def setPage(self, page):
        assert not self.m_page
        self.m_page = page
        self.m_page.printRequested.connect(self.printPreview)

    @Slot()
    def print(self):
        printer = QPrinter(QPrinter.HighResolution)
        dialog = QPrintDialog(printer, QWebEngineView.forPage(self.m_page))
        if dialog.exec_() != QDialog.Accepted:
            return
        self.printDocument(printer)

    @Slot()
    def printPreview(self):
        if not self.m_page:
            return
        if self.m_inPrintPreview:
            return
        self.m_inPrintPreview = True
        printer = QPrinter()
        preview = QPrintPreviewDialog(printer, QWebEngineView.forPage(self.m_page))
        preview.paintRequested.connect(self.printDocument)
        preview.exec()
        self.m_inPrintPreview = False

    @Slot(QPrinter)
    def printDocument(self, printer):
        loop = QEventLoop()
        result = False

        def printPreview(success):
            nonlocal result
            result = success
            loop.quit()

        view = QWebEngineView.forPage(self.m_page)
        view.printFinished.connect(printPreview)
        progressbar = QProgressDialog(view)
        progressbar.findChild(QProgressBar).setTextVisible(False)
        progressbar.setLabelText("Wait please...")
        progressbar.setRange(0, 0)
        progressbar.show()
        progressbar.canceled.connect(loop.quit)
        view.print(printer)
        loop.exec()
        progressbar.close()
        if not result:
            painter = QPainter()
            if painter.begin(printer):
                font = painter.font()
                font.setPixelSize(20)
                painter.setFont(font)
                painter.drawText(QPointF(10, 25), "Could not generate print preview.")
                painter.end()


def main():
    import sys

    QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    app = QApplication(sys.argv)
    app.setApplicationName("Previewer")

    filename = CURRENT_DIRECTORY / "template.html"
    url = QUrl.fromLocalFile(os.fspath(filename))

    view = QWebEngineView()
    view.setUrl(url)
    view.resize(1024, 750)
    view.show()

    handler = PrintHandler()
    handler.setPage(view.page())

    printPreviewShortCut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_P), view)
    printShortCut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_P), view)

    printPreviewShortCut.activated.connect(handler.printPreview)
    printShortCut.activated.connect(handler.print)

    sys.exit(app.exec())


if __name__ == "__main__":
    main()

After pressing Ctrl + P you get:

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • I am getting error dialog = QPrintDialog(printer, self.m_page.view()) AttributeError: 'PySide6.QtWebEngineCore.QWebEnginePage' object has no attribute 'view' – kumar Oct 03 '21 at 12:39
  • dialog = QPrintDialog(printer, QWebEngineView.forPage(self.m_page)) – kumar Oct 03 '21 at 12:46
  • My primany objective is to run it on a linux machine. So do I have to wait till PySide6 version 6.2 is available for linux? Any ETA for that? – kumar Oct 03 '21 at 12:50
  • thanks for all your help in this case. In my linux system I started a new project set virtual environment and setup pyside 6 version 6.1.3. Then I ran the above command. I get the error. ModuleNotFoundError: No module named 'PySide6.QtWebEngineWidgets'. I think I might just wat for the linux official release if it is going to be available in few days. The above code works well on windows. – kumar Oct 03 '21 at 18:01
  • 1
    @kumar Check the links in my post where it is explained why there is no .whl for linux and macos, as well as how to install pyside6 6.2 using the local pypi server of QtForPython in Linux. – eyllanesc Oct 03 '21 at 18:04
  • Thanks working now in line 40 you will need to update with dialog = QPrintDialog(printer, QWebEngineView.forPage(self.m_page)) – kumar Oct 03 '21 at 18:19