diff options
author | pravindalve | 2020-06-17 14:00:15 +0530 |
---|---|---|
committer | GitHub | 2020-06-17 14:00:15 +0530 |
commit | 75696d91982d07a33ff77729dc979bf7d8bb1a3f (patch) | |
tree | 0ad06f80d5c7a4dfef68a9cbd5df4f3746c33206 | |
parent | 20f1d2b0d14f5ee73cb92a62395c71c838259b67 (diff) | |
parent | 2184341d6cb906e55409513754074ab5fdb27d14 (diff) | |
download | Chemical-PFD-75696d91982d07a33ff77729dc979bf7d8bb1a3f.tar.gz Chemical-PFD-75696d91982d07a33ff77729dc979bf7d8bb1a3f.tar.bz2 Chemical-PFD-75696d91982d07a33ff77729dc979bf7d8bb1a3f.zip |
Merge pull request #19 from Blakeinstein/streamTable
Stream table Feature
-rw-r--r-- | src/main/python/main.py | 4 | ||||
-rw-r--r-- | src/main/python/shapes/line.py | 11 | ||||
-rw-r--r-- | src/main/python/utils/canvas.py | 31 | ||||
-rw-r--r-- | src/main/python/utils/fileWindow.py | 2 | ||||
-rw-r--r-- | src/main/python/utils/graphics.py | 9 | ||||
-rw-r--r-- | src/main/python/utils/streamTable.py | 183 | ||||
-rw-r--r-- | src/main/resources/base/app.qss | 13 |
7 files changed, 241 insertions, 12 deletions
diff --git a/src/main/python/main.py b/src/main/python/main.py index 9def7fb..42b4e48 100644 --- a/src/main/python/main.py +++ b/src/main/python/main.py @@ -143,7 +143,7 @@ class appWindow(QMainWindow): def closeEvent(self, event): #save alert on window close if len(self.activeFiles) and not dialogs.saveEvent(self): - event.ignore() + event.ignore() else: event.accept() self.writeSettings() @@ -184,7 +184,7 @@ class appWindow(QMainWindow): #Key input handler def keyPressEvent(self, event): #overload key press event for custom keyboard shortcuts - if event.modifiers() and Qt.ControlModifier: + if event.modifiers() & Qt.ControlModifier: if event.key() == Qt.Key_N: self.newProject() diff --git a/src/main/python/shapes/line.py b/src/main/python/shapes/line.py index 85c9274..1438fbc 100644 --- a/src/main/python/shapes/line.py +++ b/src/main/python/shapes/line.py @@ -1,8 +1,9 @@ import math from PyQt5.QtGui import QPen, QPainterPath, QBrush, QPainterPathStroker, QPainter, QCursor, QPolygonF from PyQt5.QtWidgets import QGraphicsItem, QGraphicsPathItem, QGraphicsTextItem, QMenu, QGraphicsLineItem -from PyQt5.QtCore import Qt, QPointF, QRectF, QLineF +from PyQt5.QtCore import Qt, QPointF, QRectF, QLineF, pyqtSignal +from collections import defaultdict class Grabber(QGraphicsPathItem): """ @@ -122,6 +123,8 @@ class Grabber(QGraphicsPathItem): class LineLabel(QGraphicsTextItem): + nameChanged = pyqtSignal() + def __init__(self, pos, parent=None): super(LineLabel, self).__init__() # initial text @@ -146,7 +149,8 @@ class LineLabel(QGraphicsTextItem): self.line.setFlag(QGraphicsItem.ItemStacksBehindParent) # reset position of line self.resetPos() - + self.values = defaultdict(lambda: 0) + def paint(self, painter, option, widget): # draw ellipse shape painter.save() # save painter @@ -280,6 +284,9 @@ class LineLabel(QGraphicsTextItem): def focusOutEvent(self, event): super(LineLabel, self).focusOutEvent(event) + self.setTextInteractionFlags(Qt.NoTextInteraction) + self.nameChanged.emit() + self.setTextInteractionFlags(Qt.NoTextInteraction) # set text non interactive def __getstate__(self): diff --git a/src/main/python/utils/canvas.py b/src/main/python/utils/canvas.py index 480a2e1..47e95a9 100644 --- a/src/main/python/utils/canvas.py +++ b/src/main/python/utils/canvas.py @@ -1,12 +1,13 @@ -from PyQt5.QtCore import Qt, QPointF +from PyQt5.QtCore import Qt, QPointF, QRectF from PyQt5.QtGui import QBrush, QPalette from PyQt5.QtWidgets import (QFileDialog, QApplication, QHBoxLayout, QMenu, - QTabWidget, QWidget, QSpacerItem, QStyle,) + QTabWidget, QWidget, QSpacerItem, QStyle, QGraphicsProxyWidget) from . import dialogs from .graphics import customView, customScene from .data import paperSizes, ppiList, sheetDimensionList from .app import shapeGrips, lines +from .streamTable import streamTable, moveRect import shapes @@ -24,12 +25,13 @@ class canvas(customView): self._ppi = ppi self._canvasSize = size self._landscape = landscape - # self.setFixedSize(parent.size()) + self.streamTable = None #Create area for the graphic items to be placed, this is just here right now for the future # when we will draw items on this, this might be changed if QGraphicScene is subclassed. #set layout and background color - self.painter = customScene() + self.painter = customScene() + self.painter.labelAdded.connect(self.updateStreamTable) self.painter.setBackgroundBrush(QBrush(Qt.white)) #set white background self.setScene(self.painter) @@ -38,7 +40,22 @@ class canvas(customView): self.parentMdiArea = parentMdiArea self.parentFileWindow = parentFileWindow self.customContextMenuRequested.connect(self.sideViewContextMenu) - + + def addStreamTable(self, pos=QPointF(0, 0), table=None): + self.streamTable = table if table else streamTable(self.labelItems, canvas=self) + + self.streamTableRect = moveRect() + self.streamTableRect.setFlags(moveRect.ItemIsMovable | + moveRect.ItemIsSelectable) + self.streamTableProxy = QGraphicsProxyWidget(self.streamTableRect) + self.streamTableProxy.setWidget(self.streamTable) + self.painter.addItem(self.streamTableRect) + self.streamTableRect.setPos(pos) + + def updateStreamTable(self, item): + if self.streamTable: + self.streamTable.model.insertColumn(item = item) + def sideViewContextMenu(self, pos): self.parentFileWindow.sideViewContextMenu(self.mapTo(self.parentFileWindow, pos)) @@ -80,6 +97,10 @@ class canvas(customView): # generator to filter out certain items for i in self.painter.items(): yield i + + @property + def labelItems(self): + return [i for i in self.items if isinstance(i, shapes.LineLabel)] @property def canvasSize(self): diff --git a/src/main/python/utils/fileWindow.py b/src/main/python/utils/fileWindow.py index 6d35437..e85d513 100644 --- a/src/main/python/utils/fileWindow.py +++ b/src/main/python/utils/fileWindow.py @@ -109,6 +109,8 @@ class fileWindow(QMdiSubWindow): menu.addAction("Remove Side View" if self.sideViewTab == self.tabber.currentWidget() else "View Side-By-Side", self.sideViewMode) menu.addAction("Reset Zoom", lambda : setattr(self.tabber.currentWidget().view, 'zoom', 1)) + if self.tabber.currentWidget().streamTable is None: + menu.addAction("Add Stream Table", lambda x=self, pos=point: x.tabber.currentWidget().addStreamTable(pos)) menu.exec_(self.tabber.mapToGlobal(point)) def sideViewMode(self): diff --git a/src/main/python/utils/graphics.py b/src/main/python/utils/graphics.py index e17fa49..3e1d193 100644 --- a/src/main/python/utils/graphics.py +++ b/src/main/python/utils/graphics.py @@ -1,4 +1,4 @@ -from PyQt5.QtCore import Qt, QPointF +from PyQt5.QtCore import Qt, QPointF, pyqtSignal from PyQt5.QtGui import QPen, QKeySequence from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsProxyWidget, QGraphicsItem, QUndoStack, QAction, QUndoView @@ -11,6 +11,7 @@ class customView(QGraphicsView): """ Defines custom QGraphicsView with zoom features and drag-drop accept event, overriding wheel event """ + def __init__(self, scene = None, parent=None): if scene is not None: #overloaded constructor super(customView, self).__init__(scene, parent) @@ -54,7 +55,7 @@ class customView(QGraphicsView): def wheelEvent(self, QWheelEvent): #overload wheelevent, to zoom if control is pressed, else scroll normally - if Qt.ControlModifier: #check if control is pressed + if QWheelEvent.modifiers() & Qt.ControlModifier: #check if control is pressed if QWheelEvent.source() == Qt.MouseEventNotSynthesized: #check if precision mouse(mac) # angle delta is 1/8th of a degree per scroll unit if self.zoom + QWheelEvent.angleDelta().y()/2880 > 0.1: # hit and trial value (2880) @@ -65,7 +66,7 @@ class customView(QGraphicsView): self.zoom += QWheelEvent.angleDelta().y() QWheelEvent.accept() # accept event so that scrolling doesnt happen simultaneously else: - return super().wheelEvent(self, QWheelEvent) # scroll if ctrl not pressed + return super(customView, self).wheelEvent(QWheelEvent) # scroll if ctrl not pressed @property def zoom(self): @@ -83,6 +84,8 @@ class customScene(QGraphicsScene): """ Extends QGraphicsScene with undo-redo functionality """ + labelAdded = pyqtSignal(shapes.QGraphicsItem) + def __init__(self, *args, parent=None): super(customScene, self).__init__(*args, parent=parent) diff --git a/src/main/python/utils/streamTable.py b/src/main/python/utils/streamTable.py new file mode 100644 index 0000000..acccdee --- /dev/null +++ b/src/main/python/utils/streamTable.py @@ -0,0 +1,183 @@ +from PyQt5.QtCore import Qt, QSize, QRect, QPoint, QAbstractTableModel, pyqtSignal, QModelIndex +from PyQt5.QtGui import QBrush, QPen, QColor, QCursor +from PyQt5.QtWidgets import QTableView, QMenu, QGraphicsRectItem, QInputDialog, QStyledItemDelegate, QHeaderView + +from collections import defaultdict + +class streamTableModel(QAbstractTableModel): + updateEvent = pyqtSignal() + + def __init__(self, parent, list, header, *args): + super(streamTableModel, self).__init__(parent, *args) + self.list = list + self.header = header + + def columnCount(self, parent=None): + return len(self.list) + + def rowCount(self, parent=None): + return len(self.header) + + def data(self, index, role): + 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() + else: + return self.list[index.column()].values[self.header[index.row()]] + + def setData(self, index, value, role): + if not index.isValid(): + return False + elif role != Qt.EditRole: + return False + if index.row() == 0: + self.list[index.column()].setPlainText(value) + else: + self.list[index.column()].values[self.header[index.row()]] = value + return True + + def insertColumn(self, int=None, item=None): + int = int if int else self.rowCount()+1 + self.beginInsertColumns(QModelIndex(), int, int) + self.list.insert(int, item) + self.endInsertColumns() + self.updateEvent.emit() + + def insertRow(self, int=None, name="newVal"): + self.beginInsertRows(QModelIndex(), int, int) + self.header.insert(int, name) + self.endInsertRows() + self.updateEvent.emit() + + def deleteRow(self, row): + self.beginRemoveRows(QModelIndex(), row, row) + valName = self.header.pop(row) + self.endRemoveRows() + for i in self.list: + i.values.pop(valName) + self.updateEvent.emit() + + def headerData(self, col, orientation, role): + if orientation == Qt.Vertical and role == Qt.DisplayRole: + return self.header[col] + return None + + def flags(self, index): + return (super(streamTableModel, self).flags(index) | Qt.ItemIsEditable) + +class streamTable(QTableView): + + 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"] + self.model = streamTableModel(self, itemLabels, header) + self.setShowGrid(False) + + self.horizontalHeader().hide() + header = verticalHeader(Qt.Vertical, self) + self.setVerticalHeader(header) + header.labelChangeRequested.connect(self.labelChange) + + self.setModel(self.model) + self.borderThickness = defaultdict(lambda: False) + self.model.updateEvent.connect(self.resizeHandler) + + self.setItemDelegateForRow(0, drawBorderDelegate(self)) + + def mousePressEvent(self, event): + 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)) + menu.addAction("Insert Row to bottom", lambda x=index.row(): self.insertRowBottom(x)) + menu.addAction("Delete row", lambda x=index.row(): self.model.deleteRow(x)) + menu.exec_(self.mapToGlobal(point)+ QPoint(20, 25)) + event.accept() + return super(streamTable, self).mousePressEvent(event) + + def changeRowBorder(self, row): + if self.borderThickness[row]: + self.borderThickness[row] = False + self.setItemDelegateForRow(row, QStyledItemDelegate(self)) + else: + self.borderThickness[row] = True + self.setItemDelegateForRow(row, drawBorderDelegate(self)) + + def labelChange(self, index): + newName, bool = QInputDialog.getText(self, "Change Property Name", "Enter new name", + text = self.model.header[index]) + if bool: + for i in self.model.list: + i.values[newName] = i.values.pop(self.model.header[index]) + self.model.header[index] = newName + self.repaint() + + def insertRowBottom(self, row): + name, bool = QInputDialog.getText(self, "New Property", "Enter name", + text = "newVal") + if bool: + self.model.insertRow(row + 1, name) + self.repaint() + + def resizeHandler(self): + self.resize(self.sizeHint()) + + def sizeHint(self): + return self.rect().size() + + def rect(self): + w = self.verticalHeader().width() + 4 + for i in range(self.model.columnCount()): + w += self.columnWidth(i) + h = 0 + for i in range(self.model.rowCount()): + h += self.rowHeight(i) + return QRect(0, 0, w, h) + +class drawBorderDelegate(QStyledItemDelegate): + + # def __init__(self, parent): + # super(drawBorderDelegate, self).__init__(parent) + + def paint(self, painter, option, index): + rect = option.rect + painter.drawLine(rect.bottomLeft(), rect.bottomRight()) + painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) + super(drawBorderDelegate, self).paint(painter, option, index) + +class moveRect(QGraphicsRectItem): + + def __init__(self, sideLength = 15, *args): + super(moveRect, self).__init__(-sideLength, -sideLength, sideLength, sideLength) + self.setBrush(Qt.transparent) + self.setPen(QPen(Qt.transparent)) + self.setCursor(QCursor(Qt.SizeAllCursor)) + self.setAcceptHoverEvents(True) + + def hoverEnterEvent(self, event): + self.setBrush(QBrush(QColor(0, 0, 0, 120))) + return super(moveRect, self).hoverEnterEvent(event) + + def hoverLeaveEvent(self, event): + self.setBrush(QBrush(QColor(0, 0, 0, 0))) + return super(moveRect, self).hoverLeaveEvent(event) + +class verticalHeader(QHeaderView): + labelChangeRequested = pyqtSignal(int) + + def mouseDoubleClickEvent(self, event): + index = self.logicalIndexAt(event.pos()) + self.labelChangeRequested.emit(index) + return super().mouseDoubleClickEvent(event)
\ No newline at end of file diff --git a/src/main/resources/base/app.qss b/src/main/resources/base/app.qss index e0b5140..5650279 100644 --- a/src/main/resources/base/app.qss +++ b/src/main/resources/base/app.qss @@ -264,4 +264,17 @@ customTabWidget customView { border-width: 0px; padding: 5px; border-radius: 2px; +} +QHeaderView { + qproperty-defaultAlignment: AlignHCenter; +} +QHeaderView::section { + padding: 4px; + border-style: none; + background-color: white; +} + +QTableView { + border: none; + background-color: white; }
\ No newline at end of file |