Started adding support for Banners.

This commit is contained in:
AccentuSoft
2022-12-20 01:56:53 +02:00
parent 8e0e7115c5
commit efd7bf4f1f
7 changed files with 126 additions and 15 deletions

View File

@@ -772,6 +772,8 @@ class CanvasView(QtWidgets.QGraphicsView):
viewMenu.setStyleSheet(Stylesheets.MENUS_STYLESHEET_2)
groupingMenu = self.menu.addMenu("Grouping...")
groupingMenu.setStyleSheet(Stylesheets.MENUS_STYLESHEET_2)
bannersMenu = self.menu.addMenu("Banners...")
bannersMenu.setStyleSheet(Stylesheets.MENUS_STYLESHEET_2)
actionSelectChildren = QtGui.QAction('Select Child Nodes',
selectMenu,
@@ -836,6 +838,18 @@ class CanvasView(QtWidgets.QGraphicsView):
triggered=self.importConnectedEntities)
self.menu.addAction(importConnectedEntitiesAction)
clearBannerMenu = QtGui.QAction('Clear Banners',
bannersMenu,
statusTip="Remove banners from the selected entities.",
triggered=self.clearBanners)
bannersMenu.addAction(clearBannerMenu)
setBannerIconMenu = QtGui.QAction('Set Banner Icon',
bannersMenu,
statusTip="Set a banner icon for the selected entities.",
triggered=self.setBanners)
bannersMenu.addAction(setBannerIconMenu)
def deleteItemsFromDatabase(self) -> None:
items = self.scene().selectedItems()
for item in items:
@@ -916,8 +930,7 @@ class CanvasView(QtWidgets.QGraphicsView):
if entityUID in self.scene().sceneGraph.nodes():
wasGrouped = False
for groupNode in [node for node in self.items() if isinstance(node, Entity.GroupNode)]:
wasGrouped = \
groupNode.removeSpecificItemFromGroupIfExists(entityUID)
wasGrouped = groupNode.removeSpecificItemFromGroupIfExists(entityUID)
if wasGrouped:
self.removeGroupNodeLinksForUID(groupNode.uid, entityUID)
groupNodeJson = self.tabbedPane.entityDB.getEntity(groupNode.uid)
@@ -1136,6 +1149,34 @@ class CanvasView(QtWidgets.QGraphicsView):
newNode.setSelected(True)
self.scene().rearrangeGraph()
def clearBanners(self) -> None:
selectedEntities = [item for item in self.scene().selectedItems() if isinstance(item, Entity.BaseNode)]
for entity in selectedEntities:
entity.updateBanner(True, None)
def setBanners(self) -> None:
selectedEntities = [item for item in self.scene().selectedItems() if isinstance(item, Entity.BaseNode)]
if len(selectedEntities) < 1:
self.tabbedPane.mainWindow.MESSAGEHANDLER.warning('Need to select at least one Entity to set its banner.')
return
allBanners = {bannerID: bannerPath
for bannerID, bannerPath in self.tabbedPane.mainWindow.RESOURCEHANDLER.banners.items()}
bannerDialog = BannerSelector(allBanners)
if bannerDialog.exec():
try:
selectedBannerItem = bannerDialog.bannerIconContainer.selectedItems()[0]
bannerPathStr = allBanners[selectedBannerItem.text()]
with open(bannerPathStr, 'rb') as bannerFile:
bannerByteArray = QtCore.QByteArray(bannerFile.read())
for entity in selectedEntities:
entity.updateBanner(False, bannerByteArray)
except IndexError:
self.tabbedPane.mainWindow.MESSAGEHANDLER.warning('No Banner selected.', popUp=True)
except FileNotFoundError:
self.tabbedPane.mainWindow.MESSAGEHANDLER.error('Banner Icon not found in filesystem.',
popUp=True,
exc_info=False)
def takePictureOfView(self, justViewport: bool = True, transparentBackground: bool = False) -> QtGui.QImage:
# Need to set size and format of pic before using it.
# Ref: https://qtcentre.org/threads/10975-Help-Export-QGraphicsView-to-Image-File
@@ -1796,13 +1837,13 @@ class CanvasScene(QtWidgets.QGraphicsScene):
self.removeItem(item.iconItem)
pictureByteArray = pEditor.objectJson['Icon']
item.pixmapItem = QtGui.QPixmap()
item.pixmapItem.loadFromData(pictureByteArray)
if pictureByteArray.data().startswith(b'<svg '):
item.iconItem = QGraphicsSvgItem()
item.iconItem.renderer().load(pictureByteArray)
item.iconItem.setElementId("") # Force recalculation of geometry, else this looks like 1 pixel.
else:
item.pixmapItem = QtGui.QPixmap()
item.pixmapItem.loadFromData(pictureByteArray)
item.iconItem = QGraphicsPixmapItem(item.pixmapItem)
item.iconItem.setPos(item.pos())
@@ -2146,3 +2187,48 @@ class SendToOtherTabCanvasSelector(QtWidgets.QDialog):
sendToOtherCanvasLayout.addWidget(self.canvasNameSelector, 1, 1, 1, 2)
sendToOtherCanvasLayout.addWidget(cancelButton, 2, 0, 1, 1)
sendToOtherCanvasLayout.addWidget(confirmButton, 2, 1, 1, 2)
class BannerSelector(QtWidgets.QDialog):
def __init__(self, bannerDict: dict):
super(BannerSelector, self).__init__()
self.setStyleSheet(Stylesheets.MAIN_WINDOW_STYLESHEET)
self.setModal(True)
self.setWindowTitle('Select Banner')
bannerLayout = QtWidgets.QVBoxLayout()
self.setLayout(bannerLayout)
descriptionLabel = QtWidgets.QLabel("Select the Banner that you want to apply to the selected Entities:")
descriptionLabel.setWordWrap(True)
bannerLayout.addWidget(descriptionLabel)
self.bannerIconContainer = QtWidgets.QListWidget()
self.bannerIconContainer.setFlow(self.bannerIconContainer.Flow.LeftToRight)
self.bannerIconContainer.setMovement(self.bannerIconContainer.Movement.Static)
self.bannerIconContainer.setViewMode(self.bannerIconContainer.ViewMode.IconMode)
self.bannerIconContainer.setLayoutMode(self.bannerIconContainer.LayoutMode.SinglePass)
self.bannerIconContainer.setSelectionMode(self.bannerIconContainer.SelectionMode.SingleSelection)
self.bannerIconContainer.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.bannerIconContainer.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.bannerIconContainer.setSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
for bannerID, bannerPathStr in bannerDict.items():
bannerPixmap = QtGui.QIcon(bannerPathStr)
QtWidgets.QListWidgetItem(bannerPixmap, bannerID, self.bannerIconContainer)
bannerLayout.addWidget(self.bannerIconContainer)
buttonsWidget = QtWidgets.QWidget()
buttonsWidgetLayout = QtWidgets.QHBoxLayout()
buttonsWidget.setLayout(buttonsWidgetLayout)
cancelButton = QtWidgets.QPushButton('Cancel')
cancelButton.clicked.connect(self.reject)
acceptButton = QtWidgets.QPushButton('Confirm')
acceptButton.clicked.connect(self.accept)
acceptButton.setAutoDefault(True)
acceptButton.setDefault(True)
buttonsWidgetLayout.addWidget(cancelButton)
buttonsWidgetLayout.addWidget(acceptButton)
bannerLayout.addWidget(buttonsWidget)

