예제 #1
0
    def createImage(self, transform):
        if self.type == DemoTextItem.DYNAMIC_TEXT:
            return None

        sx = min(transform.m11(), transform.m22())
        sy = max(transform.m22(), sx)

        textItem = QGraphicsTextItem()
        textItem.setHtml(self.text)
        textItem.setTextWidth(self.textWidth)
        textItem.setFont(self.font)
        textItem.setDefaultTextColor(self.textColor)
        textItem.document().setDocumentMargin(2)

        w = textItem.boundingRect().width()
        h = textItem.boundingRect().height()
        image = QImage(int(w * sx), int(h * sy),
                QImage.Format_ARGB32_Premultiplied)
        image.fill(QColor(0, 0, 0, 0).rgba())
        painter = QPainter(image)
        painter.scale(sx, sy)
        style = QStyleOptionGraphicsItem()
        textItem.paint(painter, style, None)

        return image
예제 #2
0
    def render_items(self, event=None):
        self.links = self.get_links_dict()
        self.render_preview()
        for item in self.settings['items']:
            text = QGraphicsTextItem()
            font_color = QColor('black')

            text.setPos(QPointF(item['x'], item['y']))

            font = QFont()
            font_name = item['params'].get(const.ITEM_DATA_KEY_FONT)
            if font_name:
                font.fromString(font_name)
                font_size = item['params'].get(const.ITEM_DATA_KEY_FONT_SIZE)
                if font_size:
                    font.setPointSize(font_size)
                else:
                    font.setPointSize(12)
                font_color_list = item['params'].get(
                    const.ITEM_DATA_KEY_FONT_COLOR)
                if font_color_list:
                    font_color.setRgb(*font_color_list)
                text.setFont(font)
            text_align = item['params'].get(const.ITEM_DATA_KEY_FONT_ALIGN)

            text.setFont(font)
            text.setTextWidth(item['w'])
            text.setDefaultTextColor(font_color)

            text.setHtml(
                f"<div align='{text_align}'>{self.test_row[self.links[item['name']]]}</div>"
            )

            self.scene.addItem(text)
예제 #3
0
    def createImage(self, transform):
        if self.type == DemoTextItem.DYNAMIC_TEXT:
            return None

        sx = min(transform.m11(), transform.m22())
        sy = max(transform.m22(), sx)

        textItem = QGraphicsTextItem()
        textItem.setHtml(self.text)
        textItem.setTextWidth(self.textWidth)
        textItem.setFont(self.font)
        textItem.setDefaultTextColor(self.textColor)
        textItem.document().setDocumentMargin(2)

        w = textItem.boundingRect().width()
        h = textItem.boundingRect().height()
        image = QImage(int(w * sx), int(h * sy),
                QImage.Format_ARGB32_Premultiplied)
        image.fill(QColor(0, 0, 0, 0).rgba())
        painter = QPainter(image)
        painter.scale(sx, sy)
        style = QStyleOptionGraphicsItem()
        textItem.paint(painter, style, None)

        return image
예제 #4
0
def display_text(message, view: QGraphicsView):
    text = QGraphicsTextItem()
    text.setPos(0, 0)
    text.setPlainText(message.text)
    text.setTextWidth(view.width())

    scene = QGraphicsScene()
    scene.addItem(text)
    view.setAlignment(Qt.AlignTop | Qt.AlignLeft)
    view.setScene(scene)
예제 #5
0
    def printAttributes(self, background, border, text):
        """
        Prints the attributes of the node
        The attributes are a key, value pair

        :param background: background color of the node
        :param border: border color for the node
        :param text: text color for the node
        """
        y = self.y() + self.headerHeight
        x = self.x()

        self.attributesHeight = 0

        for k, v in self.node.attributes.items():
            key = QGraphicsTextItem()
            key.setFont(Configuration.font)
            key.setDefaultTextColor(QColor(text))
            key.setTextWidth(100)
            key.setPlainText(k)
            keyHeight = int(key.boundingRect().height() / 20 + 0.5) * 20

            value = QGraphicsTextItem()
            value.setFont(Configuration.font)
            value.setDefaultTextColor(QColor(text))
            value.setTextWidth(100)
            value.setPlainText(v)
            valueHeight = int(value.boundingRect().height() / 20 + 0.5) * 20

            height = valueHeight if valueHeight > keyHeight else keyHeight

            keyRect = QGraphicsRectItem()
            keyRect.setRect(x, y, 100, height)
            valueRect = QGraphicsRectItem()
            valueRect.setRect(x + 100, y, 100, height)

            keyRect.setBrush(QBrush(QColor(background)))
            valueRect.setBrush(QBrush(QColor(background)))

            keyRect.setPen(QPen(QColor(border), 2))
            valueRect.setPen(QPen(QColor(border), 2))

            key.setPos(x, y - 2)
            value.setPos(x + 100, y - 2)

            self.attributes.addToGroup(keyRect)
            self.attributes.addToGroup(valueRect)
            self.attributes.addToGroup(key)
            self.attributes.addToGroup(value)

            y = y + height
            self.attributesHeight += height

        self.addToGroup(self.attributes)
    def showImage(self):
        (newImg, newImgInfo) = self.loadImage()
        # return PicItem(Pixmap(QPixmap(newImg)), -1, -1, xFactor, yFactor, newImgInfo)
        self.scene.clear()
        imgSz = newImgInfo.imgSize
        self.setSceneRect(QRectF(0,0,imgSz.width(), imgSz.height()))
        pixMap = QPixmap.fromImage(newImg)
        # # pixMap.setWidth(self.width())
        pixMapItem = self.scene.addPixmap(pixMap)
        # pixMapItem.setPos(50,50)
        # self.fitInView(QRectF(0, 0, self.width(), self.height()), Qt.KeepAspectRatio)
        # Add caption
        caption = QGraphicsTextItem()
        caption.setDefaultTextColor(QColor(255,255,255))
        caption.setPos(0, self.height()*0.94)
        caption.setFont(QFont("Segoe UI", 30))
        caption.setTextWidth(self.width())
        # caption.setPos(100, 100)
        # caption.setTextWidth(1500)
        # if newImgInfo.createDate is not None:
        #     caption.setPlainText(newImgInfo.createDate.format());
        # else:
        #     caption.setPlainText("Image is called bananas");
        # print("Tags", newImgInfo.tags)
        # tagStr = ""
        # for tag in newImgInfo.tags:
        #     if tag != "Duplicate":
        #         tagStr += (", " if len(tagStr) != 0 else "") + tag
        # if tagStr == "":
        #     tagStr = "NO TAGS"
        # captionStr = '<h1 style="text-align:center;width:100%">' + tagStr + '</h1>'
        # if newImgInfo.createDate is not None:
        #     print(newImgInfo.createDate.format())
        #     captionStr += '<BR><h2>' + newImgInfo.createDate.format() + '</h2>'

        captionStr = ""
        try:
            if newImgInfo.rating is not None:
                for i in range(newImgInfo.rating):
                    captionStr += "&#x2605;"
                for i in range(5-newImgInfo.rating):
                    captionStr += "&#x2606;"
            if newImgInfo.mainDate is not None:
                if len(captionStr) != 0:
                    captionStr += "  "
                captionStr += newImgInfo.mainDate.strftime("%d %b %Y")
        except Exception as excp:
            print("StaticPhotos: Cannot set caption")
        captionStr = '<div style="background-color:#000000;text-align: right;padding-right:10dp;">' + captionStr + "</div>"
        print(captionStr)
        caption.setHtml(captionStr)
        self.scene.addItem(caption)
        self.scene.update()
예제 #7
0
class AnimatedClock():
    updateTimer = None
    calDataLock = threading.Lock()
    calDataUpdated = False
    curCalendars = None
    
    def __init__(self, scene, widthClkTextArea, heightClkTextArea, borders, updateSecs):
        self.masterScene = scene
        self.widthClkTextArea = widthClkTextArea
        self.heightClkTextArea = heightClkTextArea
        self.borders = borders
        self.updateSecs = updateSecs
        # Background
        self.textBkgd = QGraphicsRectItem(0, 0, self.widthClkTextArea, self.heightClkTextArea)
        self.textBkgd.setPos(self.borders[3], self.borders[0])
        self.textBkgd.setBrush(QColor("light green"))
        self.textBkgd.setZValue(10)
        scene.addItem(self.textBkgd)
        # Text Item
        self.textItem = QGraphicsTextItem()
        self.textItem.setFont(QFont("Segoe UI", 80))
        self.textItem.setDefaultTextColor(QColor("black"))
        self.textItem.setPos(QPointF(self.borders[3]+10,self.borders[0]+5))
        self.textItem.setHtml("<B>Clock</B>")
        self.textItem.setZValue(20)
        self.textItem.setTextWidth(self.widthClkTextArea-20)
        scene.addItem(self.textItem)
        
    def start(self):
        self.updateTimer = QTimer()
        self.updateTimer.setInterval(self.updateSecs * 1000)
        self.updateTimer.timeout.connect(self.updateClock)
        self.updateTimer.start()

    def stop (self):
        if self.updateTimer != None:
            self.updateTimer.stop()
    
    def updateClock(self):
        localtime = time.localtime()
#        dateString  = time.strftime("%a %d %b %Y", localtime)
        timeString  = time.strftime("%H:%M:%S", localtime)
        self.textItem.setHtml(timeString)
#        self.textItem.setTextWidth(self.widthCalTextArea-20)
        self.textItem.update()
예제 #8
0
	def actually_show_text(self, data):
		text_item = QGraphicsTextItem()
		text_item.setZValue(5)
		if len(data) >= 8 and data[7] != None:
			text_item.setDefaultTextColor(QColor(data[7]))
		else:
			text_item.setDefaultTextColor(QColor("#FFFFFF"))

		text_item.setX(data[2])
		text_item.setY(data[3])
		text_item.setTextWidth(data[4])
		text_item.setPlainText(data[5])

		temp_font = text_item.font()
		temp_font.setPointSize(data[6])
		text_item.setFont(temp_font)

		self.addItem(text_item)
		self.texts[data[1]] = text_item
		self.call_next_action()
