diff -r ca9ef7600df7 -r f4775ae8f441 eric7/E5Graphics/E5GraphicsView.py --- a/eric7/E5Graphics/E5GraphicsView.py Fri May 21 17:54:15 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,411 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2007 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> -# - -""" -Module implementing a canvas view class. -""" - -import sys - -from PyQt6.QtCore import pyqtSignal, QRectF, QSize, QSizeF, Qt -from PyQt6.QtGui import QBrush, QPainter, QPixmap, QFont, QColor -from PyQt6.QtWidgets import QGraphicsView - -from E5Gui.E5Application import e5App - -import Preferences - - -class E5GraphicsView(QGraphicsView): - """ - Class implementing a graphics view. - - @signal zoomValueChanged(int) emitted to signal a change of the zoom value - """ - zoomValueChanged = pyqtSignal(int) - - ZoomLevels = [ - 1, 3, 5, 7, 9, - 10, 20, 30, 50, 67, 80, 90, - 100, - 110, 120, 133, 150, 170, 200, 240, 300, 400, - 500, 600, 700, 800, 900, 1000, - ] - ZoomLevelDefault = 100 - - def __init__(self, scene, parent=None): - """ - Constructor - - @param scene reference to the scene object (QGraphicsScene) - @param parent parent widget (QWidget) - """ - super().__init__(scene, parent) - self.setObjectName("E5GraphicsView") - - self.__initialSceneSize = self.scene().sceneRect().size() - self.setBackgroundBrush(QBrush(self.getBackgroundColor())) - self.setRenderHint(QPainter.RenderHint.Antialiasing, True) - self.setDragMode(QGraphicsView.DragMode.RubberBandDrag) - self.setAlignment( - Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop) - self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) - self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) - self.setViewportUpdateMode( - QGraphicsView.ViewportUpdateMode.SmartViewportUpdate) - - self.setWhatsThis(self.tr( - "<b>Graphics View</b>\n" - "<p>This graphics view is used to show a diagram. \n" - "There are various actions available to manipulate the \n" - "shown items.</p>\n" - "<ul>\n" - "<li>Clicking on an item selects it.</li>\n" - "<li>Ctrl-clicking adds an item to the selection.</li>\n" - "<li>Ctrl-clicking a selected item deselects it.</li>\n" - "<li>Clicking on an empty spot of the canvas resets the selection." - "</li>\n" - "<li>Dragging the mouse over the canvas spans a rubberband to \n" - "select multiple items.</li>\n" - "<li>Dragging the mouse over a selected item moves the \n" - "whole selection.</li>\n" - "</ul>\n" - )) - - def getDrawingColors(self): - """ - Public method to get the configured drawing colors. - - @return tuple containing the foreground and background colors - @rtype tuple of (QColor, QColor) - """ - drawingMode = Preferences.getGraphics("DrawingMode") - if drawingMode == "automatic": - if e5App().usesDarkPalette(): - drawingMode = "white_black" - else: - drawingMode = "black_white" - - if drawingMode == "white_black": - return (QColor("#ffffff"), QColor("#262626")) - else: - return (QColor("#000000"), QColor("#ffffff")) - - def getForegroundColor(self): - """ - Public method to get the configured foreground color. - - @return foreground color - @rtype QColor - """ - return self.getDrawingColors()[0] - - def getBackgroundColor(self): - """ - Public method to get the configured background color. - - @return background color - @rtype QColor - """ - return self.getDrawingColors()[1] - - def __levelForZoom(self, zoom): - """ - Private method determining the zoom level index given a zoom factor. - - @param zoom zoom factor (integer) - @return index of zoom factor (integer) - """ - try: - index = E5GraphicsView.ZoomLevels.index(zoom) - except ValueError: - for index in range(len(E5GraphicsView.ZoomLevels)): - if zoom <= E5GraphicsView.ZoomLevels[index]: - break - return index - - def zoomIn(self): - """ - Public method to zoom in. - """ - index = self.__levelForZoom(self.zoom()) - if index < len(E5GraphicsView.ZoomLevels) - 1: - self.setZoom(E5GraphicsView.ZoomLevels[index + 1]) - - def zoomOut(self): - """ - Public method to zoom out. - """ - index = self.__levelForZoom(self.zoom()) - if index > 0: - self.setZoom(E5GraphicsView.ZoomLevels[index - 1]) - - def zoomReset(self): - """ - Public method to handle the reset the zoom value. - """ - self.setZoom( - E5GraphicsView.ZoomLevels[E5GraphicsView.ZoomLevelDefault]) - - def setZoom(self, value): - """ - Public method to set the zoom value in percent. - - @param value zoom value in percent (integer) - """ - if value != self.zoom(): - self.resetTransform() - factor = value / 100.0 - self.scale(factor, factor) - self.zoomValueChanged.emit(value) - - def zoom(self): - """ - Public method to get the current zoom factor in percent. - - @return current zoom factor in percent (integer) - """ - return int(self.transform().m11() * 100.0) - - def resizeScene(self, amount, isWidth=True): - """ - Public method to resize the scene. - - @param amount size increment (integer) - @param isWidth flag indicating width is to be resized (boolean) - """ - sceneRect = self.scene().sceneRect() - width = sceneRect.width() - height = sceneRect.height() - if isWidth: - width += amount - else: - height += amount - rect = self._getDiagramRect(10) - if width < rect.width(): - width = rect.width() - if height < rect.height(): - height = rect.height() - - self.setSceneSize(width, height) - - def setSceneSize(self, width, height): - """ - Public method to set the scene size. - - @param width width for the scene (real) - @param height height for the scene (real) - """ - rect = self.scene().sceneRect() - rect.setHeight(height) - rect.setWidth(width) - self.scene().setSceneRect(rect) - - def autoAdjustSceneSize(self, limit=False): - """ - Public method to adjust the scene size to the diagram size. - - @param limit flag indicating to limit the scene to the - initial size (boolean) - """ - size = self._getDiagramSize(10) - if limit: - newWidth = max(size.width(), self.__initialSceneSize.width()) - newHeight = max(size.height(), self.__initialSceneSize.height()) - else: - newWidth = size.width() - newHeight = size.height() - self.setSceneSize(newWidth, newHeight) - - def _getDiagramRect(self, border=0): - """ - Protected method to calculate the minimum rectangle fitting the - diagram. - - @param border border width to include in the calculation (integer) - @return the minimum rectangle (QRectF) - """ - startx = sys.maxsize - starty = sys.maxsize - endx = 0 - endy = 0 - items = self.filteredItems(list(self.scene().items())) - for itm in items: - rect = itm.sceneBoundingRect() - itmEndX = rect.x() + rect.width() - itmEndY = rect.y() + rect.height() - itmStartX = rect.x() - itmStartY = rect.y() - if startx >= itmStartX: - startx = itmStartX - if starty >= itmStartY: - starty = itmStartY - if endx <= itmEndX: - endx = itmEndX - if endy <= itmEndY: - endy = itmEndY - if border: - startx -= border - starty -= border - endx += border - endy += border - - return QRectF(startx, starty, endx - startx + 1, endy - starty + 1) - - def _getDiagramSize(self, border=0): - """ - Protected method to calculate the minimum size fitting the diagram. - - @param border border width to include in the calculation (integer) - @return the minimum size (QSizeF) - """ - endx = 0 - endy = 0 - items = self.filteredItems(list(self.scene().items())) - for itm in items: - rect = itm.sceneBoundingRect() - itmEndX = rect.x() + rect.width() - itmEndY = rect.y() + rect.height() - if endx <= itmEndX: - endx = itmEndX - if endy <= itmEndY: - endy = itmEndY - if border: - endx += border - endy += border - - return QSizeF(endx + 1, endy + 1) - - def __getDiagram(self, rect, imageFormat="PNG", filename=None): - """ - Private method to retrieve the diagram from the scene fitting it - in the minimum rectangle. - - @param rect minimum rectangle fitting the diagram - @type QRectF - @param imageFormat format for the image file - @type str - @param filename name of the file for non pixmaps - str - @return paint device containing the diagram - @rtype QPixmap or QSvgGenerator - """ - selectedItems = self.scene().selectedItems() - - # step 1: deselect all widgets - if selectedItems: - for item in selectedItems: - item.setSelected(False) - - # step 2: grab the diagram - if imageFormat == "PNG": - paintDevice = QPixmap(int(rect.width()), int(rect.height())) - paintDevice.fill(self.backgroundBrush().color()) - else: - from PyQt6.QtSvg import QSvgGenerator - paintDevice = QSvgGenerator() - paintDevice.setResolution(100) # 100 dpi - paintDevice.setSize(QSize(int(rect.width()), int(rect.height()))) - paintDevice.setViewBox(rect) - paintDevice.setFileName(filename) - painter = QPainter(paintDevice) - painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) - self.scene().render(painter, QRectF(), rect) - - # step 3: reselect the widgets - if selectedItems: - for item in selectedItems: - item.setSelected(True) - - return paintDevice - - def saveImage(self, filename, imageFormat="PNG"): - """ - Public method to save the scene to a file. - - @param filename name of the file to write the image to (string) - @param imageFormat format for the image file (string) - @return flag indicating success (boolean) - """ - rect = self._getDiagramRect(self.border) - if imageFormat == "SVG": - self.__getDiagram(rect, imageFormat=imageFormat, filename=filename) - return True - else: - pixmap = self.__getDiagram(rect) - return pixmap.save(filename, imageFormat) - - def printDiagram(self, printer, diagramName=""): - """ - Public method to print the diagram. - - @param printer reference to a ready configured printer object - (QPrinter) - @param diagramName name of the diagram (string) - """ - painter = QPainter(printer) - - font = QFont(["times"], 10) - painter.setFont(font) - fm = painter.fontMetrics() - fontHeight = fm.lineSpacing() - marginX = ( - printer.pageLayout().paintRectPixels(printer.resolution()).x() - - printer.pageLayout().fullRectPixels(printer.resolution()).x() - ) - marginX = ( - Preferences.getPrinter("LeftMargin") * - int(printer.resolution() / 2.54) - marginX - ) - marginY = ( - printer.pageLayout().paintRectPixels(printer.resolution()).y() - - printer.pageLayout().fullRectPixels(printer.resolution()).y() - ) - marginY = ( - Preferences.getPrinter("TopMargin") * - int(printer.resolution() / 2.54) - marginY - ) - - width = ( - printer.width() - marginX - - Preferences.getPrinter("RightMargin") * - int(printer.resolution() / 2.54) - ) - height = ( - printer.height() - fontHeight - 4 - marginY - - Preferences.getPrinter("BottomMargin") * - int(printer.resolution() / 2.54) - ) - - self.scene().render(painter, - target=QRectF(marginX, marginY, width, height)) - - # write a foot note - tc = QColor(50, 50, 50) - painter.setPen(tc) - painter.drawRect(marginX, marginY, width, height) - painter.drawLine(marginX, marginY + height + 2, - marginX + width, marginY + height + 2) - painter.setFont(font) - painter.drawText(marginX, marginY + height + 4, width, - fontHeight, Qt.AlignmentFlag.AlignRight, diagramName) - - painter.end() - - ########################################################################### - ## The methods below should be overridden by subclasses to get special - ## behavior. - ########################################################################### - - def filteredItems(self, items): - """ - Public method to filter a list of items. - - @param items list of items as returned by the scene object - (QGraphicsItem) - @return list of interesting collision items (QGraphicsItem) - """ - # just return the list unchanged - return items