Skip to content
Snippets Groups Projects
graph.py 4.27 KiB
import random
from PyQt5.QtCore import QPoint, QRectF, Qt
from PyQt5.QtGui import QPainterPath
from PyQt5.QtGui import QFontMetrics
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QGraphicsItem
from next_graphics_index import next_graphics_index

# see also typical settings_manager values
TITLE_Y = 15
TITLE_SPACING = 25
GRAPH_W = 400
GRAPH_H = 400
NODE_W = 127
NODE_H = 17
H_SPACING = 165
V_SPACING = 55

def _xy(json_node):
    # node center point
    x = json_node[2]
    y = json_node[3] + TITLE_SPACING
    return x, y

def _xy_in(json_node):
    x, y = _xy(json_node)
    return x, y - NODE_H/2

def _xy_out(json_node):
    x, y = _xy(json_node)
    return x, y + NODE_H/2

def _add_node_path(json_node, path):
    x, y = _xy(json_node)
    w = NODE_W
    h = NODE_H
    path.addRect(x - w/2, y - h/2, w, h)

def _add_edge_path(from_json_node, to_json_node, path):
    from_x, from_y = _xy_out(from_json_node)
    to_x, to_y = _xy_in(to_json_node)
    path.moveTo(from_x, from_y)
    path.lineTo(to_x, to_y)
    center_x = (from_x + to_x)/2
    center_y = (from_y + to_y)/2
    return center_x, center_y


def _random_xy():
    x = random.randrange(int(NODE_W/2), int(GRAPH_W - NODE_W / 2))
    y = random.randrange(int(NODE_H/2) + TITLE_SPACING,
                         int(GRAPH_H - NODE_H / 2))
    return x, y

def _maybe_assign_xy(nodes):
    for node in nodes:
        if len(node) == 2:
            x, y = _random_xy()
        node.append(x)
        node.append(y)

class Graph(QGraphicsItem):
    Type = next_graphics_index()

    def __init__(self, json_data, default_x, default_y):
        super(Graph, self).__init__()

        # make graph deterministic
        random.seed(a=1)

        # graphicsItem mode
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setZValue(2)

        # json_data
        self._json_data = json_data # keep for export

        # x,y
        if json_data["x"] == 0 and json_data["y"] == 0:
            self.setPos(QPoint(default_x + NODE_W / 2, default_y))
        else:
            self.setPos(QPoint(json_data["x"], json_data["y"]))

        # graph data
        data = json_data["data"]

        # title, nodes, edges
        self.title = data[0]
        self.nodes = data[1]
        edges = data[2]
        _data3 = data[3] # ?

        # maybe assign coordinates to nodes
        _maybe_assign_xy(self.nodes)

        # indexed nodes for edges
        indexed_nodes = dict()

        # painter path
        self.painter_path = QPainterPath()

        # nodes
        for json_node in self.nodes:

            _add_node_path(json_node, self.painter_path)
            indexed_nodes[json_node[0]] = json_node

        # edges and edge labels
        self.edge_labels = list()
        for json_edge in edges:
            center_x, center_y = _add_edge_path(indexed_nodes[json_edge[0]],
                                                indexed_nodes[json_edge[1]],
                                                self.painter_path)
            self.edge_labels.append((center_x, center_y, json_edge[2]))

        # rectangle for mouse sense
        self.bounding_path = QPainterPath()
        self.bounding_path.addRect(QRectF(0,0,GRAPH_W, GRAPH_H))

    def json_data(self):
        self._json_data["x"] = self.x()
        self._json_data["y"] = self.y()
        return self._json_data
        
    def type(self):
        return Graph.Type

    # draw inside this rectangle
    def boundingRect(self):
        return self.bounding_path.boundingRect().adjusted(-1, -1, 1, 1)

    # mouse hovers when inside this rectangle
    def shape(self):
        return self.bounding_path

    def paint(self, painter, _option, _widget):

        w = NODE_W
        h = NODE_H

        # title
        painter.drawText(QRectF(0, TITLE_Y, GRAPH_W, h), Qt.AlignCenter,
                         self.title)

        # painter_path of nodes and edges
        painter.drawPath(self.painter_path)

        # node text
        for node in self.nodes:
            x, y = _xy(node)
            painter.drawText(QRectF(x - w/2, y - h/2, w, h),
                             Qt.AlignCenter, node[1])

        # edge text
        for x, y, label in self.edge_labels:
            painter.drawText(QRectF(x-w/2, y-w/2, w, h),
                             Qt.AlignCenter, node[1])