예제 #9
0
class NodeTemplateItem():
    ''' 
    This represents one node template on the diagram.  A node template can be on many diagrams
    This class creates the rectangle graphics item and the text graphics item and adds them to the scene.
    '''
    def __init__(self, scene, x, y, nodeTemplateDict=None, NZID=None):
        self.scene = scene
        self.logMsg = None
        self.x = x
        self.y = y
        self.nodeTemplateDict = nodeTemplateDict
        #        self.name = self.nodeTemplateDict.get("name", "")   THIS HAS BEEN REPLACED BY THE name FUNCTION - SEE BELOW
        self.diagramType = "Node Template"
        self.displayText = None
        self.model = self.scene.parent.model
        self.gap = 100
        self.relList = []
        # assign a unique key if it doesn't already have one
        if NZID == None:
            self.NZID = str(uuid.uuid4())
        else:
            self.NZID = NZID

        # init graphics objects to none
        self.TNode = None
        self.TNtext = None

        # draw the node template on the diagram
        self.drawIt()

    def name(self, ):
        return self.nodeTemplateDict.get("name", "")

    def getX(self, ):
        return self.TNode.boundingRect().x()

    def getY(self, ):
        return self.TNode.boundingRect().y()

    def getHeight(self, ):
        return self.TNode.boundingRect().height()

    def getWidth(self, ):
        return self.TNode.boundingRect().width()

    def getRelList(self, ):
        '''return a list of all relationitems that are inbound or outbound from this node template.
          do not include self referencing relationships
        '''
        return [
            diagramItem
            for key, diagramItem in self.scene.parent.itemDict.items()
            if diagramItem.diagramType == "Relationship Template" and (
                diagramItem.startNZID == self.NZID
                or diagramItem.endNZID == self.NZID)
        ]

    def getPoint(self, offset=None):
        '''
        This function is used by the template diagram to calculate the location to drop a node template on the diagram
        '''
        if offset is None:
            return QPointF(self.x, self.y)
        else:
            return QPointF(self.x + offset, self.y + offset)

    def getFormat(self, ):
        '''
        determine if the Node Template  has a template format or should use the project default format
        '''
        # get the node Template custom format
        customFormat = self.nodeTemplateDict.get("TNformat", None)

        if not customFormat is None:
            # get the template custom format
            self.nodeFormat = TNodeFormat(formatDict=customFormat)
        else:
            # get the project default format
            self.nodeFormat = TNodeFormat(
                formatDict=self.model.modelData["TNformat"])

    def clearItem(self, ):

        if (not self.TNode is None and not self.TNode.scene() is None):
            self.TNode.scene().removeItem(self.TNode)
        if (not self.TNtext is None and not self.TNtext.scene() is None):
            self.TNtext.scene().removeItem(self.TNtext)

    def drawIt(self, ):

        # get current format as it may have changed
        self.getFormat()

        # create the qgraphicsItems if they don't exist
        if self.TNode is None:
            # create the rectangle
            self.TNode = QGraphicsRectItem(QRectF(
                self.x, self.y, self.nodeFormat.formatDict["nodeWidth"],
                self.nodeFormat.formatDict["nodeHeight"]),
                                           parent=None)
            self.TNode.setZValue(NODELAYER)
            self.TNode.setFlag(QGraphicsItem.ItemIsMovable, True)
            self.TNode.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
            self.TNode.setFlag(QGraphicsItem.ItemIsSelectable, True)
            self.TNode.setSelected(True)
            self.TNode.setData(1, self.NZID)  # get with self.INode.data(1)
            self.TNode.setData(ITEMTYPE, NODETEMPLATE)
            # create the text box
            self.TNtext = QGraphicsTextItem("", parent=None)
            self.TNtext.setPos(self.x, self.y)
            self.TNtext.setFlag(QGraphicsItem.ItemIsMovable, True)
            self.TNtext.setFlag(QGraphicsItem.ItemIsSelectable, False)
            self.TNtext.setData(NODEID, self.NZID)
            self.TNtext.setData(ITEMTYPE, NODETEMPLATETEXT)
            self.TNtext.setZValue(NODELAYER)
            # save the location
            self.x = self.TNode.sceneBoundingRect().x()
            self.y = self.TNode.sceneBoundingRect().y()
            # generate the html and resize the rectangle
            self.formatItem()
            # add the graphics items to the scene
            self.scene.addItem(self.TNode)
            self.scene.addItem(self.TNtext)
        else:
            # generate the html and resize the rectangle
            self.formatItem()

    def formatItem(self, ):

        # configure the formatting aspects of the qgraphics item
        pen = self.nodeFormat.pen()
        brush = self.nodeFormat.brush()
        self.TNode.setBrush(brush)
        self.TNode.setPen(pen)

        # generate the HTML
        genHTML = self.generateHTML()
        self.TNtext.prepareGeometryChange()
        #        print("before html bounding rectangle width:{}".format(self.TNtext.boundingRect().width()))
        #        print("before html text width:{}".format(self.TNtext.textWidth()))
        self.TNtext.setTextWidth(
            -1
        )  # reset the width to unkonwn so it will calculate a new width based on the new html
        self.TNtext.setHtml(genHTML)
        #        print("after html bounding rectangle width:{}".format(self.TNtext.boundingRect().width()))
        #        print("after html text width:{}".format(self.TNtext.textWidth()))

        # make sure minimum width of 120
        if self.TNtext.boundingRect().width() < 120:
            self.TNtext.setTextWidth(120)
        else:
            self.TNtext.setTextWidth(
                self.TNtext.boundingRect().width()
            )  # you have to do a setTextWidth to get the html to render correctly.

        # set the rectangle item to the same size as the formatted html
        self.TNode.prepareGeometryChange()
        currentRect = self.TNode.rect()
        # insure minimum height of 120
        if self.TNtext.boundingRect().height() < 120:
            currentRect.setHeight(120)
        else:
            currentRect.setHeight(self.TNtext.boundingRect().height())
        currentRect.setWidth(self.TNtext.boundingRect().width())
        self.TNode.setRect(currentRect)

    def generateHTML(self, ):
        '''
        Generate the HTML that formats the node template data inside the rectangle
        '''
        # generate the html
        prefix = "<!DOCTYPE html><html><body>"
        #        head = "<head><style>table, th, td {border: 1px solid black; border-collapse: collapse;}</style></head>"
        suffix = "</body></html>"
        #        blankRow = "<tr><td><left>{}</left></td><td><left>{}</left></td><td><left>{}</left></td><td><left>{}</left></td></tr>".format("", "", "", "")

        name = "<center><b>{}</b></center>".format(
            self.nodeTemplateDict.get("name", ""))
        lbls = self.genLblHTML()
        props = self.genPropHTML()
        genHTML = "{}{}<hr>{}<br><hr>{}{}".format(prefix, name, lbls, props,
                                                  suffix)
        #        print("{} html: {}".format(self.name(), genHTML))

        return genHTML

    def genLblHTML(self):
        #        html = '<table width="90%">'
        html = '<table style="width:90%;border:1px solid black;">'
        if len(self.nodeTemplateDict.get("labels", [])) > 0:
            for lbl in self.nodeTemplateDict.get("labels", []):
                if lbl[NODEKEY] == Qt.Checked:
                    nk = "NK"
                else:
                    nk = "&nbsp;&nbsp;"
                if lbl[REQUIRED] == Qt.Checked:
                    rq = "R"
                else:
                    rq = ""

                html = html + '<tr align="left"><td width="15%"><left>{}</left></td><td width="65%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td></tr>'.format(
                    nk, lbl[LABEL], "", rq)
            html = html + "</table>"
        else:
            html = '<tr align="left"><td width="15%"><left>{}</left></td><td  width="65%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td></tr>'.format(
                "  ", "NO{}LABELS".format("&nbsp;"), "", "")
            html = html + "</table>"

        return html

    def genPropHTML(self):
        #        PROPERTY, DATATYPE, PROPREQ, DEFAULT, EXISTS, UNIQUE, PROPNODEKEY
        html = '<table style="width:90%;border:1px solid black;">'
        if len(self.nodeTemplateDict.get("properties", [])) > 0:
            for prop in self.nodeTemplateDict.get("properties", []):
                if prop[PROPNODEKEY] == Qt.Checked:
                    nk = "NK"
                else:
                    nk = "&nbsp;&nbsp;"
                if prop[PROPREQ] == Qt.Checked:
                    rq = "R"
                else:
                    rq = ""
                if prop[EXISTS] == Qt.Checked:
                    ex = "E"
                else:
                    ex = ""
                if prop[UNIQUE] == Qt.Checked:
                    uq = "U"
                else:
                    uq = ""
                html = html + '<tr align="left"><td width="15%"><left>{}</left></td><td width="65%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td></tr>'.format(
                    nk, prop[PROPERTY], rq, ex, uq)
            html = html + "</table>"
        else:
            html = html + '<tr align="left"><td width="15%"><left>{}</left></td><td width="65%"><left>{}</left></td><td width="10%"><left>{}</left></td><td width="10%"><left>{}</left></td></tr>'.format(
                "&nbsp;&nbsp;", "NO{}PROPERTIES".format("&nbsp;"), "", "", "")
            html = html + "</table>"
        return html

    def moveIt(self, dx, dy):
        '''
        Move the node rectangle and the node textbox to the delta x,y coordinate.
        '''
        #        print("before moveIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect()))

        self.TNode.moveBy(dx, dy)
        self.x = self.TNode.sceneBoundingRect().x()
        self.y = self.TNode.sceneBoundingRect().y()
        self.TNtext.moveBy(dx, dy)
        #        print("after moveIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect()))
        # now redraw all the relationships
        self.drawRels()

    def drawRels(self, ):
        '''Redraw all the relationship lines connected to the Node Template Rectangle'''
        # get a list of the relationship items connected to this node template
        self.relList = self.getRelList()

        # assign the correct inbound/outbound side for the rel
        for rel in self.relList:
            if rel.endNodeItem.NZID != rel.startNodeItem.NZID:  # ignore bunny ears
                rel.assignSide()
        # get a set of all the nodes and sides involved
        nodeSet = set()
        for rel in self.relList:
            if rel.endNodeItem.NZID != rel.startNodeItem.NZID:  # ignore bunny ears
                nodeSet.add((rel.endNodeItem, rel.inboundSide))
                nodeSet.add((rel.startNodeItem, rel.outboundSide))

        # tell each node side to assign rel locations
        for nodeSide in nodeSet:
            nodeSide[0].assignPoint(nodeSide[1])

        ############################################

        # now tell them all to redraw
        for rel in self.relList:
            rel.drawIt2()

    def calcOffset(self, index, totRels):
        offset = [-60, -40, -20, 0, 20, 40, 60]
        offsetStart = [3, 2, 2, 1, 1, 0, 0]
        if totRels > 7:
            totRels = 7
        return offset[offsetStart[totRels - 1] + index]

    def assignPoint(self, side):
        # go through all the rels on a side and assign their x,y coord for that side
        self.relList = self.getRelList()
        sideList = [
            rel for rel in self.relList
            if ((rel.startNZID == self.NZID and rel.outboundSide == side) or (
                rel.endNZID == self.NZID and rel.inboundSide == side))
        ]
        totRels = len(sideList)
        if totRels > 0:
            if side == R:
                # calc center of the side
                x = self.x + self.getWidth()
                y = self.y + self.getHeight() / 2
                # sort the rels connected to this side by the y value
                sideList.sort(key=self.getSortY)
                # assign each of them a position on the side starting in the center and working out in both directions
                for index, rel in enumerate(sideList):
                    if rel.startNZID == self.NZID:
                        rel.outboundPoint = QPointF(
                            x, y + (self.calcOffset(index, totRels)))
                    if rel.endNZID == self.NZID:
                        rel.inboundPoint = QPointF(
                            x, y + (self.calcOffset(index, totRels)))
            elif side == L:
                x = self.x
                y = self.y + self.getHeight() / 2
                sideList.sort(key=self.getSortY)
                for index, rel in enumerate(sideList):
                    if rel.startNZID == self.NZID:
                        rel.outboundPoint = QPointF(
                            x, y + (self.calcOffset(index, totRels)))
                    if rel.endNZID == self.NZID:
                        rel.inboundPoint = QPointF(
                            x, y + (self.calcOffset(index, totRels)))
            elif side == TOP:
                x = self.x + self.getWidth() / 2
                y = self.y
                sideList.sort(key=self.getSortX)
                for index, rel in enumerate(sideList):
                    if rel.startNZID == self.NZID:
                        rel.outboundPoint = QPointF(
                            x + (self.calcOffset(index, totRels)), y)
                    if rel.endNZID == self.NZID:
                        rel.inboundPoint = QPointF(
                            x + (self.calcOffset(index, totRels)), y)
            elif side == BOTTOM:
                x = self.x + self.getWidth() / 2
                y = self.y + self.getHeight()
                sideList.sort(key=self.getSortX)
                for index, rel in enumerate(sideList):
                    if rel.startNZID == self.NZID:
                        rel.outboundPoint = QPointF(
                            x + (self.calcOffset(index, totRels)), y)
                    if rel.endNZID == self.NZID:
                        rel.inboundPoint = QPointF(
                            x + (self.calcOffset(index, totRels)), y)
            else:
                print("error, no side")

    def getSortY(self, rel):
        # if this node is the start node then return the end node's Y
        if rel.startNZID == self.NZID:
            return rel.endNodeItem.TNode.sceneBoundingRect().center().y()
        # if this node is the end node then return the start node's Y
        if rel.endNZID == self.NZID:
            return rel.startNodeItem.TNode.sceneBoundingRect().center().y()
        # this should never happen
        return 0

    def getSortX(self, rel):
        # if this node is the start node then return the end node's X
        if rel.startNZID == self.NZID:
            return rel.endNodeItem.TNode.sceneBoundingRect().center().x()
        # if this node is the end node then return the start node's X
        if rel.endNZID == self.NZID:
            return rel.startNodeItem.TNode.sceneBoundingRect().center().x()
        # this should never happen
        return 0

    def getObjectDict(self, ):
        '''
        This function returns a dictionary with all the data that represents this node template item.  
        The dictionary is added to the Instance Diagram dictionary.'''
        objectDict = {}
        objectDict["NZID"] = self.NZID
        objectDict["name"] = self.nodeTemplateDict.get("name", "")
        objectDict["displayText"] = self.displayText
        objectDict["x"] = self.TNode.sceneBoundingRect().x()
        objectDict["y"] = self.TNode.sceneBoundingRect().y()
        objectDict["diagramType"] = self.diagramType
        objectDict["labels"] = self.nodeTemplateDict.get("labels", [])
        objectDict["properties"] = self.nodeTemplateDict.get("properties", [])

        return objectDict

    def setLogMethod(self, logMethod=None):
        if logMethod is None:
            if self.logMsg is None:
                self.logMsg = self.noLog
        else:
            self.logMsg = logMethod

    def noLog(self, msg):
        return
예제 #10
0
class AnimatedCalendar():
    updatesRunning = False
    updateTimer = None
    listUpdateThread = None
    calDataLock = threading.Lock()
    calDataUpdated = False
    curCalendars = None
    
    def __init__(self, scene, widthCalTextArea, heightCalTextArea, borders, calFeeds, calUpdateSecs):
        self.masterScene = scene
        self.widthCalTextArea = widthCalTextArea
        self.heightCalTextArea = heightCalTextArea
        self.borders = borders
        self.calFeeds = calFeeds
        self.calendarUpdateSecs = calUpdateSecs
        # Background
        self.textBkgd = QGraphicsRectItem(0, 0, self.widthCalTextArea, self.heightCalTextArea)
        self.textBkgd.setPos(self.borders[3], self.borders[0])
        self.textBkgd.setBrush(QColor("light green"))
        self.textBkgd.setZValue(10)
        
        scene.addItem(self.textBkgd)
        # Text Item
        self.textItem = QGraphicsTextItem()
        self.textItem.setFont(QFont("Segoe UI", 24))
        self.textItem.setDefaultTextColor(QColor("black"))
        self.textItem.setPos(QPointF(self.borders[3]+10,self.borders[0]+10))
        self.textItem.setHtml("<B>Hello</B>Hello")
        self.textItem.setZValue(20)
        self.textItem.setTextWidth(self.widthCalTextArea-20)
        scene.addItem(self.textItem)
        
    def start(self):
        self.updatesRunning = True
        QTimer.singleShot(100, self.updateCalendar)
        self.listUpdateThread = CalendarUpdateThread(self, self.calFeeds, self.calendarUpdateSecs)
        self.listUpdateThread.start()
#        print("CalStarted")

    def stop (self):
        self.updatesRunning = False
        if self.updateTimer != None:
            self.updateTimer.stop()
        if self.listUpdateThread != None:
            self.listUpdateThread.stop()
    
    def setNewCalendarEntries(self, calendars):
        with self.calDataLock:
            self.curCalendars = calendars
            self.calDataUpdated = True
        
    def updateCalendar(self):
#        print("Update cal")
        
        with self.calDataLock:
            if self.calDataUpdated and self.curCalendars != None:
                for calEvents in self.curCalendars:
                    calStr = ""
                    lastDay = -1
                    for anEvent in calEvents:
                        # date, duration, summary, location, UID
                        eventDate = anEvent[0]
                        duration = anEvent[1]
                        summary = anEvent[2]
                        location = anEvent[3]
                        if lastDay != eventDate.day:
                            if lastDay != -1:
                                calStr += "<br/>"
                            calStr += "<b>" + anEvent[0].strftime("%a") + " (" + anEvent[0].strftime("%d %B)") + ")</b><br/>"
                            lastDay = eventDate.day
                        strDurTime = str(duration).rpartition(":")[0]
                        durStr = (str(duration.days) + "day" + ("s" if duration.days != 1 else "")) if duration.days > 0 else strDurTime
                        locStr = "<small>("+location+")</small>" if location != "" else ""
                        calStr += anEvent[0].strftime("%H:%M") + " <small>(" + durStr + ")</small> " + summary + " " + locStr + "<br/>"
#                        print (anEvent)
    #                    print(date)
                    self.textItem.setHtml(calStr)
                    self.textItem.setTextWidth(self.widthCalTextArea-20)
                    self.textItem.update()
            self.calDataUpdated = False
        
        if not self.updatesRunning:
            return 
        self.updateTimer = QTimer()
        self.updateTimer.setInterval(5000)
        self.updateTimer.setSingleShot(True)
        self.updateTimer.timeout.connect(self.updateCalendar)
        self.updateTimer.start()
