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

Printing html using QWebEngineView fails (PyQt5)



  • Hello everyone,

    I'm trying to get print html code which is generated by my application, but unfortunately I can't get it to work.
    The print preview shows the page but the quality looks bad, all I get is a corrupted PDF file and it is kinda slow. I had it working for years with PyQt4.x.

    The code where I call my printer class:

    	def _print(self, preview=False, PDF=False):
    		if(not self.printer.readyState):
    			print('printer class not ready yet.')
    			return;
    
    		tpl = '<html><head></head><body>Test</body></html>';
    
    		self.printer.load();
    		self.printer.forceLandscape = True;
    		self.printer.fontfamily = str(self.cfg.value('fontsettings_0_font'))
    		self.printer.fontsize = str(self.cfg.value('fontsettings_0_size'))
    		self.printer.preview = preview;
    		self.printer.pdf = PDF;
    		self.printer.setHtml(tpl);
    		self.printer.print();
    

    The printer class (printer.py)

    from typing import List
    
    try:
    	import sys, os, locale, subprocess
    	from PyQt5.QtGui import *
    	from PyQt5.QtCore import *
    	from PyQt5.QtNetwork import *	
    	from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog, QPrintDialog
    	from PyQt5 import QtSql, QtGui
    	from PyQt5.QtWidgets import QWidget, QDialog, QTextEdit
    
    	from PyQt5.QtWebEngineWidgets import QWebEngineView,QWebEnginePage,QWebEngineSettings
    
    
    except ImportError as msg:
    	print("Exception: Couldn't import all required modules:\n-> ImportError: ", msg)
    	sys.exit()
    
    locale.setlocale(locale.LC_ALL, '')
    locale.setlocale(locale.LC_NUMERIC, '')
    	
    
    class printer(QWidget):
    	# Initialize printer class
    	def __init__(self, parent=None):
    		QWidget.__init__(self);
    		self.parent = parent;
    		self.pagecounter=-1;
    		self.html="";
    		self.preview=""
    		self.m_page = None
    		self.m_inPrintPreview = False
    		self.page = QWebEnginePage();
    		self.progress = 0;
    		self.waiting = False;
    
    
    		# tasklist
    		self.readyState = True;
    
    		self.defaultPageMargins=[float(self.parent.cfg.value('page_margin_rawdec_left')) \
    		, float(self.parent.cfg.value('page_margin_rawdec_top')) \
    		, float(self.parent.cfg.value('page_margin_rawdec_right')) \
    		, float(self.parent.cfg.value('page_margin_rawdec_bottom')) \
    		, int(self.parent.cfg.value('page_margin_rawdec_unit'))];
    		
    
    	def setPageMargins(self,a,b,c,d,e):
    		self.pageMargins=[a,b,c,d,e];
    		
    	
    	# Load printer for new job
    	def load(self):
    		self.fontfamily='Sans'
    		self.fontsize='12'
    		self.progress = 0;
    		self.pages = [];
    		self.pagecounter=-1;
    		self.forceLandscape=False;
    		self.pageMargins=self.defaultPageMargins;
    
    	def readyTask(self):
    		self.readyState = True;
    
    
    	def pageSetup(self):
    		ps = QPageSetupDialog(self.printer,self.doc);
    		ps.open();
    
    
    
    	def openPDF(self, filename="output.pdf"):
    		if os.name == 'mac':
    			subprocess.call(['open', filename])
    		elif os.name == 'nt':
    			subprocess.call(['start', filename], shell=True)
    		elif os.name == 'posix':
    			subprocess.call(['xdg-open', filename])
    
    
    	def setHtml(self, html):
    		self.page.setHtml(html)
    
    	def pageloaded(self):
    		print('webviewloaded')
    		if(self.waiting):
    			self.waiting = False;
    			return self.print();
    
    	def pageprogress(self,progress):
    		print(progress)
    		self.progress = progress;
    
    	@property
    	def page(self):
     		return self.m_page
    
    	@page.setter
    	def page(self, page):
    		if isinstance(page, QWebEnginePage):
    			self.m_page = page
    			self.page.printRequested.connect(self.printPreview)
    			self.page.loadFinished.connect(self.pageloaded)
    			self.page.loadProgress.connect(self.pageprogress);
    
    		else:
    			raise TypeError("page must be a QWebEnginePage")
    
    
    	def getPrinter(self):
    		pm = self.pageMargins;
    		printer = QPrinter(QPrinter.HighResolution)
    		printer.setPageSize(QPrinter.A4);
    		printer.setFullPage (True); # must be before set margin
    		printer.setPageMargins(pm[0],pm[1],pm[2],pm[3], pm[4]);
    
    		# Landscape or Portraitself.webview.show();
    		if self.forceLandscape:
    			printer.setOrientation(QPrinter.Landscape);
    			self.forceLandscape=False;
    		else:
    			printer.setOrientation(QPrinter.Portrait);
    
    		return printer;
    
    
    
    
    	@pyqtSlot()
    	def print(self):
    		if(self.progress is not 100):
    			self.waiting = True;
    			print('waiting...')
    			print(self.progress)
    			return;
    
    		print('ok')
    
    		if(self.preview):
    			return self.printPreview();
    
    
    		printer = self.getPrinter();
    
    		# PDF
    		tmpfile=None;
    		if(self.pdf):
    			tmpfile = QTemporaryFile();
    			if(tmpfile.open()):
    				print(tmpfile)
    				printer.setOutputFormat(QPrinter.PdfFormat)
    				printer.setOutputFileName(tmpfile.fileName())
    				return self.printDocument(printer,tmpfile)
    			else:
    				debug().critical("NoesAdmin printer.py couldn't open a temporary file.")
    				return False;
    
    		# Print dialog
    		dialog = QPrintDialog(printer, self.page.view())
    		if dialog.exec_() != QDialog.Accepted:
    			self.readyTask();
    			return
    		self.printDocument(printer, tmpfile)
    
    	@pyqtSlot()
    	def printPreview(self):
    		if self.page is None:
    			return
    		if self.m_inPrintPreview:
    			return
    		self.m_inPrintPreview = True
    
    		printer = self.getPrinter();
    		preview = QPrintPreviewDialog(printer,self.page.view())
    		preview.paintRequested.connect(self.printDocument);
    
    		if preview.exec_() != QDialog.Accepted:
    			self.readyTask();
    		self.m_inPrintPreview = False
    
    	@pyqtSlot(QPrinter)
    	def printDocument(self, printer, tmpfile=None):
    		result = False
    		loop = QEventLoop()
    		self.parent.changeStatusBar('Printjob loading...');
    		
    		def printPreview(sucess):
    			nonlocal result
    			result = sucess
    			loop.quit()
    	
    		self.page.print(printer,printPreview)
    		loop.exec_()
    		if not result:
    			print('no 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()
    		self.readyTask();
    		self.parent.readyStatusBar();
    
    
    		if self.pdf and tmpfile:
    			print('PDF print opening....');
    			self.openPDF(tmpfile.fileName())
    

    I tried to use QTextDocument as an alternative for QWebEngineView which worked perfectly fine except for the lack of support for modern html and css and I had trouble with the page size, so I rather use webengine if possible.

    Does anyone have an idea where it goes wrong? Sorry if the code is a bit messy.

    Thanks!

    (Qt 5.12.5 - Python 3.7.7)



  • I've got a bit further than yesterday with this issue. I went back to the example on this website:
    http://quabr.com:8182/59438021/how-do-i-create-a-print-preview-of-qwebengineview-in-pyqt5

    The problem has to do something with:

    printer = QPrinter(QPrinter.HighResolution)
    

    When I use QPrinter() without the HighResolution part, everything works as expected. I didn't realise that the example had the same problem since it calls the printPreview method which doesn't have the resolution part.

    Is this a bug in (Py)Qt?

    Since my problem is fixed, I'm will mark it as solved.


Log in to reply