1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing the icon editor main window. 8""" 9 10import os 11import contextlib 12 13from PyQt5.QtCore import ( 14 pyqtSignal, Qt, QSize, QSignalMapper, QFileInfo, QFile, QEvent 15) 16from PyQt5.QtGui import ( 17 QPalette, QImage, QImageReader, QImageWriter, QKeySequence 18) 19from PyQt5.QtWidgets import QScrollArea, QLabel, QDockWidget, QWhatsThis 20 21from E5Gui.E5Action import E5Action, createActionGroup 22from E5Gui import E5FileDialog, E5MessageBox 23from E5Gui.E5MainWindow import E5MainWindow 24from E5Gui.E5ZoomWidget import E5ZoomWidget 25 26from .IconEditorGrid import IconEditorGrid 27 28import UI.PixmapCache 29import UI.Config 30 31import Preferences 32 33 34class 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() 1406