예제 #11
0
class Scene_Base(QGraphicsScene):
	def __init__(self, window):
		super().__init__()
		self.setSceneRect(0, 0, window.game_width, window.game_height)
		self.window = window
		self.init_fields()
		self.create_fade()
		self.dialog_box = None
		self.setup()

	def init_fields(self):
		self.actions = []
		self.current_action = -1
		self.dialog_box = None
		self.target_text = None
		self.current_dialog_text = None
		self.current_dialog_text_offset = 0
		self.background = None
		self.fading_box = None
		self.fade_dir = 0
		self.next_scene = None
		self.buttons = []
		self.current_id = 1
		self.texts = {}
		self.images = {}
		self.moving_images = []
		self.updatable_images = []
		self.vaxx = None
		self.entry = None
		self.pathogen = None
		self.tempPic = None

	def create_fade(self, opc=1):
		self.fading_box = QGraphicsRectItem(0, 0, self.width(), self.height())
		self.fading_box.setBrush(QColor(0, 0, 0))
		self.fading_box.setOpacity(opc)
		self.fading_box.setZValue(100)
		self.addItem(self.fading_box)

	# ==============================================
	# * Setup
	#
	# Overwrite with child classes to add actions.
	# ==============================================

	def setup(self):
		pass

	# ==============================================
	# * Update
	#
	# Updates the scene. Handles input, waiting, etc.
	# ==============================================

	def update(self):
		self.update_fade()
		self.update_dialog()
		self.update_dialog_text()
		self.update_moving_images()
		self.update_animated_images()

	def update_fade(self):
		if self.fading_box is not None:
			if self.fade_dir == 0 and self.fading_box.opacity() > 0:
				self.fading_box.setOpacity(self.fading_box.opacity() - 0.02)
				if self.fading_box.opacity() <= 0:
					self.removeItem(self.fading_box)
					self.fading_box = None
			elif self.fade_dir == 1 and self.fading_box.opacity() < 1:
				self.fading_box.setOpacity(self.fading_box.opacity() + 0.02)
				if self.fading_box.opacity() >= 1:
					if self.next_scene is not None:
						self.window.goto_scene(self.next_scene)
					else:
						self.actually_close_game()

	def update_dialog(self):
		if self.dialog_box is not None:
			if self.dialog_box.opacity() < 0.5:
				self.dialog_box.setOpacity(self.dialog_box.opacity() + 0.04)
				if self.dialog_box.opacity() >= 0.5:
					self.dialog_box.setOpacity(0.5)
					if self.actions[0][0] == 0:
						self.actually_show_dialog(self.actions[0])

	def update_dialog_text(self):
		if self.current_dialog_text is not None:
			curr_text = self.current_dialog_text.toPlainText()
			if curr_text != self.target_text:
				if self.current_dialog_text_offset < 3:
					self.current_dialog_text_offset += 1
				else:
					self.current_dialog_text_offset = 0
					self.current_dialog_text.setPlainText(self.target_text[:len(curr_text) + 1])

	def update_moving_images(self):
		if len(self.moving_images) > 0:
			index = 0
			new_data = []
			for image in self.moving_images:
				pic_id = image[0]
				item = self.images[pic_id]
				item.setX(item.x() + image[1])
				item.setY(item.y() + image[2])
				image[3] -= 1
				if image[3] > 0:
					new_data.append(image)
				elif image[4] is not None:
					image[4]()
				index += 1

			self.moving_images = new_data

	def update_animated_images(self):
		if len(self.updatable_images) > 0:
			index = 0
			for image in self.updatable_images:
				image[2] += 1
				if image[2] > image[4]:
					image[2] = 0
					image[3] += 1
					if image[3] >= len(image[1]):
						image[3] = 0
					image[0].setPixmap(QPixmap(image[1][image[3]]))


	# ==============================================
	# * Mouse Events
	#
	# Handle mouse input.
	# ==============================================

	def mouseMoveEvent(self, mouseEvent):
		pass

	def mousePressEvent(self, mouseEvent):
		self.when_mouse_pressed(mouseEvent)

	def mouseDoubleClickEvent(self, mouseEvent):
		self.when_mouse_pressed(mouseEvent)

	def when_mouse_pressed(self, mouseEvent):
		if self.current_action == 0 and mouseEvent.button() == 1:
			if self.current_dialog_text is not None:
				if self.current_dialog_text.toPlainText() != self.target_text:
					self.current_dialog_text.setPlainText(self.target_text)
				else:
					self.removeItem(self.current_dialog_text)
					self.call_next_action()
		else:
			super(Scene_Base, self).mousePressEvent(mouseEvent)

	# ==============================================
	# * Action Management
	# ==============================================

	def finish_action(self):
		if len(self.actions) > 0:
			self.actions.pop(0)
		else:
			self.wait_for_button_press()

	def check_if_first(self):
		if len(self.actions) == 1:
			self.perform_next_action()

	def perform_next_action(self):
		if len(self.actions) > 0:
			action = self.actions[0]
			self.current_action = action_type = action[0]

			if action_type is -1:
				self.actually_hide_dialog_box()
			elif action_type is 0:
				self.actually_show_dialog(action)
			elif action_type is 1:
				self.actually_show_button(action)
			elif action_type is 2:
				self.actually_set_background(action)
			elif action_type is 3:
				self.actually_goto_scene(action)
			elif action_type is 4:
				self.actually_close_game()
			elif action_type is 5:
				self.actually_play_song(action)
			elif action_type is 6:
				self.actually_wait_for_button_press()
			elif action_type is 7:
				self.actually_remove_all_buttons()
			elif action_type is 8:
				self.actually_show_text(action)
			elif action_type is 9:
				self.actually_hide_text(action)
			elif action_type is 10:
				self.actually_show_image(action)
			elif action_type is 11:
				self.actually_hide_image(action)
			elif action_type is 12:
				self.actually_move_image(action)
			elif action_type is 50:
				self.actually_play_sound(action)

	def call_next_action(self):
		self.finish_action()
		self.perform_next_action()

	# ==============================================
	# * Actual Implementations
	# ==============================================

	def actually_show_dialog(self, data):
		if self.dialog_box is None:
			self.create_dialog_box()
		else:
			self.current_dialog_text = QGraphicsTextItem()
			self.current_dialog_text.setZValue(20)
			self.current_dialog_text.setDefaultTextColor(QColor(255, 255, 255))

			temp_font = self.current_dialog_text.font()
			temp_font.setPointSize(data[2])
			self.current_dialog_text.setFont(temp_font)

			self.addItem(self.current_dialog_text)
			self.current_dialog_text.setX(self.dialog_box.x() + 10)
			self.current_dialog_text.setY(self.dialog_box.y() + 10)
			self.current_dialog_text.setTextWidth(self.dialog_box.boundingRect().width() - 20)

			self.target_text = data[1]
			self.current_dialog_text_offset = 0

	def create_dialog_box(self):
		self.dialog_box = QGraphicsRectItem(0, 0, self.width() - 20, self.height() / 4)
		self.dialog_box.setBrush(QColor(0, 0, 0))
		self.dialog_box.setX(10)
		self.dialog_box.setY(self.height() - self.dialog_box.boundingRect().height() - 10)
		self.dialog_box.setZValue(15);
		self.dialog_box.setOpacity(0)
		self.addItem(self.dialog_box)

	def actually_hide_dialog_box(self):
		if self.dialog_box is not None:
			self.removeItem(self.dialog_box)
			self.dialog_box = None

	def actually_show_button(self, data):
		button = Button(self, data[3], data[4], data[5], data[6], data[7], data[8], data[9])
		button.setX(data[1])
		button.setY(data[2])
		self.buttons.append(button)
		self.addItem(button)
		self.call_next_action()

	def actually_set_background(self, data):
		if self.background is not None:
			self.removeItem(self.background)
			self.background = None

		self.background = QGraphicsPixmapItem(QPixmap(data[1]).scaled(self.window.game_width, self.window.game_height))
		self.background.setZValue(-10)
		self.addItem(self.background)
		self.call_next_action()

	def actually_goto_scene(self, data):
		self.next_scene = data[1]
		self.fade_dir = 1
		self.create_fade(0)

	def actually_close_game(self):
		self.window.close_game()

	def actually_play_song(self, data):
		AudioPlayer.play_song(data[1])
		self.call_next_action()

	def actually_wait_for_button_press(self):
		self.current_action = -1

	def actually_remove_all_buttons(self):
		for b in self.buttons:
			self.removeItem(b)
		self.buttons = []
		self.call_next_action()

	def actually_show_text(self, data):
		text_item = QGraphicsTextItem()
		text_item.setZValue(5)
		if len(data) >= 8 and data[7] != None:
			text_item.setDefaultTextColor(QColor(data[7]))
		else:
			text_item.setDefaultTextColor(QColor("#FFFFFF"))

		text_item.setX(data[2])
		text_item.setY(data[3])
		text_item.setTextWidth(data[4])
		text_item.setPlainText(data[5])

		temp_font = text_item.font()
		temp_font.setPointSize(data[6])
		text_item.setFont(temp_font)

		self.addItem(text_item)
		self.texts[data[1]] = text_item
		self.call_next_action()

	def actually_hide_text(self, data):
		self.removeItem(self.texts[data[1]])
		self.texts[data[1]] = None
		self.call_next_action()

	def actually_show_image(self, data):
		image = None
		if isinstance(data[2], list):
			image = QGraphicsPixmapItem(QPixmap(data[2][0]))
			self.updatable_images.append([image, data[2], 0, 0, data[5]])
		else:
			image = QGraphicsPixmapItem(QPixmap(data[2]))
		image.setX(data[3])
		image.setY(data[4])
		image.setZValue(0)
		self.addItem(image)
		self.images[data[1]] = image
		self.call_next_action()

	def actually_hide_image(self, data):
		self.removeItem(self.images[data[1]])
		self.images[data[1]] = None
		self.call_next_action()

	def actually_move_image(self, data):
		image_id = data[1]
		image = self.images[image_id]
		duration = data[4]
		x_speed = (data[2] - image.x()) / duration
		y_speed = (data[3] - image.y()) / duration
		callback = self.call_next_action if data[5] else None
		image_data = [image_id, x_speed, y_speed, duration, callback]
		self.moving_images.append(image_data)
		if not data[5]:
			self.call_next_action()

	def actually_play_sound(self, data):
		AudioPlayer.play_sound_effect(data[1])
		self.call_next_action()

	# ==============================================
	# * Setup Calls
	# ==============================================

	def add_call(self, data):
		self.actions.append(data)
		self.check_if_first()

	# ==============================================
	# * Extended Calls
	# ==============================================

	# ==============================================
	# Adds a dialog to the game.
	#
	# Ex:
	#     self.add_dialog("Hello World!")
	# ==============================================
	def add_dialog(self, msg, fontSize=20):
		self.add_call([0, msg, fontSize])

	# ==============================================
	# Hides the dialog box
	#
	# Ex:
	#     self.hide_dialog_box()
	# ==============================================
	def hide_dialog_box(self):
		self.add_call([-1])

	# ==============================================
	# Adds a button to the game.
	# After adding all buttons, be sure to call "self.wait_for_button_press".
	# Check scenes/scene_titlescreen.py for example of "font" and "buttonColors"
	#
	# Ex:
	#     self.add_button(30, 30, 200, 200, "My Button!", self.another_function)
	# ==============================================
	def add_button(self, x, y, w, h, name, action, font=None, buttonColors=None, textColors=None):
		self.add_call([1, x, y, w, h, name, font, buttonColors, textColors, action])

	# ==============================================
	# Sets the current background.
	#
	# Ex:
	#     self.set_background("images/Background1.png")
	# ==============================================
	def set_background(self, path):
		self.add_call([2, path])

	# ==============================================
	# Changes the game to the provided scene.
	#
	# Ex:
	#     self.goto_scene(scenes.my_other_scene.My_Other_Scene)
	# ==============================================
	def goto_scene(self, scene):
		self.add_call([3, scene])

	# ==============================================
	# Closes the game.
	#
	# Ex:
	#     self.close_game()
	# ==============================================
	def close_game(self):
		self.add_call([4])

	# ==============================================
	# Plays a song.
	#
	# Ex:
	#     self.play_song("audio/testmusic2.mp3")
	# ==============================================
	def play_song(self, path):
		self.add_call([5, path])

	# ==============================================
	# Plays a sound effect once.
	#
	# Ex:
	#     self.play_sound("audio/testmusic2.mp3")
	# ==============================================
	def play_sound(self, path):
		self.add_call([50, path])

	# ==============================================
	# Once all buttons are created, this will wait for the player to press one.
	#
	# Ex:
	#     self.wait_for_button_press()
	# ==============================================
	def wait_for_button_press(self):
		self.add_call([6])

	# ==============================================
	# Removes all buttons from the screen.
	#
	# Ex:
	#     self.remove_all_buttons()
	# ==============================================
	def remove_all_buttons(self):
		self.add_call([7])

	# ==============================================
	# Based on the value provided, there is a chance it will return True.
	#
	# Ex:
	#     if self.generate_random_chance(30):
	#		  # there is a 30% chance of this happening
	# 	  else:
	#		  # there is a 70% chance of this happening
	# ==============================================
	def generate_random_chance(self, val):
		return random.randint(0, 100) <= val

	# ==============================================
	# Shows text on the screen (not in dialog).
	# This function returns an ID that can be used in self.hide_text.
	#
	# Ex:
	#     text_id = self.show_text(10, 10, 200, "Hello Screen!", 40, "#FF44CC")
	# ==============================================
	def show_text(self, x, y, w, text, size=30, color=None):
		new_id = self.current_id
		self.current_id += 1
		self.add_call([8, new_id, x, y, w, text, size, color])
		return new_id

	# ==============================================
	# Hides the text connected to the ID.
	#
	# Ex:
	#     self.hide_text(text_id)
	# ==============================================
	def hide_text(self, text_id):
		self.add_call([9, text_id])

	# ==============================================
	# Shows a picture on the screen.
	# This function returns an ID that can be used in other functions.
	#
	# Ex:
	#     pic_id = self.show_picture("images/TestImage1.png", 30, 30)
	#
	# ----------------------------------------------
	#
	# Using an array of strings will create an animation.
	# In that case, the last argument is the frame-change frequency of the animation.
	#
	# Ex:
	#     pic_id = self.show_picture(["images/p1_frame_0.png", "images/p1_frame_1.png"], 100, 100, 30)
	# ==============================================
	def show_picture(self, path, x, y, animation_speed=20):
		new_id = self.current_id
		self.current_id += 1
		self.add_call([10, new_id, path, x, y, animation_speed])
		return new_id

	# ==============================================
	# Removes the picture connected to the provided ID.
	#
	# Ex:
	#     self.hide_picture(pic_id)
	# ==============================================
	def hide_picture(self, image_id):
		self.add_call([11, image_id])

	# ==============================================
	# Moves the picture to new coordinates over a duration of time.
	#
	# Ex:
	#     self.move_picture(pic_id, 90, 30, 120)
	# ==============================================
	def move_picture(self, image_id, x, y, duration, wait_until_finished=True):
		self.add_call([12, image_id, x, y, duration, wait_until_finished])

	#===============================================
	# Sets the vaccination status
	# True/False, 50% chance
	#===============================================
	def set_vaxx(self):
		if self.generate_random_chance(50):
			Scene_Base.vaxx = True
		else:
			Scene_Base.vaxx = False

	#===============================================
	# Sets the entry point
	# 0 is for blood (cut)
	# 1 is for stomach (mouth)
	#===============================================	
	def set_entry(self):
		if self.generate_random_chance(50):
			Scene_Base.entry = 0
		else:
			Scene_Base.entry = 1

	# ==============================================
	# Gets and sets global values
	#
	# Ex:
	#     self.set_value("Health", 100)
	#
	#     self.add_value("Health", -1)
	#
	#     if self.get_value("Health") <= 30:
	#         self.add_dialog("Player is less than 30 health!")
	# ==============================================
			
	def set_value(self, name, value):
		Scene_Base.GLOBAL_VARS[name] = value

	def add_value(self, name, value):
		Scene_Base.GLOBAL_VARS[name] += value

	def get_value(self, name):
		return Scene_Base.GLOBAL_VARS[name]
