summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--README.md171
-rw-r--r--build.py26
-rw-r--r--requirements.txt2
-rw-r--r--requirements/base.txt2
-rw-r--r--src/main/python/main.py105
-rw-r--r--src/main/python/resources/__init__.py0
-rw-r--r--src/main/python/resources/resources.py65
-rw-r--r--src/main/python/shapes/line.py18
-rw-r--r--src/main/python/shapes/shapes.py69
-rw-r--r--src/main/python/utils/app.py21
-rw-r--r--src/main/python/utils/canvas.py60
-rw-r--r--src/main/python/utils/custom.py289
-rw-r--r--src/main/python/utils/dialogs.py1
-rw-r--r--src/main/python/utils/fileWindow.py25
-rw-r--r--src/main/python/utils/graphics.py25
-rw-r--r--src/main/python/utils/layout.py16
-rw-r--r--src/main/python/utils/streamTable.py104
-rw-r--r--src/main/python/utils/tabs.py15
-rw-r--r--src/main/python/utils/toolbar.py8
-rw-r--r--src/main/python/utils/undo.py3
-rw-r--r--src/main/resources/base/app.qss73
-rw-r--r--src/main/ui/close.png (renamed from src/main/resources/base/ui/close.png)bin255 -> 255 bytes
-rw-r--r--src/main/ui/resources.rcc5
24 files changed, 890 insertions, 216 deletions
diff --git a/.gitignore b/.gitignore
index 9aeb547..0e61b94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ src/main/python/experimental/*
*.pfd
.vscode
.vscode/*.jsons
-.idea/* \ No newline at end of file
+.idea/*
+target/*
diff --git a/README.md b/README.md
index 77f8e4c..7122393 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,170 @@
-# Chemical-PFD
+# Chemical PFD Tool ##
+Repository for a Process Flow Diagram Software
+
+                                    
+![Python](https://img.shields.io/badge/python-v3.6-blue.svg)
+![version](https://img.shields.io/badge/version-0.0.1-blue)
+[![python-mixedCase-style](https://img.shields.io/badge/code%20style-mixed-brightgreen.svg?style=flat)](https://wiki.c2.com/?CamelCase)
+[![Dependencies](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/FOSSEE/Chemical-PFD/network/dependencies)
+[![License](https://img.shields.io/badge/license-GPLv3-blue.svg)](https://opensource.org/licenses/GPL-3.0)
+
+## Screenshots
+> Main window
+<p align="center"><img width="95%" src="https://i.imgur.com/YHBTTHE.png"></p>
+
+## Tech/framework used
+
+#### Built with
+- [PyQt5](https://www.riverbankcomputing.com/software/pyqt/)
+- [FBS](https://build-system.fman.io/)
+
+## Features ####
+> 1. Drag and Drop symbols from toolbar to scene
+> 2. Undo Redo action
+> 3. Save and Load files
+> 4. Connect symbols with lines, and add labels
+> 5. Create stream table
+> 6. Use various paper sizes
+> 7. Work on multiple diagrams and file at once
+> 8. View diagrams side by side
+> 9. Zoom In/out on the scene
+
+## Code overview #
+
+> #### src/main/python/main.py
+> main application entry point, defines the main window and runs it.
+
+> #### src/main/python/shapes
+> Contains the shape and line definitions and logic.
+
+> #### src/main/python/utils
+> contains the sub window definitions along with various utility methods.
+
+
+## Installation
+#### clone this repository by running
+```bash
+git clone https://github.com/FOSSEE/Chemical-PFD.git
+```
+or by simply pressing the Clone or Download button and using your own preferred way of obtaining a working copy of the repository
+#### requirements can be installed using (PIP should be up-to-date! tested on 20.0.2)
+```bash
+pip install --upgrade pip
+pip install -r requirements.txt
+```
+#### Then run using
+```bash
+fbs run
+```
+or
+```bash
+python3 build.py run
+```
+additionally, if fbs doesnt work, you can manually run the program as
+```bash
+python3 ./src/main/python/main.py
+```
+that is run the **main.py** file located in **./src/main/python/main.py**
+any output generated when run this way would be saved in **./src/main/python/**
+
+
+
+## Building
+There are two methods of doing this,
+### Manually
+#### Compiling the resources
+Using pyrcc5, the resource.qrc can be compiled to a python file
+```bash
+pyrcc5 -o src/main/python/resources/resources.py src/main/ui/resources.rcc
+```
+
+#### Building binaries
+Using fbs's freeze feature.
+```bash
+fbs freeze
+```
+
+#### Releasing
+One can now build installer using fbs,
+```bash
+fbs release
+```
+note: Windows user will need [nsis](https://sourceforge.net/projects/nsis/) installed on their system and added to env path.
+Additionally multiple things might need to be done, follow the onscreen instruction.
+
+
+### Build.py script
+
+#### Resource compilation and building binaries
+can be done, by using build.py build
+```bash
+python3 build.py build
+```
+
+#### TODO
+
+## Adding symbols
+The process of adding symbols, is simple.
+
+### Obtain svg for symbol
+It is necessary that the symbol is in svg format.
+One can use any of the many svg tools out there.
+We recommend [Inkscape](https://inkscape.org/)
+
+### Preparing class entries
+
+#### Class definition
+Under src/main/python/shapes/shape.py, using the following as an example, one can create his own class definition for the symbol. The grip list is the percentage position of the grip item object along with the parent's width and height, the third value is its position and the fourth value if specified is the width/height if the grip is a line grip item.
+```python
+class HorizontalVessel(NodeItem):
+ def __init__(self):
+ super(HorizontalVessel, self).__init__("svg/Process Vessels/Horizontal Vessel")
+ self.grips = [
+ [50, 100, "top", 87.08554680344],
+ [0, 50, "left"],
+ [100, 50, "right"],
+ [50, 0, "bottom", 87.08554680344]
+ ]
+```
+
+#### Items.json Entry
+The items.json is present in src/main/resources/base/config/items.json
+Once the class name and category has been decided, an items.json entry can be configured as follows,
+```javascript
+{
+ "Process Vessels": {
+ "Horizontal Vessel": {
+ "name": "Horizontal Vessel",
+ "icon": ".\\Process Vessels\\Horizontal Vessel.png",
+ "class": "Process Vessels",
+ "object": "HorizontalVessel",
+ "args": []
+ },
+ }
+}
+```
+
+#### Toolbar icon
+A 64x64 toolbar icon as a png needs to be prepared to be shown in the toolbar. It needs to be placed in src/main/resources/base/toolbar/ in the corresponding folder in the json file.
+
+### Automating
+Most of the above process can be automated, following are a few procedures one can use
+
+#### Python Script(For making icons and items.json entries for multiple svgs)
+You can find the script [here](https://gist.github.com/Blakeinstein/c349216ac1de86c66024d607140c9dfb)
+
+#### Using the symbol generator (For class + items.json)
+You can launch the tool while running the app, by clicking Add new Symbols in the edit menu.
+Or by directly launching the edit symbol menu using build.py
+
+```bash
+python3 build.py symbolGen
+```
+
+## API Reference
+
+The QtForPython docs were used to implement the program, one can reference them here
+- [QtForPython](https://doc.qt.io/qtforpython/contents.html)
+The docs for Qt for C++ library can be found here
+- [Qt](https://doc.qt.io/)
-Repository for Process Flow Diagram Software
diff --git a/build.py b/build.py
new file mode 100644
index 0000000..91a66fb
--- /dev/null
+++ b/build.py
@@ -0,0 +1,26 @@
+from fbs.cmdline import command
+from os.path import dirname
+import subprocess
+import sys
+import fbs.cmdline
+import fbs
+
+sys.path.append('src/main/python/')
+
+@command
+def compileResources():
+ subprocess.call("pyrcc5 -o src/main/python/resources/resources.py src/main/ui/resources.rcc", shell=True)
+
+@command
+def symbolGen():
+ from utils import custom
+ custom.main()
+
+@command
+def build():
+ compileResources()
+ subprocess.call("fbs freeze", shell=True)
+
+if __name__ == '__main__':
+ project_dir = dirname(__file__)
+ fbs.cmdline.main(project_dir) \ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 0c683ba..cd978ff 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
PyQt5
-fbs
+fbs \ No newline at end of file
diff --git a/requirements/base.txt b/requirements/base.txt
new file mode 100644
index 0000000..cd978ff
--- /dev/null
+++ b/requirements/base.txt
@@ -0,0 +1,2 @@
+PyQt5
+fbs \ No newline at end of file
diff --git a/src/main/python/main.py b/src/main/python/main.py
index 4cf1791..35c95c3 100644
--- a/src/main/python/main.py
+++ b/src/main/python/main.py
@@ -2,13 +2,13 @@ import sys
from fbs_runtime.application_context.PyQt5 import ApplicationContext
from PyQt5.QtCore import QObject, Qt, pyqtSignal, QSize, QPoint
-from PyQt5.QtGui import QBrush, QColor, QImage, QPainter, QPalette, QPen
+from PyQt5.QtGui import QBrush, QColor, QImage, QPainter, QPalette, QPen, QKeySequence
from PyQt5.QtWidgets import (QComboBox, QFileDialog, QFormLayout, QVBoxLayout,
QHBoxLayout, QLabel, QMainWindow, QMenu,
QPushButton, QWidget, QMdiArea, QSplitter, QGraphicsItem)
from utils.canvas import canvas
-from utils.fileWindow import fileWindow
+from utils.fileWindow import FileWindow
from utils.data import ppiList, sheetDimensionList
from utils import dialogs
from utils.toolbar import toolbar
@@ -25,23 +25,7 @@ class appWindow(QMainWindow):
super(appWindow, self).__init__(parent)
#create the menu bar
- titleMenu = self.menuBar() #fetch reference to current menu bar
- # self.mainWidget.setObjectName("Main Widget")
-
- self.menuFile = titleMenu.addMenu('File') #File Menu
- self.menuFile.addAction("New", self.newProject)
- self.menuFile.addAction("Open", self.openProject)
- self.menuFile.addAction("Save", self.saveProject)
-
- self.menuEdit = titleMenu.addMenu('Edit')
- self.undo = self.menuEdit.addAction("Undo", lambda x=self: x.activeScene.painter.undoAction.trigger())
- self.redo = self.menuEdit.addAction("Redo", lambda x=self: x.activeScene.painter.redoAction.trigger())
-
- self.menuEdit.addAction("Show Undo Stack", lambda x=self: x.activeScene.painter.createUndoView(self) )
-
- self.menuGenerate = titleMenu.addMenu('Generate') #Generate menu
- self.menuGenerate.addAction("Image", self.saveImage)
- self.menuGenerate.addAction("Report", self.generateReport)
+ self.createMenuBar()
self.mdi = QMdiArea(self) #create area for files to be displayed
self.mdi.setObjectName('mdi area')
@@ -60,7 +44,39 @@ class appWindow(QMainWindow):
# 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.menuFile = titleMenu.addMenu('File') #File Menu
+ newAction = self.menuFile.addAction("New", self.newProject)
+ openAction = self.menuFile.addAction("Open", self.openProject)
+ saveAction = self.menuFile.addAction("Save", self.saveProject)
+
+ newAction.setShortcut(QKeySequence.New)
+ openAction.setShortcut(QKeySequence.Open)
+ saveAction.setShortcut(QKeySequence.Save)
+
+ self.menuEdit = titleMenu.addMenu('Edit')
+ undoAction = self.undo = self.menuEdit.addAction("Undo", lambda x=self: x.activeScene.painter.undoAction.trigger())
+ redoAction = self.redo = self.menuEdit.addAction("Redo", lambda x=self: x.activeScene.painter.redoAction.trigger())
+
+ undoAction.setShortcut(QKeySequence.Undo)
+ redoAction.setShortcut(QKeySequence.Redo)
+
+ self.menuEdit.addAction("Show Undo Stack", lambda x=self: x.activeScene.painter.createUndoView(self) )
+ self.menuEdit.addSeparator()
+ self.menuEdit.addAction("Add new symbols", self.addSymbolWindow)
+
+ self.menuGenerate = titleMenu.addMenu('Generate') #Generate menu
+ imageAction = self.menuGenerate.addAction("Image", self.saveImage)
+ reportAction = self.menuGenerate.addAction("Report", self.generateReport)
+
+ imageAction.setShortcut(QKeySequence("Ctrl+P"))
+ reportAction.setShortcut(QKeySequence("Ctrl+R"))
+
def createToolbar(self):
#place holder for toolbar with fixed width, layout may change
self.toolbar = toolbar(self)
@@ -71,23 +87,26 @@ 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(50, 50)
currentDiagram.addItemPlus(graphic)
- graphic.setPos(20, 20)
-
+
+ def addSymbolWindow(self):
+ # Opens the add symbol window when requested
+ from utils.custom import ShapeDialog
+ ShapeDialog(self).exec()
+
def newProject(self):
#call to create a new file inside mdi area
- project = fileWindow(self.mdi)
+ project = FileWindow(self.mdi)
project.setObjectName("New Project")
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)
@@ -100,8 +119,9 @@ class appWindow(QMainWindow):
for files in name[0]:
with open(files,'r') as file:
projectData = load(file)
- project = fileWindow(self.mdi)
+ 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)
@@ -111,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)')
@@ -154,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():
@@ -162,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)))
@@ -169,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]
@@ -185,25 +208,7 @@ class appWindow(QMainWindow):
def keyPressEvent(self, event):
#overload key press event for custom keyboard shortcuts
if event.modifiers() & Qt.ControlModifier:
- if event.key() == Qt.Key_N:
- self.newProject()
-
- elif event.key() == Qt.Key_S:
- self.saveProject()
-
- elif event.key() == Qt.Key_O:
- self.openProject()
-
- elif event.key() == Qt.Key_W:
- self.close()
-
- elif event.key() == Qt.Key_P:
- if Qt.AltModifier:
- self.saveImage()
- else:
- self.generateReport()
-
- elif event.key() == Qt.Key_A:
+ if event.key() == Qt.Key_A:
#todo implement selectAll
for item in self.mdi.activeSubWindow().tabber.currentWidget().items:
item.setSelected(True)
@@ -221,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/resources/__init__.py b/src/main/python/resources/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/main/python/resources/__init__.py
diff --git a/src/main/python/resources/resources.py b/src/main/python/resources/resources.py
new file mode 100644
index 0000000..51952b7
--- /dev/null
+++ b/src/main/python/resources/resources.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+
+# Resource object code
+#
+# Created by: The Resource Compiler for PyQt5 (Qt v5.15.0)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x00\xff\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x0e\x00\x00\x00\x0e\x08\x06\x00\x00\x00\x1f\x48\x2d\xd1\
+\x00\x00\x00\x06\x62\x4b\x47\x44\x00\x00\x00\x00\x00\x00\xf9\x43\
+\xbb\x7f\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\
+\xdf\x06\x09\x0b\x33\x34\x04\x7e\xc0\x14\x00\x00\x00\x1d\x69\x54\
+\x58\x74\x43\x6f\x6d\x6d\x65\x6e\x74\x00\x00\x00\x00\x00\x43\x72\
+\x65\x61\x74\x65\x64\x20\x77\x69\x74\x68\x20\x47\x49\x4d\x50\x64\
+\x2e\x65\x07\x00\x00\x00\x63\x49\x44\x41\x54\x28\x53\x63\x60\x18\
+\x48\xe0\x03\xb4\x1c\x84\x71\x01\x14\x79\x26\x2c\xaa\xb0\x69\xc6\
+\x10\x63\x44\xd3\x88\xac\x60\x0b\x54\x0e\x9b\x18\x03\xba\x46\x90\
+\x5a\x0c\xd3\xa1\x06\xc0\x0c\x02\x73\xb1\x69\xc4\xa6\x19\x45\x13\
+\x48\x01\x36\x3f\x42\x2d\xc0\x4f\x61\xb3\x91\x2c\xa7\x62\x0b\x08\
+\x6c\x62\x28\x7e\xc4\xaa\x00\xea\x60\x0c\x39\x6c\x7e\xc4\x08\x08\
+\xa0\x66\x6c\x62\xf8\x03\x81\xea\xb2\x00\xa1\x91\x0c\x3d\x2a\x6a\
+\x45\xe6\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+"
+
+qt_resource_name = b"\
+\x00\x09\
+\x06\x99\x2e\x3e\
+\x00\x63\
+\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x49\x00\x63\x00\x6f\x00\x6e\
+"
+
+qt_resource_struct_v1 = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+"
+
+qt_resource_struct_v2 = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x72\xa2\x8c\x44\x86\
+"
+
+qt_version = [int(v) for v in QtCore.qVersion().split('.')]
+if qt_version < [5, 8, 0]:
+ rcc_version = 1
+ qt_resource_struct = qt_resource_struct_v1
+else:
+ rcc_version = 2
+ qt_resource_struct = qt_resource_struct_v2
+
+def qInitResources():
+ QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
diff --git a/src/main/python/shapes/line.py b/src/main/python/shapes/line.py
index c893fe6..856869d 100644
--- a/src/main/python/shapes/line.py
+++ b/src/main/python/shapes/line.py
@@ -292,13 +292,16 @@ class LineLabel(QGraphicsTextItem):
"text": self.toPlainText(),
"index": self.index,
"gap": self.gap,
- "pos": (self.pos().x(), self.pos().y())
+ "pos": (self.pos().x(), self.pos().y()),
+ "values": self.values
}
def __setstate__(self, dict):
self.setPlainText(dict['text'])
self.index = dict['index']
self.gap = dict['gap']
+ for key, value in dict['values'].items():
+ self.values[key] = value
def findIndex(line, pos):
@@ -1008,8 +1011,9 @@ class Line(QGraphicsPathItem):
action = contextMenu.exec_(event.screenPos())
# check for label action and add text label as child
if action == addLableAction:
- print(event.scenePos(), event.pos())
- self.label.append(LineLabel(event.scenePos(), self)) # text label as child
+ label = LineLabel(event.scenePos(), self)
+ self.label.append(label) # text label as child
+ self.scene().labelAdded.emit(label)
def __getstate__(self):
return {
@@ -1022,8 +1026,14 @@ class Line(QGraphicsPathItem):
"refLine": hex(id(self.refLine)) if self.refLine else 0,
"refIndex": self.refIndex,
"label": [i for i in self.label],
- "id": hex(id(self))
+ "id": hex(id(self)),
+ "startGap": self.startGap,
+ "endGap": self.endGap
}
def __setstate__(self, dict):
self.points = [QPointF(x, y) for x, y in dict["points"]]
+ self.startPoint = QPointF(*dict['startPoint'])
+ self.endPoint = QPointF(*dict['endPoint'])
+ self.startGap = dict['startGap']
+ self.endGap = dict['endGap']
diff --git a/src/main/python/shapes/shapes.py b/src/main/python/shapes/shapes.py
index 7205ba7..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
@@ -244,7 +246,14 @@ class LineGripItem(QGraphicsPathItem):
@property
def m_location(self):
- return directionsEnum[(self._m_location + self.parentItem().rotation)%4]
+ if self.parentItem().__class__ == Line:
+ return directionsEnum[self._m_location]
+ index = (self._m_location + self.parentItem().rotation)
+ if index%2:
+ index = (index + 2*self.parentItem().flipH)%4
+ else:
+ index = (index + 2*self.parentItem().flipV)%4
+ return directionsEnum[index]
@m_location.setter
def m_location(self, location):
@@ -267,7 +276,7 @@ class LineGripItem(QGraphicsPathItem):
if self.size:
painter.save()
pen = self.pen()
- pen.setWidth(-1)
+ pen.setWidth(1)
painter.setPen(pen)
painter.drawPath(self.path())
painter.restore()
@@ -451,22 +460,47 @@ class NodeItem(QGraphicsSvgItem):
self.sizeGripItems = []
self.label = None
self._rotation = 0
-
+ self.flipState = [False, False]
+
@property
- def rotation(self):
- return self._rotation
+ def flipH(self):
+ return self.flipState[0]
- @rotation.setter
- def rotation(self, rotation):
- self._rotation = rotation % 4
+ @property
+ def flipV(self):
+ return self.flipState[1]
+
+ def updateTransformation(self):
+ # update transformation on flipstate or rotation change
transform = QTransform()
- transform.rotate(90*rotation)
+ h = -1 if self.flipH else 1
+ w = -1 if self.flipV else 1
+ transform.rotate(90*self.rotation)
+ transform.scale(h, w)
+ self.setTransform(transform)
self.setTransform(transform)
for i in self.lineGripItems:
i.setTransform(transform)
i.updatePosition()
- for j in i.lines:
- j.createPath()
+
+ @flipH.setter
+ def flipH(self, state):
+ self.flipState[0] = state
+ self.updateTransformation()
+
+ @flipV.setter
+ def flipV(self, state):
+ self.flipState[1] = state
+ self.updateTransformation()
+
+ @property
+ def rotation(self):
+ return self._rotation
+
+ @rotation.setter
+ def rotation(self, rotation):
+ self._rotation = rotation % 4
+ self.updateTransformation()
def boundingRect(self):
"""Overrides QGraphicsSvgItem's boundingRect() virtual public function and
@@ -603,11 +637,12 @@ class NodeItem(QGraphicsSvgItem):
"""
# create a menu and add action
contextMenu = QMenu()
- addLableAction = contextMenu.addAction("add Label") # add action for text label
+ contextMenu.addAction("Add Label", lambda : setattr(self, "label", ItemLabel(self)))
+ contextMenu.addAction("Rotate right(E)", lambda : setattr(self, "rotation", self.rotation + 1))
+ contextMenu.addAction("Rotate left(Q)", lambda : setattr(self, "rotation", self.rotation - 1))
+ contextMenu.addAction("Flip Horizontally", lambda: setattr(self, "flipH", not self.flipH))
+ contextMenu.addAction("Flip Vertically", lambda: setattr(self, "flipV", not self.flipV))
action = contextMenu.exec_(event.screenPos())
- # check for label action and add text label as child
- if action == addLableAction:
- self.label = ItemLabel(self) # text label as child
def __getstate__(self):
return {
@@ -616,7 +651,9 @@ class NodeItem(QGraphicsSvgItem):
"height": self.height,
"pos": (self.pos().x(), self.pos().y()),
"lineGripItems": [(hex(id(i)), i.m_index) for i in self.lineGripItems],
- "label": self.label
+ "label": self.label,
+ "rotation": self.rotation,
+ "flipstate": self.flipState
}
def __setstate__(self, dict):
diff --git a/src/main/python/utils/app.py b/src/main/python/utils/app.py
index 96a45b0..39c3674 100644
--- a/src/main/python/utils/app.py
+++ b/src/main/python/utils/app.py
@@ -1,14 +1,16 @@
"""
-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
-from PyQt5.QtCore import QSettings, pyqtProperty
+from PyQt5.QtCore import QSettings, pyqtProperty, QResource
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget
from json import JSONEncoder, dumps, loads, dump, load
from os.path import join
+from resources import resources #application resources defined in resources.qrc
+
app = ApplicationContext()
settings = QSettings(QSettings.IniFormat, QSettings.UserScope ,"FOSSEE", "Chemical-PFD")
version = app.build_settings['version']
@@ -17,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
@@ -48,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__()
@@ -58,8 +65,4 @@ class JSON_Typer(JSONEncoder):
def encode(self, obj):
return super(JSON_Typer, self).encode(self._encode(obj))
-
-importer = pyqtProperty(str, fileImporter)
-
-shapeGrips = {}
-lines = {} \ 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 47e95a9..68b0902 100644
--- a/src/main/python/utils/canvas.py
+++ b/src/main/python/utils/canvas.py
@@ -4,14 +4,14 @@ from PyQt5.QtWidgets import (QFileDialog, QApplication, QHBoxLayout, QMenu,
QTabWidget, QWidget, QSpacerItem, QStyle, QGraphicsProxyWidget)
from . import dialogs
-from .graphics import customView, customScene
+from .graphics import CustomView, CustomScene
from .data import paperSizes, ppiList, sheetDimensionList
-from .app import shapeGrips, lines
+from .app import memMap
from .streamTable import streamTable, moveRect
import shapes
-class canvas(customView):
+class canvas(CustomView):
"""
Defines the work area for a single sheet. Contains a QGraphicScene along with necessary properties
for context menu and dialogs.
@@ -30,7 +30,7 @@ class canvas(customView):
# 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)
@@ -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):
"""
@@ -142,7 +146,8 @@ class canvas(customView):
"ObjectName": self.objectName(),
"symbols": [i for i in self.painter.items() if isinstance(i, shapes.NodeItem)],
"lines": sorted([i for i in self.painter.items() if isinstance(i, shapes.Line)], key = lambda x: 1 if x.refLine else 0),
- "landscape": self.landscape
+ "landscape": self.landscape,
+ "streamTable": [self.streamTable, (self.streamTableRect.pos().x(), self.streamTableRect.pos().y())] if self.streamTable else False
}
def __setstate__(self, dict):
@@ -151,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)
@@ -159,27 +165,30 @@ class canvas(customView):
graphic.updateLineGripItem()
graphic.updateSizeGripItem()
for gripitem in item['lineGripItems']:
- shapeGrips[gripitem[0]] = (graphic, gripitem[1])
+ memMap[gripitem[0]] = (graphic, gripitem[1])
if item['label']:
graphicLabel = shapes.ItemLabel(pos = QPointF(*item['label']['pos']), parent = graphic)
graphicLabel.__setstate__(item['label'])
self.painter.addItem(graphicLabel)
+ 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']))
- lines[item['id']] = line
+ memMap[item['id']] = line
line.__setstate__(dict = item)
self.painter.addItem(line)
- graphic, index = shapeGrips[item['startGripItem']]
+ graphic, index = memMap[item['startGripItem']]
line.startGripItem = graphic.lineGripItems[index]
- graphic.lineGripItems[index].line = line
+ graphic.lineGripItems[index].lines.append(line)
if item['endGripItem']:
- graphic, index = shapeGrips[item['endGripItem']]
+ graphic, index = memMap[item['endGripItem']]
line.endGripItem = graphic.lineGripItems[index]
- graphic.lineGripItems[index].line = line
+ graphic.lineGripItems[index].lines.append(line)
else:
- line.refLine = lines[item['refLine']]
- lines[item['refLine']].midLines.append(line)
+ line.refLine = memMap[item['refLine']]
+ memMap[item['refLine']].midLines.append(line)
line.refIndex = item['refIndex']
for label in item['label']:
labelItem = shapes.LineLabel(QPointF(*label['pos']), line)
@@ -189,10 +198,11 @@ class canvas(customView):
line.updateLine()
line.addGrabber()
- shapeGrips.clear()
- lines.clear()
- self.painter.advance()
-
-
-
- \ No newline at end of file
+ # 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() #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
new file mode 100644
index 0000000..2c8ffc6
--- /dev/null
+++ b/src/main/python/utils/custom.py
@@ -0,0 +1,289 @@
+"""
+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)
+from PyQt5.QtSvg import QGraphicsSvgItem
+from PyQt5.QtWidgets import (QBoxLayout, QDialog, QFileDialog,
+ QGraphicsEllipseItem, QGraphicsItem,
+ QGraphicsScene, QGraphicsView, QGridLayout,
+ QInputDialog, QLabel, QLineEdit, QPushButton,
+ QTextEdit)
+
+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) # 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)
+
+ saveButton = QPushButton("Save", self)
+ saveButton.clicked.connect(self.saveEvent)
+
+ self.symbolName = QLineEdit(self)
+ self.symbolName.setPlaceholderText("Enter Symbol Name")
+ symbolNameLabel = QLabel("Symbol Name")
+ symbolNameLabel.setBuddy(self.symbolName)
+
+ self.symbolClass = QLineEdit(self)
+ self.symbolClass.setPlaceholderText("Enter Symbol Class Name")
+ symbolClassLabel = QLabel("Symbol Class Name")
+ symbolClassLabel.setBuddy(self.symbolClass)
+
+ self.symbolCategory = QLineEdit(self)
+ self.symbolCategory.setPlaceholderText("Enter Symbol Category")
+ symbolCategoryLabel = QLabel("Symbol Category")
+ symbolCategoryLabel.setBuddy(self.symbolCategory)
+
+ addGripItem = QPushButton("Add Grip Item", self)
+ addGripItem.clicked.connect(self.addGrip)
+ addLineGripItem = QPushButton("Add Line Grip Item", self)
+ addLineGripItem.clicked.connect(self.addLineGrip)
+
+ self.painter = QGraphicsScene()
+ view = QGraphicsView(self.painter)
+
+ layout = QGridLayout(self)
+
+ subLayout = QBoxLayout(QBoxLayout.LeftToRight)
+ subLayout.addWidget(importButton)
+ subLayout.addWidget(saveButton)
+ subLayout.addStretch(1)
+
+ layout.addLayout(subLayout, 0, 0, 1, -1)
+
+ subLayout2 = QBoxLayout(QBoxLayout.LeftToRight)
+ subLayout2.addWidget(view, stretch=1)
+
+ subLayout3 = QBoxLayout(QBoxLayout.TopToBottom)
+ subLayout3.addWidget(symbolNameLabel)
+ subLayout3.addWidget(self.symbolName)
+ subLayout3.addWidget(symbolClassLabel)
+ subLayout3.addWidget(self.symbolClass)
+ subLayout3.addWidget(symbolCategoryLabel)
+ subLayout3.addWidget(self.symbolCategory)
+ subLayout3.addStretch(1)
+ subLayout3.addWidget(addGripItem)
+ subLayout3.addWidget(addLineGripItem)
+ subLayout2.addLayout(subLayout3)
+
+ layout.addLayout(subLayout2, 1, 0, -1, -1)
+ 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])
+ self.graphic.setZValue(-1)
+ 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
+
+ itemName = self.symbolName.text()
+ if itemName is '':
+ return
+
+ className = self.symbolClass.text()
+ if className is '':
+ return
+
+ category = self.symbolCategory.text()
+ if category == "":
+ category = "misc"
+
+ # get rect for calculating grip positions
+ graphicRect = self.graphic.boundingRect()
+
+ #save file
+ name = QFileDialog.getSaveFileName(self, 'Save Icon', className, 'PNG (*.png)')
+ if name:
+ QIcon(self.name[0]).pixmap(QSize(64, 64)).toImage().save(name[0])
+ else:
+ return
+
+ #calculate grip positions and build a list
+ gripList = []
+ x, y, w, h = graphicRect.getRect()
+ for i in self.grips:
+ pos = i.pos()
+ entry = [abs((x-pos.x())/w)*100, abs((y-pos.y())/h)*100, i.location]
+ if isinstance(i, gripRect):
+ if i.location in ["top", "bottom"]:
+ entry.append(i.height)
+ else:
+ 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"""
+<b> Class Definition:</b>
+<pre>
+class {className}(NodeItem):
+ def __init__(self):
+ super({className}, self).__init__("svg/{category}/{str.split(name[0], "/")[-1][:-4]}")
+ {grips}
+</pre>
+<b> Items.json entry:</b>
+<pre>
+"{category}": {{
+ "{itemName}": {{
+ "name": "{itemName}",
+ "icon": ".\\{category}\\{str.split(name[0], "/")[-1]}",
+ "class": "{category}",
+ "object": "{className}",
+ "args": []
+ }}
+}}</pre>""")
+ tempLayout.addWidget(output)
+ temp.setLayout(tempLayout)
+ temp.exec()
+
+ @property
+ def grips(self):
+ 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"
+ self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
+
+ def mouseDoubleClickEvent(self, event):
+ self.location, _ = QInputDialog.getItem(None, "Change location", "Select location", directionsEnum,
+ 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
+ self.sizeGripItems = []
+ 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)
+
+ def paint(self, painter, option, index):
+ 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,
+ Qt.Horizontal)):
+ 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()
+ return
+ # check if item is add on scene
+ if change == QGraphicsItem.ItemSceneHasChanged and self.scene():
+ # add grips and update them
+ self.addGripItems()
+ self.updateSizeGripItem()
+ return
+ 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()
+ self.height -= movement.y()
+ else:
+ self.width += movement.x()
+ self.height += movement.y()
+ transform = QTransform()
+ transform.translate(movement.x() / 2, movement.y() / 2)
+ self.setTransform(transform, True)
+ self.updateSizeGripItem([index])
+
+class gripDot(gripAbstract):
+ """
+ class for circular grips
+ """
+ def boundingRect(self):
+ return QRectF(0, 0, 10, 10)
+
+ def paint(self, painter, option, index):
+ painter.setPen(QPen(Qt.black, 1, Qt.SolidLine))
+ painter.setBrush(QBrush(Qt.red))
+ 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)
+
+def 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 e85d513..7ea5619 100644
--- a/src/main/python/utils/fileWindow.py
+++ b/src/main/python/utils/fileWindow.py
@@ -5,14 +5,14 @@ from PyQt5.QtWidgets import (QFileDialog, QHBoxLayout,
QSplitter, QWidget, QStyle, QSizePolicy)
from os import path
from . import dialogs
-from .graphics import customView
+from .graphics import CustomView
from .canvas import canvas
-from .tabs import customTabWidget
+from .tabs import CustomTabWidget
from .undo import resizeCommand
from .app import dump, loads, JSON_Typer, version
-class fileWindow(QMdiSubWindow):
+class FileWindow(QMdiSubWindow):
"""
This defines a single file, inside the application, consisting of multiple tabs that contain
canvases. Pre-Defined so that a file can be instantly created without defining the structure again.
@@ -21,14 +21,14 @@ class fileWindow(QMdiSubWindow):
tabChangeEvent = pyqtSignal()
def __init__(self, parent = None, title = 'New Project', size = 'A0', ppi = '72'):
- super(fileWindow, self).__init__(parent)
+ super(FileWindow, self).__init__(parent)
self._sideViewTab = None
self.index = None
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
#Uses a custom QTabWidget that houses a custom new Tab Button, used to house the seperate
# diagrams inside a file
- self.tabber = customTabWidget(self)
+ self.tabber = CustomTabWidget(self)
self.tabber.setObjectName(title) #store title as object name for pickling
self.tabber.tabCloseRequested.connect(self.closeTab) # Show save alert on tab close
self.tabber.currentChanged.connect(self.changeTab) # placeholder just to detect tab change
@@ -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,8 +67,7 @@ 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 = CustomView(parent = self)
self.sideView.setInteractive(False)
self.sideViewCloseButton = QPushButton('×', self.sideView)
self.sideViewCloseButton.setObjectName("sideViewCloseButton")
@@ -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 3e1d193..bb113ce 100644
--- a/src/main/python/utils/graphics.py
+++ b/src/main/python/utils/graphics.py
@@ -7,24 +7,19 @@ from .dialogs import showUndoDialog
import shapes
-class customView(QGraphicsView):
+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)
+ super(CustomView, self).__init__(scene, parent)
else:
- super(customView, self).__init__(parent)
+ super(CustomView, self).__init__(parent)
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)
#following four functions are required to be overridden for drag-drop functionality
def dragEnterEvent(self, QDragEnterEvent):
@@ -47,10 +42,8 @@ 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)
- self.scene().addItemPlus(graphic)
graphic.setPos(QDropEvent.pos().x(), QDropEvent.pos().y())
+ self.scene().addItemPlus(graphic)
QDropEvent.acceptProposedAction()
def wheelEvent(self, QWheelEvent):
@@ -66,7 +59,7 @@ class customView(QGraphicsView):
self.zoom += QWheelEvent.angleDelta().y()
QWheelEvent.accept() # accept event so that scrolling doesnt happen simultaneously
else:
- return super(customView, self).wheelEvent(QWheelEvent) # scroll if ctrl not pressed
+ return super(CustomView, self).wheelEvent(QWheelEvent) # scroll if ctrl not pressed
@property
def zoom(self):
@@ -80,14 +73,14 @@ class customView(QGraphicsView):
self._zoom = value
self.scale(self.zoom / temp, self.zoom / temp)
-class customScene(QGraphicsScene):
+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)
+ super(CustomScene, self).__init__(*args, parent=parent)
self.undoStack = QUndoStack(self) #Used to store undo-redo moves
self.createActions() #creates necessary actions that need to be called for undo-redo
@@ -132,7 +125,7 @@ class customScene(QGraphicsScene):
if self.movingItem and event.button() == Qt.LeftButton:
self.oldPos = self.movingItem.pos() #if left click is held, then store old pos
self.clearSelection() #clears selected items
- return super(customScene, self).mousePressEvent(event)
+ return super(CustomScene, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
# overloaded mouse release event to check if an item was moved
@@ -141,4 +134,4 @@ class customScene(QGraphicsScene):
#if item pos had changed, when mouse was realeased, emit itemMoved signal
self.itemMoved(self.movingItem, self.oldPos)
self.movingItem = None #clear movingitem reference
- return super(customScene, self).mouseReleaseEvent(event)
+ return super(CustomScene, self).mouseReleaseEvent(event)
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 acccdee..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,85 +23,108 @@ 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)
+ item.nameChanged.connect(self.parent().repaint)
self.endInsertColumns()
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() # remove horizontal header
- self.horizontalHeader().hide()
- header = verticalHeader(Qt.Vertical, self)
+ 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 # 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))
@@ -108,14 +135,17 @@ class streamTable(QTableView):
return super(streamTable, self).mousePressEvent(event)
def changeRowBorder(self, row):
+ # toggle column border thicnkess
if self.borderThickness[row]:
- self.borderThickness[row] = False
+ self.borderThickness.pop(row)
self.setItemDelegateForRow(row, QStyledItemDelegate(self))
else:
self.borderThickness[row] = True
self.setItemDelegateForRow(row, drawBorderDelegate(self))
+ 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:
@@ -125,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:
@@ -145,12 +176,23 @@ class streamTable(QTableView):
for i in range(self.model.rowCount()):
h += self.rowHeight(i)
return QRect(0, 0, w, h)
+
+ def __getstate__(self):
+ return {
+ "borderThickness": self.borderThickness,
+ "header": self.model.header
+ }
+
+ def __setstate__(self, dict):
+ for key, value in dict['borderThickness'].items():
+ self.borderThickness[key] = value
+ self.model.header = dict['header']
+ 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())
@@ -158,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)
@@ -175,9 +219,21 @@ 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):
index = self.logicalIndexAt(event.pos())
self.labelChangeRequested.emit(index)
- return super().mouseDoubleClickEvent(event) \ No newline at end of file
+ return super().mouseDoubleClickEvent(event)
+
+ def paintSection(self, painter, option, index):
+ painter.save()
+ super(verticalHeader, self).paintSection(painter, option, index)
+ painter.restore()
+ if self.parentWidget().borderThickness[index]:
+ rect = option
+ painter.drawLine(rect.bottomLeft(), rect.bottomRight())
+ painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) \ No newline at end of file
diff --git a/src/main/python/utils/tabs.py b/src/main/python/utils/tabs.py
index 1186191..70f9def 100644
--- a/src/main/python/utils/tabs.py
+++ b/src/main/python/utils/tabs.py
@@ -1,9 +1,9 @@
from PyQt5.QtWidgets import QTabBar, QPushButton, QTabWidget, QInputDialog
from PyQt5.QtCore import pyqtSignal, QSize, Qt
-class tabBarPlus(QTabBar):
+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()
@@ -26,17 +27,17 @@ class tabBarPlus(QTabBar):
self.nameChanged.emit(index, newName)
-class customTabWidget(QTabWidget):
+class CustomTabWidget(QTabWidget):
"""
QTabWidget with a new tab button, also catches layoutChange signal by
- the tabBarPlus to dynamically move the button to the correct location
+ the TabBarPlus to dynamically move the button to the correct location
"""
plusClicked = pyqtSignal()
def __init__(self, parent=None):
- super(customTabWidget, self).__init__(parent)
+ super(CustomTabWidget, self).__init__(parent)
- self.tab = tabBarPlus()
- self.setTabBar(self.tab) #set tabBar to our custom tabBarPlus
+ self.tab = TabBarPlus()
+ self.setTabBar(self.tab) #set tabBar to our custom TabBarPlus
self.plusButton = QPushButton('+', self) #create the new tab button
#style the new tab button
diff --git a/src/main/python/utils/toolbar.py b/src/main/python/utils/toolbar.py
index a175aff..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.
@@ -107,7 +105,7 @@ class toolbar(QDockWidget):
#helper functions to create required buttons
for itemClass in itemClasses:
self.toolbarButtonDict[itemClass] = {}
- label = sectionLabel(itemClass)
+ label = SectionLabel(itemClass)
self.toolbarLabelDict[itemClass] = label
for item in toolbarItems[itemClass].keys():
obj = toolbarItems[itemClass][item]
@@ -165,7 +163,7 @@ class toolbarButton(QToolButton):
#defines button size
return QSize(55, 55)
-class sectionLabel(QLabel):
+class SectionLabel(QLabel):
def __init__(self, *args):
- super(sectionLabel, self).__init__(*args) \ No newline at end of file
+ super(SectionLabel, self).__init__(*args) \ No newline at end of file
diff --git a/src/main/python/utils/undo.py b/src/main/python/utils/undo.py
index 8426494..6a46b27 100644
--- a/src/main/python/utils/undo.py
+++ b/src/main/python/utils/undo.py
@@ -26,7 +26,7 @@ class addCommand(QUndoCommand):
self.scene = scene
self.diagramItem = addItem
self.itemPos = addItem.pos()
- self.setText(f"Add {objectName(self.diagramItem)}")
+ self.setText(f"Add {objectName(self.diagramItem)} at {self.itemPos.x()}, {self.itemPos.y()}")
def undo(self):
self.scene.removeItem(self.diagramItem)
@@ -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)
diff --git a/src/main/resources/base/app.qss b/src/main/resources/base/app.qss
index d7b25b4..cd11034 100644
--- a/src/main/resources/base/app.qss
+++ b/src/main/resources/base/app.qss
@@ -37,40 +37,40 @@ QLineEdit {
QLineEdit:focus{
border-color: #7cabf9;
}
-tabBarPlus {
+TabBarPlus {
qproperty-drawBase: 0;
left: 5px;
background-color: transparent;
font-size: 15px;
}
-tabBarPlus:focus {
+TabBarPlus:focus {
border: 0px transparent black;
}
-tabBarPlus::close-button {
+TabBarPlus::close-button {
+ background-image: url(:/closeIcon);
padding: 0px;
margin: 0px;
border-radius: 2px;
- background-image: url("src/main/resources/base/ui/close.png");
background-position: center center;
background-repeat: none;
}
-tabBarPlus::close-button:hover {
+TabBarPlus::close-button:hover {
background-color: #006666;
}
-tabBarPlus::close-button:pressed {
+TabBarPlus::close-button:pressed {
background-color: #adc5ed;
}
-tabBarPlus::scroller { /* the width of the scroll buttons */
+TabBarPlus::scroller { /* the width of the scroll buttons */
width: 20px;
}
-tabBarPlus::tab:top,
-tabBarPlus::tab:bottom {
+TabBarPlus::tab:top,
+TabBarPlus::tab:bottom {
color: black;
border: 1px solid #b6b6b6;
border-left-color: #e6e6e6;
@@ -80,61 +80,60 @@ tabBarPlus::tab:bottom {
width: 100px;
}
-tabBarPlus::tab:top:first,
-tabBarPlus::tab:bottom:first {
+TabBarPlus::tab:top:first,
+TabBarPlus::tab:bottom:first {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
}
-tabBarPlus::tab:top:last,
-tabBarPlus::tab:bottom:last {
+TabBarPlus::tab:top:last,
+TabBarPlus::tab:bottom:last {
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
border-right-width: 1px;
}
-tabBarPlus::tab:top:selected,
-tabBarPlus::tab:bottom:selected {
+TabBarPlus::tab:top:selected,
+TabBarPlus::tab:bottom:selected {
color: white;
background-color: qlineargradient(spread:pad, x1:1, y1:0.545, x2:1, y2:0, stop:0 #00b0b0, stop:1 #00BaBa);
border-color: #006666;
}
-tabBarPlus::tab:top:!selected:hover,
-tabBarPlus::tab:bottom:!selected:hover {
+TabBarPlus::tab:top:!selected:hover,
+TabBarPlus::tab:bottom:!selected:hover {
color: black;
}
-tabBarPlus::tab:top:only-one ,
-tabBarPlus::tab:bottom:only-one {
+TabBarPlus::tab:top:only-one ,
+TabBarPlus::tab:bottom:only-one {
border: 1px solid #1b3774;
border-radius: 6px;
}
-fileWindow {
+FileWindow {
border-width: 2px;
outline: 0;
color: white;
background-color: white;
}
-fileWindow::title{
+FileWindow::title{
color: black;
border-width: 0px;
text-align: center;
background-color: white;
}
-fileWindow::close-button {
+FileWindow::close-button {
padding: 0px;
margin: 0px;
border-radius: 2px;
- background-image: url("src/main/resources/base/ui/close.png");
background-position: center center;
background-repeat: none;
}
-fileWindow::close-button:hover {
+FileWindow::close-button:hover {
background-color: #004646;
}
-fileWindow::close-button:pressed {
+FileWindow::close-button:pressed {
background-color: #adc5ed;
}
@@ -156,8 +155,6 @@ QDockWidget::float-button {
background: transparent;
subcontrol-origin: padding;
subcontrol-position: right center;
-}
-QDockWidget::float-button {
right: 4px;
}
@@ -166,7 +163,6 @@ QDockWidget::float-button:hover {
}
QDockWidget::float-button:pressed {
- /*padding: 1px -1px -1px 1px;*/
background-color: #e0e0e0;
}
@@ -210,7 +206,7 @@ QToolButton:checked {
border-color: #004646;
}
-sectionLabel{
+SectionLabel{
border-color: #00B8B8;
border-style: solid;
border-width: 0px 0px 2px 0px;
@@ -222,11 +218,11 @@ sectionLabel{
color: gray;
}
-sectionLabel:first{
+SectionLabel:first{
margin-top: 0px;
}
-customView QPushButton{
+CustomView QPushButton{
background-color: rgba(214, 54, 40, 50%);
border: 1px dashed white;
border-radius: 2px;
@@ -235,21 +231,21 @@ customView QPushButton{
padding: 1px 2px 3px 3px;
color: rgba(255, 255, 255, 50%);
}
-customView QPushButton:Hover{
+CustomView QPushButton:Hover{
background-color: rgba(214, 54, 40, 90%);
}
-customTabWidget QPushButton{
+CustomTabWidget QPushButton{
background: rgba(230, 230, 227, 0%);
padding: 1px;
border: 0px solid #E6E6E3;
border-radius: 10px;
}
-customTabWidget QPushButton:hover{
+CustomTabWidget QPushButton:hover{
background: rgba(230, 230, 227, 60%);
}
-customTabWidget::pane {
+CustomTabWidget::pane {
margin: 0px,1px,1px,1px;
border: 2px solid #E6E6E3;
border-radius: 7px;
@@ -260,11 +256,12 @@ canvas{
bottom: -20px;
}
-customTabWidget customView {
+CustomTabWidget CustomView {
border-width: 0px;
padding: 5px;
border-radius: 2px;
}
+
QHeaderView {
qproperty-defaultAlignment: AlignHCenter;
}
@@ -275,10 +272,6 @@ QHeaderView::section {
background-color: white;
}
-QHeaderView::section:first{
- border-bottom: 1px solid black;
-}
-
QTableView {
border: none;
}
diff --git a/src/main/resources/base/ui/close.png b/src/main/ui/close.png
index 8771a0b..8771a0b 100644
--- a/src/main/resources/base/ui/close.png
+++ b/src/main/ui/close.png
Binary files differ
diff --git a/src/main/ui/resources.rcc b/src/main/ui/resources.rcc
new file mode 100644
index 0000000..b7dcdfc
--- /dev/null
+++ b/src/main/ui/resources.rcc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource>
+ <file alias="closeIcon">close.png</file>
+ </qresource>
+</RCC>