View File

@@ -22,9 +22,6 @@ class BaseNode(QGraphicsItemGroup):
self.setCacheMode(QGraphicsItemGroup.CacheMode.DeviceCoordinateCache)
self.pixmapItem = QtGui.QPixmap()
self.pixmapItem.loadFromData(pictureByteArray)
if pictureByteArray.data().startswith(b'<svg '):
self.iconItem = QGraphicsSvgItem()
self.iconItem.renderer().load(pictureByteArray)
@@ -32,7 +29,9 @@ class BaseNode(QGraphicsItemGroup):
# https://stackoverflow.com/a/68182093
self.iconItem.setElementId("")
else:
self.iconItem = QGraphicsPixmapItem(self.pixmapItem)
pixmapItem = QtGui.QPixmap()
pixmapItem.loadFromData(pictureByteArray)
self.iconItem = QGraphicsPixmapItem(pixmapItem)
self.labelItem = QGraphicsTextItem('')
# Have to do it this way; directly assigning stuff does not work due to how PySide6 works.
@@ -44,8 +43,13 @@ class BaseNode(QGraphicsItemGroup):
labelDocument.setDefaultTextOption(textOption)
self.labelItem.setDocument(labelDocument)
self.bannerIconItem = QGraphicsSvgItem()
self.bannerIconItem.setElementId("")
self.addToGroup(self.iconItem)
self.addToGroup(self.labelItem)
self.addToGroup(self.bannerIconItem)
if font is not None:
self.labelItem.setFont(font)
else:
@@ -56,6 +60,8 @@ class BaseNode(QGraphicsItemGroup):
self.labelItem.setPos(self.iconItem.x() - 120, self.iconItem.y() + 45)
self.updateLabel(primaryAttribute)
self.bannerIconItem.setPos(self.iconItem.x() + 15, self.iconItem.y() - 9)
self.uid = uid
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
@@ -64,9 +70,6 @@ class BaseNode(QGraphicsItemGroup):
self.setAcceptHoverEvents(True)
self.connectors = []
self.bookmarked = False
self.isBeingResolved = False
self.parentGroup = None
def updateLabel(self, newText: str = '') -> None:
if not isinstance(newText, str):
@@ -76,6 +79,16 @@ class BaseNode(QGraphicsItemGroup):
newText = f"{newText[:47]}..."
self.labelItem.setPlainText(newText)
def updateBanner(self, bannerHidden: bool = True, bannerGraphic: QtCore.QByteArray = None) -> None:
if bannerHidden: # No icon visible
self.bannerIconItem.hide()
self.bannerIconItem.setVisible(False)
return
self.bannerIconItem.renderer().load(bannerGraphic)
self.bannerIconItem.show()
self.bannerIconItem.setVisible(True)
self.bannerIconItem.setElementId("")
def removeConnector(self, connector) -> None:
# Exception could be thrown if the connector is already deleted.
with contextlib.suppress(ValueError):

