|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the icon editor main window. |
|
8 """ |
|
9 |
|
10 import os |
|
11 import contextlib |
|
12 |
|
13 from PyQt5.QtCore import ( |
|
14 pyqtSignal, Qt, QSize, QSignalMapper, QFileInfo, QFile, QEvent |
|
15 ) |
|
16 from PyQt5.QtGui import ( |
|
17 QPalette, QImage, QImageReader, QImageWriter, QKeySequence |
|
18 ) |
|
19 from PyQt5.QtWidgets import QScrollArea, QLabel, QDockWidget, QWhatsThis |
|
20 |
|
21 from E5Gui.E5Action import E5Action, createActionGroup |
|
22 from E5Gui import E5FileDialog, E5MessageBox |
|
23 from E5Gui.E5MainWindow import E5MainWindow |
|
24 from E5Gui.E5ZoomWidget import E5ZoomWidget |
|
25 |
|
26 from .IconEditorGrid import IconEditorGrid |
|
27 |
|
28 import UI.PixmapCache |
|
29 import UI.Config |
|
30 |
|
31 import Preferences |
|
32 |
|
33 |
|
34 class IconEditorWindow(E5MainWindow): |
|
35 """ |
|
36 Class implementing the web browser main window. |
|
37 |
|
38 @signal editorClosed() emitted after the window was requested to close down |
|
39 """ |
|
40 editorClosed = pyqtSignal() |
|
41 |
|
42 windows = [] |
|
43 |
|
44 def __init__(self, fileName="", parent=None, fromEric=False, |
|
45 initShortcutsOnly=False, project=None): |
|
46 """ |
|
47 Constructor |
|
48 |
|
49 @param fileName name of a file to load on startup (string) |
|
50 @param parent parent widget of this window (QWidget) |
|
51 @param fromEric flag indicating whether it was called from within |
|
52 eric (boolean) |
|
53 @param initShortcutsOnly flag indicating to just initialize the |
|
54 keyboard shortcuts (boolean) |
|
55 @param project reference to the project object (Project) |
|
56 """ |
|
57 super().__init__(parent) |
|
58 self.setObjectName("eric6_icon_editor") |
|
59 |
|
60 self.fromEric = fromEric |
|
61 self.initShortcutsOnly = initShortcutsOnly |
|
62 self.setWindowIcon(UI.PixmapCache.getIcon("iconEditor")) |
|
63 |
|
64 if self.initShortcutsOnly: |
|
65 self.__initActions() |
|
66 else: |
|
67 if not self.fromEric: |
|
68 self.setStyle(Preferences.getUI("Style"), |
|
69 Preferences.getUI("StyleSheet")) |
|
70 self.__editor = IconEditorGrid() |
|
71 self.__scrollArea = QScrollArea() |
|
72 self.__scrollArea.setWidget(self.__editor) |
|
73 self.__scrollArea.viewport().setBackgroundRole( |
|
74 QPalette.ColorRole.Dark) |
|
75 self.__scrollArea.viewport().setAutoFillBackground(True) |
|
76 self.setCentralWidget(self.__scrollArea) |
|
77 |
|
78 g = Preferences.getGeometry("IconEditorGeometry") |
|
79 if g.isEmpty(): |
|
80 s = QSize(600, 500) |
|
81 self.resize(s) |
|
82 else: |
|
83 self.restoreGeometry(g) |
|
84 |
|
85 self.__initActions() |
|
86 self.__initMenus() |
|
87 self.__initToolbars() |
|
88 self.__createStatusBar() |
|
89 self.__initFileFilters() |
|
90 self.__createPaletteDock() |
|
91 |
|
92 self.__palette.previewChanged(self.__editor.previewPixmap()) |
|
93 self.__palette.colorChanged(self.__editor.penColor()) |
|
94 self.__palette.setCompositingMode(self.__editor.compositingMode()) |
|
95 |
|
96 self.__class__.windows.append(self) |
|
97 |
|
98 state = Preferences.getIconEditor("IconEditorState") |
|
99 self.restoreState(state) |
|
100 |
|
101 self.__editor.imageChanged.connect(self.__modificationChanged) |
|
102 self.__editor.positionChanged.connect(self.__updatePosition) |
|
103 self.__editor.sizeChanged.connect(self.__updateSize) |
|
104 self.__editor.previewChanged.connect(self.__palette.previewChanged) |
|
105 self.__editor.colorChanged.connect(self.__palette.colorChanged) |
|
106 self.__palette.colorSelected.connect(self.__editor.setPenColor) |
|
107 self.__palette.compositingChanged.connect( |
|
108 self.__editor.setCompositingMode) |
|
109 |
|
110 self.__setCurrentFile("") |
|
111 if fileName: |
|
112 self.__loadIconFile(fileName) |
|
113 |
|
114 self.__checkActions() |
|
115 |
|
116 self.__project = project |
|
117 self.__lastOpenPath = "" |
|
118 self.__lastSavePath = "" |
|
119 |
|
120 self.grabGesture(Qt.GestureType.PinchGesture) |
|
121 |
|
122 def __initFileFilters(self): |
|
123 """ |
|
124 Private method to define the supported image file filters. |
|
125 """ |
|
126 filters = { |
|
127 'bmp': self.tr("Windows Bitmap File (*.bmp)"), |
|
128 'cur': self.tr("Windows Cursor File (*.cur)"), |
|
129 'dds': self.tr("DirectDraw-Surface File (*.dds)"), |
|
130 'gif': self.tr("Graphic Interchange Format File (*.gif)"), |
|
131 'icns': self.tr("Apple Icon File (*.icns)"), |
|
132 'ico': self.tr("Windows Icon File (*.ico)"), |
|
133 'jp2': self.tr("JPEG2000 File (*.jp2)"), |
|
134 'jpg': self.tr("JPEG File (*.jpg)"), |
|
135 'jpeg': self.tr("JPEG File (*.jpeg)"), |
|
136 'mng': self.tr("Multiple-Image Network Graphics File (*.mng)"), |
|
137 'pbm': self.tr("Portable Bitmap File (*.pbm)"), |
|
138 'pcx': self.tr("Paintbrush Bitmap File (*.pcx)"), |
|
139 'pgm': self.tr("Portable Graymap File (*.pgm)"), |
|
140 'png': self.tr("Portable Network Graphics File (*.png)"), |
|
141 'ppm': self.tr("Portable Pixmap File (*.ppm)"), |
|
142 'sgi': self.tr("Silicon Graphics Image File (*.sgi)"), |
|
143 'svg': self.tr("Scalable Vector Graphics File (*.svg)"), |
|
144 'svgz': self.tr("Compressed Scalable Vector Graphics File" |
|
145 " (*.svgz)"), |
|
146 'tga': self.tr("Targa Graphic File (*.tga)"), |
|
147 'tif': self.tr("TIFF File (*.tif)"), |
|
148 'tiff': self.tr("TIFF File (*.tiff)"), |
|
149 'wbmp': self.tr("WAP Bitmap File (*.wbmp)"), |
|
150 'webp': self.tr("WebP Image File (*.webp)"), |
|
151 'xbm': self.tr("X11 Bitmap File (*.xbm)"), |
|
152 'xpm': self.tr("X11 Pixmap File (*.xpm)"), |
|
153 } |
|
154 |
|
155 inputFormats = [] |
|
156 readFormats = QImageReader.supportedImageFormats() |
|
157 for readFormat in readFormats: |
|
158 with contextlib.suppress(KeyError): |
|
159 inputFormats.append(filters[bytes(readFormat).decode()]) |
|
160 inputFormats.sort() |
|
161 inputFormats.append(self.tr("All Files (*)")) |
|
162 self.__inputFilter = ';;'.join(inputFormats) |
|
163 |
|
164 outputFormats = [] |
|
165 writeFormats = QImageWriter.supportedImageFormats() |
|
166 for writeFormat in writeFormats: |
|
167 with contextlib.suppress(KeyError): |
|
168 outputFormats.append(filters[bytes(writeFormat).decode()]) |
|
169 outputFormats.sort() |
|
170 self.__outputFilter = ';;'.join(outputFormats) |
|
171 |
|
172 self.__defaultFilter = filters['png'] |
|
173 |
|
174 def __initActions(self): |
|
175 """ |
|
176 Private method to define the user interface actions. |
|
177 """ |
|
178 # list of all actions |
|
179 self.__actions = [] |
|
180 |
|
181 self.__initFileActions() |
|
182 self.__initEditActions() |
|
183 self.__initViewActions() |
|
184 self.__initToolsActions() |
|
185 self.__initHelpActions() |
|
186 |
|
187 def __initFileActions(self): |
|
188 """ |
|
189 Private method to define the file related user interface actions. |
|
190 """ |
|
191 self.newAct = E5Action( |
|
192 self.tr('New'), |
|
193 UI.PixmapCache.getIcon("new"), |
|
194 self.tr('&New'), |
|
195 QKeySequence(self.tr("Ctrl+N", "File|New")), |
|
196 0, self, 'iconEditor_file_new') |
|
197 self.newAct.setStatusTip(self.tr('Create a new icon')) |
|
198 self.newAct.setWhatsThis(self.tr( |
|
199 """<b>New</b>""" |
|
200 """<p>This creates a new icon.</p>""" |
|
201 )) |
|
202 self.newAct.triggered.connect(self.__newIcon) |
|
203 self.__actions.append(self.newAct) |
|
204 |
|
205 self.newWindowAct = E5Action( |
|
206 self.tr('New Window'), |
|
207 UI.PixmapCache.getIcon("newWindow"), |
|
208 self.tr('New &Window'), |
|
209 0, 0, self, 'iconEditor_file_new_window') |
|
210 self.newWindowAct.setStatusTip(self.tr( |
|
211 'Open a new icon editor window')) |
|
212 self.newWindowAct.setWhatsThis(self.tr( |
|
213 """<b>New Window</b>""" |
|
214 """<p>This opens a new icon editor window.</p>""" |
|
215 )) |
|
216 self.newWindowAct.triggered.connect(self.__newWindow) |
|
217 self.__actions.append(self.newWindowAct) |
|
218 |
|
219 self.openAct = E5Action( |
|
220 self.tr('Open'), |
|
221 UI.PixmapCache.getIcon("open"), |
|
222 self.tr('&Open...'), |
|
223 QKeySequence(self.tr("Ctrl+O", "File|Open")), |
|
224 0, self, 'iconEditor_file_open') |
|
225 self.openAct.setStatusTip(self.tr('Open an icon file for editing')) |
|
226 self.openAct.setWhatsThis(self.tr( |
|
227 """<b>Open File</b>""" |
|
228 """<p>This opens a new icon file for editing.""" |
|
229 """ It pops up a file selection dialog.</p>""" |
|
230 )) |
|
231 self.openAct.triggered.connect(self.__openIcon) |
|
232 self.__actions.append(self.openAct) |
|
233 |
|
234 self.saveAct = E5Action( |
|
235 self.tr('Save'), |
|
236 UI.PixmapCache.getIcon("fileSave"), |
|
237 self.tr('&Save'), |
|
238 QKeySequence(self.tr("Ctrl+S", "File|Save")), |
|
239 0, self, 'iconEditor_file_save') |
|
240 self.saveAct.setStatusTip(self.tr('Save the current icon')) |
|
241 self.saveAct.setWhatsThis(self.tr( |
|
242 """<b>Save File</b>""" |
|
243 """<p>Save the contents of the icon editor window.</p>""" |
|
244 )) |
|
245 self.saveAct.triggered.connect(self.__saveIcon) |
|
246 self.__actions.append(self.saveAct) |
|
247 |
|
248 self.saveAsAct = E5Action( |
|
249 self.tr('Save As'), |
|
250 UI.PixmapCache.getIcon("fileSaveAs"), |
|
251 self.tr('Save &As...'), |
|
252 QKeySequence(self.tr("Shift+Ctrl+S", "File|Save As")), |
|
253 0, self, 'iconEditor_file_save_as') |
|
254 self.saveAsAct.setStatusTip( |
|
255 self.tr('Save the current icon to a new file')) |
|
256 self.saveAsAct.setWhatsThis(self.tr( |
|
257 """<b>Save As...</b>""" |
|
258 """<p>Saves the current icon to a new file.</p>""" |
|
259 )) |
|
260 self.saveAsAct.triggered.connect(self.__saveIconAs) |
|
261 self.__actions.append(self.saveAsAct) |
|
262 |
|
263 self.closeAct = E5Action( |
|
264 self.tr('Close'), |
|
265 UI.PixmapCache.getIcon("close"), |
|
266 self.tr('&Close'), |
|
267 QKeySequence(self.tr("Ctrl+W", "File|Close")), |
|
268 0, self, 'iconEditor_file_close') |
|
269 self.closeAct.setStatusTip(self.tr( |
|
270 'Close the current icon editor window')) |
|
271 self.closeAct.setWhatsThis(self.tr( |
|
272 """<b>Close</b>""" |
|
273 """<p>Closes the current icon editor window.</p>""" |
|
274 )) |
|
275 self.closeAct.triggered.connect(self.close) |
|
276 self.__actions.append(self.closeAct) |
|
277 |
|
278 self.closeAllAct = E5Action( |
|
279 self.tr('Close All'), |
|
280 self.tr('Close &All'), |
|
281 0, 0, self, 'iconEditor_file_close_all') |
|
282 self.closeAllAct.setStatusTip(self.tr( |
|
283 'Close all icon editor windows')) |
|
284 self.closeAllAct.setWhatsThis(self.tr( |
|
285 """<b>Close All</b>""" |
|
286 """<p>Closes all icon editor windows except the first one.</p>""" |
|
287 )) |
|
288 self.closeAllAct.triggered.connect(self.__closeAll) |
|
289 self.__actions.append(self.closeAllAct) |
|
290 |
|
291 self.closeOthersAct = E5Action( |
|
292 self.tr('Close Others'), |
|
293 self.tr('Close Others'), |
|
294 0, 0, self, 'iconEditor_file_close_others') |
|
295 self.closeOthersAct.setStatusTip(self.tr( |
|
296 'Close all other icon editor windows')) |
|
297 self.closeOthersAct.setWhatsThis(self.tr( |
|
298 """<b>Close Others</b>""" |
|
299 """<p>Closes all other icon editor windows.</p>""" |
|
300 )) |
|
301 self.closeOthersAct.triggered.connect(self.__closeOthers) |
|
302 self.__actions.append(self.closeOthersAct) |
|
303 |
|
304 self.exitAct = E5Action( |
|
305 self.tr('Quit'), |
|
306 UI.PixmapCache.getIcon("exit"), |
|
307 self.tr('&Quit'), |
|
308 QKeySequence(self.tr("Ctrl+Q", "File|Quit")), |
|
309 0, self, 'iconEditor_file_quit') |
|
310 self.exitAct.setStatusTip(self.tr('Quit the icon editor')) |
|
311 self.exitAct.setWhatsThis(self.tr( |
|
312 """<b>Quit</b>""" |
|
313 """<p>Quit the icon editor.</p>""" |
|
314 )) |
|
315 if not self.fromEric: |
|
316 self.exitAct.triggered.connect(self.__closeAll) |
|
317 self.__actions.append(self.exitAct) |
|
318 |
|
319 def __initEditActions(self): |
|
320 """ |
|
321 Private method to create the Edit actions. |
|
322 """ |
|
323 self.undoAct = E5Action( |
|
324 self.tr('Undo'), |
|
325 UI.PixmapCache.getIcon("editUndo"), |
|
326 self.tr('&Undo'), |
|
327 QKeySequence(self.tr("Ctrl+Z", "Edit|Undo")), |
|
328 QKeySequence(self.tr("Alt+Backspace", "Edit|Undo")), |
|
329 self, 'iconEditor_edit_undo') |
|
330 self.undoAct.setStatusTip(self.tr('Undo the last change')) |
|
331 self.undoAct.setWhatsThis(self.tr( |
|
332 """<b>Undo</b>""" |
|
333 """<p>Undo the last change done.</p>""" |
|
334 )) |
|
335 self.undoAct.triggered.connect(self.__editor.editUndo) |
|
336 self.__actions.append(self.undoAct) |
|
337 |
|
338 self.redoAct = E5Action( |
|
339 self.tr('Redo'), |
|
340 UI.PixmapCache.getIcon("editRedo"), |
|
341 self.tr('&Redo'), |
|
342 QKeySequence(self.tr("Ctrl+Shift+Z", "Edit|Redo")), |
|
343 0, self, 'iconEditor_edit_redo') |
|
344 self.redoAct.setStatusTip(self.tr('Redo the last change')) |
|
345 self.redoAct.setWhatsThis(self.tr( |
|
346 """<b>Redo</b>""" |
|
347 """<p>Redo the last change done.</p>""" |
|
348 )) |
|
349 self.redoAct.triggered.connect(self.__editor.editRedo) |
|
350 self.__actions.append(self.redoAct) |
|
351 |
|
352 self.cutAct = E5Action( |
|
353 self.tr('Cut'), |
|
354 UI.PixmapCache.getIcon("editCut"), |
|
355 self.tr('Cu&t'), |
|
356 QKeySequence(self.tr("Ctrl+X", "Edit|Cut")), |
|
357 QKeySequence(self.tr("Shift+Del", "Edit|Cut")), |
|
358 self, 'iconEditor_edit_cut') |
|
359 self.cutAct.setStatusTip(self.tr('Cut the selection')) |
|
360 self.cutAct.setWhatsThis(self.tr( |
|
361 """<b>Cut</b>""" |
|
362 """<p>Cut the selected image area to the clipboard.</p>""" |
|
363 )) |
|
364 self.cutAct.triggered.connect(self.__editor.editCut) |
|
365 self.__actions.append(self.cutAct) |
|
366 |
|
367 self.copyAct = E5Action( |
|
368 self.tr('Copy'), |
|
369 UI.PixmapCache.getIcon("editCopy"), |
|
370 self.tr('&Copy'), |
|
371 QKeySequence(self.tr("Ctrl+C", "Edit|Copy")), |
|
372 QKeySequence(self.tr("Ctrl+Ins", "Edit|Copy")), |
|
373 self, 'iconEditor_edit_copy') |
|
374 self.copyAct.setStatusTip(self.tr('Copy the selection')) |
|
375 self.copyAct.setWhatsThis(self.tr( |
|
376 """<b>Copy</b>""" |
|
377 """<p>Copy the selected image area to the clipboard.</p>""" |
|
378 )) |
|
379 self.copyAct.triggered.connect(self.__editor.editCopy) |
|
380 self.__actions.append(self.copyAct) |
|
381 |
|
382 self.pasteAct = E5Action( |
|
383 self.tr('Paste'), |
|
384 UI.PixmapCache.getIcon("editPaste"), |
|
385 self.tr('&Paste'), |
|
386 QKeySequence(self.tr("Ctrl+V", "Edit|Paste")), |
|
387 QKeySequence(self.tr("Shift+Ins", "Edit|Paste")), |
|
388 self, 'iconEditor_edit_paste') |
|
389 self.pasteAct.setStatusTip(self.tr('Paste the clipboard image')) |
|
390 self.pasteAct.setWhatsThis(self.tr( |
|
391 """<b>Paste</b>""" |
|
392 """<p>Paste the clipboard image.</p>""" |
|
393 )) |
|
394 self.pasteAct.triggered.connect(self.__editor.editPaste) |
|
395 self.__actions.append(self.pasteAct) |
|
396 |
|
397 self.pasteNewAct = E5Action( |
|
398 self.tr('Paste as New'), |
|
399 self.tr('Paste as &New'), |
|
400 0, 0, self, 'iconEditor_edit_paste_as_new') |
|
401 self.pasteNewAct.setStatusTip(self.tr( |
|
402 'Paste the clipboard image replacing the current one')) |
|
403 self.pasteNewAct.setWhatsThis(self.tr( |
|
404 """<b>Paste as New</b>""" |
|
405 """<p>Paste the clipboard image replacing the current one.</p>""" |
|
406 )) |
|
407 self.pasteNewAct.triggered.connect(self.__editor.editPasteAsNew) |
|
408 self.__actions.append(self.pasteNewAct) |
|
409 |
|
410 self.deleteAct = E5Action( |
|
411 self.tr('Clear'), |
|
412 UI.PixmapCache.getIcon("editDelete"), |
|
413 self.tr('Cl&ear'), |
|
414 QKeySequence(self.tr("Alt+Shift+C", "Edit|Clear")), |
|
415 0, |
|
416 self, 'iconEditor_edit_clear') |
|
417 self.deleteAct.setStatusTip(self.tr('Clear the icon image')) |
|
418 self.deleteAct.setWhatsThis(self.tr( |
|
419 """<b>Clear</b>""" |
|
420 """<p>Clear the icon image and set it to be completely""" |
|
421 """ transparent.</p>""" |
|
422 )) |
|
423 self.deleteAct.triggered.connect(self.__editor.editClear) |
|
424 self.__actions.append(self.deleteAct) |
|
425 |
|
426 self.selectAllAct = E5Action( |
|
427 self.tr('Select All'), |
|
428 self.tr('&Select All'), |
|
429 QKeySequence(self.tr("Ctrl+A", "Edit|Select All")), |
|
430 0, |
|
431 self, 'iconEditor_edit_select_all') |
|
432 self.selectAllAct.setStatusTip(self.tr( |
|
433 'Select the complete icon image')) |
|
434 self.selectAllAct.setWhatsThis(self.tr( |
|
435 """<b>Select All</b>""" |
|
436 """<p>Selects the complete icon image.</p>""" |
|
437 )) |
|
438 self.selectAllAct.triggered.connect(self.__editor.editSelectAll) |
|
439 self.__actions.append(self.selectAllAct) |
|
440 |
|
441 self.resizeAct = E5Action( |
|
442 self.tr('Change Size'), |
|
443 UI.PixmapCache.getIcon("transformResize"), |
|
444 self.tr('Change Si&ze...'), |
|
445 0, 0, |
|
446 self, 'iconEditor_edit_change_size') |
|
447 self.resizeAct.setStatusTip(self.tr('Change the icon size')) |
|
448 self.resizeAct.setWhatsThis(self.tr( |
|
449 """<b>Change Size...</b>""" |
|
450 """<p>Changes the icon size.</p>""" |
|
451 )) |
|
452 self.resizeAct.triggered.connect(self.__editor.editResize) |
|
453 self.__actions.append(self.resizeAct) |
|
454 |
|
455 self.grayscaleAct = E5Action( |
|
456 self.tr('Grayscale'), |
|
457 UI.PixmapCache.getIcon("grayscale"), |
|
458 self.tr('&Grayscale'), |
|
459 0, 0, |
|
460 self, 'iconEditor_edit_grayscale') |
|
461 self.grayscaleAct.setStatusTip(self.tr( |
|
462 'Change the icon to grayscale')) |
|
463 self.grayscaleAct.setWhatsThis(self.tr( |
|
464 """<b>Grayscale</b>""" |
|
465 """<p>Changes the icon to grayscale.</p>""" |
|
466 )) |
|
467 self.grayscaleAct.triggered.connect(self.__editor.grayScale) |
|
468 self.__actions.append(self.grayscaleAct) |
|
469 |
|
470 self.redoAct.setEnabled(False) |
|
471 self.__editor.canRedoChanged.connect(self.redoAct.setEnabled) |
|
472 |
|
473 self.undoAct.setEnabled(False) |
|
474 self.__editor.canUndoChanged.connect(self.undoAct.setEnabled) |
|
475 |
|
476 self.cutAct.setEnabled(False) |
|
477 self.copyAct.setEnabled(False) |
|
478 self.__editor.selectionAvailable.connect(self.cutAct.setEnabled) |
|
479 self.__editor.selectionAvailable.connect(self.copyAct.setEnabled) |
|
480 |
|
481 self.pasteAct.setEnabled(self.__editor.canPaste()) |
|
482 self.pasteNewAct.setEnabled(self.__editor.canPaste()) |
|
483 self.__editor.clipboardImageAvailable.connect( |
|
484 self.pasteAct.setEnabled) |
|
485 self.__editor.clipboardImageAvailable.connect( |
|
486 self.pasteNewAct.setEnabled) |
|
487 |
|
488 def __initViewActions(self): |
|
489 """ |
|
490 Private method to create the View actions. |
|
491 """ |
|
492 self.zoomInAct = E5Action( |
|
493 self.tr('Zoom in'), |
|
494 UI.PixmapCache.getIcon("zoomIn"), |
|
495 self.tr('Zoom &in'), |
|
496 QKeySequence(self.tr("Ctrl++", "View|Zoom in")), |
|
497 0, self, 'iconEditor_view_zoom_in') |
|
498 self.zoomInAct.setStatusTip(self.tr('Zoom in on the icon')) |
|
499 self.zoomInAct.setWhatsThis(self.tr( |
|
500 """<b>Zoom in</b>""" |
|
501 """<p>Zoom in on the icon. This makes the grid bigger.</p>""" |
|
502 )) |
|
503 self.zoomInAct.triggered.connect(self.__zoomIn) |
|
504 self.__actions.append(self.zoomInAct) |
|
505 |
|
506 self.zoomOutAct = E5Action( |
|
507 self.tr('Zoom out'), |
|
508 UI.PixmapCache.getIcon("zoomOut"), |
|
509 self.tr('Zoom &out'), |
|
510 QKeySequence(self.tr("Ctrl+-", "View|Zoom out")), |
|
511 0, self, 'iconEditor_view_zoom_out') |
|
512 self.zoomOutAct.setStatusTip(self.tr('Zoom out on the icon')) |
|
513 self.zoomOutAct.setWhatsThis(self.tr( |
|
514 """<b>Zoom out</b>""" |
|
515 """<p>Zoom out on the icon. This makes the grid smaller.</p>""" |
|
516 )) |
|
517 self.zoomOutAct.triggered.connect(self.__zoomOut) |
|
518 self.__actions.append(self.zoomOutAct) |
|
519 |
|
520 self.zoomResetAct = E5Action( |
|
521 self.tr('Zoom reset'), |
|
522 UI.PixmapCache.getIcon("zoomReset"), |
|
523 self.tr('Zoom &reset'), |
|
524 QKeySequence(self.tr("Ctrl+0", "View|Zoom reset")), |
|
525 0, self, 'iconEditor_view_zoom_reset') |
|
526 self.zoomResetAct.setStatusTip(self.tr( |
|
527 'Reset the zoom of the icon')) |
|
528 self.zoomResetAct.setWhatsThis(self.tr( |
|
529 """<b>Zoom reset</b>""" |
|
530 """<p>Reset the zoom of the icon. """ |
|
531 """This sets the zoom factor to 100%.</p>""" |
|
532 )) |
|
533 self.zoomResetAct.triggered.connect(self.__zoomReset) |
|
534 self.__actions.append(self.zoomResetAct) |
|
535 |
|
536 self.showGridAct = E5Action( |
|
537 self.tr('Show Grid'), |
|
538 UI.PixmapCache.getIcon("grid"), |
|
539 self.tr('Show &Grid'), |
|
540 0, 0, |
|
541 self, 'iconEditor_view_show_grid') |
|
542 self.showGridAct.setStatusTip(self.tr( |
|
543 'Toggle the display of the grid')) |
|
544 self.showGridAct.setWhatsThis(self.tr( |
|
545 """<b>Show Grid</b>""" |
|
546 """<p>Toggle the display of the grid.</p>""" |
|
547 )) |
|
548 self.showGridAct.triggered[bool].connect(self.__editor.setGridEnabled) |
|
549 self.__actions.append(self.showGridAct) |
|
550 self.showGridAct.setCheckable(True) |
|
551 self.showGridAct.setChecked(self.__editor.isGridEnabled()) |
|
552 |
|
553 def __initToolsActions(self): |
|
554 """ |
|
555 Private method to create the View actions. |
|
556 """ |
|
557 self.esm = QSignalMapper(self) |
|
558 try: |
|
559 self.esm.mappedInt.connect(self.__editor.setTool) |
|
560 except AttributeError: |
|
561 # pre Qt 5.15 |
|
562 self.esm.mapped[int].connect(self.__editor.setTool) |
|
563 |
|
564 self.drawingActGrp = createActionGroup(self) |
|
565 self.drawingActGrp.setExclusive(True) |
|
566 |
|
567 self.drawPencilAct = E5Action( |
|
568 self.tr('Freehand'), |
|
569 UI.PixmapCache.getIcon("drawBrush"), |
|
570 self.tr('&Freehand'), |
|
571 0, 0, |
|
572 self.drawingActGrp, 'iconEditor_tools_pencil') |
|
573 self.drawPencilAct.setWhatsThis(self.tr( |
|
574 """<b>Free hand</b>""" |
|
575 """<p>Draws non linear lines.</p>""" |
|
576 )) |
|
577 self.drawPencilAct.setCheckable(True) |
|
578 self.esm.setMapping(self.drawPencilAct, IconEditorGrid.Pencil) |
|
579 self.drawPencilAct.triggered.connect(self.esm.map) |
|
580 self.__actions.append(self.drawPencilAct) |
|
581 |
|
582 self.drawColorPickerAct = E5Action( |
|
583 self.tr('Color Picker'), |
|
584 UI.PixmapCache.getIcon("colorPicker"), |
|
585 self.tr('&Color Picker'), |
|
586 0, 0, |
|
587 self.drawingActGrp, 'iconEditor_tools_color_picker') |
|
588 self.drawColorPickerAct.setWhatsThis(self.tr( |
|
589 """<b>Color Picker</b>""" |
|
590 """<p>The color of the pixel clicked on will become """ |
|
591 """the current draw color.</p>""" |
|
592 )) |
|
593 self.drawColorPickerAct.setCheckable(True) |
|
594 self.esm.setMapping(self.drawColorPickerAct, |
|
595 IconEditorGrid.ColorPicker) |
|
596 self.drawColorPickerAct.triggered.connect(self.esm.map) |
|
597 self.__actions.append(self.drawColorPickerAct) |
|
598 |
|
599 self.drawRectangleAct = E5Action( |
|
600 self.tr('Rectangle'), |
|
601 UI.PixmapCache.getIcon("drawRectangle"), |
|
602 self.tr('&Rectangle'), |
|
603 0, 0, |
|
604 self.drawingActGrp, 'iconEditor_tools_rectangle') |
|
605 self.drawRectangleAct.setWhatsThis(self.tr( |
|
606 """<b>Rectangle</b>""" |
|
607 """<p>Draw a rectangle.</p>""" |
|
608 )) |
|
609 self.drawRectangleAct.setCheckable(True) |
|
610 self.esm.setMapping(self.drawRectangleAct, IconEditorGrid.Rectangle) |
|
611 self.drawRectangleAct.triggered.connect(self.esm.map) |
|
612 self.__actions.append(self.drawRectangleAct) |
|
613 |
|
614 self.drawFilledRectangleAct = E5Action( |
|
615 self.tr('Filled Rectangle'), |
|
616 UI.PixmapCache.getIcon("drawRectangleFilled"), |
|
617 self.tr('F&illed Rectangle'), |
|
618 0, 0, |
|
619 self.drawingActGrp, 'iconEditor_tools_filled_rectangle') |
|
620 self.drawFilledRectangleAct.setWhatsThis(self.tr( |
|
621 """<b>Filled Rectangle</b>""" |
|
622 """<p>Draw a filled rectangle.</p>""" |
|
623 )) |
|
624 self.drawFilledRectangleAct.setCheckable(True) |
|
625 self.esm.setMapping(self.drawFilledRectangleAct, |
|
626 IconEditorGrid.FilledRectangle) |
|
627 self.drawFilledRectangleAct.triggered.connect(self.esm.map) |
|
628 self.__actions.append(self.drawFilledRectangleAct) |
|
629 |
|
630 self.drawCircleAct = E5Action( |
|
631 self.tr('Circle'), |
|
632 UI.PixmapCache.getIcon("drawCircle"), |
|
633 self.tr('Circle'), |
|
634 0, 0, |
|
635 self.drawingActGrp, 'iconEditor_tools_circle') |
|
636 self.drawCircleAct.setWhatsThis(self.tr( |
|
637 """<b>Circle</b>""" |
|
638 """<p>Draw a circle.</p>""" |
|
639 )) |
|
640 self.drawCircleAct.setCheckable(True) |
|
641 self.esm.setMapping(self.drawCircleAct, IconEditorGrid.Circle) |
|
642 self.drawCircleAct.triggered.connect(self.esm.map) |
|
643 self.__actions.append(self.drawCircleAct) |
|
644 |
|
645 self.drawFilledCircleAct = E5Action( |
|
646 self.tr('Filled Circle'), |
|
647 UI.PixmapCache.getIcon("drawCircleFilled"), |
|
648 self.tr('Fille&d Circle'), |
|
649 0, 0, |
|
650 self.drawingActGrp, 'iconEditor_tools_filled_circle') |
|
651 self.drawFilledCircleAct.setWhatsThis(self.tr( |
|
652 """<b>Filled Circle</b>""" |
|
653 """<p>Draw a filled circle.</p>""" |
|
654 )) |
|
655 self.drawFilledCircleAct.setCheckable(True) |
|
656 self.esm.setMapping(self.drawFilledCircleAct, |
|
657 IconEditorGrid.FilledCircle) |
|
658 self.drawFilledCircleAct.triggered.connect(self.esm.map) |
|
659 self.__actions.append(self.drawFilledCircleAct) |
|
660 |
|
661 self.drawEllipseAct = E5Action( |
|
662 self.tr('Ellipse'), |
|
663 UI.PixmapCache.getIcon("drawEllipse"), |
|
664 self.tr('&Ellipse'), |
|
665 0, 0, |
|
666 self.drawingActGrp, 'iconEditor_tools_ellipse') |
|
667 self.drawEllipseAct.setWhatsThis(self.tr( |
|
668 """<b>Ellipse</b>""" |
|
669 """<p>Draw an ellipse.</p>""" |
|
670 )) |
|
671 self.drawEllipseAct.setCheckable(True) |
|
672 self.esm.setMapping(self.drawEllipseAct, IconEditorGrid.Ellipse) |
|
673 self.drawEllipseAct.triggered.connect(self.esm.map) |
|
674 self.__actions.append(self.drawEllipseAct) |
|
675 |
|
676 self.drawFilledEllipseAct = E5Action( |
|
677 self.tr('Filled Ellipse'), |
|
678 UI.PixmapCache.getIcon("drawEllipseFilled"), |
|
679 self.tr('Fille&d Elli&pse'), |
|
680 0, 0, |
|
681 self.drawingActGrp, 'iconEditor_tools_filled_ellipse') |
|
682 self.drawFilledEllipseAct.setWhatsThis(self.tr( |
|
683 """<b>Filled Ellipse</b>""" |
|
684 """<p>Draw a filled ellipse.</p>""" |
|
685 )) |
|
686 self.drawFilledEllipseAct.setCheckable(True) |
|
687 self.esm.setMapping(self.drawFilledEllipseAct, |
|
688 IconEditorGrid.FilledEllipse) |
|
689 self.drawFilledEllipseAct.triggered.connect(self.esm.map) |
|
690 self.__actions.append(self.drawFilledEllipseAct) |
|
691 |
|
692 self.drawFloodFillAct = E5Action( |
|
693 self.tr('Flood Fill'), |
|
694 UI.PixmapCache.getIcon("drawFill"), |
|
695 self.tr('Fl&ood Fill'), |
|
696 0, 0, |
|
697 self.drawingActGrp, 'iconEditor_tools_flood_fill') |
|
698 self.drawFloodFillAct.setWhatsThis(self.tr( |
|
699 """<b>Flood Fill</b>""" |
|
700 """<p>Fill adjoining pixels with the same color with """ |
|
701 """the current color.</p>""" |
|
702 )) |
|
703 self.drawFloodFillAct.setCheckable(True) |
|
704 self.esm.setMapping(self.drawFloodFillAct, IconEditorGrid.Fill) |
|
705 self.drawFloodFillAct.triggered.connect(self.esm.map) |
|
706 self.__actions.append(self.drawFloodFillAct) |
|
707 |
|
708 self.drawLineAct = E5Action( |
|
709 self.tr('Line'), |
|
710 UI.PixmapCache.getIcon("drawLine"), |
|
711 self.tr('&Line'), |
|
712 0, 0, |
|
713 self.drawingActGrp, 'iconEditor_tools_line') |
|
714 self.drawLineAct.setWhatsThis(self.tr( |
|
715 """<b>Line</b>""" |
|
716 """<p>Draw a line.</p>""" |
|
717 )) |
|
718 self.drawLineAct.setCheckable(True) |
|
719 self.esm.setMapping(self.drawLineAct, IconEditorGrid.Line) |
|
720 self.drawLineAct.triggered.connect(self.esm.map) |
|
721 self.__actions.append(self.drawLineAct) |
|
722 |
|
723 self.drawEraserAct = E5Action( |
|
724 self.tr('Eraser (Transparent)'), |
|
725 UI.PixmapCache.getIcon("drawEraser"), |
|
726 self.tr('Eraser (&Transparent)'), |
|
727 0, 0, |
|
728 self.drawingActGrp, 'iconEditor_tools_eraser') |
|
729 self.drawEraserAct.setWhatsThis(self.tr( |
|
730 """<b>Eraser (Transparent)</b>""" |
|
731 """<p>Erase pixels by setting them to transparent.</p>""" |
|
732 )) |
|
733 self.drawEraserAct.setCheckable(True) |
|
734 self.esm.setMapping(self.drawEraserAct, IconEditorGrid.Rubber) |
|
735 self.drawEraserAct.triggered.connect(self.esm.map) |
|
736 self.__actions.append(self.drawEraserAct) |
|
737 |
|
738 self.drawRectangleSelectionAct = E5Action( |
|
739 self.tr('Rectangular Selection'), |
|
740 UI.PixmapCache.getIcon("selectRectangle"), |
|
741 self.tr('Rect&angular Selection'), |
|
742 0, 0, |
|
743 self.drawingActGrp, 'iconEditor_tools_selection_rectangle') |
|
744 self.drawRectangleSelectionAct.setWhatsThis(self.tr( |
|
745 """<b>Rectangular Selection</b>""" |
|
746 """<p>Select a rectangular section of the icon using""" |
|
747 """ the mouse.</p>""" |
|
748 )) |
|
749 self.drawRectangleSelectionAct.setCheckable(True) |
|
750 self.esm.setMapping(self.drawRectangleSelectionAct, |
|
751 IconEditorGrid.RectangleSelection) |
|
752 self.drawRectangleSelectionAct.triggered.connect(self.esm.map) |
|
753 self.__actions.append(self.drawRectangleSelectionAct) |
|
754 |
|
755 self.drawCircleSelectionAct = E5Action( |
|
756 self.tr('Circular Selection'), |
|
757 UI.PixmapCache.getIcon("selectCircle"), |
|
758 self.tr('Rect&angular Selection'), |
|
759 0, 0, |
|
760 self.drawingActGrp, 'iconEditor_tools_selection_circle') |
|
761 self.drawCircleSelectionAct.setWhatsThis(self.tr( |
|
762 """<b>Circular Selection</b>""" |
|
763 """<p>Select a circular section of the icon using""" |
|
764 """ the mouse.</p>""" |
|
765 )) |
|
766 self.drawCircleSelectionAct.setCheckable(True) |
|
767 self.esm.setMapping(self.drawCircleSelectionAct, |
|
768 IconEditorGrid.CircleSelection) |
|
769 self.drawCircleSelectionAct.triggered.connect(self.esm.map) |
|
770 self.__actions.append(self.drawCircleSelectionAct) |
|
771 |
|
772 self.drawPencilAct.setChecked(True) |
|
773 |
|
774 def __initHelpActions(self): |
|
775 """ |
|
776 Private method to create the Help actions. |
|
777 """ |
|
778 self.aboutAct = E5Action( |
|
779 self.tr('About'), |
|
780 self.tr('&About'), |
|
781 0, 0, self, 'iconEditor_help_about') |
|
782 self.aboutAct.setStatusTip(self.tr( |
|
783 'Display information about this software')) |
|
784 self.aboutAct.setWhatsThis(self.tr( |
|
785 """<b>About</b>""" |
|
786 """<p>Display some information about this software.</p>""")) |
|
787 self.aboutAct.triggered.connect(self.__about) |
|
788 self.__actions.append(self.aboutAct) |
|
789 |
|
790 self.aboutQtAct = E5Action( |
|
791 self.tr('About Qt'), |
|
792 self.tr('About &Qt'), |
|
793 0, 0, self, 'iconEditor_help_about_qt') |
|
794 self.aboutQtAct.setStatusTip( |
|
795 self.tr('Display information about the Qt toolkit')) |
|
796 self.aboutQtAct.setWhatsThis(self.tr( |
|
797 """<b>About Qt</b>""" |
|
798 """<p>Display some information about the Qt toolkit.</p>""" |
|
799 )) |
|
800 self.aboutQtAct.triggered.connect(self.__aboutQt) |
|
801 self.__actions.append(self.aboutQtAct) |
|
802 |
|
803 self.whatsThisAct = E5Action( |
|
804 self.tr('What\'s This?'), |
|
805 UI.PixmapCache.getIcon("whatsThis"), |
|
806 self.tr('&What\'s This?'), |
|
807 QKeySequence(self.tr("Shift+F1", "Help|What's This?'")), |
|
808 0, self, 'iconEditor_help_whats_this') |
|
809 self.whatsThisAct.setStatusTip(self.tr('Context sensitive help')) |
|
810 self.whatsThisAct.setWhatsThis(self.tr( |
|
811 """<b>Display context sensitive help</b>""" |
|
812 """<p>In What's This? mode, the mouse cursor shows an arrow""" |
|
813 """ with a question mark, and you can click on the interface""" |
|
814 """ elements to get a short description of what they do and""" |
|
815 """ how to use them. In dialogs, this feature can be accessed""" |
|
816 """ using the context help button in the titlebar.</p>""" |
|
817 )) |
|
818 self.whatsThisAct.triggered.connect(self.__whatsThis) |
|
819 self.__actions.append(self.whatsThisAct) |
|
820 |
|
821 def __initMenus(self): |
|
822 """ |
|
823 Private method to create the menus. |
|
824 """ |
|
825 mb = self.menuBar() |
|
826 |
|
827 menu = mb.addMenu(self.tr('&File')) |
|
828 menu.setTearOffEnabled(True) |
|
829 menu.addAction(self.newAct) |
|
830 menu.addAction(self.newWindowAct) |
|
831 menu.addAction(self.openAct) |
|
832 menu.addSeparator() |
|
833 menu.addAction(self.saveAct) |
|
834 menu.addAction(self.saveAsAct) |
|
835 menu.addSeparator() |
|
836 menu.addAction(self.closeAct) |
|
837 menu.addAction(self.closeOthersAct) |
|
838 if self.fromEric: |
|
839 menu.addAction(self.closeAllAct) |
|
840 else: |
|
841 menu.addSeparator() |
|
842 menu.addAction(self.exitAct) |
|
843 |
|
844 menu = mb.addMenu(self.tr("&Edit")) |
|
845 menu.setTearOffEnabled(True) |
|
846 menu.addAction(self.undoAct) |
|
847 menu.addAction(self.redoAct) |
|
848 menu.addSeparator() |
|
849 menu.addAction(self.cutAct) |
|
850 menu.addAction(self.copyAct) |
|
851 menu.addAction(self.pasteAct) |
|
852 menu.addAction(self.pasteNewAct) |
|
853 menu.addAction(self.deleteAct) |
|
854 menu.addSeparator() |
|
855 menu.addAction(self.selectAllAct) |
|
856 menu.addSeparator() |
|
857 menu.addAction(self.resizeAct) |
|
858 menu.addAction(self.grayscaleAct) |
|
859 |
|
860 menu = mb.addMenu(self.tr('&View')) |
|
861 menu.setTearOffEnabled(True) |
|
862 menu.addAction(self.zoomInAct) |
|
863 menu.addAction(self.zoomResetAct) |
|
864 menu.addAction(self.zoomOutAct) |
|
865 menu.addSeparator() |
|
866 menu.addAction(self.showGridAct) |
|
867 |
|
868 menu = mb.addMenu(self.tr('&Tools')) |
|
869 menu.setTearOffEnabled(True) |
|
870 menu.addAction(self.drawPencilAct) |
|
871 menu.addAction(self.drawColorPickerAct) |
|
872 menu.addAction(self.drawRectangleAct) |
|
873 menu.addAction(self.drawFilledRectangleAct) |
|
874 menu.addAction(self.drawCircleAct) |
|
875 menu.addAction(self.drawFilledCircleAct) |
|
876 menu.addAction(self.drawEllipseAct) |
|
877 menu.addAction(self.drawFilledEllipseAct) |
|
878 menu.addAction(self.drawFloodFillAct) |
|
879 menu.addAction(self.drawLineAct) |
|
880 menu.addAction(self.drawEraserAct) |
|
881 menu.addSeparator() |
|
882 menu.addAction(self.drawRectangleSelectionAct) |
|
883 menu.addAction(self.drawCircleSelectionAct) |
|
884 |
|
885 mb.addSeparator() |
|
886 |
|
887 menu = mb.addMenu(self.tr("&Help")) |
|
888 menu.addAction(self.aboutAct) |
|
889 menu.addAction(self.aboutQtAct) |
|
890 menu.addSeparator() |
|
891 menu.addAction(self.whatsThisAct) |
|
892 |
|
893 def __initToolbars(self): |
|
894 """ |
|
895 Private method to create the toolbars. |
|
896 """ |
|
897 filetb = self.addToolBar(self.tr("File")) |
|
898 filetb.setObjectName("FileToolBar") |
|
899 filetb.setIconSize(UI.Config.ToolBarIconSize) |
|
900 filetb.addAction(self.newAct) |
|
901 filetb.addAction(self.newWindowAct) |
|
902 filetb.addAction(self.openAct) |
|
903 filetb.addSeparator() |
|
904 filetb.addAction(self.saveAct) |
|
905 filetb.addAction(self.saveAsAct) |
|
906 filetb.addSeparator() |
|
907 filetb.addAction(self.closeAct) |
|
908 if not self.fromEric: |
|
909 filetb.addAction(self.exitAct) |
|
910 |
|
911 edittb = self.addToolBar(self.tr("Edit")) |
|
912 edittb.setObjectName("EditToolBar") |
|
913 edittb.setIconSize(UI.Config.ToolBarIconSize) |
|
914 edittb.addAction(self.undoAct) |
|
915 edittb.addAction(self.redoAct) |
|
916 edittb.addSeparator() |
|
917 edittb.addAction(self.cutAct) |
|
918 edittb.addAction(self.copyAct) |
|
919 edittb.addAction(self.pasteAct) |
|
920 edittb.addSeparator() |
|
921 edittb.addAction(self.resizeAct) |
|
922 edittb.addAction(self.grayscaleAct) |
|
923 |
|
924 viewtb = self.addToolBar(self.tr("View")) |
|
925 viewtb.setObjectName("ViewToolBar") |
|
926 viewtb.setIconSize(UI.Config.ToolBarIconSize) |
|
927 viewtb.addAction(self.showGridAct) |
|
928 |
|
929 toolstb = self.addToolBar(self.tr("Tools")) |
|
930 toolstb.setObjectName("ToolsToolBar") |
|
931 toolstb.setIconSize(UI.Config.ToolBarIconSize) |
|
932 toolstb.addAction(self.drawPencilAct) |
|
933 toolstb.addAction(self.drawColorPickerAct) |
|
934 toolstb.addAction(self.drawRectangleAct) |
|
935 toolstb.addAction(self.drawFilledRectangleAct) |
|
936 toolstb.addAction(self.drawCircleAct) |
|
937 toolstb.addAction(self.drawFilledCircleAct) |
|
938 toolstb.addAction(self.drawEllipseAct) |
|
939 toolstb.addAction(self.drawFilledEllipseAct) |
|
940 toolstb.addAction(self.drawFloodFillAct) |
|
941 toolstb.addAction(self.drawLineAct) |
|
942 toolstb.addAction(self.drawEraserAct) |
|
943 toolstb.addSeparator() |
|
944 toolstb.addAction(self.drawRectangleSelectionAct) |
|
945 toolstb.addAction(self.drawCircleSelectionAct) |
|
946 |
|
947 helptb = self.addToolBar(self.tr("Help")) |
|
948 helptb.setObjectName("HelpToolBar") |
|
949 helptb.setIconSize(UI.Config.ToolBarIconSize) |
|
950 helptb.addAction(self.whatsThisAct) |
|
951 |
|
952 def __createStatusBar(self): |
|
953 """ |
|
954 Private method to initialize the status bar. |
|
955 """ |
|
956 self.__statusBar = self.statusBar() |
|
957 self.__statusBar.setSizeGripEnabled(True) |
|
958 |
|
959 self.__sbSize = QLabel(self.__statusBar) |
|
960 self.__statusBar.addPermanentWidget(self.__sbSize) |
|
961 self.__sbSize.setWhatsThis(self.tr( |
|
962 """<p>This part of the status bar displays the icon size.</p>""" |
|
963 )) |
|
964 self.__updateSize(*self.__editor.iconSize()) |
|
965 |
|
966 self.__sbPos = QLabel(self.__statusBar) |
|
967 self.__statusBar.addPermanentWidget(self.__sbPos) |
|
968 self.__sbPos.setWhatsThis(self.tr( |
|
969 """<p>This part of the status bar displays the cursor""" |
|
970 """ position.</p>""" |
|
971 )) |
|
972 self.__updatePosition(0, 0) |
|
973 |
|
974 self.__zoomWidget = E5ZoomWidget( |
|
975 UI.PixmapCache.getPixmap("zoomOut"), |
|
976 UI.PixmapCache.getPixmap("zoomIn"), |
|
977 UI.PixmapCache.getPixmap("zoomReset"), self) |
|
978 self.__zoomWidget.setMinimum(IconEditorGrid.ZoomMinimum) |
|
979 self.__zoomWidget.setMaximum(IconEditorGrid.ZoomMaximum) |
|
980 self.__zoomWidget.setDefault(IconEditorGrid.ZoomDefault) |
|
981 self.__zoomWidget.setSingleStep(IconEditorGrid.ZoomStep) |
|
982 self.__zoomWidget.setPercent(IconEditorGrid.ZoomPercent) |
|
983 self.__statusBar.addPermanentWidget(self.__zoomWidget) |
|
984 self.__zoomWidget.setValue(self.__editor.zoomFactor()) |
|
985 self.__zoomWidget.valueChanged.connect(self.__editor.setZoomFactor) |
|
986 self.__editor.zoomChanged.connect(self.__zoomWidget.setValue) |
|
987 |
|
988 self.__updateZoom() |
|
989 |
|
990 def __createPaletteDock(self): |
|
991 """ |
|
992 Private method to initialize the palette dock widget. |
|
993 """ |
|
994 from .IconEditorPalette import IconEditorPalette |
|
995 |
|
996 self.__paletteDock = QDockWidget(self) |
|
997 self.__paletteDock.setObjectName("paletteDock") |
|
998 self.__paletteDock.setFeatures( |
|
999 QDockWidget.DockWidgetFeatures( |
|
1000 QDockWidget.DockWidgetFeature.DockWidgetClosable | |
|
1001 QDockWidget.DockWidgetFeature.DockWidgetMovable | |
|
1002 QDockWidget.DockWidgetFeature.DockWidgetFloatable |
|
1003 ) |
|
1004 ) |
|
1005 self.__paletteDock.setWindowTitle("Palette") |
|
1006 self.__palette = IconEditorPalette() |
|
1007 self.__paletteDock.setWidget(self.__palette) |
|
1008 self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, |
|
1009 self.__paletteDock) |
|
1010 |
|
1011 def closeEvent(self, evt): |
|
1012 """ |
|
1013 Protected event handler for the close event. |
|
1014 |
|
1015 @param evt the close event (QCloseEvent) |
|
1016 <br />This event is simply accepted after the history has been |
|
1017 saved and all window references have been deleted. |
|
1018 """ |
|
1019 if self.__maybeSave(): |
|
1020 self.__editor.shutdown() |
|
1021 |
|
1022 state = self.saveState() |
|
1023 Preferences.setIconEditor("IconEditorState", state) |
|
1024 |
|
1025 Preferences.setGeometry("IconEditorGeometry", self.saveGeometry()) |
|
1026 |
|
1027 with contextlib.suppress(ValueError): |
|
1028 if self.fromEric or len(self.__class__.windows) > 1: |
|
1029 del self.__class__.windows[ |
|
1030 self.__class__.windows.index(self)] |
|
1031 |
|
1032 if not self.fromEric: |
|
1033 Preferences.syncPreferences() |
|
1034 |
|
1035 evt.accept() |
|
1036 self.editorClosed.emit() |
|
1037 else: |
|
1038 evt.ignore() |
|
1039 |
|
1040 def __newIcon(self): |
|
1041 """ |
|
1042 Private slot to create a new icon. |
|
1043 """ |
|
1044 if self.__maybeSave(): |
|
1045 self.__editor.editNew() |
|
1046 self.__setCurrentFile("") |
|
1047 |
|
1048 self.__checkActions() |
|
1049 |
|
1050 def __newWindow(self): |
|
1051 """ |
|
1052 Private slot called to open a new icon editor window. |
|
1053 """ |
|
1054 ie = IconEditorWindow(parent=self.parent(), fromEric=self.fromEric, |
|
1055 project=self.__project) |
|
1056 ie.setRecentPaths(self.__lastOpenPath, self.__lastSavePath) |
|
1057 ie.show() |
|
1058 |
|
1059 def __openIcon(self): |
|
1060 """ |
|
1061 Private slot to open an icon file. |
|
1062 """ |
|
1063 if self.__maybeSave(): |
|
1064 if ( |
|
1065 not self.__lastOpenPath and |
|
1066 self.__project is not None and |
|
1067 self.__project.isOpen() |
|
1068 ): |
|
1069 self.__lastOpenPath = self.__project.getProjectPath() |
|
1070 |
|
1071 fileName = E5FileDialog.getOpenFileNameAndFilter( |
|
1072 self, |
|
1073 self.tr("Open icon file"), |
|
1074 self.__lastOpenPath, |
|
1075 self.__inputFilter, |
|
1076 self.__defaultFilter)[0] |
|
1077 if fileName: |
|
1078 self.__loadIconFile(fileName) |
|
1079 self.__lastOpenPath = os.path.dirname(fileName) |
|
1080 self.__checkActions() |
|
1081 |
|
1082 def __saveIcon(self): |
|
1083 """ |
|
1084 Private slot to save the icon. |
|
1085 |
|
1086 @return flag indicating success (boolean) |
|
1087 """ |
|
1088 if not self.__fileName: |
|
1089 return self.__saveIconAs() |
|
1090 else: |
|
1091 return self.__saveIconFile(self.__fileName) |
|
1092 |
|
1093 def __saveIconAs(self): |
|
1094 """ |
|
1095 Private slot to save the icon with a new name. |
|
1096 |
|
1097 @return flag indicating success (boolean) |
|
1098 """ |
|
1099 if ( |
|
1100 not self.__lastSavePath and |
|
1101 self.__project is not None and |
|
1102 self.__project.isOpen() |
|
1103 ): |
|
1104 self.__lastSavePath = self.__project.getProjectPath() |
|
1105 if not self.__lastSavePath and self.__lastOpenPath: |
|
1106 self.__lastSavePath = self.__lastOpenPath |
|
1107 |
|
1108 fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( |
|
1109 self, |
|
1110 self.tr("Save icon file"), |
|
1111 self.__lastSavePath, |
|
1112 self.__outputFilter, |
|
1113 self.__defaultFilter, |
|
1114 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) |
|
1115 if not fileName: |
|
1116 return False |
|
1117 |
|
1118 ext = QFileInfo(fileName).suffix() |
|
1119 if not ext: |
|
1120 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
1121 if ex: |
|
1122 fileName += ex |
|
1123 if QFileInfo(fileName).exists(): |
|
1124 res = E5MessageBox.yesNo( |
|
1125 self, |
|
1126 self.tr("Save icon file"), |
|
1127 self.tr("<p>The file <b>{0}</b> already exists." |
|
1128 " Overwrite it?</p>").format(fileName), |
|
1129 icon=E5MessageBox.Warning) |
|
1130 if not res: |
|
1131 return False |
|
1132 |
|
1133 self.__lastSavePath = os.path.dirname(fileName) |
|
1134 |
|
1135 return self.__saveIconFile(fileName) |
|
1136 |
|
1137 def __closeAll(self): |
|
1138 """ |
|
1139 Private slot to close all windows. |
|
1140 """ |
|
1141 self.__closeOthers() |
|
1142 self.close() |
|
1143 |
|
1144 def __closeOthers(self): |
|
1145 """ |
|
1146 Private slot to close all other windows. |
|
1147 """ |
|
1148 for win in self.__class__.windows[:]: |
|
1149 if win != self: |
|
1150 win.close() |
|
1151 |
|
1152 def __loadIconFile(self, fileName): |
|
1153 """ |
|
1154 Private method to load an icon file. |
|
1155 |
|
1156 @param fileName name of the icon file to load (string). |
|
1157 """ |
|
1158 file = QFile(fileName) |
|
1159 if not file.exists(): |
|
1160 E5MessageBox.warning( |
|
1161 self, self.tr("eric Icon Editor"), |
|
1162 self.tr("The file '{0}' does not exist.") |
|
1163 .format(fileName)) |
|
1164 return |
|
1165 |
|
1166 if not file.open(QFile.ReadOnly): |
|
1167 E5MessageBox.warning( |
|
1168 self, self.tr("eric Icon Editor"), |
|
1169 self.tr("Cannot read file '{0}:\n{1}.") |
|
1170 .format(fileName, file.errorString())) |
|
1171 return |
|
1172 file.close() |
|
1173 |
|
1174 img = QImage(fileName) |
|
1175 self.__editor.setIconImage(img, clearUndo=True) |
|
1176 self.__setCurrentFile(fileName) |
|
1177 |
|
1178 def __saveIconFile(self, fileName): |
|
1179 """ |
|
1180 Private method to save to the given file. |
|
1181 |
|
1182 @param fileName name of the file to save to (string) |
|
1183 @return flag indicating success (boolean) |
|
1184 """ |
|
1185 file = QFile(fileName) |
|
1186 if not file.open(QFile.WriteOnly): |
|
1187 E5MessageBox.warning( |
|
1188 self, self.tr("eric Icon Editor"), |
|
1189 self.tr("Cannot write file '{0}:\n{1}.") |
|
1190 .format(fileName, file.errorString())) |
|
1191 |
|
1192 self.__checkActions() |
|
1193 |
|
1194 return False |
|
1195 |
|
1196 img = self.__editor.iconImage() |
|
1197 res = img.save(file) |
|
1198 file.close() |
|
1199 |
|
1200 if not res: |
|
1201 E5MessageBox.warning( |
|
1202 self, self.tr("eric Icon Editor"), |
|
1203 self.tr("Cannot write file '{0}:\n{1}.") |
|
1204 .format(fileName, file.errorString())) |
|
1205 |
|
1206 self.__checkActions() |
|
1207 |
|
1208 return False |
|
1209 |
|
1210 self.__editor.setDirty(False, setCleanState=True) |
|
1211 |
|
1212 self.__setCurrentFile(fileName) |
|
1213 self.__statusBar.showMessage(self.tr("Icon saved"), 2000) |
|
1214 |
|
1215 self.__checkActions() |
|
1216 |
|
1217 return True |
|
1218 |
|
1219 def __setCurrentFile(self, fileName): |
|
1220 """ |
|
1221 Private method to register the file name of the current file. |
|
1222 |
|
1223 @param fileName name of the file to register (string) |
|
1224 """ |
|
1225 self.__fileName = fileName |
|
1226 |
|
1227 shownName = ( |
|
1228 self.__strippedName(self.__fileName) |
|
1229 if self.__fileName else |
|
1230 self.tr("Untitled") |
|
1231 ) |
|
1232 |
|
1233 self.setWindowTitle(self.tr("{0}[*] - {1}") |
|
1234 .format(shownName, self.tr("Icon Editor"))) |
|
1235 |
|
1236 self.setWindowModified(self.__editor.isDirty()) |
|
1237 |
|
1238 def __strippedName(self, fullFileName): |
|
1239 """ |
|
1240 Private method to return the filename part of the given path. |
|
1241 |
|
1242 @param fullFileName full pathname of the given file (string) |
|
1243 @return filename part (string) |
|
1244 """ |
|
1245 return QFileInfo(fullFileName).fileName() |
|
1246 |
|
1247 def __maybeSave(self): |
|
1248 """ |
|
1249 Private method to ask the user to save the file, if it was modified. |
|
1250 |
|
1251 @return flag indicating, if it is ok to continue (boolean) |
|
1252 """ |
|
1253 if self.__editor.isDirty(): |
|
1254 ret = E5MessageBox.okToClearData( |
|
1255 self, |
|
1256 self.tr("eric Icon Editor"), |
|
1257 self.tr("""The icon image has unsaved changes."""), |
|
1258 self.__saveIcon) |
|
1259 if not ret: |
|
1260 return False |
|
1261 return True |
|
1262 |
|
1263 def setRecentPaths(self, openPath, savePath): |
|
1264 """ |
|
1265 Public method to set the last open and save paths. |
|
1266 |
|
1267 @param openPath least recently used open path (string) |
|
1268 @param savePath least recently used save path (string) |
|
1269 """ |
|
1270 if openPath: |
|
1271 self.__lastOpenPath = openPath |
|
1272 if savePath: |
|
1273 self.__lastSavePath = savePath |
|
1274 |
|
1275 def __checkActions(self): |
|
1276 """ |
|
1277 Private slot to check some actions for their enable/disable status. |
|
1278 """ |
|
1279 self.saveAct.setEnabled(self.__editor.isDirty()) |
|
1280 |
|
1281 def __modificationChanged(self, m): |
|
1282 """ |
|
1283 Private slot to handle the modificationChanged signal. |
|
1284 |
|
1285 @param m modification status |
|
1286 """ |
|
1287 self.setWindowModified(m) |
|
1288 self.__checkActions() |
|
1289 |
|
1290 def __updatePosition(self, x, y): |
|
1291 """ |
|
1292 Private slot to show the current cursor position. |
|
1293 |
|
1294 @param x x-coordinate (integer) |
|
1295 @param y y-coordinate (integer) |
|
1296 """ |
|
1297 self.__sbPos.setText("X: {0:d} Y: {1:d}".format(x + 1, y + 1)) |
|
1298 |
|
1299 def __updateSize(self, w, h): |
|
1300 """ |
|
1301 Private slot to show the current icon size. |
|
1302 |
|
1303 @param w width of the icon (integer) |
|
1304 @param h height of the icon (integer) |
|
1305 """ |
|
1306 self.__sbSize.setText("Size: {0:d} x {1:d}".format(w, h)) |
|
1307 |
|
1308 def __updateZoom(self): |
|
1309 """ |
|
1310 Private slot to show the current zoom factor. |
|
1311 """ |
|
1312 self.zoomOutAct.setEnabled( |
|
1313 self.__editor.zoomFactor() > IconEditorGrid.ZoomMinimum) |
|
1314 self.zoomInAct.setEnabled( |
|
1315 self.__editor.zoomFactor() < IconEditorGrid.ZoomMaximum) |
|
1316 |
|
1317 def __zoomIn(self): |
|
1318 """ |
|
1319 Private slot called to handle the zoom in action. |
|
1320 """ |
|
1321 self.__editor.setZoomFactor( |
|
1322 self.__editor.zoomFactor() + IconEditorGrid.ZoomStep) |
|
1323 self.__updateZoom() |
|
1324 |
|
1325 def __zoomOut(self): |
|
1326 """ |
|
1327 Private slot called to handle the zoom out action. |
|
1328 """ |
|
1329 self.__editor.setZoomFactor( |
|
1330 self.__editor.zoomFactor() - IconEditorGrid.ZoomStep) |
|
1331 self.__updateZoom() |
|
1332 |
|
1333 def __zoomReset(self): |
|
1334 """ |
|
1335 Private slot called to handle the zoom reset action. |
|
1336 """ |
|
1337 self.__editor.setZoomFactor(IconEditorGrid.ZoomDefault) |
|
1338 self.__updateZoom() |
|
1339 |
|
1340 def __about(self): |
|
1341 """ |
|
1342 Private slot to show a little About message. |
|
1343 """ |
|
1344 E5MessageBox.about( |
|
1345 self, self.tr("About eric Icon Editor"), |
|
1346 self.tr("The eric Icon Editor is a simple editor component" |
|
1347 " to perform icon drawing tasks.")) |
|
1348 |
|
1349 def __aboutQt(self): |
|
1350 """ |
|
1351 Private slot to handle the About Qt dialog. |
|
1352 """ |
|
1353 E5MessageBox.aboutQt(self, "eric Icon Editor") |
|
1354 |
|
1355 def __whatsThis(self): |
|
1356 """ |
|
1357 Private slot called in to enter Whats This mode. |
|
1358 """ |
|
1359 QWhatsThis.enterWhatsThisMode() |
|
1360 |
|
1361 def wheelEvent(self, evt): |
|
1362 """ |
|
1363 Protected method to handle wheel events. |
|
1364 |
|
1365 @param evt reference to the wheel event (QWheelEvent) |
|
1366 """ |
|
1367 if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: |
|
1368 delta = evt.angleDelta().y() |
|
1369 if delta < 0: |
|
1370 self.__zoomOut() |
|
1371 elif delta > 0: |
|
1372 self.__zoomIn() |
|
1373 evt.accept() |
|
1374 return |
|
1375 |
|
1376 super().wheelEvent(evt) |
|
1377 |
|
1378 def event(self, evt): |
|
1379 """ |
|
1380 Public method handling events. |
|
1381 |
|
1382 @param evt reference to the event (QEvent) |
|
1383 @return flag indicating, if the event was handled (boolean) |
|
1384 """ |
|
1385 if evt.type() == QEvent.Type.Gesture: |
|
1386 self.gestureEvent(evt) |
|
1387 return True |
|
1388 |
|
1389 return super().event(evt) |
|
1390 |
|
1391 def gestureEvent(self, evt): |
|
1392 """ |
|
1393 Protected method handling gesture events. |
|
1394 |
|
1395 @param evt reference to the gesture event (QGestureEvent |
|
1396 """ |
|
1397 pinch = evt.gesture(Qt.GestureType.PinchGesture) |
|
1398 if pinch: |
|
1399 if pinch.state() == Qt.GestureState.GestureStarted: |
|
1400 pinch.setTotalScaleFactor(self.__editor.zoomFactor() / 100.0) |
|
1401 elif pinch.state() == Qt.GestureState.GestureUpdated: |
|
1402 self.__editor.setZoomFactor( |
|
1403 int(pinch.totalScaleFactor() * 100)) |
|
1404 self.__updateZoom() |
|
1405 evt.accept() |