예제 #12
0
class GShaderNode(QGraphicsWidget):
    """This abstract class defines the look and feel of a Node. Specialized classes can subclass this instead of the Node class.
    """
    edge_started = pyqtSignal(uuid.UUID, GEdge)
    edge_ended = pyqtSignal(uuid.UUID, GEdge)
    deleted = pyqtSignal(object)
    connection_changed = pyqtSignal(GNodeSocket, object)  # Node
    input_changed = pyqtSignal(object)  # Node

    @abc.abstractmethod
    def __init__(self,
                 node_scene: NodeScene,
                 shader: Shader = None,
                 label: str = "",
                 parent=None,
                 backend_node: ShaderNode = None):
        super(QGraphicsWidget, self).__init__(parent)

        self._node = backend_node
        if self._node is None:
            if shader is None:
                raise ValueError(
                    "shader or backend_node needs to be provided!")
            else:
                self._node = ShaderNode(shader=shader,
                                        label=label,
                                        set_default_inputs=True,
                                        container=self)
        else:
            self._node.set_container(self)
        if label:
            self._node.set_label(label)

        self.node_scene = node_scene

        self._in_socket_modules = []
        self._out_socket_modules = []

        # define Node properties
        self._selected = False
        self._deletable = True
        self._input_index = 1
        self._output_index = 1
        self._width = 250
        self._height = 50
        self._rounding = 5
        self._padding = 8
        self._bg_color = QColor(80, 80, 100, 200)
        self._title_color = Qt.white
        self._title_font = QFont("Corbel", 11)
        self._title_font.setBold(True)
        self._title_item = QGraphicsTextItem(self)

        # Define layout
        self._master_layout = GraphicsGridLayout()

        # Define widgets properties
        self._input_label_font = QFont("Corbel", 8)
        self._input_label_palette = QPalette()
        self._input_label_palette.setColor(QPalette.Background,
                                           QColor(0, 0, 0, 0))
        self._input_label_palette.setColor(QPalette.Foreground,
                                           self._title_color)

        # Set flags to enable the widget to be moved and selected
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)

        self._init_title()
        self._init_layout()
        self._init_sockets()

    def _init_layout(self):
        self._master_layout.setContentsMargins(-5, 4, 4, -15)
        self._master_layout.setRowSpacing(
            0,
            self._title_font.pointSize() +
            12)  # Add empty space for first row so that title is visible
        self._master_layout.setHorizontalSpacing(2.0)
        self._master_layout.setVerticalSpacing(0.0)
        self._master_layout.setColumnAlignment(2, Qt.AlignRight)
        self._master_layout.setColumnFixedWidth(0, 15)  # Input socket column
        self._master_layout.setColumnFixedWidth(2, 15)  # Output socket column
        self._master_layout.setColumnFixedWidth(1, self._width - 15 - 15)

        self.setLayout(self._master_layout)

    def _init_sockets(self):
        shader = self._node.get_shader()

        for i in range(self._node.num_output_sockets()):
            shader_output = shader.get_outputs()[i]
            label = shader_output.get_display_label()
            socket = self._node.get_output_socket(i)
            # socket.set_index(i)
            self._add_output_module(output_label=label, node_socket=socket)

        for i in range(self._node.num_input_sockets()):
            shader_input = shader.get_inputs()[i]
            socket = self._node.get_input_socket(i)
            # socket.set_index(i)
            self._add_input_module(socket, shader_input)

    def _notify_change(self):
        """Event is called when any of this node's widget's inputs are changed"""
        self.input_changed.emit(self)

    def delete(self):
        output_nodes = self.get_output_target_nodes()
        self._remove_edges_from_sockets(self.get_output_sockets())
        self._remove_edges_from_sockets(self.get_input_sockets())
        self._node.delete()
        for n in output_nodes:
            n.update_module_enabled_state()

        self.deleted.emit(self)

    def _remove_edges_from_sockets(self, sockets):
        for s in sockets:
            edges = s.get_connected_edges()
            for e in edges:
                s.remove_connected_edge(e)
                self.node_scene.removeItem(e)

            del s

    def id(self) -> uuid.UUID:
        return self._node.id()

    def label(self) -> str:
        return self._node.label()

    def set_label(self, label: str = None):
        """
        Sets the label of this node and updates the node title accordingly.
        :param label: the new label, or None to use the old label and just update the title.
        """
        if label is None:
            label = self.label()
        self._node.set_label(label)
        self._title_item.setPlainText(self.label() + " (" +
                                      str(self.get_num()) + ")")

    def get_num(self) -> int:
        """Returns the number that is assigned to this node. This number is unique among nodes with the same shader type."""
        return self._node.get_num()

    def set_num(self, num: int):
        self._node.set_num(num)
        self.set_label(None)

    def _init_title(self):
        self._title_item.setDefaultTextColor(self._title_color)
        self._title_item.setFont(self._title_font)
        self._title_item.setPos(self._padding, 0)
        self._title_item.setTextWidth(self._width - self._padding)
        self.set_label()

    def is_deletable(self) -> bool:
        return self._deletable

    def set_deletable(self, value: bool):
        self._deletable = value

    def get_input(
        self, exclude_connected: True
    ) -> typing.List[typing.Tuple[str, str, typing.Any]]:
        """Returns a list of argument names, its modified name as well as the the value in the node input for that argument.

        Note that is the shader held by this node has not been recompiled, the modified name of the argument is undefined."""
        out = []
        for socket, mod in self._in_socket_modules:
            if not exclude_connected or not socket.is_connected():
                value = mod.get_gl_value()
                argument: str = socket.label()
                modified_name = self.get_shader().get_parsed_code(
                ).primary_function.get_argument(argument).get_modified_name()

                out.append((argument, modified_name, value))

        return out

    def get_output_target_nodes(self) -> list:
        return [
            n.get_container() for n in self._node.get_output_target_nodes()
        ]

    def get_ancestor_nodes(self, add_self: bool = False) -> IndexedSet:
        """
        Returns a list of all connected ancestors of this node.
        :param add_self: if True, adds this node to the set of returned nodes.
        :return: a Set of nodes that are ancestors of this node.
        """
        nodes = self._node.get_ancestor_nodes(add_self=add_self)
        out = IndexedSet()
        for n in nodes:
            out.add(n.get_container())
        return out

    def save_graph_state(self):
        """Saves the state (socket values) of this node and all ancestor nodes."""
        self._node.save_graph_state()

    def save_state(self):
        """Saves the values of all sockets of this node."""
        self._node.save_state()

    def restore_graph_state(self):
        """Restores the state (socket values) of this node and all ancestor nodes."""
        self._node.restore_graph_state()

    def restore_state(self):
        """Restores the values of all sockets of this node."""
        self._node.restore_state()

    def get_backend_node(self) -> ShaderNode:
        return self._node

    def get_shader(self) -> Shader:
        return self._node.get_shader()

    def has_socket(self, socket: GNodeSocket) -> bool:
        return self._node.has_socket(socket.get_backend_socket())

    def get_input_sockets(self) -> typing.List[GNodeSocket]:
        return [s.get_container() for s in self._node.get_input_sockets()]

    def get_input_socket(self, index: int) -> typing.Union[GNodeSocket, None]:
        return self._node.get_input_socket(index).get_container()

    def update_module_enabled_state(self):
        for socket in self.get_input_sockets():
            mod = self.get_input_module(socket)

            if mod is not None:
                mod.setEnabled(not socket.is_connected())

    def get_input_module(self, socket=None, index=None) -> SocketModule:
        assert not (socket is None and index is None
                    ), "Specify at least one identifier to find input module!"

        for i, (s, m) in enumerate(self._in_socket_modules):
            if socket and s == socket:
                return m
            elif index and i == index:
                return m

        return None

    def get_output_sockets(self) -> typing.List[GNodeSocket]:
        return [s.get_container() for s in self._node.get_output_sockets()]

    def get_output_socket(self, identifier) -> 'GNodeSocket':
        return self._node.get_output_socket(identifier).get_container()

    def _create_g_socket(self, socket: NodeSocket) -> GNodeSocket:
        socket = GNodeSocket(self, socket)
        socket.edge_started.connect(self._spawn_edge)
        socket.edge_released.connect(self._release_edge)
        socket.connection_changed.connect(self._handle_socket_connection)

        return socket

    def _add_input_module(self, node_socket: NodeSocket, shader_input: Shader):
        dtype = node_socket.dtype()
        socket = self._create_g_socket(node_socket)
        lim_min = shader_input.get_limits()[0]
        lim_max = shader_input.get_limits()[1]
        display_label = shader_input.get_display_label()
        z_value = 0

        if dtype == DataType.Float:
            # Create an widgets widget
            input_widget = FloatInput(min_=lim_min, max_=lim_max, dtype=dtype)
        elif dtype == DataType.Int:
            input_widget = IntInput(min_=lim_min, max_=lim_max, dtype=dtype)
        elif dtype == DataType.Vec3_RGB:
            input_widget = ColorInput(dtype)
        elif dtype == DataType.Shader:
            input_widget = ShaderInput(dtype)
        elif dtype == DataType.Vec3_Float:
            size = 3
            input_widget = ArrayInput(size,
                                      min_=lim_min,
                                      max_=lim_max,
                                      dtype=dtype)
        elif dtype == DataType.Int_Choice:
            input_widget = IntChoiceInput(values=shader_input.get_names())
            input_widget.raise_()
            z_value = 1
        else:
            raise TypeError("Data Type {} is not yet supported!".format(dtype))

        # Create a module and add to this node
        module = SocketModule(socket, display_label, input_widget)
        module.input_changed.connect(self._notify_change)
        module.set_label_palette(self._input_label_palette)
        module.set_value(socket.value())
        self._in_socket_modules.append((socket, module))

        module_item = self.node_scene.addWidget(module)
        module_item.setZValue(z_value)
        self._master_layout.addItem(socket, self._input_index, 0,
                                    Qt.AlignVCenter)
        self._master_layout.addItem(module_item, self._input_index, 1,
                                    Qt.AlignVCenter)
        self._input_index += 1
        self._height = self._master_layout.preferredHeight() + 10

        if not shader_input.is_connectable():
            socket.setVisible(False)

    def _add_output_module(self, output_label: str, node_socket: NodeSocket):
        socket = self._create_g_socket(node_socket)
        module = OutputModule(output_label)
        module.set_label_palette(self._input_label_palette)
        module_item = self.node_scene.addWidget(module)
        self._out_socket_modules.append((socket, module))

        self._master_layout.addItem(module_item, self._output_index, 1,
                                    Qt.AlignVCenter)
        self._master_layout.addItem(socket, self._input_index, 2,
                                    Qt.AlignVCenter)
        self._output_index += 1
        self._input_index += 1

    def _handle_socket_connection(self, socket: GNodeSocket, _):
        mod = self.get_input_module(socket)

        if socket.is_connected(
        ):  # Disable socket module to show that is not user controllable anymore
            mod.setEnabled(False)
        else:
            mod.setEnabled(True)

        self.connection_changed.emit(socket, self)

    def _spawn_edge(self, edge):
        self.edge_started.emit(self.id(), edge)

    def _release_edge(self, edge):
        self.edge_ended.emit(self.id(), edge)

    def boundingRect(self) -> QtCore.QRectF:
        return QRectF(0, 0, self._width, self._height).normalized()

    def paint(self, painter: QPainter, option, widget=None):
        if self.isSelected():
            painter.setPen(QPen(Qt.black))  # Disables the border
        else:
            painter.setPen(Qt.NoPen)

        painter.setBrush(QBrush(self._bg_color))
        painter.drawRoundedRect(0, 0, self._width, self._height,
                                self._rounding, 1)

    def render(self,
               width: int,
               height: int,
               retain_graph=False) -> typing.Tuple[torch.Tensor, dict]:
        """
        Renders an image from this node graph.

        :param width: pixel width of rendered image
        :param height: pixel height of rendered image
        :param retain_graph: If True, updated socket values will not be fetched, instead, saved tensor values will be used. If using
            backpropagation that updates the returned tensor parameters in-place, set this to True, otherwise set to False so that parameter values
            are fetched from input Sockets.
        :return: a Tensor containing the rendered image and a list of parameter Tensors (one for each unconnected graph input)
        """
        return self._node.render(width, height, retain_graph=retain_graph)

    def randomize_input(self):
        """Randomizes the values of all input sockets."""
        for socket, inp in zip(self.get_input_sockets(),
                               self.get_shader().get_inputs()):
            if not socket.is_connected():
                rand_val = inp.get_centered_random_value()
                socket.set_value(rand_val)
                mod = self.get_input_module(socket)
                mod.update_from_socket()

    def __str__(self):
        cls = self.__class__
        return "{} '{}({})'".format(cls, self.label(), self.get_num())

    def __hash__(self):
        return self._node.__hash__()

    def __eq__(self, other):
        if isinstance(other, GShaderNode):
            return self._node.__eq__(other.get_backend_node())

        return False
