src/eric7/Graphics/SvgDiagram.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2008 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog showing a SVG graphic.
8 """
9
10 from PyQt6.QtCore import Qt, QSize, QEvent, QMarginsF
11 from PyQt6.QtGui import QPalette, QPainter, QFont, QColor, QAction, QPageLayout
12 from PyQt6.QtWidgets import QSizePolicy, QScrollArea, QMenu, QToolBar
13 from PyQt6.QtPrintSupport import QPrinter, QPrintDialog
14 from PyQt6.QtSvgWidgets import QSvgWidget
15
16 from EricWidgets.EricMainWindow import EricMainWindow
17 from EricWidgets.EricZoomWidget import EricZoomWidget
18
19 import UI.Config
20
21 import Preferences
22
23
24 class SvgDiagram(EricMainWindow):
25 """
26 Class implementing a dialog showing a SVG graphic.
27 """
28 ZoomLevels = [
29 1, 3, 5, 7, 9,
30 10, 20, 30, 50, 67, 80, 90,
31 100,
32 110, 120, 133, 150, 170, 200, 240, 300, 400,
33 500, 600, 700, 800, 900, 1000,
34 ]
35 ZoomLevelDefault = 100
36
37 def __init__(self, svgFile, parent=None, name=None):
38 """
39 Constructor
40
41 @param svgFile filename of a SVG graphics file to show
42 @type str
43 @param parent parent widget of the view
44 @type QWidget
45 @param name name of the view widget
46 @type str
47 """
48 super().__init__(parent)
49 if name:
50 self.setObjectName(name)
51 else:
52 self.setObjectName("SvgDiagram")
53 self.setWindowTitle(self.tr("SVG-Viewer"))
54
55 self.svgWidget = QSvgWidget()
56 self.svgWidget.setObjectName("svgWidget")
57 self.svgWidget.setBackgroundRole(QPalette.ColorRole.Base)
58 self.svgWidget.setSizePolicy(
59 QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Ignored)
60
61 self.svgView = QScrollArea()
62 self.svgView.setObjectName("svgView")
63 self.svgView.setBackgroundRole(QPalette.ColorRole.Dark)
64 self.svgView.setWidget(self.svgWidget)
65
66 self.setCentralWidget(self.svgView)
67
68 self.__zoomWidget = EricZoomWidget(
69 UI.PixmapCache.getPixmap("zoomOut"),
70 UI.PixmapCache.getPixmap("zoomIn"),
71 UI.PixmapCache.getPixmap("zoomReset"), self)
72 self.statusBar().addPermanentWidget(self.__zoomWidget)
73 self.__zoomWidget.setMapping(
74 SvgDiagram.ZoomLevels, SvgDiagram.ZoomLevelDefault)
75 self.__zoomWidget.valueChanged.connect(self.__doZoom)
76
77 # polish up the dialog
78 self.resize(QSize(800, 600).expandedTo(self.minimumSizeHint()))
79
80 self.zoom = 1.0
81 self.svgFile = svgFile
82 self.svgWidget.load(self.svgFile)
83 self.svgWidget.resize(self.svgWidget.renderer().defaultSize())
84
85 self.__initActions()
86 self.__initContextMenu()
87 self.__initToolBars()
88
89 self.grabGesture(Qt.GestureType.PinchGesture)
90
91 def __initActions(self):
92 """
93 Private method to initialize the view actions.
94 """
95 self.closeAct = QAction(
96 UI.PixmapCache.getIcon("close"),
97 self.tr("Close"), self)
98 self.closeAct.triggered.connect(self.close)
99
100 self.printAct = QAction(
101 UI.PixmapCache.getIcon("print"),
102 self.tr("Print"), self)
103 self.printAct.triggered.connect(self.__printDiagram)
104
105 self.printPreviewAct = QAction(
106 UI.PixmapCache.getIcon("printPreview"),
107 self.tr("Print Preview"), self)
108 self.printPreviewAct.triggered.connect(self.__printPreviewDiagram)
109
110 def __initContextMenu(self):
111 """
112 Private method to initialize the context menu.
113 """
114 self.__menu = QMenu(self)
115 self.__menu.addAction(self.closeAct)
116 self.__menu.addSeparator()
117 self.__menu.addAction(self.printPreviewAct)
118 self.__menu.addAction(self.printAct)
119
120 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
121 self.customContextMenuRequested.connect(self.__showContextMenu)
122
123 def __showContextMenu(self, coord):
124 """
125 Private slot to show the context menu of the listview.
126
127 @param coord the position of the mouse pointer
128 @type QPoint
129 """
130 self.__menu.popup(self.mapToGlobal(coord))
131
132 def __initToolBars(self):
133 """
134 Private method to populate the toolbars with our actions.
135 """
136 self.windowToolBar = QToolBar(self.tr("Window"), self)
137 self.windowToolBar.setIconSize(UI.Config.ToolBarIconSize)
138 self.windowToolBar.addAction(self.closeAct)
139
140 self.graphicsToolBar = QToolBar(self.tr("Graphics"), self)
141 self.graphicsToolBar.setIconSize(UI.Config.ToolBarIconSize)
142 self.graphicsToolBar.addAction(self.printPreviewAct)
143 self.graphicsToolBar.addAction(self.printAct)
144
145 self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.windowToolBar)
146 self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.graphicsToolBar)
147
148 def getDiagramName(self):
149 """
150 Public method to retrieve a name for the diagram.
151
152 @return name for the diagram
153 @rtype str
154 """
155 return self.svgFile
156
157 def wheelEvent(self, evt):
158 """
159 Protected method to handle wheel events.
160
161 @param evt reference to the wheel event
162 @type QWheelEvent
163 """
164 if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
165 delta = evt.angleDelta().y()
166 if delta < 0:
167 self.__zoomOut()
168 elif delta > 0:
169 self.__zoomIn()
170 evt.accept()
171 return
172
173 super().wheelEvent(evt)
174
175 def event(self, evt):
176 """
177 Public method handling events.
178
179 @param evt reference to the event
180 @type QEvent
181 @return flag indicating, if the event was handled
182 @rtype bool
183 """
184 if evt.type() == QEvent.Type.Gesture:
185 self.gestureEvent(evt)
186 return True
187
188 return super().event(evt)
189
190 def gestureEvent(self, evt):
191 """
192 Protected method handling gesture events.
193
194 @param evt reference to the gesture event
195 @type QGestureEvent
196 """
197 pinch = evt.gesture(Qt.GestureType.PinchGesture)
198 if pinch:
199 if pinch.state() == Qt.GestureState.GestureStarted:
200 pinch.setTotalScaleFactor(self.__zoom() / 100)
201 elif pinch.state() == Qt.GestureState.GestureUpdated:
202 self.__doZoom(int(pinch.totalScaleFactor() * 100))
203 evt.accept()
204
205 ###########################################################################
206 ## Private menu handling methods below.
207 ###########################################################################
208
209 def __adjustScrollBar(self, scrollBar, factor):
210 """
211 Private method to adjust a scrollbar by a certain factor.
212
213 @param scrollBar reference to the scrollbar object
214 @type QScrollBar
215 @param factor factor to adjust by
216 @type float
217 """
218 scrollBar.setValue(
219 int(factor * scrollBar.value() +
220 ((factor - 1) * scrollBar.pageStep() / 2)))
221
222 def __levelForZoom(self, zoom):
223 """
224 Private method determining the zoom level index given a zoom factor.
225
226 @param zoom zoom factor
227 @type int
228 @return index of zoom factor
229 @rtype int
230 """
231 try:
232 index = SvgDiagram.ZoomLevels.index(zoom)
233 except ValueError:
234 for index in range(len(SvgDiagram.ZoomLevels)):
235 if zoom <= SvgDiagram.ZoomLevels[index]:
236 break
237 return index
238
239 def __doZoom(self, value):
240 """
241 Private method to set the zoom value in percent.
242
243 @param value zoom value in percent
244 @type int
245 """
246 oldValue = self.__zoom()
247 if value != oldValue:
248 self.svgWidget.resize(value / 100 * self.svgWidget.sizeHint())
249
250 factor = value / oldValue
251 self.__adjustScrollBar(self.svgView.horizontalScrollBar(), factor)
252 self.__adjustScrollBar(self.svgView.verticalScrollBar(), factor)
253
254 self.__zoomWidget.setValue(value)
255
256 def __zoomIn(self):
257 """
258 Private method to zoom into the SVG.
259 """
260 index = self.__levelForZoom(self.__zoom())
261 if index < len(SvgDiagram.ZoomLevels) - 1:
262 self.__doZoom(SvgDiagram.ZoomLevels[index + 1])
263
264 def __zoomOut(self):
265 """
266 Private method to zoom out of the SVG.
267 """
268 index = self.__levelForZoom(self.__zoom())
269 if index > 0:
270 self.__doZoom(SvgDiagram.ZoomLevels[index - 1])
271
272 def __zoomReset(self):
273 """
274 Private method to reset the zoom value.
275 """
276 self.__doZoom(SvgDiagram.ZoomLevels[SvgDiagram.ZoomLevelDefault])
277
278 def __zoom(self):
279 """
280 Private method to get the current zoom factor in percent.
281
282 @return current zoom factor in percent
283 @rtype int
284 """
285 return int(self.svgWidget.width() /
286 self.svgWidget.sizeHint().width() * 100.0)
287
288 def __printDiagram(self):
289 """
290 Private slot called to print the diagram.
291 """
292 printer = QPrinter(mode=QPrinter.PrinterMode.ScreenResolution)
293 printer.setFullPage(True)
294 if Preferences.getPrinter("ColorMode"):
295 printer.setColorMode(QPrinter.ColorMode.Color)
296 else:
297 printer.setColorMode(QPrinter.ColorMode.GrayScale)
298 if Preferences.getPrinter("FirstPageFirst"):
299 printer.setPageOrder(QPrinter.PageOrder.FirstPageFirst)
300 else:
301 printer.setPageOrder(QPrinter.PageOrder.LastPageFirst)
302 printerName = Preferences.getPrinter("PrinterName")
303 if printerName:
304 printer.setPrinterName(printerName)
305
306 printDialog = QPrintDialog(printer, self)
307 if printDialog.exec():
308 self.__print(printer)
309
310 def __printPreviewDiagram(self):
311 """
312 Private slot called to show a print preview of the diagram.
313 """
314 from PyQt6.QtPrintSupport import QPrintPreviewDialog
315
316 printer = QPrinter(mode=QPrinter.PrinterMode.ScreenResolution)
317 printer.setFullPage(True)
318 if Preferences.getPrinter("ColorMode"):
319 printer.setColorMode(QPrinter.ColorMode.Color)
320 else:
321 printer.setColorMode(QPrinter.ColorMode.GrayScale)
322 if Preferences.getPrinter("FirstPageFirst"):
323 printer.setPageOrder(QPrinter.PageOrder.FirstPageFirst)
324 else:
325 printer.setPageOrder(QPrinter.PageOrder.LastPageFirst)
326 printer.setPageMargins(QMarginsF(
327 Preferences.getPrinter("LeftMargin") * 10,
328 Preferences.getPrinter("TopMargin") * 10,
329 Preferences.getPrinter("RightMargin") * 10,
330 Preferences.getPrinter("BottomMargin") * 10),
331 QPageLayout.Unit.Millimeter
332 )
333 printerName = Preferences.getPrinter("PrinterName")
334 if printerName:
335 printer.setPrinterName(printerName)
336
337 preview = QPrintPreviewDialog(printer, self)
338 preview.paintRequested[QPrinter].connect(self.__print)
339 preview.exec()
340
341 def __print(self, printer):
342 """
343 Private slot to the actual printing.
344
345 @param printer reference to the printer object
346 @type QPrinter
347 """
348 painter = QPainter()
349 painter.begin(printer)
350
351 # calculate margin and width of printout
352 font = QFont(["times"], 10)
353 painter.setFont(font)
354 fm = painter.fontMetrics()
355 fontHeight = fm.lineSpacing()
356 marginX = (
357 printer.pageLayout().paintRectPixels(printer.resolution()).x() -
358 printer.pageLayout().fullRectPixels(printer.resolution()).x()
359 )
360 marginX = (
361 Preferences.getPrinter("LeftMargin") *
362 int(printer.resolution() / 2.54) - marginX
363 )
364 marginY = (
365 printer.pageLayout().paintRectPixels(printer.resolution()).y() -
366 printer.pageLayout().fullRectPixels(printer.resolution()).y()
367 )
368 marginY = (
369 Preferences.getPrinter("TopMargin") *
370 int(printer.resolution() / 2.54) - marginY
371 )
372
373 width = (
374 printer.width() - marginX -
375 Preferences.getPrinter("RightMargin") *
376 int(printer.resolution() / 2.54)
377 )
378 height = (
379 printer.height() - fontHeight - 4 - marginY -
380 Preferences.getPrinter("BottomMargin") *
381 int(printer.resolution() / 2.54)
382 )
383
384 # write a foot note
385 s = self.tr("Diagram: {0}").format(self.getDiagramName())
386 tc = QColor(50, 50, 50)
387 painter.setPen(tc)
388 painter.drawRect(marginX, marginY, width, height)
389 painter.drawLine(marginX, marginY + height + 2,
390 marginX + width, marginY + height + 2)
391 painter.setFont(font)
392 painter.drawText(marginX, marginY + height + 4, width,
393 fontHeight, Qt.AlignmentFlag.AlignRight, s)
394
395 # render the diagram
396 painter.setViewport(marginX, marginY, width, height)
397 self.svgWidget.renderer().render(painter)
398 painter.end()

eric ide

mercurial