summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/python/main.py36
-rw-r--r--src/main/python/shapes/__init__.py2
-rw-r--r--src/main/python/utils/dialogs.py10
-rw-r--r--src/main/python/utils/fileWindow.py2
-rw-r--r--src/main/python/utils/graphics.py75
-rw-r--r--src/main/python/utils/undo.py63
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