예제 #13
0
class NodeItem():
    ''' 
    This represents one node on the diagram.
    This class creates the ellipse graphics item and the text graphics item and adds them to the scene.
    '''
    def __init__(self, scene, x, y, nodeInstance=None):
        self.scene = scene
        self.logMsg = None

        self.x = x
        self.y = y
        self.itemInstance = nodeInstance
        self.diagramType = self.itemInstance.diagramType
        self.displayText = None
        self.model = self.scene.parent.model
        self.neoCon = self.scene.parent.model.modelNeoCon
        self.getFormat()
        # remember current width and height
        self.oldNodeWidth = 0
        self.oldNodeHeight = 0
        # init graphics objects to none
        self.INode = None
        self.INtext = None
        #        # init list of qgrapphicellipseitems to none
        #        self.ellipsePoints = []
        #        self.ellipseGraphicItems = []
        # draw the ellipse
        self.drawIt()

    def name(self, ):
        return self.itemInstance.NZID

    def NZID(self, ):
        return self.itemInstance.NZID

    def getFormat(self, ):
        '''
        determine the format to use to draw the instance node
        - start with the project default
        - if the instance node has a template then use the instance format defined on the template
        '''
        # get the default
        self.nodeFormat = INodeFormat(
            formatDict=self.model.modelData["INformat"])
        # get a custom template format if there is one
        if not self.itemInstance.nodeTemplate is None:
            index, nodeTemplateDict = self.model.getDictByName(
                topLevel="Node Template",
                objectName=self.itemInstance.nodeTemplate)
            if not nodeTemplateDict is None:
                self.instanceNodeFormatDict = nodeTemplateDict.get(
                    "INformat", None)
                if not self.instanceNodeFormatDict is None:
                    self.nodeFormat = INodeFormat(
                        formatDict=self.instanceNodeFormatDict)

    def clearItem(self, ):

        if (not self.INode is None and not self.INode.scene() is None):
            self.INode.scene().removeItem(self.INode)
        if (not self.INtext is None and not self.INtext.scene() is None):
            self.INtext.scene().removeItem(self.INtext)

#        # remove the points on the ellipse - this code is only for debugging
#        for point in self.ellipseGraphicItems:
#            if (not point is None and not point.scene() is None):
#                point.scene().removeItem(point)

    def drawIt(self, ):
        # force the node instance to update its values in case it has been updated from another diagram or the tree view
        self.itemInstance.reloadDictValues()
        # get current format as it may have changed
        self.getFormat()
        if self.oldNodeWidth != self.nodeFormat.formatDict[
                "nodeWidth"] or self.oldNodeHeight != self.nodeFormat.formatDict[
                    "nodeHeight"]:
            # remove graphic items that already exist
            self.clearItem()
            # create the node ellipse
            self.INode = QGraphicsEllipseItem(QRectF(
                self.x, self.y, self.nodeFormat.formatDict["nodeWidth"],
                self.nodeFormat.formatDict["nodeHeight"]),
                                              parent=None)
            # create the node text
            self.INtext = QGraphicsTextItem("", parent=None)
            self.INtext.setPos(self.x, self.y)
            self.x = self.INode.sceneBoundingRect().x()
            self.y = self.INode.sceneBoundingRect().y()
            #            print("after create items before drawIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect()))
            #            print("x:{} y:{}".format(self.x, self.y))
            self.formatItem()
            self.scene.addItem(self.INode)
            self.scene.addItem(self.INtext)
            #            # add points
            #            for point in self.ellipseGraphicItems:
            #                self.scene.addItem(point)

            # redraw all the rels associated to this node.
            self.moveRels()
        else:
            #            print("before drawIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect()))
            #            print("x:{} y:{}".format(self.x, self.y))
            self.formatItem()
        # remember current width and height
        self.oldNodeWidth = self.nodeFormat.formatDict["nodeWidth"]
        self.oldNodeHeight = self.nodeFormat.formatDict["nodeHeight"]
#        print("after drawIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect()))
#        print("x:{} y:{}".format(self.x, self.y))

#    def genPoints(self, ):
#        '''Ellipse Constructor - not sure of these, need to verify
#        def __init__(self, mx, my, rh, rv):
#        mx - center point x
#        my - center point y
#        rh - height of ellipse
#        rv - width of ellipse'''
#        x = self.INode.sceneBoundingRect().center().x()
#        y = self.INode.sceneBoundingRect().center().y()
#        w = self.INode.sceneBoundingRect().width()/2.0
#        h = self.INode.sceneBoundingRect().height()/2.0
#        myEllipse = Ellipse(x, y, w, h)
#        for d in range(0, 360, 10):
#            x, y = myEllipse.pointFromAngle(radians(d))
#            self.ellipsePoints.append([d, x, y])
#            aPoint = QGraphicsEllipseItem(QRectF(x-2.5,y-2.5,5, 5), parent=None)
#            self.ellipseGraphicItems.append(aPoint)
##        print(self.ellipsePoints)

    def formatItem(self, ):
        # configure the formatting aspects of the qgraphics item
        pen = self.nodeFormat.pen()
        brush = self.nodeFormat.brush()
        self.INode.setZValue(NODELAYER)
        self.INode.setBrush(brush)
        self.INode.setPen(pen)
        self.INode.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.INode.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
        self.INode.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.INode.setSelected(True)
        self.INode.setData(
            1, self.itemInstance.NZID)  # get with self.INode.data(1)
        self.INode.setData(ITEMTYPE, NODEINSTANCE)
        # draw the text
        self.updateText()
        self.INtext.setZValue(NODELAYER)
        self.INtext.setTextWidth(self.INode.boundingRect().width())
        self.INtext.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.INtext.setFlag(QGraphicsItem.ItemIsSelectable, False)
        self.INtext.setData(NODEID, self.itemInstance.NZID)
        self.INtext.setData(ITEMTYPE, NODEINSTANCETEXT)

    def updateText(self, ):
        '''
        Generate the HTML that formats the node  data inside the ellipse
        '''
        # generate the html
        prefix = "<!DOCTYPE html><html><body>"
        suffix = "</body></html>"
        try:
            Lbl = str(self.itemInstance.labelList[0][0])
        except:
            Lbl = "No Labels"
        firstLbl = "<center><b>{}</b></center>".format(Lbl)
        try:
            propName = str(self.itemInstance.propList[0][PROPERTY])
            propVal = str(self.itemInstance.propList[0][VALUE])
            prop = "{}: {}".format(propName, propVal)
        except:
            prop = "No Properties"
        firstProp = "<center>{}</center>".format(prop)
        genHTML = '{}{}<hr width="75%">{}{}'.format(prefix, firstLbl,
                                                    firstProp, suffix)
        self.INtext.setHtml(genHTML)

    def moveIt(self, dx, dy):
        '''Move the node ellipse and the node textbox to the delta x,y coordinate.'''
        #        print("before moveIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect()))

        self.INode.moveBy(dx, dy)
        self.x = self.INode.sceneBoundingRect().x()
        self.y = self.INode.sceneBoundingRect().y()
        self.INtext.moveBy(dx, dy)
        #        print("after moveIt: sceneboundingrect {} ".format( self.INode.sceneBoundingRect()))

        #        # recalc points
        #        self.genPoints()

        #        for point in self.ellipseGraphicItems:
        #            point.moveBy(dx, dy)

        self.moveRels()

    def moveRels(self, ):
        '''Redraw all the relationship arcs connected to the Node ellipse.'''
        #        print("moveRels")
        for key, diagramItem in self.scene.parent.itemDict.items():
            if diagramItem.diagramType == "Instance Relationship":
                if self.itemInstance.NZID in [
                        diagramItem.relationInstance.startNZID,
                        diagramItem.relationInstance.endNZID
                ]:
                    diagramItem.drawRelationship()
#                if diagramItem.relationInstance.startNZID == self.itemInstance.NZID:
#                    diagramItem.moveRelationshipLine()
##                    print("move startnode {}-{}".format(self.x, self.y))
#                if diagramItem.relationInstance.endNZID == self.itemInstance.NZID:
#                    diagramItem.moveRelationshipLine()
##                    print("move endnode {}-{}".format(self.x, self.y))

    def getObjectDict(self, ):
        '''
        This function returns a dictionary with all the data that represents this node item.  
        The dictionary is added to the Instance Diagram dictionary.'''
        objectDict = {}
        objectDict["NZID"] = self.itemInstance.NZID
        objectDict["x"] = self.INode.sceneBoundingRect().x()
        objectDict["y"] = self.INode.sceneBoundingRect().y()
        objectDict["diagramType"] = self.diagramType
        return objectDict

    def setLogMethod(self, logMethod=None):
        if logMethod is None:
            if self.logMsg is None:
                self.logMsg = self.noLog
        else:
            self.logMsg = logMethod

    def noLog(self, msg):
        return
예제 #14
0
class Edge(Connection):
    ''' B-spline/Bezier connection shape '''
    def __init__(self, edge, graph):
        ''' Set generic parameters from Connection class '''
        self.text_label = None
        super(Edge, self).__init__(edge['source'], edge['target'])
        self.edge = edge
        self.graph = graph
        # Set connection points as not visible, by default
        self.bezier_visible = False

        # Initialize control point coordinates
        self.bezier = [self.mapFromScene(*self.edge['spline'][0])]
        # Bezier control points (groups of three points):
        assert(len(self.edge['spline']) % 3 == 1)
        for i in xrange(1, len(self.edge['spline']), 3):
            self.bezier.append([Controlpoint(
                          self.mapFromScene(*self.edge['spline'][i + j]), self)
                          for j in range(3)])

        # Create connection points at start and end of the edge
        self.source_connection = Connectionpoint(
                self.start_point or self.bezier[0], self, self.parent)
        self.parent.movable_points.append(self.source_connection)
        self.end_connection = Connectionpoint(
                self.end_point or self.bezier[-1], self, self.child)
        self.child.movable_points.append(self.end_connection)
        self.reshape()

    @property
    def start_point(self):
        ''' Compute connection origin - redefine in subclasses '''
        # Start point is optional - graphviz decision
        return self.mapFromScene(*self.edge['start']) \
               if self.edge.get('start') else None

    @property
    def end_point(self):
        ''' Compute connection end point - redefine in subclasses '''
        return self.mapFromScene(*self.edge['end']) \
               if self.edge.get('end') else None

    def bezier_set_visible(self, visible=True):
        ''' Display or hide the edge control points '''
        self.bezier_visible = visible
        for group in self.bezier[1:]:
            for ctrl_point in group:
                if visible:
                    ctrl_point.show()
                else:
                    ctrl_point.hide()
        if visible:
            self.end_connection.show()
            self.source_connection.show()
        else:
            self.end_connection.hide()
            self.source_connection.hide()
        self.update()

    def mousePressEvent(self, event):
        ''' On a mouse click, display the control points '''
        self.bezier_set_visible(True)

  # pylint: disable=R0914
    def reshape(self):
        ''' Update the shape of the edge (redefined function) '''
        path = QPainterPath()
        # If there is a starting point, draw a line to the first curve point
        if self.start_point:
            path.moveTo(self.source_connection.center)
            path.lineTo(self.bezier[0])
        else:
            path.moveTo(self.source_connection.center)
        # Loop over the curve points:
        for group in self.bezier[1:]:
            path.cubicTo(*[point.center for point in group])

        # If there is an ending point, draw a line to it
        if self.end_point:
            path.lineTo(self.end_connection.center)

        end_point = path.currentPosition()
        arrowhead = self.angle_arrow(path)
        path.lineTo(arrowhead[0])
        path.moveTo(end_point)
        path.lineTo(arrowhead[1])
        path.moveTo(end_point)
        try:
            # Add the transition label, if any (none for the START edge)
            font = QFont('arial', pointSize=8)
            metrics = QFontMetrics(font)
            label = self.edge.get('label', '')
            lines = label.split('\n')
            width = metrics.width(max(lines)) # longest line
            height = metrics.height() * len(lines)
            # lp is the position of the center of the text
            pos = self.mapFromScene(*self.edge['lp'])
            if not self.text_label:
                self.text_label = QGraphicsTextItem(
                                 self.edge.get('label', ''), parent=self)
            self.text_label.setX(pos.x() - width / 2)
            self.text_label.setY(pos.y() - height / 2)
            self.text_label.setFont(font)
            # Make horizontal center alignment, as dot does
            self.text_label.setTextWidth(self.text_label.boundingRect().width())
            fmt = QTextBlockFormat()
            fmt.setAlignment(Qt.AlignHCenter)
            cursor = self.text_label.textCursor()
            cursor.select(QTextCursor.Document)
            cursor.mergeBlockFormat(fmt)
            cursor.clearSelection()
            self.text_label.setTextCursor(cursor)
            self.text_label.show()
        except KeyError:
            # no label
            pass
        self.setPath(path)

    def __str__(self):
        ''' user-friendly information about the edge coordinates '''
        return('Edge between ' + self.edge['source'].name + ' and ' +
                self.edge['target'].name + str(self.edge['spline'][0]))

    def paint(self, painter, option, widget):
        ''' Apply anti-aliasing to Edge Connections '''
        painter.setRenderHint(QPainter.Antialiasing, True)
        super(Edge, self).paint(painter, option, widget)
        # Draw lines between connection points, if visible
        if self.bezier_visible:
            painter.setPen(
                    QPen(Qt.lightGray, 0, Qt.SolidLine))
            painter.setBrush(Qt.NoBrush)
            points_flat = [point.center
                           for sub1 in self.bezier[1:] for point in sub1]
            painter.drawPolyline([self.source_connection.center]
                                  + points_flat + [self.end_connection.center])
