diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main/python/main.py | 36 | ||||
-rw-r--r-- | src/main/python/shapes/__init__.py | 2 | ||||
-rw-r--r-- | src/main/python/utils/dialogs.py | 10 | ||||
-rw-r--r-- | src/main/python/utils/fileWindow.py | 2 | ||||
-rw-r--r-- | src/main/python/utils/graphics.py | 75 | ||||
-rw-r--r-- | src/main/python/utils/undo.py | 63 |
6 files changed, 169 insertions, 19 deletions
diff --git a/src/main/python/main.py b/src/main/python/main.py index 7d72bae..db34119 100644 --- a/src/main/python/main.py +++ b/src/main/python/main.py @@ -7,7 +7,6 @@ from PyQt5.QtGui import QBrush, QColor, QImage, QPainter, QPalette, QPen from PyQt5.QtWidgets import (QComboBox, QFileDialog, QFormLayout, QVBoxLayout, QHBoxLayout, QLabel, QMainWindow, QMenu, QPushButton, QWidget, QMdiArea, QSplitter, QGraphicsItem) -from PyQt5 import QtWidgets from utils.canvas import canvas from utils.fileWindow import fileWindow @@ -15,6 +14,8 @@ from utils.data import ppiList, sheetDimensionList from utils import dialogs from utils.toolbar import toolbar +import shapes + class appWindow(QMainWindow): """ Application entry point, subclasses QMainWindow and implements the main widget, @@ -32,6 +33,10 @@ class appWindow(QMainWindow): self.menuFile.addAction("Open", self.openProject) self.menuFile.addAction("Save", self.saveProject) + self.menuEdit = titleMenu.addMenu('Edit') + self.undo = self.menuEdit.addAction("Undo") + self.redo = self.menuEdit.addAction("Redo") + self.menuGenerate = titleMenu.addMenu('Generate') #Generate menu self.menuGenerate.addAction("Image", self.saveImage) self.menuGenerate.addAction("Report", self.generateReport) @@ -53,7 +58,10 @@ class appWindow(QMainWindow): self.setCentralWidget(self.mdi) self.resize(1280, 720) #set collapse dim self.mdi.subWindowActivated.connect(self.tabSwitched) - + + def updateMenuBar(self): + self.undo.setAction(self.activeScene.painter.undoAction) + self.redo.setAction(self.activeScene.painter.redoAction) def createToolbar(self): #place holder for toolbar with fixed width, layout may change @@ -67,10 +75,10 @@ class appWindow(QMainWindow): def toolButtonClicked(self, object): currentDiagram = self.mdi.currentSubWindow().tabber.currentWidget().painter if currentDiagram: - graphic = getattr(QtWidgets, object['object'])(*object['args']) + graphic = getattr(shapes, object['object'])(*object['args']) graphic.setPen(QPen(Qt.black, 2)) graphic.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable) - currentDiagram.addItem(graphic) + currentDiagram.addItemPlus(graphic) def newProject(self): #call to create a new file inside mdi area @@ -81,9 +89,11 @@ class appWindow(QMainWindow): project.newDiagram() #create a new tab in the new file project.resizeHandler() project.fileCloseEvent.connect(self.fileClosed) #closed file signal to switch to sub window view + project.tabChangeEvent.connect(self.updateMenuBar) if self.count > 1: #switch to tab view if needed self.mdi.setViewMode(QMdiArea.TabbedView) project.show() + project.tabber.currentWidget().painter.createUndoView(self) def openProject(self): #show the open file dialog to open a saved file, then unpickle it. @@ -142,7 +152,7 @@ class appWindow(QMainWindow): if self.count <= 2 : self.mdi.setViewMode(QMdiArea.SubWindowView) - @property + @property def activeFiles(self): return [i for i in self.mdi.subWindowList() if i.tabCount] @@ -150,6 +160,10 @@ class appWindow(QMainWindow): def count(self): return len(self.mdi.subWindowList()) + @property + def activeScene(self): + return self.mdi.currentSubWindow().tabber.currentWidget() + #Key input handler def keyPressEvent(self, event): #overload key press event for custom keyboard shortcuts @@ -176,16 +190,18 @@ class appWindow(QMainWindow): #todo implement selectAll for item in self.mdi.activeSubWindow().tabber.currentWidget().items: item.setSelected(True) - - #todo copy, paste, undo redo + #todo copy, paste, undo redo + else: + return + event.accept() elif event.key() == Qt.Key_Delete or event.key() == Qt.Key_Backspace: - for item in self.mdi.activeSubWindow().tabber.currentWidget().painter.selectedItems(): - item.setEnabled(False) + for item in reversed(self.mdi.activeSubWindow().tabber.currentWidget().painter.selectedItems()): + # self.mdi.currentSubWindow().tabber.currentWidget().deleteItem(item) + pass #donot delete, to manage undo redo - event.accept() if __name__ == '__main__': app = ApplicationContext() # 1. Instantiate ApplicationContext diff --git a/src/main/python/shapes/__init__.py b/src/main/python/shapes/__init__.py new file mode 100644 index 0000000..a5a98f2 --- /dev/null +++ b/src/main/python/shapes/__init__.py @@ -0,0 +1,2 @@ +from . import * +from PyQt5.QtWidgets import *
\ No newline at end of file diff --git a/src/main/python/utils/dialogs.py b/src/main/python/utils/dialogs.py index e008aa5..3791599 100644 --- a/src/main/python/utils/dialogs.py +++ b/src/main/python/utils/dialogs.py @@ -99,4 +99,12 @@ def saveEvent(parent = None): if alert == QMessageBox.Save: if not parent.saveProject(): #the parent's saveProject method is called which returns false if saving was cancelled by the user return False - return True
\ No newline at end of file + return True + +def showUndoDialog(undoView, parent): + dialogBox = QDialog(parent) + dialogBox.resize(400, 400) + layout = QFormLayout(dialogBox) + layout.addWidget(undoView) + dialogBox.setWindowTitle("Undo Stack") + dialogBox.show()
\ No newline at end of file diff --git a/src/main/python/utils/fileWindow.py b/src/main/python/utils/fileWindow.py index e549568..8f2fd32 100644 --- a/src/main/python/utils/fileWindow.py +++ b/src/main/python/utils/fileWindow.py @@ -18,6 +18,7 @@ class fileWindow(QMdiSubWindow): canvases. Pre-Defined so that a file can be instantly created without defining the structure again. """ fileCloseEvent = pyqtSignal(int) + tabChangeEvent = pyqtSignal() def __init__(self, parent = None, title = 'New Project', size = 'A4', ppi = '72'): super(fileWindow, self).__init__(parent) @@ -203,6 +204,7 @@ class fileWindow(QMdiSubWindow): def changeTab(self, currentIndex): #placeholder function to detect tab change self.resizeHandler() + self.tabChangeEvent.emit() def closeTab(self, currentIndex): #show save alert on tab close diff --git a/src/main/python/utils/graphics.py b/src/main/python/utils/graphics.py index 0fe0030..62c62f7 100644 --- a/src/main/python/utils/graphics.py +++ b/src/main/python/utils/graphics.py @@ -1,7 +1,11 @@ -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPen -from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsProxyWidget, QGraphicsItem -from PyQt5 import QtWidgets +from PyQt5.QtCore import Qt, QPointF +from PyQt5.QtGui import QPen, QKeySequence +from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsProxyWidget, QGraphicsItem, QUndoStack, QAction, QUndoView + +from .undo import * +from .dialogs import showUndoDialog + +import shapes class customView(QGraphicsView): """ @@ -15,6 +19,10 @@ class customView(QGraphicsView): self._zoom = 1 self.setDragMode(True) #sets pannable using mouse self.setAcceptDrops(True) #sets ability to accept drops + if scene: + self.addAction(scene.undoAction) + self.addAction(scene.redoAction) + self.addAction(scene.deleteAction) #following four functions are required to be overridden for drag-drop functionality def dragEnterEvent(self, QDragEnterEvent): @@ -35,10 +43,10 @@ class customView(QGraphicsView): #defines item drop, fetches text, creates corresponding QGraphicItem and adds it to scene if QDropEvent.mimeData().hasText(): #QDropEvent.mimeData().text() defines intended drop item, the pos values define position - graphic = getattr(QtWidgets, QDropEvent.mimeData().text())(QDropEvent.pos().x()-150, QDropEvent.pos().y()-150, 300, 300) + graphic = getattr(shapes, QDropEvent.mimeData().text())(QDropEvent.pos().x()-150, QDropEvent.pos().y()-150, 300, 300) graphic.setPen(QPen(Qt.black, 2)) graphic.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable) - self.scene().addItem(graphic) + self.scene().addItemPlus(graphic) QDropEvent.acceptProposedAction() def wheelEvent(self, QWheelEvent): @@ -73,5 +81,56 @@ class customScene(QGraphicsScene): re-implement QGraphicsScene for future functionality hint: QUndoFramework """ - pass -
\ No newline at end of file + def __init__(self, *args, parent=None): + super(customScene, self).__init__(*args, parent=parent) + + self.undoStack = QUndoStack(self) + self.createActions() + + + def createActions(self): + self.deleteAction = QAction("Delete Item", self) + self.deleteAction.setShortcut(Qt.Key_Delete) + self.deleteAction.triggered.connect(self.deleteItem) + + self.undoAction = self.undoStack.createUndoAction(self, "Undo") + self.undoAction.setShortcut(QKeySequence.Undo) + self.redoAction = self.undoStack.createRedoAction(self, "Redo") + self.redoAction.setShortcut(QKeySequence.Redo) + + def createUndoView(self, parent): + undoView = QUndoView(self.undoStack, parent) + # undoView.resize(400, 400) + # undoView.show() + # undoView.setAttribute(Qt.WA_QuitOnClose, False) + showUndoDialog(undoView, parent) + + def deleteItem(self): + if self.selectedItems(): + for item in self.selectedItems(): + self.undoStack.push(deleteCommand(item, self)) + + def itemMoved(self, movedItem, lastPos): + self.undoStack.push(moveCommand(movedItem, lastPos)) + + def addItemPlus(self, item): + # returnVal = self.addItem(item) + self.undoStack.push(addCommand(item, self)) + # return returnVal + + def mousePressEvent(self, event): + bdsp = event.buttonDownScenePos(Qt.LeftButton) + point = QPointF(bdsp.x(), bdsp.y()) + itemList = self.items(point) + self.movingItem = itemList[0] if itemList else None + if self.movingItem and event.button() == Qt.LeftButton: + self.oldPos = self.movingItem.pos() + self.clearSelection() + return super(customScene, self).mousePressEvent(event) + + def mouseReleaseEvent(self, event): + if self.movingItem and event.button() == Qt.LeftButton: + if self.oldPos != self.movingItem.pos(): + self.itemMoved(self.movingItem, self.oldPos) + self.movingItem = None + return super(customScene, self).mouseReleaseEvent(event) diff --git a/src/main/python/utils/undo.py b/src/main/python/utils/undo.py new file mode 100644 index 0000000..7832483 --- /dev/null +++ b/src/main/python/utils/undo.py @@ -0,0 +1,63 @@ +from PyQt5.QtWidgets import QUndoCommand + +class addCommand(QUndoCommand): + + def __init__(self, addItem, scene, parent = None): + super(addCommand, self).__init__(parent) + self.scene = scene + self.diagramItem = addItem + self.itemPos = addItem.pos() + self.setText(f"Add {self.diagramItem} {self.itemPos}") + + def undo(self): + self.scene.removeItem(self.diagramItem) + self.scene.update() + + def redo(self): + self.scene.addItem(self.diagramItem) + self.diagramItem.setPos(self.itemPos) + self.scene.clearSelection() + self.scene.update() + +class deleteCommand(QUndoCommand): + + def __init__(self, item, scene, parent = None): + super(deleteCommand, self).__init__(parent) + self.scene = scene + item.setSelected(False) + self.diagramItem = item + self.setText(f"Delete {self.diagramItem} {self.diagramItem.pos()}") + + def undo(self): + self.scene.addItem(self.diagramItem) + self.scene.update() + + def redo(self): + self.scene.removeItem(self.diagramItem) + +class moveCommand(QUndoCommand): + + def __init__(self, item, lastPos, parent = None): + super(moveCommand, self).__init__(parent) + self.diagramItem = item + self.lastPos = lastPos + self.newPos = item.pos() + + def undo(self): + self.diagramItem.setPos(self.lastPos) + self.diagramItem.scene().update() + self.setText(f"Move {self.diagramItem} {self.newPos}") + + def redo(self): + self.diagramItem.setPos(self.newPos) + self.setText(f"Move {self.diagramItem} {self.newPos}") + + def mergeWith(self, move): + item = move.diagramItem + + if self.diagramItem != item: + return False + + self.newPos = item.pos() + self.setText(f"Move {self.diagramItem} {self.newPos}") + return True
\ No newline at end of file |