-
Allen, Bruce (CIV) authoredAllen, Bruce (CIV) authored
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])