예제 #15
0
class GraphicsNode(QGraphicsRectItem):
    """Implement the graphics version of a node"""
    def __init__(self,
                 node: 'Node',
                 parent=None,
                 resizeable=True,
                 min_width=180,
                 width=180,
                 min_height=240,
                 height=240):
        super().__init__(parent=parent)
        self.node = node  # Reference to parent class Node implementing the logic
        # init flags
        self._was_moved = False
        self._resized = False
        self._last_selected_state = False
        self._hovered = False

        try:
            self.handles = {}
            self._currentRect = None
            self._currentPos = None

            self.initSizes(resizeable=resizeable,
                           min_width=min_width,
                           width=width,
                           min_height=min_height,
                           height=height)
            self.initAssets()
            self.initUI()

        except Exception as e:
            dumpException(e)

    @property
    def content(self):
        """Reference to the `Node` Content"""
        return self.node.content if self.node else None

    @property
    def height(self):
        return self.rect().height()

    @property
    def width(self):
        return self.rect().width()

    @height.setter
    def height(self, value):
        currentRect = self.rect()
        currentRect.setHeight(value)
        self.setRect(currentRect)

    @width.setter
    def width(self, value):
        currentRect = self.rect()
        currentRect.setWidth(value)
        self.setRect(currentRect)

    @property
    def title(self, ):
        return self._title

    @title.setter
    def title(self, value):
        self._title = value
        # fm = QFontMetrics(self._title_font)
        # width = fm.width(self.node.title)
        self.title_item.setPlainText(self._title)
        # self.title_item.setTextWidth(width)

    def initUI(self):
        # Define the node as selectable and movable
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setAcceptHoverEvents(True)
        if self.resizeable:
            self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
            self.setFlag(QGraphicsItem.ItemIsFocusable, True)
            self.initHandles()

        # init _title
        self.title_item = None
        self.initTitle()
        self.title = self.node.title
        # init content
        self.initContent()
        self.updateLayout()

    def initSizes(self,
                  resizeable=True,
                  min_width=180,
                  width=180,
                  min_height=240,
                  height=240):
        # Diverse parameters for drawing
        self.edge_roundness = 15.
        self.edge_padding = 10.
        self.title_height = 24
        self.title_horizontal_padding = 5.
        self.title_vertical_padding = 4.

        self.content_offset = 2  # define margin for the content

        self.resizeable = resizeable
        self.min_width = min_width
        self.min_height = min_height
        self.width = width
        self.height = height

        # Socket parameters
        self.socket_spacing = 22
        self.socket_offsets = {
            SocketPosition.BottomLeft: -1,
            SocketPosition.MiddleLeft: -1,
            SocketPosition.TopLeft: -1,
            SocketPosition.BottomRight: 1,
            SocketPosition.MiddleRight: 1,
            SocketPosition.TopRight: 1,
        }

    def initAssets(self):
        self._title_color = colors[THEME]['title_color']
        self._title_font = QFont('Ubuntu', 8)

        self._color = colors[THEME]['color']
        self._color_selected = colors[THEME]['color_selected']
        self._color_hovered = colors[THEME]['color_hovered']

        self._pen_default = QPen(self._color)
        self._pen_default.setWidthF(OUTLINE_WIDTH)
        self._pen_selected = QPen(self._color_selected)
        self._pen_selected.setWidthF(OUTLINE_WIDTH)
        self._pen_hovered = QPen(self._color_hovered)
        self._pen_hovered.setWidthF(OUTLINE_WIDTH + 1)

        self._brush_title = colors[THEME]['brush_title']
        self._brush_background = colors[THEME]['brush_background']

        self.icons = QImage('../../node_editor/icons/status_icons.png')

        shadow = QGraphicsDropShadowEffect(blurRadius=5, xOffset=3, yOffset=3)
        self.setGraphicsEffect(shadow)

    def initHandles(self):
        for position in (HandlePosition.MiddleRight,
                         HandlePosition.BottomMiddle,
                         HandlePosition.BottomRight):
            self.handles[position] = Handle(self, position)

    def initContent(self):
        """Add the `Content` of the `Node` to the `Graphical Scene`"""
        # Draw the contents
        # defines the content as a proxy widget with parent self
        if self.content:
            self.grContent = self.node.scene.grScene.addWidget(self.content)
            self.grContent.setParentItem(self)

    def initTitle(self):
        """Title is instanciated as `QGraphicsTextItem`"""
        # Draw the _title
        self.title_item = QGraphicsTextItem(self)
        self.title_item.node = self.node  # add reference to the node
        self.title_item.setDefaultTextColor(self._title_color)
        self.title_item.setFont(self._title_font)
        self.title_item.setPos(self.title_horizontal_padding, 0)

    def setContentGeometry(self, ):
        """Set Content geometry to fit the space available in the `Graphical Node`"""
        if self.title_item:
            self.title_item.setTextWidth(self.width -
                                         2 * self.title_horizontal_padding)
        offset = self.content_offset
        if self.content is not None:
            self.content.setGeometry(
                self.edge_padding + offset,
                self.title_height + self.edge_padding + offset,
                self.width - 2 * self.edge_padding - 2 * offset, self.height -
                2 * self.edge_padding - self.title_height - 2 * offset)

    def enableHoverEvents(self, enabled: bool = True):
        self.setAcceptHoverEvents(enabled)
        if not enabled:
            self._hovered = False

    def doSelect(self, new_state=True):
        self.setSelected(new_state)
        self._last_selected_state = new_state
        if new_state:
            self.onSelected()

    def onSelected(self):
        """onSelected event"""
        self.print('On selected event', self.node)
        self.node.scene.grScene.itemSelected.emit()

    def mousePressEvent(self, event: 'QGraphicsSceneMouseEvent') -> None:
        """Overrides Qt's mousePressEvent.
        Parameters
        ----------
        event : QGraphicsSceneMouseEvent
            current event
        """
        try:
            # self.handleSelected = self.getHandleAt(event.pos())
            # if self.handleSelected:
            #     # record the position where the mouse was pressed
            #     self._currentPos = event.pos()
            #     # # current rectangle at mouse pressed
            #     self._currentRect = self.boundingRect()
            super().mousePressEvent(event)
        except Exception as e:
            dumpException(e)

    def mouseMoveEvent(self, event: 'QGraphicsSceneMouseEvent') -> None:
        """Mouse move event

        Handle two cases :
            - either the `Graphical Node` is resizeable and a handle is selected, the trigger the resize event
            - or the node is selected and update the `Graphical Socket` and `Graphical Edge`.
        Parameters
        ----------
        event : QGraphicsSceneMouseEvent
            event trigerring the mouseMoveEvent
        """
        self.updateSocketAndEdges()
        self._was_moved = True
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event: 'QGraphicsSceneMouseEvent'):
        """Mouse Release Event

        On release, if moved, trigger storeHistory from :class:`~node_editor.node_scene_history.SceneHistory`.
        Also handles selection of the node and triggers onSelected event.
        Parameters
        ----------
        event : QGraphicsSceneMouseEvent
            event triggering the mouseReleaseEvent
        """
        # TODO onDeselected
        super().mouseReleaseEvent(event)

        if self.resizeable:
            self.handleSelected = None
            self._currentRect = None
            self._currentPos = None

        # handle when grNode moved
        if self._was_moved:
            self._was_moved = False
            self.node.scene.history.storeHistory('Node moved',
                                                 setModified=True)

            self.node.scene.resetLastSelectedStates()
            self.doSelect()
            # store the last selected state, because moving also select the node
            self.node.scene._last_selected_items = self.node.scene.getSelectedItems(
            )

            # skip storing selection
            return

        # handle when grNode was clicked on
        # condition met when changing from one selection to another or
        # when multiple items are selected and the current node is then selected
        if self._last_selected_state != self.isSelected() or \
                self.node.scene._last_selected_items != self.node.scene.getSelectedItems():
            # reset all other selected flags to False
            self.node.scene.resetLastSelectedStates()
            # set the new state of this object only
            self._last_selected_state = self.isSelected()
            self.onSelected()

    def mouseDoubleClickEvent(self, event: 'QGraphicsSceneMouseEvent') -> None:
        """Overrides double click event. Resent to `Node::onDoubleClicked`"""
        self.node.onDoubleClicked(event)

    def wheelEvent(self, event: 'QGraphicsSceneWheelEvent') -> None:
        """wheelEvent on the GraphicsNode. Avoid the wheelEvent from the GraphicsScene."""
        self.print('wheelEvent')

    def hoverEnterEvent(self, event: 'QGraphicsSceneHoverEvent') -> None:
        super().hoverEnterEvent(event)
        self._hovered = True
        self.update()

    def hoverLeaveEvent(self, event: 'QGraphicsSceneHoverEvent') -> None:
        super().hoverLeaveEvent(event)
        self._hovered = False
        self.update()

    def resize(self, rect: QRectF):
        """Update rectangle and bounding rectangle"""
        if rect.width() < self.min_width:
            rect.setWidth(self.min_width)
        if rect.height() < self.min_height:
            rect.setHeight(self.min_height)
        self.setRect(rect)
        self.updateSocketAndEdges()
        self.setContentGeometry()

    def getSocketPosition(self,
                          index: int,
                          position: SocketPosition,
                          num_out_of: int = 1) -> List[float]:
        """Helper function - returns the position of a socket in pixels relative to the `Graphical Node`

        Parameters
        ----------
        index : ```int```
            Index of the socket in the list
        position : SocketPosition
            One of enumeration
        num_out_of : ```int```
            total number of `Socket` on this position

        Returns
        -------
        ```list```
            x, y position relative to the node
        """
        if position in (SocketPosition.TopLeft, SocketPosition.MiddleLeft,
                        SocketPosition.BottomLeft):
            x = self.socket_offsets[position]
        else:
            x = self.width + self.socket_offsets[position]

        if position in (SocketPosition.BottomLeft, SocketPosition.BottomRight):
            # start from bottom
            y = self.height - (index * self.socket_spacing +
                               self.grNode.title_vertical_padding +
                               self.edge_roundness)

        elif position in (SocketPosition.MiddleLeft,
                          SocketPosition.MiddleRight):
            num_sockets = num_out_of
            node_height = self.height
            top_offset = self.title_height + 2 * self.title_vertical_padding + self.edge_padding
            available_height = node_height - top_offset

            total_height_of_all_socket = num_sockets * self.socket_spacing

            new_top = available_height - total_height_of_all_socket

            # y = top_offset + index * self.socket_spacing + new_top / 2
            y = top_offset + available_height / 2. + (index - 0.5) * self.socket_spacing - \
                (num_sockets - 1) * self.socket_spacing / 2

        elif position in (SocketPosition.TopLeft, SocketPosition.TopRight):
            # start from top
            y = index * self.socket_spacing + self.title_height + self.title_vertical_padding + self.edge_roundness
        else:
            y = 0

        return [x, y]

    def updateSocketAndEdges(self):
        """Update the `Socket` position and the `Graphical Edges` when available"""
        # As this method can
        if self.resizeable:
            # update the socket position
            if hasattr(self.node, 'inputs') and hasattr(self.node, 'outputs'):
                for socket in self.node.getSockets():
                    socket.setSocketPosition()

        # in any case
        if self.scene() is not None:
            for node in self.scene().scene.nodes:
                if node.isSelected():
                    node.updateConnectedEdges()

    def updateHandles(self):
        for handle in self.handles.values():
            handle.setHandlePosition()

    def updateLayout(self):
        """Updates the layout.

        Updates :
        - Socket position
        - Edges connected to the node
        - Position of the handle to resize
        - Size of the content
        """
        self.updateSocketAndEdges()
        self.updateHandles()
        self.setContentGeometry()

    def print(self, *args):
        if DEBUG:
            print('>GraphicsNode : ', *args)

    def boundingRect(self):
        # Return rectangle for selection detection
        rect = self.rect()
        x, y = rect.left(), rect.top()
        return QRectF(x, y, self.width, self.height)

    def paint(self,
              painter: QPainter,
              option: 'QStyleOptionGraphicsItem',
              widget: Optional[QWidget] = ...) -> None:
        x = y = 0

        path_title = QPainterPath()
        path_title.setFillRule(Qt.WindingFill)

        # Rectangle for title
        path_title.addRoundedRect(x, y, self.width, self.title_height,
                                  self.edge_roundness, self.edge_roundness)
        path_title.addRect(x, y + self.title_height - self.edge_roundness,
                           self.edge_roundness, self.edge_roundness)
        path_title.addRect(x + self.width - self.edge_roundness,
                           y + self.title_height - self.edge_roundness,
                           self.edge_roundness, self.edge_roundness)
        painter.setPen(Qt.NoPen)
        painter.setBrush(self._brush_title)
        painter.drawPath(path_title.simplified())

        # content
        path_content = QPainterPath()
        path_content.setFillRule(Qt.WindingFill)
        path_content.addRoundedRect(x, y + self.title_height, self.width,
                                    self.height - self.title_height,
                                    self.edge_roundness, self.edge_roundness)
        path_content.addRect(x, y + self.title_height, self.edge_roundness,
                             self.edge_roundness)
        path_content.addRect(x + self.width - self.edge_roundness,
                             y + self.title_height, self.edge_roundness,
                             self.edge_roundness)
        painter.setPen(Qt.NoPen)
        painter.setBrush(self._brush_background)
        painter.drawPath(path_content.simplified())

        # outline
        path_outline = QPainterPath()
        path_outline.addRoundedRect(x, y, self.width, self.height,
                                    self.edge_roundness, self.edge_roundness)
        painter.setBrush(Qt.NoBrush)
        if self._hovered:
            painter.setPen(self._pen_hovered)
            painter.drawPath(path_outline.simplified())
            painter.setPen(self._pen_default)
            painter.drawPath(path_outline.simplified())
        else:
            painter.setPen(self._pen_default
                           if not self.isSelected() else self._pen_selected)
            painter.drawPath(path_outline.simplified())
