Beispiel #1
0
class MainWindow(QtGui.QMainWindow):
    """ Main windows of the application """
    dropped = QtCore.pyqtSignal(list)
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.setAcceptDrops(True)
        self.dropped.connect(self.importAsLayer)
        
        self.project = Project(self)
        self.toolsWidget = ToolsWidget(self.project)
        self.optionsWidget = OptionsWidget(self.project)
        self.paletteWidget = PaletteWidget(self.project)
        self.onionSkinWidget = OnionSkinWidget(self.project)
        self.timelineWidget = TimelineWidget(self.project)
        self.scene = Scene(self.project)
        
        self.updateTitle()
        self.project.updateTitleSign.connect(self.updateTitle)

        ### layout #####################################################
        self.setDockNestingEnabled(True)
        self.setCentralWidget(self.scene)
        
        QtGui.QApplication.setOrganizationName("pixeditor")
        QtGui.QApplication.setApplicationName("pixeditor")
        settings = QtCore.QSettings()
        settings.beginGroup("mainWindow")
        try:
            lock = bool(int(settings.value("lock")))
        except TypeError:
            lock = True
        
        toolsDock = Dock(self.toolsWidget, "tools", lock)
        toolsDock.setObjectName("toolsDock")
        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, toolsDock)
        self.scene.coords=toolsDock.widget().coords

        optionsDock = Dock(self.optionsWidget, "options", lock)
        optionsDock.setObjectName("optionsDock")
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, optionsDock)

        paletteDock = Dock(self.paletteWidget, "palette", lock)
        paletteDock.setObjectName("paletteDock")
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, paletteDock)
        
        onionSkinDock = Dock(self.onionSkinWidget, "onion skin", lock)
        onionSkinDock.setObjectName("onionSkinDock")
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, onionSkinDock)
        
        timelineDock = Dock(self.timelineWidget, "timeline", lock)
        timelineDock.setObjectName("timelineDock")
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, timelineDock)

        ### File menu ###
        menubar = self.menuBar()
        openAction = QtGui.QAction('Open', self)
        openAction.triggered.connect(self.openAction)
        saveAsAction = QtGui.QAction('Save as', self)
        saveAsAction.triggered.connect(self.saveAsAction)
        saveAction = QtGui.QAction('Save', self)
        saveAction.triggered.connect(self.saveAction)
        saveAction.setShortcut('Ctrl+S')
        
        importNewAction = QtGui.QAction('Import as new', self)
        importNewAction.triggered.connect(self.importAsNewAction)
        importLayerAction = QtGui.QAction('Import as layer', self)
        importLayerAction.triggered.connect(self.importAsLayerAction)
        exportAction = QtGui.QAction('Export', self)
        exportAction.triggered.connect(self.exportAction)
        exportAction.setShortcut('Ctrl+E')
        
        exitAction = QtGui.QAction('Exit', self)
        exitAction.triggered.connect(self.close)
        exitAction.setShortcut('Ctrl+Q')
        
        fileMenu = menubar.addMenu('File')
        fileMenu.addAction(openAction)
        fileMenu.addAction(saveAsAction)
        fileMenu.addAction(saveAction)
        fileMenu.addSeparator()
        fileMenu.addAction(importNewAction)
        fileMenu.addAction(importLayerAction)
        fileMenu.addAction(exportAction)
        fileMenu.addSeparator()
        fileMenu.addAction(exitAction)
        
        ### Edit menu ###
        undoAction = QtGui.QAction('Undo', self)
        undoAction.triggered.connect(self.project.undo)
        undoAction.setShortcut('Ctrl+Z')
        redoAction = QtGui.QAction('Redo', self)
        redoAction.triggered.connect(self.project.redo)
        redoAction.setShortcut('Ctrl+Y')
        
        cutAction = QtGui.QAction('Cut', self)
        cutAction.triggered.connect(self.timelineWidget.cut)
        cutAction.setShortcut('Ctrl+X')
        copyAction = QtGui.QAction('Copy', self)
        copyAction.triggered.connect(self.timelineWidget.copy)
        copyAction.setShortcut('Ctrl+C')
        pasteAction = QtGui.QAction('Paste', self)
        pasteAction.triggered.connect(self.timelineWidget.paste)
        pasteAction.setShortcut('Ctrl+V')
        
        editMenu = menubar.addMenu('Edit')
        editMenu.addAction(undoAction)
        editMenu.addAction(redoAction)
        editMenu.addSeparator()
        editMenu.addAction(cutAction)
        editMenu.addAction(copyAction)
        editMenu.addAction(pasteAction)
        
        ### project menu ###
        newAction = QtGui.QAction('New', self)
        newAction.triggered.connect(self.newAction)
        cropAction = QtGui.QAction('Crop', self)
        cropAction.triggered.connect(self.cropAction)
        resizeAction = QtGui.QAction('Resize', self)
        resizeAction.triggered.connect(self.resizeAction)
        replacePaletteAction = QtGui.QAction('Replace palette', self)
        replacePaletteAction.triggered.connect(self.replacePaletteAction)
        minimizePaletteAction = QtGui.QAction('Minimize palette', self)
        minimizePaletteAction.triggered.connect(self.minimizePaletteAction)
        prefAction = QtGui.QAction('Background', self)
        prefAction.triggered.connect(self.backgroundAction)
        
        projectMenu = menubar.addMenu('Project')
        projectMenu.addAction(newAction)
        projectMenu.addAction(cropAction)
        projectMenu.addAction(resizeAction)
        projectMenu.addAction(replacePaletteAction)
        projectMenu.addAction(minimizePaletteAction)
        projectMenu.addAction(prefAction)

        ### resources menu ###
        savePaletteAction = QtGui.QAction('save  current palette', self)
        savePaletteAction.triggered.connect(self.savePaletteAction)
        savePenAction = QtGui.QAction('save custom pen', self)
        savePenAction.triggered.connect(self.savePenAction)
        reloadResourcesAction = QtGui.QAction('reload resources', self)
        reloadResourcesAction.triggered.connect(self.reloadResourcesAction)
        
        resourcesMenu = menubar.addMenu('Resources')
        resourcesMenu.addAction(savePaletteAction)
        resourcesMenu.addAction(savePenAction)
        resourcesMenu.addAction(reloadResourcesAction)
        
        ### view menu ###
        viewMenu = menubar.addMenu('View')
        dockWidgets = self.findChildren(QtGui.QDockWidget)
        for dock in dockWidgets:
            viewMenu.addAction(dock.toggleViewAction())
        viewMenu.addSeparator()
        self.lockLayoutWidget = QtGui.QAction('Lock Layout', self)
        self.lockLayoutWidget.setCheckable(True)
        self.lockLayoutWidget.setChecked(lock)
        self.lockLayoutWidget.toggled.connect(self.lockLayoutAction)
        viewMenu.addAction(self.lockLayoutWidget)
        
        ### shortcuts ###
        QtGui.QShortcut(QtCore.Qt.Key_Left, self, lambda : self.selectFrame(-1))
        QtGui.QShortcut(QtCore.Qt.Key_Right, self, lambda : self.selectFrame(1))
        QtGui.QShortcut(QtCore.Qt.Key_Up, self, lambda : self.selectLayer(-1))
        QtGui.QShortcut(QtCore.Qt.Key_Down, self, lambda : self.selectLayer(1))
        QtGui.QShortcut(QtCore.Qt.Key_Space, self, self.timelineWidget.playPauseClicked)
        QtGui.QShortcut(QtCore.Qt.Key_1, self, toolsDock.widget().penClicked)
        QtGui.QShortcut(QtCore.Qt.Key_2, self, toolsDock.widget().pipetteClicked)
        QtGui.QShortcut(QtCore.Qt.Key_3, self, toolsDock.widget().fillClicked)
        QtGui.QShortcut(QtCore.Qt.Key_4, self, toolsDock.widget().moveClicked)
        QtGui.QShortcut(QtCore.Qt.Key_5, self, toolsDock.widget().selectClicked)
        self.hiddenDock = []
        QtGui.QShortcut(QtCore.Qt.Key_Tab, self, self.hideDock)
        QtGui.QShortcut(QtCore.Qt.Key_E, self, self.project.changeColor)
        
        ### settings ###
        try:
            self.restoreGeometry(settings.value("geometry"))
        except TypeError:
            pass # no geometry to restore so leave as is
        try:
            self.restoreState(settings.value("windowState"))
        except TypeError:
            pass # no state to restore so leave as is
        settings.endGroup()
        self.show()

    ######## File menu #################################################
    def openAction(self):
        xml, url = open_pix(self.project.dirUrl)
        if xml and url:
            self.project.saveToUndo("all")
            self.project.importXml(xml)
            self.project.updateViewSign.emit()
            self.project.updatePaletteSign.emit()
            self.project.updateTimelineSign.emit()
            self.project.updateBackgroundSign.emit()
            self.project.updateFpsSign.emit()
            self.project.url = url
            self.project.dirUrl = os.path.dirname(url)

    def saveAsAction(self):
        url = get_save_url(self.project.dirUrl)
        if url:
            url = save_pix(self.project.exportXml(), url)
            if url:
                self.project.url = url
                self.project.dirUrl = os.path.dirname(url)
                self.project.saved = True
                self.updateTitle()
        
    def saveAction(self):
        if self.project.url:
            url = save_pix(self.project.exportXml(), self.project.url)
            if url:
                self.project.url = url
                self.project.dirUrl = os.path.dirname(url)
                self.project.saved = True
                self.updateTitle()
        else:
            self.saveAsAction()

    def importAsNewAction(self):
        urls = QtGui.QFileDialog.getOpenFileNames(
                    None, "Import PNG and GIF", 
                    self.project.dirUrl or os.path.expanduser("~"), 
                    "PNG and GIF files (*.png *.gif);;All files (*)")
        if not urls:
            return
        size, frames, colorTable = import_img(self.project, urls)
        if size and frames and colorTable:
            self.project.saveToUndo("all")
            self.project.initProject(size, colorTable, frames)
            self.project.updateViewSign.emit()
            self.project.updatePaletteSign.emit()
            self.project.updateTimelineSign.emit()
            
    def importAsLayerAction(self):
        urls = QtGui.QFileDialog.getOpenFileNames(
                    None, "Import PNG and GIF", 
                    self.project.dirUrl or os.path.expanduser("~"), 
                    "PNG and GIF files (*.png *.gif);;All files (*)")
        if urls:
            self.importAsLayer(urls)
            
    def importAsLayer(self, urls):
        size, frames, colorTable = import_img(self.project, urls,
                                              self.project.size,
                                              self.project.colorTable)
        if size and frames and colorTable:
            self.project.saveToUndo("all")
            self.project.importImg(size, colorTable, frames)
            self.project.updateViewSign.emit()
            self.project.updatePaletteSign.emit()
            self.project.updateTimelineSign.emit()
        
    def exportAction(self):
        export_png(self.project, self.project.dirUrl)
    
    def closeEvent(self, event):
        ret = True
        if not self.project.saved:
            message = QtGui.QMessageBox()
            message.setWindowTitle("Quit?")
            message.setText("Are you sure you want to quit?");
            message.setIcon(QtGui.QMessageBox.Warning)
            message.addButton("Cancel", QtGui.QMessageBox.RejectRole)
            message.addButton("Yes", QtGui.QMessageBox.AcceptRole)
            ret = message.exec_();
        if ret:
            settings = QtCore.QSettings()
            settings.beginGroup("mainWindow")
            settings.setValue("geometry", self.saveGeometry())
            settings.setValue("windowState", self.saveState())
            settings.setValue("lock", int(self.lockLayoutWidget.isChecked()))
            settings.endGroup()
            event.accept()
        else:
            event.ignore()
        
    ######## Project menu ##############################################
    def newAction(self):
        size, palette = NewDialog().getReturn()
        if size and palette:
            self.project.saveToUndo("all")
            self.project.initProject(size, palette)
            self.project.updateViewSign.emit()
            self.project.updatePaletteSign.emit()
            self.project.updateTimelineSign.emit()
            self.project.updateBackgroundSign.emit()
            self.project.updateFpsSign.emit()
            self.updateTitle()

    def cropAction(self):
        rect = CropDialog(self.project.size).getReturn()
        if rect:
            self.project.saveToUndo("size")
            self.project.timeline.applyToAllCanvas(
                    lambda c: Canvas(self.project, c.copy(rect)))
            self.project.size = rect.size()
            self.project.updateViewSign.emit()

    def resizeAction(self):
        newSize = ResizeDialog(self.project.size).getReturn()
        if newSize:
            self.project.saveToUndo("size")
            self.project.timeline.applyToAllCanvas(
                    lambda c: Canvas(self.project, c.scaled(newSize)))
            self.project.size = newSize
            self.project.updateViewSign.emit()
            
    def replacePaletteAction(self):
        url = QtGui.QFileDialog.getOpenFileName(None, "open palette file", 
                os.path.join("resources", "palette"), "Palette files (*.pal, *.gpl );;All files (*)")
        if url:
            pal = import_palette(url, len(self.project.colorTable))
            if pal:
                self.project.saveToUndo("colorTable_frames")
                self.project.changeColorTable(pal)
                self.project.color = 1
                self.project.currentColor = 1
                self.project.updateViewSign.emit()
                self.project.updatePaletteSign.emit()
                self.project.colorChangedSign.emit()
            
    def minimizePaletteAction(self):
        self.project.saveToUndo("colorTable_frames")
        usedColorsIndices = self.project.getUsedColorList()
        newPalette = [0]+[self.project.colorTable[i] for i in usedColorsIndices]
        self.project.changeColorTable(newPalette)
        self.project.color = 1
        self.project.currentColor = 1
        self.project.updateViewSign.emit()
        self.project.updatePaletteSign.emit()
        self.project.colorChangedSign.emit()
        
    def backgroundAction(self):
        color, pattern = BackgroundDialog(self.project.bgColor,
                                self.project.bgPattern).getReturn()
        if color and pattern:
            self.project.saveToUndo("background")
            self.project.bgColor = color
            self.project.bgPattern = pattern
            self.project.updateBackgroundSign.emit()

    def savePaletteAction(self):
        url = get_save_url(os.path.join("resources", "palette"), "pal")
        pal = export_palette(self.project.colorTable)
        if url:
            try:
                save = open(url, "w")
                save.write(pal)
                save.close()
                print("saved")
            except IOError:
                print("Can't open file")
        
    def savePenAction(self):
        if self.project.penDict["custom"]:
            url = get_save_url(os.path.join("resources", "pen"), "py")
            pen = export_pen(self.project.penDict["custom"], os.path.splitext(os.path.basename(url))[0])
            if url:
                try:
                    save = open(url, "w")
                    save.write(pen)
                    save.close()
                    print("saved")
                except IOError:
                    print("Can't open file")

    def reloadResourcesAction(self):
        self.project.importResources()
        self.toolsWidget.penWidget.loadPen()
        self.toolsWidget.brushWidget.loadBrush()
        
    ######## View menu ##############################################
    def lockLayoutAction(self, check):
        for dock in self.findChildren(QtGui.QDockWidget):
            dock.lock(check)
    
    def hideDock(self):
        hide = False
        for dock in self.findChildren(QtGui.QDockWidget):
            if dock.isVisible():
                hide = True
        if hide:
            self.hiddenDock = []
            for dock in self.findChildren(QtGui.QDockWidget):
                if dock.isVisible():
                    self.hiddenDock.append(dock.objectName())
                    dock.hide()
        elif self.hiddenDock:
            for dock in self.findChildren(QtGui.QDockWidget):
                if dock.objectName() in self.hiddenDock:
                    dock.show()
        else:
            for dock in self.findChildren(QtGui.QDockWidget):
                dock.show()
            
    ######## Shortcuts #################################################
    def selectFrame(self, n):
        exf = self.project.curFrame
        f = self.project.curFrame + n
        lf = self.project.timeline.frameVisibleCount()
        if f < 0:
            if self.project.loop:
                self.project.curFrame = lf -1
        elif f >= lf:
            if self.project.loop:
                self.project.curFrame = 0
            else: 
                self.project.curFrame = f
        else:
            self.project.curFrame = f
        if self.project.curFrame != exf:
            self.project.updateTimelineSign.emit()
            self.project.updateViewSign.emit()

    def selectLayer(self, n):
        if 0 <= self.project.curLayer+n < len(self.project.timeline):
            self.project.curLayer += n
            self.project.updateTimelineSign.emit()
            self.project.updateViewSign.emit()
            
    def updateTitle(self):
        url, sav = "untitled", "* "
        if self.project.saved:
            sav = ""
        if self.project.url:
            url = os.path.basename(self.project.url)
        self.setWindowTitle("%s%s - pixeditor" %(sav, url))

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
            l = []
            for url in event.mimeData().urls():
                l.append(url.toLocalFile())
            self.dropped.emit(l)
        else:
            event.ignore()