View File

@@ -130,6 +130,18 @@ MAIN_WINDOW_STYLESHEET = """
QTextBrowser {
background-color:rgb(60, 60, 80);
}
QListWidget::item{
height: 70px;
width: 70px;
}
QListWidget::item:selected {
border-left: 2px solid rgb(0, 173, 238);
border-right: 2px solid rgb(0, 173, 238);
border-top: 2px solid rgb(0, 173, 238);
border-bottom: 2px solid rgb(0, 173, 238);
}
"""
DOCK_BAR_TWO_LINK = """

View File

@@ -54,7 +54,7 @@ class ResourceHandler:
"colorPicker": str(self.programBaseDirPath / "Resources" / "Icons" / "ColorPicker.png"),
}
self.banners = {f"{bannerPath.split('_')[1].split('.')[0]}": str(bannerPath)
self.banners = {f"{bannerPath.split('Banner_')[-1].split('.')[0]}": str(bannerPath)
for bannerPath in glob(str(self.programBaseDirPath / "Resources" / "Icons" / "Banner_*.svg"))}
# These are not meant to be strict - just restrictive enough such that users don't put in utter nonsense.
# Note that regex isn't always the best way of validating fields, but it should be good enough for our

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10" height="10" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
<svg width="37" height="37" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
<path d="m797.97 905.21c4.3125-1.7852 8-4.8125 10.594-8.6953 2.5977-3.8867 3.9805-8.4492 3.9805-13.121v-566.79c0-6.2656-2.4883-12.27-6.918-16.699-4.4297-4.4297-10.434-6.918-16.699-6.918h-377.86c-6.2656 0-12.27 2.4883-16.699 6.918-4.4297 4.4297-6.918 10.434-6.918 16.699v566.79c0 6.2617 2.4883 12.27 6.918 16.699 4.4258 4.4297 10.434 6.918 16.699 6.918 6.2617-0.003907 12.27-2.4922 16.695-6.918l172.23-172.23 172.23 172.23c3.3008 3.3008 7.5078 5.5508 12.09 6.4648 4.582 0.91016 9.332 0.44141 13.648-1.3477z" fill="#12b0fb"/>
</svg>

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10" height="10" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
<svg width="37" height="37" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
<path d="m797.97 905.21c4.3125-1.7852 8-4.8125 10.594-8.6953 2.5977-3.8867 3.9805-8.4492 3.9805-13.121v-566.79c0-6.2656-2.4883-12.27-6.918-16.699-4.4297-4.4297-10.434-6.918-16.699-6.918h-377.86c-6.2656 0-12.27 2.4883-16.699 6.918-4.4297 4.4297-6.918 10.434-6.918 16.699v566.79c0 6.2617 2.4883 12.27 6.918 16.699 4.4258 4.4297 10.434 6.918 16.699 6.918 6.2617-0.003907 12.27-2.4922 16.695-6.918l172.23-172.23 172.23 172.23c3.3008 3.3008 7.5078 5.5508 12.09 6.4648 4.582 0.91016 9.332 0.44141 13.648-1.3477z" fill="#ff814a"/>
</svg>

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10" height="10" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
<svg width="37" height="37" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
<path d="m797.97 905.21c4.3125-1.7852 8-4.8125 10.594-8.6953 2.5977-3.8867 3.9805-8.4492 3.9805-13.121v-566.79c0-6.2656-2.4883-12.27-6.918-16.699-4.4297-4.4297-10.434-6.918-16.699-6.918h-377.86c-6.2656 0-12.27 2.4883-16.699 6.918-4.4297 4.4297-6.918 10.434-6.918 16.699v566.79c0 6.2617 2.4883 12.27 6.918 16.699 4.4258 4.4297 10.434 6.918 16.699 6.918 6.2617-0.003907 12.27-2.4922 16.695-6.918l172.23-172.23 172.23 172.23c3.3008 3.3008 7.5078 5.5508 12.09 6.4648 4.582 0.91016 9.332 0.44141 13.648-1.3477z" fill="#ff001b"/>
</svg>

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B