src/eric7/WebBrowser/WebBrowserView.py

branch
eric7
changeset 10240
cbd566d49a88
parent 10069
435cc5875135
child 10334
24300d16a154
--- a/src/eric7/WebBrowser/WebBrowserView.py	Thu Oct 12 17:03:41 2023 +0200
+++ b/src/eric7/WebBrowser/WebBrowserView.py	Thu Oct 12 17:05:08 2023 +0200
@@ -18,7 +18,9 @@
     QDataStream,
     QDateTime,
     QEvent,
+    QEventLoop,
     QIODevice,
+    QMarginsF,
     QPoint,
     QPointF,
     QStandardPaths,
@@ -28,7 +30,20 @@
     pyqtSignal,
     pyqtSlot,
 )
-from PyQt6.QtGui import QClipboard, QCursor, QDesktopServices, QIcon, QPixmap
+from PyQt6.QtGui import (
+    QClipboard,
+    QCursor,
+    QDesktopServices,
+    QIcon,
+    QPageLayout,
+    QPixmap,
+)
+from PyQt6.QtPrintSupport import (
+    QAbstractPrintDialog,
+    QPrintDialog,
+    QPrinter,
+    QPrintPreviewDialog,
+)
 from PyQt6.QtWebEngineCore import QWebEngineDownloadRequest, QWebEnginePage
 from PyQt6.QtWebEngineWidgets import QWebEngineView
 from PyQt6.QtWidgets import QApplication, QDialog, QMenu, QStyle
@@ -38,16 +53,34 @@
 from eric7.EricWidgets import EricFileDialog, EricMessageBox
 from eric7.EricWidgets.EricApplication import ericApp
 from eric7.SystemUtilities import FileSystemUtilities, OSUtilities
+from eric7.UI.Info import Homepage, VersionOnly
 from eric7.WebBrowser.WebBrowserWindow import WebBrowserWindow
 from eric7.WebBrowser.ZoomManager import ZoomManager
 
 from . import WebInspector
-from .Tools import Scripts
+from .Tools import Scripts, WebBrowserTools
 from .Tools.WebBrowserTools import getHtmlPage, pixmapToDataUrl
 from .Tools.WebIconLoader import WebIconLoader
 from .WebBrowserPage import WebBrowserPage
 
 
