From f8519766d7f7fc70180411f912ed8ca15abb6a95 Mon Sep 17 00:00:00 2001 From: Blaine Date: Sat, 20 Jun 2020 14:43:57 +0530 Subject: refactor and comment code --- src/main/python/main.py | 63 +++++++++++++++-------------- src/main/python/shapes/shapes.py | 3 ++ src/main/python/utils/app.py | 18 +++++---- src/main/python/utils/canvas.py | 25 +++++++----- src/main/python/utils/custom.py | 64 +++++++++++++++++++++++------ src/main/python/utils/dialogs.py | 1 + src/main/python/utils/fileWindow.py | 13 +++--- src/main/python/utils/graphics.py | 8 ---- src/main/python/utils/layout.py | 16 ++++++++ src/main/python/utils/streamTable.py | 78 +++++++++++++++++++++++++----------- src/main/python/utils/tabs.py | 3 +- src/main/python/utils/toolbar.py | 2 - src/main/python/utils/undo.py | 1 + 13 files changed, 192 insertions(+), 103 deletions(-) diff --git a/src/main/python/main.py b/src/main/python/main.py index db938b5..35c95c3 100644 --- a/src/main/python/main.py +++ b/src/main/python/main.py @@ -25,8 +25,30 @@ class appWindow(QMainWindow): super(appWindow, self).__init__(parent) #create the menu bar + self.createMenuBar() + + self.mdi = QMdiArea(self) #create area for files to be displayed + self.mdi.setObjectName('mdi area') + + #create toolbar and add the toolbar plus mdi to layout + self.createToolbar() + + #set flags so that window doesnt look weird + self.mdi.setOption(QMdiArea.DontMaximizeSubWindowOnActivation, True) + self.mdi.setTabsClosable(True) + self.mdi.setTabsMovable(True) + self.mdi.setDocumentMode(False) + + #declare main window layout + self.setCentralWidget(self.mdi) + # self.resize(1280, 720) #set collapse dim + self.mdi.subWindowActivated.connect(self.tabSwitched) + self.readSettings() + + def createMenuBar(self): + # Fetches a reference to the menu bar in the main window, and adds actions to it. + titleMenu = self.menuBar() #fetch reference to current menu bar - # self.mainWidget.setObjectName("Main Widget") self.menuFile = titleMenu.addMenu('File') #File Menu newAction = self.menuFile.addAction("New", self.newProject) @@ -54,25 +76,7 @@ class appWindow(QMainWindow): imageAction.setShortcut(QKeySequence("Ctrl+P")) reportAction.setShortcut(QKeySequence("Ctrl+R")) - - self.mdi = QMdiArea(self) #create area for files to be displayed - self.mdi.setObjectName('mdi area') - - #create toolbar and add the toolbar plus mdi to layout - self.createToolbar() - - #set flags so that window doesnt look weird - self.mdi.setOption(QMdiArea.DontMaximizeSubWindowOnActivation, True) - self.mdi.setTabsClosable(True) - self.mdi.setTabsMovable(True) - self.mdi.setDocumentMode(False) - - #declare main window layout - self.setCentralWidget(self.mdi) - # self.resize(1280, 720) #set collapse dim - self.mdi.subWindowActivated.connect(self.tabSwitched) - self.readSettings() - + def createToolbar(self): #place holder for toolbar with fixed width, layout may change self.toolbar = toolbar(self) @@ -83,16 +87,16 @@ class appWindow(QMainWindow): self.toolbar.populateToolbar() def toolButtonClicked(self, object): + # To add the corresponding symbol for the clicked button to active scene. if self.mdi.currentSubWindow(): currentDiagram = self.mdi.currentSubWindow().tabber.currentWidget().painter if currentDiagram: graphic = getattr(shapes, object['object'])(*map(lambda x: int(x) if x.isdigit() else x, object['args'])) - # graphic.setPen(QPen(Qt.black, 2)) - # graphic.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable) - graphic.setPos(20, 20) + graphic.setPos(50, 50) currentDiagram.addItemPlus(graphic) def addSymbolWindow(self): + # Opens the add symbol window when requested from utils.custom import ShapeDialog ShapeDialog(self).exec() @@ -103,7 +107,6 @@ class appWindow(QMainWindow): self.mdi.addSubWindow(project) if not project.tabList: # important when unpickling a file instead 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 if self.count > 1: #switch to tab view if needed self.mdi.setViewMode(QMdiArea.TabbedView) @@ -118,6 +121,7 @@ class appWindow(QMainWindow): projectData = load(file) project = FileWindow(self.mdi) self.mdi.addSubWindow(project) + #create blank window and set its state project.__setstate__(projectData) project.resizeHandler() project.fileCloseEvent.connect(self.fileClosed) @@ -127,7 +131,7 @@ class appWindow(QMainWindow): self.mdi.setViewMode(QMdiArea.TabbedView) def saveProject(self): - #pickle all files in mdi area + #serialize all files in mdi area for j, i in enumerate(self.activeFiles): #get list of all windows with atleast one tab if i.tabCount: name = QFileDialog.getSaveFileName(self, 'Save File', f'New Diagram {j}', 'Process Flow Diagram (*.pfd)') @@ -170,6 +174,7 @@ class appWindow(QMainWindow): self.mdi.setViewMode(QMdiArea.SubWindowView) def writeSettings(self): + # write window state on window close settings.beginGroup("MainWindow") settings.setValue("maximized", self.isMaximized()) if not self.isMaximized(): @@ -178,6 +183,7 @@ class appWindow(QMainWindow): settings.endGroup() def readSettings(self): + # read window state when app launches settings.beginGroup("MainWindow") self.resize(settings.value("size", QSize(1280, 720))) self.move(settings.value("pos", QPoint(320, 124))) @@ -185,6 +191,7 @@ class appWindow(QMainWindow): self.showMaximized() settings.endGroup() + #useful one liner properties for getting data @property def activeFiles(self): return [i for i in self.mdi.subWindowList() if i.tabCount] @@ -219,11 +226,9 @@ class appWindow(QMainWindow): if self.mdi.activeSubWindow() and self.mdi.activeSubWindow().tabber.currentWidget(): for item in self.mdi.activeSubWindow().tabber.currentWidget().painter.selectedItems(): item.rotation += 1 - - - + if __name__ == '__main__': # 1. Instantiate ApplicationContext main = appWindow() main.show() - exit_code = app.app.exec_() # 2. Invoke appctxt.app.exec_() + exit_code = app.app.exec_() # 2. Invoke app.app.exec_() sys.exit(exit_code) diff --git a/src/main/python/shapes/shapes.py b/src/main/python/shapes/shapes.py index ca6490c..5876f6e 100644 --- a/src/main/python/shapes/shapes.py +++ b/src/main/python/shapes/shapes.py @@ -12,6 +12,7 @@ from PyQt5.QtWidgets import (QGraphicsColorizeEffect, QGraphicsEllipseItem, from .line import Line, findIndex from utils.app import fileImporter +# enum for all directions for line grip items directionsEnum = [ "top", "right", @@ -19,6 +20,7 @@ directionsEnum = [ "left" ] +# orientation enum for size grip items orientationEnum = [ Qt.Horizontal, Qt.Vertical @@ -469,6 +471,7 @@ class NodeItem(QGraphicsSvgItem): return self.flipState[1] def updateTransformation(self): + # update transformation on flipstate or rotation change transform = QTransform() h = -1 if self.flipH else 1 w = -1 if self.flipV else 1 diff --git a/src/main/python/utils/app.py b/src/main/python/utils/app.py index 275ef7e..39c3674 100644 --- a/src/main/python/utils/app.py +++ b/src/main/python/utils/app.py @@ -1,5 +1,5 @@ """ -Declare fbs application so that it can be imported in other modules. +Declare fbs application and various contextual variables so that it can be imported in other modules. """ from fbs_runtime.application_context.PyQt5 import ApplicationContext @@ -9,7 +9,7 @@ from PyQt5.QtWidgets import QWidget from json import JSONEncoder, dumps, loads, dump, load from os.path import join -from resources import resources +from resources import resources #application resources defined in resources.qrc app = ApplicationContext() settings = QSettings(QSettings.IniFormat, QSettings.UserScope ,"FOSSEE", "Chemical-PFD") @@ -19,11 +19,14 @@ def fileImporter(*file): # Helper function to fetch files from src/main/resources return app.get_resource(join(*file)) +#set application stylesheet with open(fileImporter("app.qss"), "r") as stylesheet: app.app.setStyleSheet(stylesheet.read()) class JSON_Encoder: - + """ + Defines serialization methods for differnt data types for json module + """ def _encode(obj): if isinstance(obj, dict): ## We'll need to iterate not just the value that default() usually gets passed @@ -50,7 +53,9 @@ class JSON_Encoder: return obj class JSON_Typer(JSONEncoder): - + """ + derived class for redirecting encode calls + """ def default(self, o): return o.__getstate__() @@ -60,7 +65,4 @@ class JSON_Typer(JSONEncoder): def encode(self, obj): return super(JSON_Typer, self).encode(self._encode(obj)) - -importer = pyqtProperty(str, fileImporter) - -memMap = {} \ No newline at end of file +memMap = {} #memory map for id references for loading projects \ No newline at end of file diff --git a/src/main/python/utils/canvas.py b/src/main/python/utils/canvas.py index 8608f7b..68b0902 100644 --- a/src/main/python/utils/canvas.py +++ b/src/main/python/utils/canvas.py @@ -42,6 +42,9 @@ class canvas(CustomView): self.customContextMenuRequested.connect(self.sideViewContextMenu) def addStreamTable(self, pos=QPointF(0, 0), table=None): + """ + build stream table at pos with table, if table is not passed, builds a blank table + """ self.streamTable = table if table else streamTable(self.labelItems, canvas=self) self.streamTableRect = moveRect() @@ -53,10 +56,16 @@ class canvas(CustomView): self.streamTableRect.setPos(pos) def updateStreamTable(self, item): + """ + updates stream table with any new line labels added + """ if self.streamTable: self.streamTable.model.insertColumn(item = item) def sideViewContextMenu(self, pos): + """ + shows the context menu for the side view + """ self.parentFileWindow.sideViewContextMenu(self.mapTo(self.parentFileWindow, pos)) def resizeView(self, w, h): @@ -69,11 +78,6 @@ class canvas(CustomView): frameWidth = self.frameWidth() #update view size self.setSceneRect(0, 0, width - frameWidth*2, height) - - def resizeEvent(self, event): - #overloaded function to also view size on window update - # self.adjustView() - pass def setCanvasSize(self, size): """ @@ -152,6 +156,7 @@ class canvas(CustomView): self.landscape = dict['landscape'] self.setObjectName(dict['ObjectName']) + #load symbols from the file, while building the memory map as well. for item in dict['symbols']: graphic = getattr(shapes, item['_classname_'])() graphic.__setstate__(dict = item) @@ -168,6 +173,7 @@ class canvas(CustomView): graphic.rotation = item['rotation'] graphic.flipH, graphic.flipV = item['flipstate'] + #load lines from the file, while using and building the memory map. for item in dict['lines']: line = shapes.Line(QPointF(*item['startPoint']), QPointF(*item['endPoint'])) memMap[item['id']] = line @@ -192,14 +198,11 @@ class canvas(CustomView): line.updateLine() line.addGrabber() + # add streamtable if it existed in the scene. if dict['streamTable']: table = streamTable(self.labelItems, self) self.addStreamTable(QPointF(*dict['streamTable'][1]), table) table.__setstate__(dict['streamTable'][0]) - memMap.clear() - self.painter.advance() - - - - \ No newline at end of file + memMap.clear() #clear out memory map as we now have no use for it. Hopefully garbage collected + self.painter.advance() #request collision detection \ No newline at end of file diff --git a/src/main/python/utils/custom.py b/src/main/python/utils/custom.py index 7e6b197..fedecdb 100644 --- a/src/main/python/utils/custom.py +++ b/src/main/python/utils/custom.py @@ -1,3 +1,6 @@ +""" +Holds the custom window to generate new symbols, can be called while running or throught the build interface. +""" from PyQt5.QtCore import QRectF, Qt, QSize from PyQt5.QtGui import (QBrush, QIcon, QImage, QPainter, QPainterPath, QPen, QPixmap, QTransform) @@ -12,17 +15,19 @@ from shapes import SizeGripItem, directionsEnum from .app import fileImporter - class ShapeDialog(QDialog): - + """ + The main dialog box for the custom symbol window. + """ def __init__(self, parent=None): super(ShapeDialog, self).__init__(parent) - self.resize(500, 300) + self.resize(500, 300) # resize to a fixed dim self.setWindowTitle("Add New Shapes") self.createLayout() self.graphic = None def createLayout(self): + #build layout for the dialog box importButton = QPushButton("Import", self) importButton.clicked.connect(self.importSVG) @@ -80,6 +85,7 @@ class ShapeDialog(QDialog): self.setLayout(layout) def importSVG(self): + # Imports svg file through user input, adds it to the scene and stores it as a reference self.name = QFileDialog.getOpenFileName(self, 'Open SVG File', '', 'Scalable Vector Graphics (*svg)') if self.name: self.graphic = QGraphicsSvgItem(self.name[0]) @@ -87,6 +93,9 @@ class ShapeDialog(QDialog): self.painter.addItem(self.graphic) def saveEvent(self): + # executes the build procedure + + #check if all necessary values are there, each is seperate to show qalerts later on if self.graphic is None: return @@ -102,10 +111,9 @@ class ShapeDialog(QDialog): if category == "": category = "misc" + # get rect for calculating grip positions graphicRect = self.graphic.boundingRect() - - QIcon - + #save file name = QFileDialog.getSaveFileName(self, 'Save Icon', className, 'PNG (*.png)') if name: @@ -113,6 +121,7 @@ class ShapeDialog(QDialog): else: return + #calculate grip positions and build a list gripList = [] x, y, w, h = graphicRect.getRect() for i in self.grips: @@ -125,10 +134,12 @@ class ShapeDialog(QDialog): entry.append(i.width) gripList.append(entry) - + # format list in class definition flavor grips = ",\n ".join([str(i) for i in gripList]) if gripList else "" if grips: grips = "self.grips = [" + grips + "]\n" + + # build output dialog box temp = QDialog(self) tempLayout = QBoxLayout(QBoxLayout.TopToBottom) output = OutputBox(temp, f""" @@ -159,15 +170,19 @@ class {className}(NodeItem): return [i for i in self.painter.items() if isinstance(i, gripAbstract)] def addGrip(self): + #adds a grip dot to the scene grip = gripDot() self.painter.addItem(grip) def addLineGrip(self): + #adds a line grip item rect = gripRect() self.painter.addItem(rect) class gripAbstract(QGraphicsItem): - + """ + Abstract class for mouse click behaviour + """ def __init__(self): super(gripAbstract, self).__init__() self.location = "top" @@ -178,6 +193,10 @@ class gripAbstract(QGraphicsItem): directionsEnum.index(self.location), False) class gripRect(gripAbstract): + """ + simulates line grip item with resizeablity + (Haha grip items on grip items. Progress) + """ def __init__(self, x=0, y=0, w=80, h=10 ): super(gripRect, self).__init__() self.rotation = 0 @@ -185,7 +204,8 @@ class gripRect(gripAbstract): self.width = w self.height = h self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) - + + #bounding rect and paint need to be implemented def boundingRect(self): return QRectF(-self.width / 2, -self.height / 2, self.width, self.height) @@ -193,8 +213,9 @@ class gripRect(gripAbstract): painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) painter.setBrush(QBrush(Qt.red)) painter.drawRect(self.boundingRect()) - + def addGripItems(self): + # adds the resizeable line grip items from the shape file :D for i, (direction) in enumerate((Qt.Vertical, Qt.Horizontal, Qt.Vertical, @@ -202,12 +223,14 @@ class gripRect(gripAbstract): self.sizeGripItems.append(SizeGripItem(i, direction, parent=self)) def updateSizeGripItem(self, index_no_updates=None): + #update positions for existing grip items index_no_updates = index_no_updates or [] for i, item in enumerate(self.sizeGripItems): if i not in index_no_updates: item.updatePosition() def itemChange(self, change, value): + #do the needful when item is updated if change == QGraphicsItem.ItemPositionHasChanged: # update grips self.updateSizeGripItem() @@ -221,6 +244,7 @@ class gripRect(gripAbstract): return super(gripRect, self).itemChange(change, value) def resize(self, index, movement): + #resize method self.prepareGeometryChange() if index in [0, 1]: self.width -= movement.x() @@ -233,7 +257,10 @@ class gripRect(gripAbstract): self.setTransform(transform, True) self.updateSizeGripItem([index]) -class gripDot(gripAbstract): +class gripDot(gripAbstract): + """ + class for circular grips + """ def boundingRect(self): return QRectF(0, 0, 10, 10) @@ -243,9 +270,20 @@ class gripDot(gripAbstract): painter.drawEllipse(self.boundingRect()) class OutputBox(QTextEdit): - + """ + Defines a read only text box for class output + """ def __init__(self, parent, text): super(OutputBox, self).__init__(parent) self.setReadOnly(True) self.setFontWeight(10) - self.setHtml(text) \ No newline at end of file + self.setHtml(text) + +if __name__ == '__main__': # 1. Instantiate ApplicationContext + #if app is launched directly + from .app import app + import sys + main = ShapeDialog() + main.show() + exit_code = app.app.exec_() # 2. Invoke app.app.exec_() + sys.exit(exit_code) \ No newline at end of file diff --git a/src/main/python/utils/dialogs.py b/src/main/python/utils/dialogs.py index f2c4eb9..043597c 100644 --- a/src/main/python/utils/dialogs.py +++ b/src/main/python/utils/dialogs.py @@ -110,6 +110,7 @@ def saveEvent(parent = None): return True def showUndoDialog(undoView, parent): + # helper function to show a dialog box containing the undo view dialogBox = QDialog(parent) dialogBox.resize(400, 400) layout = QHBoxLayout(dialogBox) diff --git a/src/main/python/utils/fileWindow.py b/src/main/python/utils/fileWindow.py index b2f5304..7ea5619 100644 --- a/src/main/python/utils/fileWindow.py +++ b/src/main/python/utils/fileWindow.py @@ -39,6 +39,7 @@ class FileWindow(QMdiSubWindow): layout = QHBoxLayout(self.mainWidget) self.createSideViewArea() #create the side view objects + # set size policies for window left = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) left.setHorizontalStretch(1) self.tabber.setSizePolicy(left) @@ -47,8 +48,8 @@ class FileWindow(QMdiSubWindow): right.setHorizontalStretch(1) self.sideView.setSizePolicy(right) + #build widget layout layout.addWidget(self.tabber) - # layout.addWidget(self.splitter) layout.addWidget(self.sideView) self.mainWidget.setLayout(layout) self.setWidget(self.mainWidget) @@ -66,7 +67,6 @@ class FileWindow(QMdiSubWindow): def createSideViewArea(self): #creates the side view widgets and sets them to invisible - # self.splitter = QSplitter(Qt.Vertical ,self) self.sideView = CustomView(parent = self) self.sideView.setInteractive(False) self.sideViewCloseButton = QPushButton('×', self.sideView) @@ -75,7 +75,6 @@ class FileWindow(QMdiSubWindow): self.sideViewCloseButton.setFixedSize(20, 20) self.moveSideViewCloseButton() self.sideViewCloseButton.clicked.connect(lambda: setattr(self, 'sideViewTab', None)) - # self.splitter.setVisible(False) self.sideView.setVisible(False) self.sideView.setContextMenuPolicy(Qt.CustomContextMenu) self.sideView.customContextMenuRequested.connect(self.sideViewContextMenu) @@ -127,13 +126,11 @@ class FileWindow(QMdiSubWindow): def sideViewToggle(self): #Function checks if current side view tab is set, and toggles view as required if self.sideViewTab: - # self.splitter.setVisible(True) self.sideView.setVisible(True) self.sideView.setScene(self.tabber.currentWidget().painter) self.resizeHandler() return True - else: - # self.splitter.setVisible(False) + else: self.sideView.setVisible(False) self.resizeHandler() return False @@ -163,6 +160,7 @@ class FileWindow(QMdiSubWindow): menu.exec_(self.sideView.mapToGlobal(point)) def sideViewSwitchCMenu(self, index): + # helper function to swtich side view tab self.sideViewTab = self.tabber.widget(index) def sideViewSwitchTab(self): @@ -234,8 +232,7 @@ class FileWindow(QMdiSubWindow): else: event.ignore() - #following 2 methods are defined for correct pickling of the scene. may be changed to json or xml later so as - # to not have a binary file. + #following 2 methods are defined for correct serialization of the scene. def __getstate__(self) -> dict: return { "_classname_": self.__class__.__name__, diff --git a/src/main/python/utils/graphics.py b/src/main/python/utils/graphics.py index 05ffc46..bb113ce 100644 --- a/src/main/python/utils/graphics.py +++ b/src/main/python/utils/graphics.py @@ -20,12 +20,6 @@ class CustomView(QGraphicsView): self._zoom = 1 self.setDragMode(True) #sets pannable using mouse self.setAcceptDrops(True) #sets ability to accept drops - if scene: - #create necessary undo redo actions to accept keyboard shortcuts - # self.addAction(scene.undoAction) - # self.addAction(scene.redoAction) - # self.addAction(scene.deleteAction) - pass #following four functions are required to be overridden for drag-drop functionality def dragEnterEvent(self, QDragEnterEvent): @@ -48,8 +42,6 @@ class CustomView(QGraphicsView): #QDropEvent.mimeData().text() defines intended drop item, the pos values define position obj = QDropEvent.mimeData().text().split('/') graphic = getattr(shapes, obj[0])(*map(lambda x: int(x) if x.isdigit() else x, obj[1:])) - # graphic.setPen(QPen(Qt.black, 2)) - # graphic.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable) graphic.setPos(QDropEvent.pos().x(), QDropEvent.pos().y()) self.scene().addItemPlus(graphic) QDropEvent.acceptProposedAction() diff --git a/src/main/python/utils/layout.py b/src/main/python/utils/layout.py index b977a68..e1354b0 100644 --- a/src/main/python/utils/layout.py +++ b/src/main/python/utils/layout.py @@ -2,6 +2,10 @@ from PyQt5.QtCore import Qt, QRect, QPoint, QSize from PyQt5.QtWidgets import QLayout, QSizePolicy class flowLayout(QLayout): + """ + Custom layout that flows horizontally first, then vertically. + From Qt examples. + """ def __init__(self, parent=None, margin=0, spacing=12): super(flowLayout, self).__init__(parent) @@ -12,46 +16,57 @@ class flowLayout(QLayout): self.itemList = [] def __del__(self): + # Delete layout call item = self.takeAt(0) while item: item = self.takeAt(0) def addItem(self, item): + # add item to layout self.itemList.append(item) def count(self): + # return a list of items return len(self.itemList) def itemAt(self, index): + # return item at index if index >= 0 and index < len(self.itemList): return self.itemList[index] return None def takeAt(self, index): + # pop item at index if index >= 0 and index < len(self.itemList): return self.itemList.pop(index) return None def expandingDirections(self): + # define orientation return Qt.Orientations(Qt.Orientation(0)) def hasHeightForWidth(self): + # height for width flag, height value for a particular width return True def heightForWidth(self, width): + # returns the height for a given width height = self.doLayout(QRect(0, 0, width, 0), True) return height def setGeometry(self, rect): + # change layout geometry super(flowLayout, self).setGeometry(rect) self.doLayout(rect, False) def sizeHint(self): + # returns the expected size return self.minimumSize() def minimumSize(self): + # calucalate minimum possible size below which, resize is impossible size = QSize() for item in self.itemList: @@ -63,6 +78,7 @@ class flowLayout(QLayout): return size def doLayout(self, rect, testOnly): + # build layout, testOnly defines if the geometry needs to be changed or not x = rect.x() y = rect.y() lineHeight = 0 diff --git a/src/main/python/utils/streamTable.py b/src/main/python/utils/streamTable.py index c8e682c..8b3b220 100644 --- a/src/main/python/utils/streamTable.py +++ b/src/main/python/utils/streamTable.py @@ -5,13 +5,17 @@ from PyQt5.QtWidgets import QTableView, QMenu, QGraphicsRectItem, QInputDialog, from collections import defaultdict class streamTableModel(QAbstractTableModel): + """ + Defines the table model for the table view + """ updateEvent = pyqtSignal() def __init__(self, parent, list, header, *args): super(streamTableModel, self).__init__(parent, *args) self.list = list self.header = header - + + # column count, row count, data and setdata are important methods to be overloaded def columnCount(self, parent=None): return len(self.list) @@ -19,31 +23,41 @@ class streamTableModel(QAbstractTableModel): return len(self.header) def data(self, index, role): + # retunrs data at index if role == Qt.TextAlignmentRole: return Qt.AlignHCenter + if role == Qt.BackgroundColorRole: return Qt.white + if not index.isValid(): return None + elif role != Qt.DisplayRole: return None + if index.row() == 0: - return self.list[index.column()].toPlainText() + return self.list[index.column()].toPlainText() # return label name else: - return self.list[index.column()].values[self.header[index.row()]] + return self.list[index.column()].values[self.header[index.row()]] # retunr label values def setData(self, index, value, role): + # defines how to manipulate data at a given index with value returns sucess value if not index.isValid(): return False + elif role != Qt.EditRole: return False + if index.row() == 0: - self.list[index.column()].setPlainText(value) + self.list[index.column()].setPlainText(value) # change label text else: - self.list[index.column()].values[self.header[index.row()]] = value + self.list[index.column()].values[self.header[index.row()]] = value #change label values + return True def insertColumn(self, int=None, item=None): + # inserting a label item int = int if int else self.rowCount()+1 self.beginInsertColumns(QModelIndex(), int, int) self.list.insert(int, item) @@ -52,54 +66,65 @@ class streamTableModel(QAbstractTableModel): self.updateEvent.emit() def insertRow(self, int=None, name="newVal"): + # inserting a header property self.beginInsertRows(QModelIndex(), int, int) self.header.insert(int, name) self.endInsertRows() self.updateEvent.emit() def deleteRow(self, row): + # removing a property self.beginRemoveRows(QModelIndex(), row, row) - valName = self.header.pop(row) + valName = self.header.pop(row) # remove from header self.endRemoveRows() + for i in self.list: - i.values.pop(valName) - self.updateEvent.emit() + i.values.pop(valName) #clear dictionary + + self.updateEvent.emit() # update request def headerData(self, col, orientation, role): + # definds how to fetch header data if orientation == Qt.Vertical and role == Qt.DisplayRole: return self.header[col] return None def flags(self, index): + # defines item editable flag return (super(streamTableModel, self).flags(index) | Qt.ItemIsEditable) class streamTable(QTableView): - + """ + subclasses stream table to display data properly + """ def __init__(self, itemLabels=[], canvas=None, parent=None): super(streamTable, self).__init__(parent=parent) self.canvas = canvas + for i in itemLabels: - i.nameChanged.connect(self.repaint) - header = ["name", "val1", "val2", "val3", "val4", "val5"] + i.nameChanged.connect(self.repaint) # connect repaint requests on name change + + header = ["name", "val1", "val2", "val3", "val4", "val5"] # prepare header names self.model = streamTableModel(self, itemLabels, header) - self.setShowGrid(False) + self.setShowGrid(False) # disable table grid - self.horizontalHeader().hide() - header = verticalHeader(Qt.Vertical, self) + self.horizontalHeader().hide() # remove horizontal header + + header = verticalHeader(Qt.Vertical, self) #create custom vertical header self.setVerticalHeader(header) header.labelChangeRequested.connect(self.labelChange) - self.setModel(self.model) - self.borderThickness = defaultdict(lambda: False) + self.setModel(self.model) #declare model + self.borderThickness = defaultdict(lambda: False) #thickness bool dict self.model.updateEvent.connect(self.resizeHandler) self.setItemDelegateForRow(0, drawBorderDelegate(self)) - self.borderThickness[0] = True + self.borderThickness[0] = True # set border true for name row def mousePressEvent(self, event): + # handle context menu request if event.button() == Qt.RightButton: point = event.pos() - # col = self.getCol(point.x()) index = self.indexAt(point) menu = QMenu("Context Menu", self) menu.addAction("Toggle bottom border thickness", lambda x=index.row(): self.changeRowBorder(x)) @@ -110,6 +135,7 @@ class streamTable(QTableView): return super(streamTable, self).mousePressEvent(event) def changeRowBorder(self, row): + # toggle column border thicnkess if self.borderThickness[row]: self.borderThickness.pop(row) self.setItemDelegateForRow(row, QStyledItemDelegate(self)) @@ -119,6 +145,7 @@ class streamTable(QTableView): self.verticalHeader().repaint() def labelChange(self, index): + # label name change newName, bool = QInputDialog.getText(self, "Change Property Name", "Enter new name", text = self.model.header[index]) if bool: @@ -128,6 +155,7 @@ class streamTable(QTableView): self.repaint() def insertRowBottom(self, row): + # dialog box for new property name, bool = QInputDialog.getText(self, "New Property", "Enter name", text = "newVal") if bool: @@ -162,10 +190,9 @@ class streamTable(QTableView): self.repaint() class drawBorderDelegate(QStyledItemDelegate): - - # def __init__(self, parent): - # super(drawBorderDelegate, self).__init__(parent) - + """ + class for drawing border line + """ def paint(self, painter, option, index): rect = option.rect painter.drawLine(rect.bottomLeft(), rect.bottomRight()) @@ -173,7 +200,9 @@ class drawBorderDelegate(QStyledItemDelegate): super(drawBorderDelegate, self).paint(painter, option, index) class moveRect(QGraphicsRectItem): - + """ + use to move the table on the scene + """ def __init__(self, sideLength = 15, *args): super(moveRect, self).__init__(-sideLength, -sideLength, sideLength, sideLength) self.setBrush(Qt.transparent) @@ -190,6 +219,9 @@ class moveRect(QGraphicsRectItem): return super(moveRect, self).hoverLeaveEvent(event) class verticalHeader(QHeaderView): + """ + Custom Vertical header for the table, with line border against corresponding rows + """ labelChangeRequested = pyqtSignal(int) def mouseDoubleClickEvent(self, event): diff --git a/src/main/python/utils/tabs.py b/src/main/python/utils/tabs.py index cedb298..70f9def 100644 --- a/src/main/python/utils/tabs.py +++ b/src/main/python/utils/tabs.py @@ -3,7 +3,7 @@ from PyQt5.QtCore import pyqtSignal, QSize, Qt class TabBarPlus(QTabBar): """ - Just implemented to overload resize and layout change to emit a signal + Just implemented to overload resize and layout change to emit a signal and change tab names. """ layoutChanged = pyqtSignal() nameChanged = pyqtSignal(int, str) @@ -16,6 +16,7 @@ class TabBarPlus(QTabBar): self.layoutChanged.emit() def mouseDoubleClickEvent(self, event): + # tab name change request if event.button() != Qt.LeftButton: return super().mouseDoubleClickEvent() index = self.currentIndex() diff --git a/src/main/python/utils/toolbar.py b/src/main/python/utils/toolbar.py index 3f6338b..f2bbd5a 100644 --- a/src/main/python/utils/toolbar.py +++ b/src/main/python/utils/toolbar.py @@ -9,8 +9,6 @@ from .data import toolbarItems from .app import fileImporter, app from .layout import flowLayout -# resourceManager = ApplicationContext() #Used to load images, mainly toolbar icons - class toolbar(QDockWidget): """ Defines the right side toolbar, using QDockWidget. diff --git a/src/main/python/utils/undo.py b/src/main/python/utils/undo.py index 92a8de8..6a46b27 100644 --- a/src/main/python/utils/undo.py +++ b/src/main/python/utils/undo.py @@ -91,6 +91,7 @@ class moveCommand(QUndoCommand): class resizeCommand(QUndoCommand): """ + Defines the resize event for the custom scene. """ def __init__(self, new, canvas, widget, parent = None): super(resizeCommand, self).__init__(parent) -- cgit