19 |
19 |
20 |
20 |
21 class EricGraphicsView(QGraphicsView): |
21 class EricGraphicsView(QGraphicsView): |
22 """ |
22 """ |
23 Class implementing a graphics view. |
23 Class implementing a graphics view. |
24 |
24 |
25 @signal zoomValueChanged(int) emitted to signal a change of the zoom value |
25 @signal zoomValueChanged(int) emitted to signal a change of the zoom value |
26 """ |
26 """ |
|
27 |
27 zoomValueChanged = pyqtSignal(int) |
28 zoomValueChanged = pyqtSignal(int) |
28 |
29 |
29 ZoomLevels = [ |
30 ZoomLevels = [ |
30 1, 3, 5, 7, 9, |
31 1, |
31 10, 20, 30, 50, 67, 80, 90, |
32 3, |
|
33 5, |
|
34 7, |
|
35 9, |
|
36 10, |
|
37 20, |
|
38 30, |
|
39 50, |
|
40 67, |
|
41 80, |
|
42 90, |
32 100, |
43 100, |
33 110, 120, 133, 150, 170, 200, 240, 300, 400, |
44 110, |
34 500, 600, 700, 800, 900, 1000, |
45 120, |
|
46 133, |
|
47 150, |
|
48 170, |
|
49 200, |
|
50 240, |
|
51 300, |
|
52 400, |
|
53 500, |
|
54 600, |
|
55 700, |
|
56 800, |
|
57 900, |
|
58 1000, |
35 ] |
59 ] |
36 ZoomLevelDefault = 100 |
60 ZoomLevelDefault = 100 |
37 |
61 |
38 def __init__(self, scene, parent=None): |
62 def __init__(self, scene, parent=None): |
39 """ |
63 """ |
40 Constructor |
64 Constructor |
41 |
65 |
42 @param scene reference to the scene object (QGraphicsScene) |
66 @param scene reference to the scene object (QGraphicsScene) |
43 @param parent parent widget (QWidget) |
67 @param parent parent widget (QWidget) |
44 """ |
68 """ |
45 super().__init__(scene, parent) |
69 super().__init__(scene, parent) |
46 self.setObjectName("EricGraphicsView") |
70 self.setObjectName("EricGraphicsView") |
47 |
71 |
48 self.__initialSceneSize = self.scene().sceneRect().size() |
72 self.__initialSceneSize = self.scene().sceneRect().size() |
49 self.setBackgroundBrush(QBrush(self.getBackgroundColor())) |
73 self.setBackgroundBrush(QBrush(self.getBackgroundColor())) |
50 self.setRenderHint(QPainter.RenderHint.Antialiasing, True) |
74 self.setRenderHint(QPainter.RenderHint.Antialiasing, True) |
51 self.setDragMode(QGraphicsView.DragMode.RubberBandDrag) |
75 self.setDragMode(QGraphicsView.DragMode.RubberBandDrag) |
52 self.setAlignment( |
76 self.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop) |
53 Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop) |
|
54 self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) |
77 self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) |
55 self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) |
78 self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) |
56 self.setViewportUpdateMode( |
79 self.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.SmartViewportUpdate) |
57 QGraphicsView.ViewportUpdateMode.SmartViewportUpdate) |
80 |
58 |
81 self.setWhatsThis( |
59 self.setWhatsThis(self.tr( |
82 self.tr( |
60 "<b>Graphics View</b>\n" |
83 "<b>Graphics View</b>\n" |
61 "<p>This graphics view is used to show a diagram. \n" |
84 "<p>This graphics view is used to show a diagram. \n" |
62 "There are various actions available to manipulate the \n" |
85 "There are various actions available to manipulate the \n" |
63 "shown items.</p>\n" |
86 "shown items.</p>\n" |
64 "<ul>\n" |
87 "<ul>\n" |
65 "<li>Clicking on an item selects it.</li>\n" |
88 "<li>Clicking on an item selects it.</li>\n" |
66 "<li>Ctrl-clicking adds an item to the selection.</li>\n" |
89 "<li>Ctrl-clicking adds an item to the selection.</li>\n" |
67 "<li>Ctrl-clicking a selected item deselects it.</li>\n" |
90 "<li>Ctrl-clicking a selected item deselects it.</li>\n" |
68 "<li>Clicking on an empty spot of the canvas resets the selection." |
91 "<li>Clicking on an empty spot of the canvas resets the selection." |
69 "</li>\n" |
92 "</li>\n" |
70 "<li>Dragging the mouse over the canvas spans a rubberband to \n" |
93 "<li>Dragging the mouse over the canvas spans a rubberband to \n" |
71 "select multiple items.</li>\n" |
94 "select multiple items.</li>\n" |
72 "<li>Dragging the mouse over a selected item moves the \n" |
95 "<li>Dragging the mouse over a selected item moves the \n" |
73 "whole selection.</li>\n" |
96 "whole selection.</li>\n" |
74 "</ul>\n" |
97 "</ul>\n" |
75 )) |
98 ) |
76 |
99 ) |
|
100 |
77 def getDrawingColors(self): |
101 def getDrawingColors(self): |
78 """ |
102 """ |
79 Public method to get the configured drawing colors. |
103 Public method to get the configured drawing colors. |
80 |
104 |
81 @return tuple containing the foreground and background colors |
105 @return tuple containing the foreground and background colors |
82 @rtype tuple of (QColor, QColor) |
106 @rtype tuple of (QColor, QColor) |
83 """ |
107 """ |
84 drawingMode = Preferences.getGraphics("DrawingMode") |
108 drawingMode = Preferences.getGraphics("DrawingMode") |
85 if drawingMode == "automatic": |
109 if drawingMode == "automatic": |
86 if ericApp().usesDarkPalette(): |
110 if ericApp().usesDarkPalette(): |
87 drawingMode = "white_black" |
111 drawingMode = "white_black" |
88 else: |
112 else: |
89 drawingMode = "black_white" |
113 drawingMode = "black_white" |
90 |
114 |
91 if drawingMode == "white_black": |
115 if drawingMode == "white_black": |
92 return (QColor("#ffffff"), QColor("#262626")) |
116 return (QColor("#ffffff"), QColor("#262626")) |
93 else: |
117 else: |
94 return (QColor("#000000"), QColor("#ffffff")) |
118 return (QColor("#000000"), QColor("#ffffff")) |
95 |
119 |
96 def getForegroundColor(self): |
120 def getForegroundColor(self): |
97 """ |
121 """ |
98 Public method to get the configured foreground color. |
122 Public method to get the configured foreground color. |
99 |
123 |
100 @return foreground color |
124 @return foreground color |
101 @rtype QColor |
125 @rtype QColor |
102 """ |
126 """ |
103 return self.getDrawingColors()[0] |
127 return self.getDrawingColors()[0] |
104 |
128 |
105 def getBackgroundColor(self): |
129 def getBackgroundColor(self): |
106 """ |
130 """ |
107 Public method to get the configured background color. |
131 Public method to get the configured background color. |
108 |
132 |
109 @return background color |
133 @return background color |
110 @rtype QColor |
134 @rtype QColor |
111 """ |
135 """ |
112 return self.getDrawingColors()[1] |
136 return self.getDrawingColors()[1] |
113 |
137 |
114 def __levelForZoom(self, zoom): |
138 def __levelForZoom(self, zoom): |
115 """ |
139 """ |
116 Private method determining the zoom level index given a zoom factor. |
140 Private method determining the zoom level index given a zoom factor. |
117 |
141 |
118 @param zoom zoom factor (integer) |
142 @param zoom zoom factor (integer) |
119 @return index of zoom factor (integer) |
143 @return index of zoom factor (integer) |
120 """ |
144 """ |
121 try: |
145 try: |
122 index = EricGraphicsView.ZoomLevels.index(zoom) |
146 index = EricGraphicsView.ZoomLevels.index(zoom) |
123 except ValueError: |
147 except ValueError: |
124 for index in range(len(EricGraphicsView.ZoomLevels)): |
148 for index in range(len(EricGraphicsView.ZoomLevels)): |
125 if zoom <= EricGraphicsView.ZoomLevels[index]: |
149 if zoom <= EricGraphicsView.ZoomLevels[index]: |
126 break |
150 break |
127 return index |
151 return index |
128 |
152 |
129 def zoomIn(self): |
153 def zoomIn(self): |
130 """ |
154 """ |
131 Public method to zoom in. |
155 Public method to zoom in. |
132 """ |
156 """ |
133 index = self.__levelForZoom(self.zoom()) |
157 index = self.__levelForZoom(self.zoom()) |
134 if index < len(EricGraphicsView.ZoomLevels) - 1: |
158 if index < len(EricGraphicsView.ZoomLevels) - 1: |
135 self.setZoom(EricGraphicsView.ZoomLevels[index + 1]) |
159 self.setZoom(EricGraphicsView.ZoomLevels[index + 1]) |
136 |
160 |
137 def zoomOut(self): |
161 def zoomOut(self): |
138 """ |
162 """ |
139 Public method to zoom out. |
163 Public method to zoom out. |
140 """ |
164 """ |
141 index = self.__levelForZoom(self.zoom()) |
165 index = self.__levelForZoom(self.zoom()) |
142 if index > 0: |
166 if index > 0: |
143 self.setZoom(EricGraphicsView.ZoomLevels[index - 1]) |
167 self.setZoom(EricGraphicsView.ZoomLevels[index - 1]) |
144 |
168 |
145 def zoomReset(self): |
169 def zoomReset(self): |
146 """ |
170 """ |
147 Public method to handle the reset the zoom value. |
171 Public method to handle the reset the zoom value. |
148 """ |
172 """ |
149 self.setZoom( |
173 self.setZoom(EricGraphicsView.ZoomLevels[EricGraphicsView.ZoomLevelDefault]) |
150 EricGraphicsView.ZoomLevels[EricGraphicsView.ZoomLevelDefault]) |
174 |
151 |
|
152 def setZoom(self, value): |
175 def setZoom(self, value): |
153 """ |
176 """ |
154 Public method to set the zoom value in percent. |
177 Public method to set the zoom value in percent. |
155 |
178 |
156 @param value zoom value in percent (integer) |
179 @param value zoom value in percent (integer) |
157 """ |
180 """ |
158 if value != self.zoom(): |
181 if value != self.zoom(): |
159 self.resetTransform() |
182 self.resetTransform() |
160 factor = value / 100.0 |
183 factor = value / 100.0 |
161 self.scale(factor, factor) |
184 self.scale(factor, factor) |
162 self.zoomValueChanged.emit(value) |
185 self.zoomValueChanged.emit(value) |
163 |
186 |
164 def zoom(self): |
187 def zoom(self): |
165 """ |
188 """ |
166 Public method to get the current zoom factor in percent. |
189 Public method to get the current zoom factor in percent. |
167 |
190 |
168 @return current zoom factor in percent (integer) |
191 @return current zoom factor in percent (integer) |
169 """ |
192 """ |
170 return int(self.transform().m11() * 100.0) |
193 return int(self.transform().m11() * 100.0) |
171 |
194 |
172 def resizeScene(self, amount, isWidth=True): |
195 def resizeScene(self, amount, isWidth=True): |
173 """ |
196 """ |
174 Public method to resize the scene. |
197 Public method to resize the scene. |
175 |
198 |
176 @param amount size increment (integer) |
199 @param amount size increment (integer) |
177 @param isWidth flag indicating width is to be resized (boolean) |
200 @param isWidth flag indicating width is to be resized (boolean) |
178 """ |
201 """ |
179 sceneRect = self.scene().sceneRect() |
202 sceneRect = self.scene().sceneRect() |
180 width = sceneRect.width() |
203 width = sceneRect.width() |
273 if endy <= itmEndY: |
296 if endy <= itmEndY: |
274 endy = itmEndY |
297 endy = itmEndY |
275 if border: |
298 if border: |
276 endx += border |
299 endx += border |
277 endy += border |
300 endy += border |
278 |
301 |
279 return QSizeF(endx + 1, endy + 1) |
302 return QSizeF(endx + 1, endy + 1) |
280 |
303 |
281 def __getDiagram(self, rect, imageFormat="PNG", filename=None): |
304 def __getDiagram(self, rect, imageFormat="PNG", filename=None): |
282 """ |
305 """ |
283 Private method to retrieve the diagram from the scene fitting it |
306 Private method to retrieve the diagram from the scene fitting it |
284 in the minimum rectangle. |
307 in the minimum rectangle. |
285 |
308 |
286 @param rect minimum rectangle fitting the diagram |
309 @param rect minimum rectangle fitting the diagram |
287 @type QRectF |
310 @type QRectF |
288 @param imageFormat format for the image file |
311 @param imageFormat format for the image file |
289 @type str |
312 @type str |
290 @param filename name of the file for non pixmaps |
313 @param filename name of the file for non pixmaps |
291 str |
314 str |
292 @return paint device containing the diagram |
315 @return paint device containing the diagram |
293 @rtype QPixmap or QSvgGenerator |
316 @rtype QPixmap or QSvgGenerator |
294 """ |
317 """ |
295 selectedItems = self.scene().selectedItems() |
318 selectedItems = self.scene().selectedItems() |
296 |
319 |
297 # step 1: deselect all widgets |
320 # step 1: deselect all widgets |
298 if selectedItems: |
321 if selectedItems: |
299 for item in selectedItems: |
322 for item in selectedItems: |
300 item.setSelected(False) |
323 item.setSelected(False) |
301 |
324 |
302 # step 2: grab the diagram |
325 # step 2: grab the diagram |
303 if imageFormat == "PNG": |
326 if imageFormat == "PNG": |
304 paintDevice = QPixmap(int(rect.width()), int(rect.height())) |
327 paintDevice = QPixmap(int(rect.width()), int(rect.height())) |
305 paintDevice.fill(self.backgroundBrush().color()) |
328 paintDevice.fill(self.backgroundBrush().color()) |
306 else: |
329 else: |
307 from PyQt6.QtSvg import QSvgGenerator |
330 from PyQt6.QtSvg import QSvgGenerator |
|
331 |
308 paintDevice = QSvgGenerator() |
332 paintDevice = QSvgGenerator() |
309 paintDevice.setResolution(100) # 100 dpi |
333 paintDevice.setResolution(100) # 100 dpi |
310 paintDevice.setSize(QSize(int(rect.width()), int(rect.height()))) |
334 paintDevice.setSize(QSize(int(rect.width()), int(rect.height()))) |
311 paintDevice.setViewBox(rect) |
335 paintDevice.setViewBox(rect) |
312 paintDevice.setFileName(filename) |
336 paintDevice.setFileName(filename) |
313 painter = QPainter(paintDevice) |
337 painter = QPainter(paintDevice) |
314 painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) |
338 painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) |
315 self.scene().render(painter, QRectF(), rect) |
339 self.scene().render(painter, QRectF(), rect) |
316 |
340 |
317 # step 3: reselect the widgets |
341 # step 3: reselect the widgets |
318 if selectedItems: |
342 if selectedItems: |
319 for item in selectedItems: |
343 for item in selectedItems: |
320 item.setSelected(True) |
344 item.setSelected(True) |
321 |
345 |
322 return paintDevice |
346 return paintDevice |
323 |
347 |
324 def saveImage(self, filename, imageFormat="PNG"): |
348 def saveImage(self, filename, imageFormat="PNG"): |
325 """ |
349 """ |
326 Public method to save the scene to a file. |
350 Public method to save the scene to a file. |
327 |
351 |
328 @param filename name of the file to write the image to (string) |
352 @param filename name of the file to write the image to (string) |
329 @param imageFormat format for the image file (string) |
353 @param imageFormat format for the image file (string) |
330 @return flag indicating success (boolean) |
354 @return flag indicating success (boolean) |
331 """ |
355 """ |
332 rect = self._getDiagramRect(self.border) |
356 rect = self._getDiagramRect(self.border) |
334 self.__getDiagram(rect, imageFormat=imageFormat, filename=filename) |
358 self.__getDiagram(rect, imageFormat=imageFormat, filename=filename) |
335 return True |
359 return True |
336 else: |
360 else: |
337 pixmap = self.__getDiagram(rect) |
361 pixmap = self.__getDiagram(rect) |
338 return pixmap.save(filename, imageFormat) |
362 return pixmap.save(filename, imageFormat) |
339 |
363 |
340 def printDiagram(self, printer, diagramName=""): |
364 def printDiagram(self, printer, diagramName=""): |
341 """ |
365 """ |
342 Public method to print the diagram. |
366 Public method to print the diagram. |
343 |
367 |
344 @param printer reference to a ready configured printer object |
368 @param printer reference to a ready configured printer object |
345 (QPrinter) |
369 (QPrinter) |
346 @param diagramName name of the diagram (string) |
370 @param diagramName name of the diagram (string) |
347 """ |
371 """ |
348 painter = QPainter(printer) |
372 painter = QPainter(printer) |
349 |
373 |
350 font = QFont(["times"], 10) |
374 font = QFont(["times"], 10) |
351 painter.setFont(font) |
375 painter.setFont(font) |
352 fm = painter.fontMetrics() |
376 fm = painter.fontMetrics() |
353 fontHeight = fm.lineSpacing() |
377 fontHeight = fm.lineSpacing() |
354 marginX = ( |
378 marginX = ( |
355 printer.pageLayout().paintRectPixels(printer.resolution()).x() - |
379 printer.pageLayout().paintRectPixels(printer.resolution()).x() |
356 printer.pageLayout().fullRectPixels(printer.resolution()).x() |
380 - printer.pageLayout().fullRectPixels(printer.resolution()).x() |
357 ) |
381 ) |
358 marginX = ( |
382 marginX = ( |
359 Preferences.getPrinter("LeftMargin") * |
383 Preferences.getPrinter("LeftMargin") * int(printer.resolution() / 2.54) |
360 int(printer.resolution() / 2.54) - marginX |
384 - marginX |
361 ) |
385 ) |
362 marginY = ( |
386 marginY = ( |
363 printer.pageLayout().paintRectPixels(printer.resolution()).y() - |
387 printer.pageLayout().paintRectPixels(printer.resolution()).y() |
364 printer.pageLayout().fullRectPixels(printer.resolution()).y() |
388 - printer.pageLayout().fullRectPixels(printer.resolution()).y() |
365 ) |
389 ) |
366 marginY = ( |
390 marginY = ( |
367 Preferences.getPrinter("TopMargin") * |
391 Preferences.getPrinter("TopMargin") * int(printer.resolution() / 2.54) |
368 int(printer.resolution() / 2.54) - marginY |
392 - marginY |
369 ) |
393 ) |
370 |
394 |
371 width = ( |
395 width = ( |
372 printer.width() - marginX - |
396 printer.width() |
373 Preferences.getPrinter("RightMargin") * |
397 - marginX |
374 int(printer.resolution() / 2.54) |
398 - Preferences.getPrinter("RightMargin") * int(printer.resolution() / 2.54) |
375 ) |
399 ) |
376 height = ( |
400 height = ( |
377 printer.height() - fontHeight - 4 - marginY - |
401 printer.height() |
378 Preferences.getPrinter("BottomMargin") * |
402 - fontHeight |
379 int(printer.resolution() / 2.54) |
403 - 4 |
380 ) |
404 - marginY |
381 |
405 - Preferences.getPrinter("BottomMargin") * int(printer.resolution() / 2.54) |
382 self.scene().render(painter, |
406 ) |
383 target=QRectF(marginX, marginY, width, height)) |
407 |
384 |
408 self.scene().render(painter, target=QRectF(marginX, marginY, width, height)) |
|
409 |
385 # write a foot note |
410 # write a foot note |
386 tc = QColor(50, 50, 50) |
411 tc = QColor(50, 50, 50) |
387 painter.setPen(tc) |
412 painter.setPen(tc) |
388 painter.drawRect(marginX, marginY, width, height) |
413 painter.drawRect(marginX, marginY, width, height) |
389 painter.drawLine(marginX, marginY + height + 2, |
414 painter.drawLine( |
390 marginX + width, marginY + height + 2) |
415 marginX, marginY + height + 2, marginX + width, marginY + height + 2 |
|
416 ) |
391 painter.setFont(font) |
417 painter.setFont(font) |
392 painter.drawText(marginX, marginY + height + 4, width, |
418 painter.drawText( |
393 fontHeight, Qt.AlignmentFlag.AlignRight, diagramName) |
419 marginX, |
394 |
420 marginY + height + 4, |
|
421 width, |
|
422 fontHeight, |
|
423 Qt.AlignmentFlag.AlignRight, |
|
424 diagramName, |
|
425 ) |
|
426 |
395 painter.end() |
427 painter.end() |
396 |
428 |
397 ########################################################################### |
429 ########################################################################### |
398 ## The methods below should be overridden by subclasses to get special |
430 ## The methods below should be overridden by subclasses to get special |
399 ## behavior. |
431 ## behavior. |
400 ########################################################################### |
432 ########################################################################### |
401 |
433 |
402 def filteredItems(self, items): |
434 def filteredItems(self, items): |
403 """ |
435 """ |
404 Public method to filter a list of items. |
436 Public method to filter a list of items. |
405 |
437 |
406 @param items list of items as returned by the scene object |
438 @param items list of items as returned by the scene object |
407 (QGraphicsItem) |
439 (QGraphicsItem) |
408 @return list of interesting collision items (QGraphicsItem) |
440 @return list of interesting collision items (QGraphicsItem) |
409 """ |
441 """ |
410 # just return the list unchanged |
442 # just return the list unchanged |