+def isCupsAvailable():
+    """
+    Static method to test the availability of CUPS.
+
+    @return flag indicating the availability of CUPS
+    @rtype bool
+    """
+    if OSUtilities.isMacPlatform():
+        # OS X/MacOS always have CUPS
+        return True
+    elif OSUtilities.isLinuxPlatform():
+        testPrinter = QPrinter()
+        return testPrinter.supportsMultipleCopies()
+    else:
+        return False
+
+
 class WebBrowserView(QWebEngineView):
     """
     Class implementing the web browser view widget.
@@ -130,6 +163,8 @@
         self.__clickedPos = QPoint()
         self.__firstLoad = False
         self.__preview = QPixmap()
+        self.__currentPrinter = None
+        self.__printPreviewLoop = None
 
         self.__currentZoom = 100
         self.__zoomLevels = WebBrowserView.ZoomLevels[:]
@@ -146,6 +181,10 @@
         self.loadFinished.connect(self.__loadFinished)
         self.renderProcessTerminated.connect(self.__renderProcessTerminated)
 
+        self.printRequested.connect(self.printPage)
+        self.printFinished.connect(self.__printPageFinished)
+        self.pdfPrintingFinished.connect(self.__printPageToPdfFinished)
+
         self.__mw.openSearchManager().currentEngineChanged.connect(
             self.__currentEngineChanged
         )
@@ -177,7 +216,6 @@
 
         self.__page.safeBrowsingAbort.connect(self.safeBrowsingAbort)
         self.__page.safeBrowsingBad.connect(self.safeBrowsingBad)
-        self.__page.printPageRequested.connect(self.__printPage)
         with contextlib.suppress(AttributeError):
             # deprecated with Qt 6.5+
             self.__page.quotaRequested.connect(self.__quotaRequested)
@@ -2299,15 +2337,185 @@
             return True
 
     ###########################################################################
-    ## Methods below implement print support from the page
+    ## Methods below implement print support
     ###########################################################################
 
+    def __setupPrinter(self, filePath=None):
+        """
+        Private method to create and initialize a QPrinter object.
+
+        @param filePath name of the output file for the printer (defaults to None)
+        @type str (optional)
+        @return initialized QPrinter object
+        @rtype QPrinter
+        """
+        printer = QPrinter(mode=QPrinter.PrinterMode.HighResolution)
+        if Preferences.getPrinter("ColorMode"):
+            printer.setColorMode(QPrinter.ColorMode.Color)
+        else:
+            printer.setColorMode(QPrinter.ColorMode.GrayScale)
+        if Preferences.getPrinter("FirstPageFirst"):
+            printer.setPageOrder(QPrinter.PageOrder.FirstPageFirst)
+        else:
+            printer.setPageOrder(QPrinter.PageOrder.LastPageFirst)
+        printer.setPageMargins(
+            QMarginsF(
+                Preferences.getPrinter("LeftMargin") * 10,
+                Preferences.getPrinter("TopMargin") * 10,
+                Preferences.getPrinter("RightMargin") * 10,
+                Preferences.getPrinter("BottomMargin") * 10,
+            ),
+            QPageLayout.Unit.Millimeter,
+        )
+        printerName = Preferences.getPrinter("PrinterName")
+        if printerName:
+            printer.setPrinterName(printerName)
+        printer.setResolution(Preferences.getPrinter("Resolution"))
+        documentName = WebBrowserTools.getFileNameFromUrl(self.url())
+        printer.setDocName(documentName)
+        documentsPath = QStandardPaths.writableLocation(
+            QStandardPaths.StandardLocation.DocumentsLocation
+        )
+        if filePath is None:
+            filePath = "{0}.pdf".format(documentName)
+        filePath = (
+            os.path.join(documentsPath, filePath)
+            if documentsPath
+            else os.path.abspath(filePath)
+        )
+        printer.setOutputFileName(filePath)
+        printer.setCreator(self.tr("eric7 {0} ({1})").format(VersionOnly, Homepage))
+        return printer
+
+    @pyqtSlot()
+    def printPage(self):
+        """
+        Public slot to print the current page.
+        """
+        if self.__currentPrinter is not None:
+            EricMessageBox.warning(
+                self,
+                self.tr("Print Page"),
+                self.tr(
+                    "There is already a print job in progress. Printing is temporarily"
+                    " disabled until the current job is finished."
+                ),
+            )
+            return
+
+        printer = self.__setupPrinter()
+
+        printDialog = QPrintDialog(printer, self)
+        printDialog.setOptions(
+            QAbstractPrintDialog.PrintDialogOption.PrintToFile
+            | QAbstractPrintDialog.PrintDialogOption.PrintShowPageSize
+        )
+        if not OSUtilities.isWindowsPlatform():
+            if isCupsAvailable():
+                printDialog.setOption(
+                    QAbstractPrintDialog.PrintDialogOption.PrintCollateCopies
+                )
+            printDialog.setOption(QAbstractPrintDialog.PrintDialogOption.PrintPageRange)
+        if printDialog.exec() == QDialog.DialogCode.Accepted:
+            if printer.outputFormat() == QPrinter.OutputFormat.PdfFormat:
+                self.printToPdf(
+                    printer.outputFileName(), printer.pageLayout(), printer.pageRanges()
+                )
+            else:
+                self.__currentPrinter = printer
+                self.print(printer)
+
     @pyqtSlot()
-    def __printPage(self):
+    def printPageToPdf(self):
+        """
+        Public slot to save the current page as a PDF file.
+        """
+        from .Tools.PrintToPdfDialog import PrintToPdfDialog
+
+        name = WebBrowserTools.getFileNameFromUrl(self.url())
+        name = name.rsplit(".", 1)[0] + ".pdf" if name else "printout.pdf"
+        dlg = PrintToPdfDialog(self.__setupPrinter(filePath=name), self)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            filePath, pageLayout = dlg.getData()
+            if filePath:
+                if os.path.exists(filePath):
+                    res = EricMessageBox.warning(
+                        self,
+                        self.tr("Print to PDF"),
+                        self.tr(
+                            """<p>The file <b>{0}</b> exists"""
+                            """ already. Shall it be"""
+                            """ overwritten?</p>"""
+                        ).format(filePath),
+                        EricMessageBox.No | EricMessageBox.Yes,
+                        EricMessageBox.No,
+                    )
+                    if res == EricMessageBox.No:
+                        return
+                self.printToPdf(filePath, pageLayout)
+
+    @pyqtSlot()
+    def printPreviewPage(self):
+        """
+        Public slot to create a print preview of the current page.
+        """
+        printer = self.__setupPrinter()
+        preview = QPrintPreviewDialog(printer, self)
+        preview.resize(800, 750)
+        preview.paintRequested.connect(self.__printPreviewRequested)
+        preview.exec()
+
+    @pyqtSlot(QPrinter)
+    def __printPreviewRequested(self, printer):
         """
-        Private slot to support printing from the web page.
+        Private slot to generate the print preview.
+
+        @param printer reference to the printer object
+        @type QPrinter
+        """
+        # This needs to run its own event loop to prevent a premature return from
+        # the method.
+        self.__printPreviewLoop = QEventLoop()
+
+        self.print(printer)
+
+        self.__printPreviewLoop.exec()
+        self.__printPreviewLoop = None
+
+    @pyqtSlot(bool)
+    def __printPageFinished(self, success):
+        """
+        Private slot to handle the finishing of a print job.
+
+        @param success flag indicating success (not used)
+        @type bool
         """
-        self.__mw.tabWidget.printBrowser(browser=self)
+        if self.__printPreviewLoop is not None:
+            # The print preview was created, now stop the print preview loop.
+            self.__printPreviewLoop.quit()
+            return
+
+        # we printed to a real printer
+        self.__currentPrinter = None
+
+    @pyqtSlot(str, bool)
+    def __printPageToPdfFinished(self, filepath, success):
+        """
+        Private slot to handle the finishing of a PDF print job.
+
+        @param filepath path of the output PDF file
+        @type str
+        @param success flag indicating success
+        @type bool
+        """
+        if not success:
+            EricMessageBox.critical(
+                self,
+                self.tr("Print to PDF"),
+                self.tr(
+                    """<p>The PDF file <b>{0}</b> could not be generated.</p>"""
+                ).format(filepath),
+            )
 
     ###########################################################################
     ## Methods below implement slots for Qt 6.0 to 6.4

eric ide

mercurial