예제 #16
0
class QDMGraphicsNode(QGraphicsItem):
    def __init__(self, node, parent=None):
        super().__init__(parent)

        self.node = node
        self.content = self.node.content

        self._title_color = Qt.white
        self._title_font = QFont("Arial", 12)

        self.width = 180
        self.height = 240
        self.edge_size = 10
        self.title_height = 24
        self._padding = 4

        self._pen_default = QPen(QColor(self.get_outline_color()))
        self._pen_selected = QPen(QColor("#e18b32"))
        self._title_brush = QBrush(QColor(self.get_title_color()))
        self._background_brush = QBrush(QColor(self.get_background_color()))

        self.initTitle()
        self.title = self.node.title

        # init content
        self.initContent()

        # init sockets
        #self.initSockets()

        self.setFlag(QGraphicsItem.ItemIsFocusable, True)

        self.initUI()

    def boundingRect(self):
        return QRectF(
            0,
            0,
            self.width,
            self.height
        ).normalized()

    def initUI(self):
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsMovable)

    def initTitle(self):
        self.title_item = QGraphicsTextItem(self)
        self.title_item.setDefaultTextColor(self._title_color)
        self.title_item.setFont(self._title_font)
        self.title_item.setTextWidth(self.width - 2 * self._padding)
        self.title_item.setX(self._padding)

    def get_title_color(self):
        return "#954a4a"

    def get_background_color(self):
        return "#2b2b2b"

    def get_outline_color(self):
        return "#181818"

    def initSockets(self):
        self.content.drawSocketItems()

    def initContent(self):
        self.grContent = QGraphicsProxyWidget(self)
        self.content.setGeometry(self.edge_size, self.title_height + self.edge_size,
                                 self.width - 2 * self.edge_size, self.height - 2 * self.edge_size - self.title_height)
        self.grContent.setWidget(self.content)

    @property
    def title(self): return self._title
    @title.setter
    def title(self, value):
        self._title = value
        self.title_item.setPlainText(self._title)

    def paint(self, painter, option, widget=None):
        # title
        path_title = QPainterPath()
        path_title.setFillRule(Qt.WindingFill)
        path_title.addRoundedRect(0, 0, self.width, self.title_height, self.edge_size, self.edge_size)
        path_title.addRect(0, self.title_height - self.edge_size, self.edge_size, self.edge_size)
        path_title.addRect(self.width - self.edge_size, self.title_height - self.edge_size,
                           self.edge_size, self.edge_size)
        painter.setPen(Qt.NoPen)
        painter.setBrush(self._title_brush)
        painter.drawPath(path_title.simplified())

        # content
        path_content = QPainterPath()
        path_content.setFillRule(Qt.WindingFill)
        path_content.addRoundedRect(0, self.title_height, self.width, self.height - self.title_height,
                                    self.edge_size, self.edge_size)
        path_content.addRect(0, self.title_height, self.edge_size, self.edge_size)
        path_content.addRect(self.width - self.edge_size, self.title_height, self.edge_size, self.edge_size)
        painter.setPen(Qt.NoPen)
        painter.setBrush(self._background_brush)
        painter.drawPath(path_content.simplified())

        # outline
        path_outline = QPainterPath()
        path_outline.addRoundedRect(0, 0, self.width, self.height, self.edge_size, self.edge_size)
        painter.setPen(self._pen_default if not self.isSelected() else self._pen_selected)
        painter.setBrush(Qt.NoBrush)
        painter.drawPath(path_outline.simplified())

    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        self.node.update_linked_sockets()

    def keyReleaseEvent(self, event):
        super().keyReleaseEvent(event)
        if not self.hasFocus():
            return
        if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete:
            self.node.scene.removeNode(self.node)
    def initdetailgroup(self):
        view = QGraphicsView(parent=self)
        brush = QBrush(QColor(242, 242, 242))
        view.setBackgroundBrush(brush)
        view.setFrameStyle(16)  # QFrame.Plain

        def clickEventHandler(event):
            self.detailFigure_2Clicked.emit()

        detailFigure_1 = QGraphicsPixmapItem(
            QPixmap(cwd + '/guiunits/imags/pon56gdemo/detailfigure_1.png'))
        detailFigure_2_Qobj = fadingPic(
            QPixmap(cwd + '/guiunits/imags/pon56gdemo/detailfigure_2.png'))
        detailFigure_2 = detailFigure_2_Qobj.pixmap_item
        detailFigure_2_title = detailFigure_2_Qobj.text_item
        detailFigure_1.mousePressEvent = clickEventHandler
        title = QGraphicsTextItem("Our Innovation/Contribution")
        font = QFont("Nokia Pure Text Light", 25, QFont.Bold)
        title.setFont(font)
        title.setDefaultTextColor(self.nokia_blue)

        textItem1 = QGraphicsTextItem()
        textItem1.setHtml(
            '''<body style="font-family:Nokia Pure Text Light;color:#124191;font-size:23px;">
                          <div >10GHz</div>
                          <div > Optics </div>
                          </body>''')
        textItem1.setTextWidth(80)
        textItem2 = QGraphicsTextItem()
        textItem2.setHtml(
            '''<body style="font-family:Nokia Pure Text Light;color:#124191;font-size:23px;">
                          <div > 10GHz</div>
                          <div > Optics </div>
                          </body>''')
        textItem2.setTextWidth(100)

        fan = Fan()  # a QObject which wraps a QGraphicsItem inside

        scene = QGraphicsScene()
        scene.setSceneRect(0, 0, 1285, 420)
        scene.addItem(detailFigure_2)
        scene.addItem(detailFigure_1)
        scene.addItem(detailFigure_2_title)
        scene.addItem(textItem1)
        scene.addItem(textItem2)
        scene.addItem(title)
        scene.addItem(fan.pixmap_item)

        detailFigure_1.setPos(QPointF(35, 88))
        detailFigure_2.setPos(QPointF(570, 96))
        detailFigure_2.setOpacity(0)  # hided at first
        detailFigure_2_title.setPos(QPointF(750, 46))
        detailFigure_2_title.setOpacity(0)
        title.setPos(QPointF(50, 20))
        textItem1.setPos(QPointF(40, 168))
        textItem2.setPos(QPointF(361, 168))
        fan.pixmap_item.setPos(QPointF(456.5, 138))
        self.fanAnim = fan.fanAnimation()

        view.setScene(scene)
        view.setSceneRect(0, 0, 1285, 420)
        view.setAlignment(Qt.AlignLeft | Qt.AlignTop)
        view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        view.setRenderHint(QPainter.Antialiasing)

        self.detailGrpTextItem1 = textItem1
        self.detailGrpTextItem2 = textItem2
        self.detailFigTitle = title
        self.detailFigure_2_title = detailFigure_2_title
        self.turbofan = fan
        self.NNfigure_fadeIn = detailFigure_2_Qobj.fadeIn()
        self.NNfigure_fadeOut = detailFigure_2_Qobj.fadeOut()
        self._detailFigure_2_state = 0  # 0-hided, 1-showed
        return view
예제 #18
0
class Node(QGraphicsItemGroup):
    """
    Parent class for all types of nodes.

    It contains all necessary functions to print a node in the GUI
    """

    def __init__(self, node, parent, background, border, text, x=0, y=0, offset=20):
        """
        Constructor for the node class.
        It generates all necessary variables and calls the draw function

        :param node: data node which it gets the data from
        :param parent: parent widget
        :param background: background color of the node
        :param border: border color for the node
        :param text: text color for the node
        :param x: x-position of the node
        :param y: y-position of the node
        :param offset: offset for the type to center it
        """
        super().__init__()

        self.typeOffset = offset

        self.node = node

        self.parent = parent

        self.threatEdge = None
        self.counterEdge = None
        self.defaultEdge = None

        self.childEdges = []
        self.parentEdges = []

        self.headerGroup = QGraphicsItemGroup()
        self.attributes = QGraphicsItemGroup()
        self.footerGroup = QGraphicsItemGroup()

        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)

        self.attributesHeight = 0
        self.headerHeight = 0

        self.printHeader(background, border, text)
        self.printAttributes(background, border, text)
        self.printFooter(background, border, text)

        self.setAcceptDrops(True)

        self.setPos(x, y)

    def getTypeRecursiveDown(self):
        """
        Searches the children of a node to get a node with type != Conjunction
        If there is no other node with type != Conjunction, Conjunction will be returned
        :return: type of first child node with type != Conjunction or Conjunction
        """
        if isinstance(self, Conjunction):
            for c in self.childEdges:
                if isinstance(c.dst, Conjunction):
                    return c.dst.getTypeRecursiveDown()
                else:
                    return type(c.dst)
            return type(self)
        else:
            return type(self)

    def getTypeRecursiveUp(self):
        """
        Searches the parents of a node to get a node with type != Conjunction
        If there is no other node with type != Conjunction, Conjunction will be returned
        :return: type of first parent node with type != Conjunction or Conjunction
        """
        if isinstance(self, Conjunction):
            for c in self.childEdges:
                if isinstance(c.dst, Conjunction):
                    return c.dst.getTypeRecursiveUp()
                else:
                    return type(c.dst)
            return type(self)
        else:
            return type(self)

    def addEdge(self, dst):
        """
        Adds an child edge to this node an places the start of the arrow in the right place
        :param dst: destination node for the edge
        """
        if isinstance(self, Threat) and dst.getTypeRecursiveDown() is Threat:
            edge = Edge(self, dst, -50)
        elif isinstance(self, Threat) and dst.getTypeRecursiveDown() is Countermeasure:
            edge = Edge(self, dst, 50)
        else:
            edge = Edge(self, dst, 0)
        self.parent.scene.addItem(edge)
        self.childEdges.append(edge)
        dst.parentEdges.append(self.childEdges[-1])

    def actualizeEdges(self):
        """
        Actualizes all child edges of this node so they start at the right position
        """
        for e in self.childEdges:
            if isinstance(self, Threat) and e.dst.getTypeRecursiveDown() is Threat:
                e.offset = -50
            elif isinstance(self, Threat) and e.dst.getTypeRecursiveDown() is Countermeasure:
                e.offset = 50
            else:
                e.offset = 0

    def fixParentEdgeRec(self):
        """
        Fixes all starts of the parent edges so they start at the right position
        """
        for p in self.parentEdges:
            if isinstance(p.start, Conjunction):
                p.start.fixParentEdgeRec()
            else:
                p.start.actualizeEdges()

    def getLeftRightChildren(self):
        """
        Splits the children in to arrays with the same size
        :return: Tuple (left, right) with child elements split in to arrays
        """
        left = []
        right = []
        neutralLeft = False
        for e in self.childEdges:
            if e.offset < 0:
                left.append(e.dst)
            elif e.offset > 0:
                right.append(e.dst)
            else:
                if neutralLeft is False:
                    left.append(e.dst)
                    neutralLeft = True
                else:
                    right.append(e.dst)
                    neutralLeft = False
        return left, right

    def printHeader(self, background, border, text):
        """
        Prints the the header of the node.
        It contains the Node id, title and type

        :param background: background color of the node
        :param border: border color for the node
        :param text: text color for the node
        """
        x = self.x()
        y = self.y()

        self.idText = QGraphicsTextItem()
        self.typeText = QGraphicsTextItem()
        self.titleText = QGraphicsTextItem()

        self.idRect = QGraphicsRectItem()
        self.typeRect = QGraphicsRectItem()
        self.titleRect = QGraphicsRectItem()

        self.typeText.setFont(Configuration.font)
        self.titleText.setFont(Configuration.font)
        self.idText.setFont(Configuration.font)

        self.typeText.setDefaultTextColor(QColor(text))
        self.titleText.setDefaultTextColor(QColor(text))
        self.idText.setDefaultTextColor(QColor(text))

        self.titleText.setTextWidth(200)

        self.idText.setPlainText(self.node.id)
        self.typeText.setPlainText(type(self.node).__name__)
        self.titleText.setPlainText(self.node.title)

        titleHeight = int(self.titleText.boundingRect().height() / 20 + 0.5) * 20

        self.idRect.setRect(x, y, 50, 20)
        self.typeRect.setRect(x + 50, y, 150, 20)
        self.titleRect.setRect(x, y + 20, 200, titleHeight)

        self.idRect.setBrush(QBrush(QColor(background)))
        self.typeRect.setBrush(QBrush(QColor(background)))
        self.titleRect.setBrush(QBrush(QColor(background)))

        self.idRect.setPen(QPen(QColor(border), 2))
        self.typeRect.setPen(QPen(QColor(border), 2))
        self.titleRect.setPen(QPen(QColor(border), 2))

        self.idText.setPos(x, y - 2)
        self.typeText.setPos(x + self.typeOffset, y - 2)
        self.titleText.setPos(x, y + 18)

        self.headerHeight = titleHeight + 20

        self.setToolTip(self.node.description)

        self.headerGroup.addToGroup(self.idRect)
        self.headerGroup.addToGroup(self.typeRect)
        self.headerGroup.addToGroup(self.titleRect)
        self.headerGroup.addToGroup(self.idText)
        self.headerGroup.addToGroup(self.typeText)
        self.headerGroup.addToGroup(self.titleText)
        self.addToGroup(self.headerGroup)

    def printAttributes(self, background, border, text):
        """
        Prints the attributes of the node
        The attributes are a key, value pair

        :param background: background color of the node
        :param border: border color for the node
        :param text: text color for the node
        """
        y = self.y() + self.headerHeight
        x = self.x()

        self.attributesHeight = 0

        for k, v in self.node.attributes.items():
            key = QGraphicsTextItem()
            key.setFont(Configuration.font)
            key.setDefaultTextColor(QColor(text))
            key.setTextWidth(100)
            key.setPlainText(k)
            keyHeight = int(key.boundingRect().height() / 20 + 0.5) * 20

            value = QGraphicsTextItem()
            value.setFont(Configuration.font)
            value.setDefaultTextColor(QColor(text))
            value.setTextWidth(100)
            value.setPlainText(v)
            valueHeight = int(value.boundingRect().height() / 20 + 0.5) * 20

            height = valueHeight if valueHeight > keyHeight else keyHeight

            keyRect = QGraphicsRectItem()
            keyRect.setRect(x, y, 100, height)
            valueRect = QGraphicsRectItem()
            valueRect.setRect(x + 100, y, 100, height)

            keyRect.setBrush(QBrush(QColor(background)))
            valueRect.setBrush(QBrush(QColor(background)))

            keyRect.setPen(QPen(QColor(border), 2))
            valueRect.setPen(QPen(QColor(border), 2))

            key.setPos(x, y - 2)
            value.setPos(x + 100, y - 2)

            self.attributes.addToGroup(keyRect)
            self.attributes.addToGroup(valueRect)
            self.attributes.addToGroup(key)
            self.attributes.addToGroup(value)

            y = y + height
            self.attributesHeight += height

        self.addToGroup(self.attributes)

    def redrawOptions(self, background, border, text):
        """
        Redraws the node with option for the background, border and text color

        :param background: background color of the node
        :param border: border color for the node
        :param text: text color for the node
        """
        y = self.y()
        x = self.x()

        self.prepareGeometryChange()

        for i in self.attributes.childItems():
            self.attributes.removeFromGroup(i)
            self.parent.scene.removeItem(i)

        for i in self.headerGroup.childItems():
            self.headerGroup.removeFromGroup(i)
            self.parent.scene.removeItem(i)

        for i in self.footerGroup.childItems():
            self.footerGroup.removeFromGroup(i)
            self.parent.scene.removeItem(i)

        self.removeFromGroup(self.attributes)
        self.removeFromGroup(self.footerGroup)
        self.removeFromGroup(self.headerGroup)

        self.printHeader(background, border, text)

        self.printAttributes(background, border, text)

        self.printFooter(background, border, text)

        self.parent.scene.removeItem(self)
        self.parent.scene.addItem(self)

        self.setPos(x, y)

    def redraw(self):
        """
        Redraws the node with standard colors
        """
        self.redrawOptions(Qt.white, Qt.black, Qt.black)

    def printFooter(self, background, border, text):
        """
        Prototype function for the footer.
        Implemented in the child classes

        :param background: background color of the node
        :param border: border color for the node
        :param text: text color for the node
        """
        pass

    def setPos(self, x, y):
        """
        Overloads setPos to set the position of the visible node in the data node

        :param x: X part of the position
        :param y: Y part of the position
        """
        self.node.position = (x, y)
        super().setPos(x, y)

    def paint(self, painter, options, widget=None):
        """
        Reimplementation for the paint function of the QGraphicsItemGroup.
        The Reimplementation is needed to print a proper border when the item is selected

        :param painter: The painter, which draws the node
        :param options: options for the paint job
        :param widget: widget of the Item
        """
        myOption = QStyleOptionGraphicsItem(options)
        myOption.state &= ~QStyle.State_Selected

        super().paint(painter, myOption, widget=None)

        if options.state & QStyle.State_Selected:
            painter.setPen(QPen(Qt.black, 2, Qt.DotLine))
            rect = QRect(self.boundingRect().x() - 1.5, self.boundingRect().y() - 1.5,
                         self.boundingRect().x() + self.boundingRect().width() + 3,
                         self.boundingRect().y() + self.boundingRect().height() + 0.5)
            painter.drawRect(rect)

    def selectChildren(self):
        """
        Select all children
        """
        self.setSelected(True)
        for i in self.childEdges:
            i.setSelected(True)
            i.dst.selectChildren()

    def delete(self):
        """
        Deletes this node
        """
        for e in self.parentEdges:
            self.parent.scene.removeItem(e)
        for e in self.childEdges:
            self.parent.scene.removeItem(e)
        self.parent.tree.removeNode(self.node.id)
        self.parent.scene.removeItem(self)

    def edit(self):
        """
        Opens the edit dialog
        """
        NodeEdit(self, self.parent).exec()

    def mouseDoubleClickEvent(self, event):
        """
        Handles a double click on the node.
        The double click opens the edit window for this node

        :param event: click event
        """
        self.edit()

    def dropEvent(self, event):
        """
        Sets the correct position to the data node if the item is drag & dropped

        :param event: Drop event
        :return: Changed Value
        """

        print("ok")
        self.node.position = (event.pos().x(), event.pos().y())
        return super().dropEvent(self, event)
