def _render_qwebpage_full(web_page, logger, viewport_size, image_size): image = QImage(image_size, QImage.Format_ARGB32) image.fill(0) painter = QPainter(image) try: painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.setWindow(QRect(QPoint(0, 0), web_page.viewportSize())) painter.setViewport(QRect(QPoint(0, 0), viewport_size)) if image_size != viewport_size: # Try not to draw stuff that won't fit into the image. Clipping # must be specified in input (aka logical) coordinates, but we know # it in output (aka physical) coordinates, so we have to do an # inverse transformation. If, for some reason, we cannot, skip the # clipping altogether. clip_rect = QRect(QPoint(0, 0), viewport_size) inv_transform, invertible = painter.combinedTransform().inverted() if invertible: painter.setClipRect(inv_transform.mapRect(clip_rect)) web_page.mainFrame().render(painter) finally: # It is important to end painter explicitly in python code, because # Python finalizer invocation order, unlike C++ destructors, is not # deterministic and there is a possibility of image's finalizer running # before painter's which may break tests and kill your cat. painter.end() return qimage_to_pil_image(image)
def _render_qwebpage_full(self, web_rect, render_rect, canvas_size): """Render web page in one step.""" if self._qpainter_needs_tiling(render_rect, canvas_size): # If this condition is true, this function may get stuck. raise ValueError("Rendering region is too large to be drawn" " in one step, use tile-by-tile renderer instead") canvas = QImage(canvas_size, self.qt_image_format) if self.is_jpeg(): # White background for JPEG images, same as we have in all browsers. canvas.fill(Qt.white) else: # Preserve old behaviour for PNG format. canvas.fill(0) painter = QPainter(canvas) try: painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.setWindow(web_rect) painter.setViewport(render_rect) painter.setClipRect(web_rect) self.web_page.mainFrame().render(painter) finally: painter.end() return WrappedQImage(canvas)
def print_photo(self): print_dialog = QPrintDialog(self._printer,self) if print_dialog.exec_() == QDialog.Accepted: painter = QPainter(self._printer) rect = painter.viewport() size = self._lbl_photo.pixmap().size() size.scale(rect.size(), Qt.KeepAspectRatio) painter.setViewport(rect.x(), rect.y(), size.width(), size.height()) painter.setWindow(self._lbl_photo.pixmap().rect()) painter.drawPixmap(0, 0, self._lbl_photo.pixmap())
def paintEvent(self, event): painter = QPainter() painter.begin(self) painter.fillRect(event.rect(), QBrush(Qt.white)) size = min(self.width(), self.height()) painter.setViewport(self.width() / 2 - size / 2, self.height() / 2 - size / 2, size, size) painter.setWindow(0, 0, 100, 100) painter.drawText(10, 10, 80, 80, Qt.AlignCenter, "Python") painter.end()
def on_actionPrint_triggered(self): if self.printer is None: self.printer = QPrinter(QPrinter.HighResolution) self.printer.setPageSize(QPrinter.Letter) form = QPrintDialog(self.printer, self) if form.exec_(): painter = QPainter(self.printer) rect = painter.viewport() size = self.image.size() size.scale(rect.size(), Qt.KeepAspectRatio) painter.setViewport(rect.x(), rect.y(), size.width(), size.height()) painter.drawImage(0, 0, self.image)
def filePrint(self): if self.image.isNull(): return if self.printer is None: self.printer = QPrinter(QPrinter.HighResolution) self.printer.setPageSize(QPrinter.Letter) form = QPrintDialog(self.printer, self) if form.exec_(): painter = QPainter(self.printer) rect = painter.viewport() size = self.image.size() size.scale(rect.size(), Qt.KeepAspectRatio) painter.setViewport(rect.x(), rect.y(), size.width(), size.height()) painter.drawImage(0, 0, self.image)
def _render_qwebpage_full(web_page, logger, web_rect, render_rect, canvas_size): """Render web page in one step.""" if _qpainter_needs_tiling(render_rect, canvas_size): # If this condition is true, this function may get stuck. raise ValueError("Rendering region is too large to be drawn" " in one step, use tile-by-tile renderer instead") canvas = QImage(canvas_size, QImage.Format_ARGB32) canvas.fill(0) painter = QPainter(canvas) try: painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.setWindow(web_rect) painter.setViewport(render_rect) painter.setClipRect(web_rect) web_page.mainFrame().render(painter) finally: painter.end() return WrappedQImage(canvas)
def paintEvent(self, event = None): LogicalSize = 100.0 def logicalFromPhysical(length, side): return (length / side) * LogicalSize fm = QFontMetricsF(self.font()) ymargin = ((LogicalSize / 30.0) + logicalFromPhysical(self.leftSpinBox.height(), self.height())) ymax = (LogicalSize - logicalFromPhysical(fm.height() * 2, self.height())) width = LogicalSize / 4.0 cx, cy = LogicalSize / 2.0, LogicalSize / 3.0 ax, ay = cx - (2 * width), ymargin bx, by = cx - width, ay dx, dy = cx + width, ay ex, ey = cx + (2 * width), ymargin fx, fy = cx + (width / 2), cx + (LogicalSize / 24.0) gx, gy = fx, ymax hx, hy = cx - (width / 2), ymax ix, iy = hx, fy painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) side = min(self.width(), self.height()) painter.setViewport((self.width() - side) / 2, (self.height() - side) / 2, side, side) painter.setWindow(0, 0, LogicalSize, LogicalSize) painter.setPen(Qt.NoPen) gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 100)) gradient.setColorAt(0, Qt.white) a = self.leftSpinBox.value() gradient.setColorAt(1, (Qt.red if a != 0 else Qt.white)) painter.setBrush(QBrush(gradient)) painter.drawPolygon(QPolygon([ax, ay, bx, by, cx, cy, ix, iy])) gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 100)) gradient.setColorAt(0, Qt.white) b = self.rightSpinBox.value() gradient.setColorAt(1, (Qt.blue if b != 0 else Qt.white)) painter.setBrush(QBrush(gradient)) painter.drawPolygon(QPolygon([cx, cy, dx, dy, ex, ey, fx, fy])) if (a + b) == 0: color = QColor(Qt.white) else: ashare = (a / (a + b)) * 255.0 bshare = 255.0 - ashare color = QColor(ashare, 0, bshare) gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 100)) gradient.setColorAt(0, Qt.white) gradient.setColorAt(1, color) painter.setBrush(QBrush(gradient)) painter.drawPolygon(QPolygon([cx, cy, fx, fy, gx, gy, hx, hy, ix, iy])) painter.setPen(Qt.black) painter.drawPolyline(QPolygon([ax, ay, ix, iy, hx, hy])) painter.drawPolyline(QPolygon([gx, gy, fx, fy, ex, ey])) painter.drawPolyline(QPolygon([bx, by, cx, cy, dx, dy]))
def paintEvent(self, event=None): LogicalSize = 100.0 def logicalFromPhysical(length, side): return (length / side) * LogicalSize fm = QFontMetricsF(self.font()) ymargin = ( (LogicalSize / 30.0) + logicalFromPhysical(self.leftSpinBox.height(), self.height())) ymax = (LogicalSize - logicalFromPhysical(fm.height() * 2, self.height())) width = LogicalSize / 4.0 cx, cy = LogicalSize / 2.0, LogicalSize / 3.0 ax, ay = cx - (2 * width), ymargin bx, by = cx - width, ay dx, dy = cx + width, ay ex, ey = cx + (2 * width), ymargin fx, fy = cx + (width / 2), cx + (LogicalSize / 24.0) gx, gy = fx, ymax hx, hy = cx - (width / 2), ymax ix, iy = hx, fy painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) side = min(self.width(), self.height()) painter.setViewport((self.width() - side) / 2, (self.height() - side) / 2, side, side) painter.setWindow(0, 0, LogicalSize, LogicalSize) painter.setPen(Qt.NoPen) gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 100)) gradient.setColorAt(0, Qt.white) a = self.leftSpinBox.value() gradient.setColorAt(1, (Qt.red if a != 0 else Qt.white)) painter.setBrush(QBrush(gradient)) painter.drawPolygon(QPolygon([ax, ay, bx, by, cx, cy, ix, iy])) gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 100)) gradient.setColorAt(0, Qt.white) b = self.rightSpinBox.value() gradient.setColorAt(1, (Qt.blue if b != 0 else Qt.white)) painter.setBrush(QBrush(gradient)) painter.drawPolygon(QPolygon([cx, cy, dx, dy, ex, ey, fx, fy])) if (a + b) == 0: color = QColor(Qt.white) else: ashare = (a / (a + b)) * 255.0 bshare = 255.0 - ashare color = QColor(ashare, 0, bshare) gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 100)) gradient.setColorAt(0, Qt.white) gradient.setColorAt(1, color) painter.setBrush(QBrush(gradient)) painter.drawPolygon(QPolygon([cx, cy, fx, fy, gx, gy, hx, hy, ix, iy])) painter.setPen(Qt.black) painter.drawPolyline(QPolygon([ax, ay, ix, iy, hx, hy])) painter.drawPolyline(QPolygon([gx, gy, fx, fy, ex, ey])) painter.drawPolyline(QPolygon([bx, by, cx, cy, dx, dy]))
def _render_qwebpage_tiled(web_page, logger, viewport_size, image_size): tile_maxsize = defaults.TILE_MAXSIZE draw_width = viewport_size.width() draw_height = min(viewport_size.height(), image_size.height()) # One bug is worked around by rendering the page one tile at a time onto a # small-ish temporary image. The magic happens in viewport-window # transformation: # # - Sizes of tile painter viewport and tile painter window match # webpage viewport size to avoid rescaling. # - Tile painter window is moved appropriately so that tile region is # overlayed onto the temporary image. tile_hsize = min(tile_maxsize, draw_width) tile_vsize = min(tile_maxsize, draw_height) htiles = 1 + (draw_width - 1) // tile_hsize vtiles = 1 + (draw_height - 1) // tile_vsize tile_image = QImage(QSize(tile_hsize, tile_vsize), QImage.Format_ARGB32) ratio = viewport_size.width() / float(web_page.viewportSize().width()) # The other bug manifests itself when you do painter.drawImage trying to # concatenate tiles onto a single image and once you reach 32'768 along # either dimension all of a sudden drawImage simply stops drawing anything. # The simplest workaround that comes to mind is to use pillow for pasting # images. pil_image = Image.new(mode='RGBA', size=(image_size.width(), image_size.height())) painter = QPainter(tile_image) try: painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.setWindow(QRect(QPoint(0, 0), web_page.viewportSize())) painter_viewport = QRect(QPoint(0, 0), viewport_size) for i in xrange(htiles): for j in xrange(vtiles): left, top = i * tile_hsize, j * tile_vsize painter.setViewport(painter_viewport.translated(-left, -top)) logger.log("Rendering with viewport=%s" % painter.viewport(), min_level=2) clip_rect = QRect( QPoint(floor(left / ratio), floor(top / ratio)), QPoint(ceil((left + tile_hsize) / ratio), ceil((top + tile_vsize) / ratio))) web_page.mainFrame().render(painter, QRegion(clip_rect)) pil_tile_image = qimage_to_pil_image(tile_image) if viewport_size.height() - top < tile_vsize: # If this is the last tile, make sure that the bottom of # the image is not garbled: the last tile's bottom may be # clipped and will then have stuff left over from rendering # the previous tile. Crop it, because the image can be # taller than the viewport because of "height=" option. box = (0, 0, tile_hsize, viewport_size.height() - top) pil_tile_image = pil_tile_image.crop(box) if logger: logger.log("Pasting rendered tile to coords: %s" % ((left, top), ), min_level=2) pil_image.paste(pil_tile_image, (left, top)) # Make sure that painter.end() is invoked before destroying the # underlying image. finally: painter.end() return pil_image
def _render_qwebpage_tiled(web_page, logger, web_rect, render_rect, canvas_size): """ Render web page tile-by-tile. This function works around bugs in QPaintEngine that occur when render_rect is larger than 32k pixels in either dimension. """ # One bug is worked around by rendering the page one tile at a time onto a # small-ish temporary image. The magic happens in viewport-window # transformation: painter viewport is moved appropriately so that rendering # region is overlayed onto a temporary "render" image which is then pasted # into the resulting one. # # The other bug manifests itself when you do painter.drawImage when pasting # the rendered tiles. Once you reach 32'768 along either dimension all of # a sudden drawImage simply stops drawing anything. This is a known # limitation of Qt painting system where coordinates are signed short ints. # The simplest workaround that comes to mind is to use pillow for pasting. tile_conf = _calculate_tiling( to_paint=render_rect.intersected(QRect(QPoint(0, 0), canvas_size))) canvas = Image.new('RGBA', _qsize_to_tuple(canvas_size)) ratio = render_rect.width() / float(web_rect.width()) tile_qimage = QImage(tile_conf['tile_size'], QImage.Format_ARGB32) painter = QPainter(tile_qimage) try: painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.setWindow(web_rect) # painter.setViewport here seems superfluous (actual viewport is being # set inside the loop below), but it is not. For some reason, if # viewport is reset after setClipRect, clipping rectangle is adjusted, # which is not what we want. painter.setViewport(render_rect) # painter.setClipRect(web_rect) for i in xrange(tile_conf['horizontal_count']): left = i * tile_qimage.width() for j in xrange(tile_conf['vertical_count']): top = j * tile_qimage.height() painter.setViewport(render_rect.translated(-left, -top)) logger.log("Rendering with viewport=%s" % painter.viewport(), min_level=2) clip_rect = QRect( QPoint(floor(left / ratio), floor(top / ratio)), QPoint(ceil((left + tile_qimage.width()) / ratio), ceil((top + tile_qimage.height()) / ratio))) web_page.mainFrame().render(painter, QRegion(clip_rect)) tile_image = qimage_to_pil_image(tile_qimage) # If this is the bottommost tile, its bottom may have stuff # left over from rendering the previous tile. Make sure these # leftovers don't garble the bottom of the canvas which can be # larger than render_rect because of "height=" option. rendered_vsize = min(render_rect.height() - top, tile_qimage.height()) if rendered_vsize < tile_qimage.height(): box = (0, 0, tile_qimage.width(), rendered_vsize) tile_image = tile_image.crop(box) logger.log("Pasting rendered tile to coords: %s" % ((left, top),), min_level=2) canvas.paste(tile_image, (left, top)) finally: # It is important to end painter explicitly in python code, because # Python finalizer invocation order, unlike C++ destructors, is not # deterministic and there is a possibility of image's finalizer running # before painter's which may break tests and kill your cat. painter.end() return WrappedPillowImage(canvas)
def paintEvent(self, event): painter = QPainter(self) painter.setViewport(self.centeredViewport(self.size())) self.renderer().render(painter)