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