Started adding support for Banners.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 = """
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
Reference in New Issue
Block a user