class QGraphicsResizableRectItem(QGraphicsRectItem):
    def __init__(self, min_height, min_width, *args):
        super().__init__(*args)
        # Diverse parameters for drawing

        self.handleSelected = None
        self.handles = {}
        self.min_width = min_width
        self.min_height = min_height
        self.initSizes()
        self.initContent()
        self.initAssets()
        self.initUI()
        self.initTitle()

    def initUI(self):
        # set flags
        self.setAcceptHoverEvents(True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, True)
        self.updateHandles()
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)

    def initContent(self):
        self.content = NodeContentWidget(None)
        self.grContent = QGraphicsProxyWidget(self)
        self.setContentGeometry()
        self.grContent.setWidget(self.content)

    def initAssets(self):
        self._title_color = Qt.white
        self._title_font = QFont('Ubuntu', 8)

        self._color = QColor("#7F00000")
        self._color_selected = QColor("#FFFFA637")
        self._color_hovered = QColor("#FF37A6FF")

        self._pen_default = QPen(self._color)
        self._pen_default.setWidthF(OUTLINE_WIDTH)
        self._pen_selected = QPen(self._color_selected)
        self._pen_selected.setWidthF(OUTLINE_WIDTH)
        self._pen_hovered = QPen(self._color_hovered)
        self._pen_hovered.setWidthF(OUTLINE_WIDTH + 1)

        self._brush_title = QBrush(QColor("#FF313131"))
        self._brush_background = QBrush(QColor("#E3212121"))

    def initSizes(self):
        # self.width = 180
        # self.height = 240

        # Diverse parameters for drawing
        self.handleSize = 5
        self.edge_roundness = 15.
        self.edge_padding = 10.
        self.title_height = 24
        self.title_horizontal_padding = 5.
        self.title_vertical_padding = 4.

    def setContentGeometry(self):
        self.content.setGeometry(
            self.edge_roundness, self.title_height + self.edge_roundness,
            self.width - 2 * self.edge_roundness,
            self.height - 2 * self.edge_roundness - self.title_height)

    def initTitle(self):
        # Draw the _title
        self._title_color = Qt.white
        self._title_font = QFont('Ubuntu', 10)
        self._padding = 5.
        self.title_height = 24
        self.title_item = QGraphicsTextItem(self)
        # self.title_item.node = self.node
        self.title_item.setDefaultTextColor(self._title_color)
        self.title_item.setFont(self._title_font)
        self.title_item.setPos(self._padding, 0)
        self.title_item.setTextWidth(self.width - 2 * self._padding)
        self.title_item.setPlainText('Resizeable node')

    @property
    def height(self):
        return self.rect().height()

    @property
    def width(self):
        return self.rect().width()

    def updateHandles(self):
        rect = self.boundingRect()
        left, width, top, height = rect.left(), rect.width(), rect.top(
        ), rect.height()
        offset = self.handleSize
        self.handles[Handle.TopLeft] = QRectF(left, top, offset, offset)
        self.handles[Handle.TopMiddle] = QRectF(left + offset, top,
                                                width - 2 * offset, offset)
        self.handles[Handle.TopRight] = QRectF(left + width - offset, top,
                                               offset, offset)
        self.handles[Handle.BottomLeft] = QRectF(left, top + height - offset,
                                                 offset, offset)
        self.handles[Handle.MiddleLeft] = QRectF(left, top + offset, offset,
                                                 height - 2 * offset)
        self.handles[Handle.BottomRight] = QRectF(left + width - offset,
                                                  top + height - offset,
                                                  offset, offset)
        self.handles[Handle.MiddleRight] = QRectF(left + width - offset,
                                                  top + offset, offset,
                                                  height - 2 * offset)
        self.handles[Handle.BottomMiddle] = QRectF(left + offset,
                                                   top + height - offset,
                                                   width - 2 * offset, offset)

    def boundingRect(self):
        # Return rectangle for selection detection
        return self.rect().normalized()

    def handleAt(self, point):
        for handle, rect in self.handles.items():
            if rect.contains(point):
                if DEBUG: print(handle, rect)
                return handle
        else:
            return None

    def hoverMoveEvent(self, event: 'QGraphicsSceneHoverEvent') -> None:
        # if self.isSelected():
        handle = self.handleAt(event.pos())
        if handle is not None:
            self.setCursor(handleCursors[handle])
        else:
            self.setCursor(Qt.ArrowCursor)
        super().hoverMoveEvent(event)

    def hoverLeaveEvent(self, event: 'QGraphicsSceneHoverEvent') -> None:
        # if self.isSelected():
        self.setCursor(Qt.ArrowCursor)
        super().hoverLeaveEvent(event)

    def mousePressEvent(self, event: 'QGraphicsSceneMouseEvent') -> None:
        """
        Executed when the mouse is pressed on the item.
        """
        try:
            self.handleSelected = self.handleAt(event.pos())
            if self.handleSelected:
                # record the position where the mouse was pressed
                self.currentPos = event.pos()
                # current rectangle at mouse pressed
                self.currentRect = self.boundingRect()

            super().mousePressEvent(event)
        except Exception as e:
            print(e)

    def mouseReleaseEvent(self, event: 'QGraphicsSceneMouseEvent') -> None:
        """
        Executed when the mouse is released from the item.
        """
        super().mouseReleaseEvent(event)
        self.handleSelected = None
        self.currentPos = None
        self.currentRect = None
        self.update()

    def mouseMoveEvent(self, event: 'QGraphicsSceneMouseEvent') -> None:
        """
        Executed when the mouse is being moved over the item while being pressed.
        """
        if self.handleSelected is not None:
            self.resize(event.pos())
        else:
            super().mouseMoveEvent(event)

    def resize(self, pos):
        """Update rectangle and bounding rectangle"""
        rect = self.rect()
        boundingRect = self.boundingRect()
        from_left = self.currentRect.left()
        from_right = self.currentRect.right()
        from_top = self.currentRect.top()
        from_bottom = self.currentRect.bottom()
        to_left = from_left + pos.x() - self.currentPos.x()
        to_right = from_right + pos.x() - self.currentPos.x()
        to_top = from_top + pos.y() - self.currentPos.y()
        to_bottom = from_bottom + pos.y() - self.currentPos.y()

        self.prepareGeometryChange()
        update_left, update_top, update_right, update_bottom = handleUpdate[
            self.handleSelected]
        if update_left:
            if from_right - to_left <= self.min_width:
                boundingRect.setLeft(from_right - self.min_width)
            else:
                boundingRect.setLeft(to_left)
            rect.setLeft(boundingRect.left())
        if update_top:
            if from_bottom - to_top <= self.min_height:
                boundingRect.setTop(from_bottom - self.min_height)
            else:
                boundingRect.setTop(to_top)
            rect.setTop(boundingRect.top())
        if update_bottom:
            if to_bottom - from_top <= self.min_height:
                boundingRect.setBottom(from_top + self.min_height)
            else:
                boundingRect.setBottom(to_bottom)
            rect.setBottom(boundingRect.bottom())
        if update_right:
            if to_right - from_left <= self.min_width:
                boundingRect.setRight(from_left + self.min_width)
            else:
                boundingRect.setRight(to_right)
            rect.setRight(boundingRect.right())

        self.setRect(rect)
        self.updateHandles()
        self.setContentGeometry()

    def shape(self):
        """
        Returns the shape of this item as a QPainterPath in local coordinates.
        """
        path = QPainterPath()
        # path.addRoundedRect(self.rect(), self.edge_size, self.edge_size)
        path.addRect(self.rect())
        return path

    def paint(self,
              painter: QPainter,
              option: 'QStyleOptionGraphicsItem',
              widget: Optional[QWidget] = ...) -> None:
        # content
        rect = self.rect()
        path_content = QPainterPath()
        path_content.setFillRule(Qt.WindingFill)
        path_content.addRoundedRect(rect, self.edge_roundness,
                                    self.edge_roundness)

        painter.setPen(Qt.NoPen)
        painter.setBrush(self._brush_background)
        painter.drawPath(path_content.simplified())

        # outline
        path_outline = QPainterPath()
        path_outline.addRoundedRect(rect, self.edge_roundness,
                                    self.edge_roundness)
        painter.setPen(
            self._pen_default if not self.isSelected() else self._pen_selected)
        painter.setBrush(Qt.NoBrush)
        painter.drawPath(path_outline.simplified())

        for handle in self.handles.values():

            path_handle = QPainterPath()
            path_handle.addRect(handle)
            painter.drawPath(path_handle)
예제 #20
0
class ItemConfigDialog(QMainWindow, Ui_MainWindow):
    """
    Окно настроек элемента
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setupUi(self)

        self.text_aligns = [
            ('center', 'По центру'),
            ('left', 'По левому краю'),
            ('right', 'По правому краю'),
        ]
        self.text_align = 'center'

        for align, align_text in self.text_aligns:
            self.textPosH.addItem(align_text)

        self.item: QGraphicsRectItem = QGraphicsRectItem()
        self.main_view: QGraphicsView = QGraphicsView()

        self.fontSelect.currentFontChanged.connect(self.render_text)
        self.fontSize.valueChanged.connect(self.render_text)
        self.textPosH.currentTextChanged.connect(self.render_text)
        self.exText.textChanged.connect(self.render_text)

        self.font_color = QColor('black')

        self.scene = QGraphicsScene()
        self.graphicsView.setScene(self.scene)
        self.text = QGraphicsTextItem()

        self.buttonBox.accepted.connect(self.accepted)

        self.colorPicker.clicked.connect(self.select_color)

    def show(self):
        super(ItemConfigDialog, self).show()
        self.text = QGraphicsTextItem()
        self.name.setText(self.item.toolTip())
        font = QFont()
        font_name = self.item.data(int(const.ITEM_DATA_KEY_FONT))
        if font_name:
            font.fromString(font_name)
            font_size = self.item.data(int(const.ITEM_DATA_KEY_FONT_SIZE))
            if font_size:
                font.setPointSize(font_size)
            else:
                font.setPointSize(12)
            font_color = self.item.data(int(const.ITEM_DATA_KEY_FONT_COLOR))
            if font_color:
                self.font_color.setRgb(*font_color)
            self.text.setFont(font)
        self.text_align = self.item.data(int(const.ITEM_DATA_KEY_FONT_ALIGN))

        self.render_text()
        self.render_item()

    def select_color(self):
        color = QColorDialog.getColor()

        if color.isValid():
            self.font_color = color
            self.render_text()

    def render_text(self, event=None):
        font = self.fontSelect.currentFont()
        font.setPointSize(self.fontSize.value())
        self.text.setFont(font)
        self.text.setTextWidth(self.item.rect().width())
        self.text.setDefaultTextColor(self.font_color)
        for align, align_text in self.text_aligns:
            if align_text == self.textPosH.currentText():
                self.text_align = align

        self.text.setHtml(f"<div align='{self.text_align}'>{self.exText.text()}</div>")

    def render_item(self):
        self.scene.clear()
        pos_from = self.main_view.mapFromScene(self.item.sceneBoundingRect())
        pos = self.main_view.mapToScene(pos_from)
        point = pos.boundingRect()
        width, height = point.width(), point.height()

        img = QImage(width, height, QImage.Format_ARGB32_Premultiplied)
        painter = QPainter(img)
        painter.setRenderHint(QPainter.Antialiasing)
        self.item.scene().render(painter, source=QRectF(point.x(), point.y(), width, height))
        painter.end()

        self.scene.addItem(QGraphicsPixmapItem(QPixmap(img)))
        self.scene.addItem(self.text)
        self.graphicsView.fitInView(QRectF(0, 0, width, height), Qt.KeepAspectRatio)

        self.scene.update()

    def accepted(self):
        self.item.setData(int(const.ITEM_DATA_KEY_FONT), self.text.font().toString().split(',')[0])
        self.item.setData(int(const.ITEM_DATA_KEY_FONT_SIZE), self.text.font().pointSize())
        self.item.setData(int(const.ITEM_DATA_KEY_FONT_ALIGN), self.text_align)
        self.item.setData(int(const.ITEM_DATA_KEY_FONT_COLOR), self.font_color.getRgb())
        self.item.setToolTip(self.name.text())
        self.item.scene().update()
        self.close()