eric7/IconEditor/IconEditorWindow.py

branch
eric7
changeset 8312
800c432b34c8
parent 8259
2bbec88047dd
child 8314
e3642a6a1e71
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
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()

eric ide

mercurial