-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsvgBox.py
197 lines (159 loc) · 8.43 KB
/
svgBox.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
from nicegui import ui
from GraphicMaker.main import SVG, Circle, Rectangle, Text, Group, Line, Polygon, PolyLine, Oval
from xml.etree import ElementTree as ET
def parse_svg(filename):
tree = ET.parse(filename)
root = tree.getroot()
return root
def generate_svg_from_element(element, indent=0 ):
if element.tag == "{http://www.w3.org/2000/svg}svg":
width = float(element.attrib.get("width", 0))
height = float(element.attrib.get("height", 0))
svg = SVG(width, height, viewBox={"x":0, "y":0, "width":width, "height":height})
with svg:
for child in element:
children = generate_svg_from_element(child, indent+1)
svg.add_child(children)
return svg
if element.tag == "{http://www.w3.org/2000/svg}circle":
x = float(element.attrib.get("cx", 0))
y = float(element.attrib.get("cy", 0))
r = float(element.attrib.get("r", 0))
fill = element.attrib.get("fill", "black")
return Circle(x, y, r, fill=fill)
if element.tag == "{http://www.w3.org/2000/svg}rect":
x = float(element.attrib.get("x", 0))
y = float(element.attrib.get("y", 0))
width = float(element.attrib.get("width", 0))
height = float(element.attrib.get("height", 0))
fill = element.attrib.get("fill", "black")
return Rectangle(x, y, width, height, fill=fill)
if element.tag == "{http://www.w3.org/2000/svg}line": # <line x1="300" y1="50" x2="400" y2="100" stroke="orange" stroke-width="4"/>
x1 = float(element.attrib.get("x1", 0))
y1 = float(element.attrib.get("y1", 0))
x2 = float(element.attrib.get("x2", 0))
y2 = float(element.attrib.get("y2", 0))
stroke = element.attrib.get("stroke", "black")
stroke_width = float(element.attrib.get("stroke-width", 1))
return Line(x1, y1, x2, y2, stroke=stroke, stroke_width=stroke_width)
if element.tag == "{http://www.w3.org/2000/svg}polygon":
points = element.attrib.get("points", "")
fill = element.attrib.get("fill", "black")
return Polygon(points, fill=fill)
if element.tag == "{http://www.w3.org/2000/svg}polyline":
points = element.attrib.get("points", "")
fill = element.attrib.get("fill", "black")
return PolyLine(points, fill=fill)
if element.tag == "{http://www.w3.org/2000/svg}ellipse":
cx = float(element.attrib.get("cx", 0))
cy = float(element.attrib.get("cy", 0))
rx = float(element.attrib.get("rx", 0))
ry = float(element.attrib.get("ry", 0))
fill = element.attrib.get("fill", "black")
return Oval(cx, cy, rx, ry, fill=fill)
if element.tag == "{http://www.w3.org/2000/svg}g":
x = float(element.attrib.get("x", 0))
y = float(element.attrib.get("y", 0))
group = Group(x, y)
with group:
for child in element:
# print(child.tag)
children = generate_svg_from_element(child, indent+1)
group.add_child(children)
return group
if element.tag == "{http://www.w3.org/2000/svg}text":
x = float(element.attrib.get("x", 0))
y = float(element.attrib.get("y", 0))
font_size = float(element.attrib.get("font-size", 12))
text = str(element.text)
return Text(x, y, text, font_size=font_size)
root = parse_svg("svgSample.svg")
svg_instance = generate_svg_from_element(root)
svg_instance.set_visibility(False)
class SVGBox:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.panning = False
self.scale = 1
self.right_drawer = ui.right_drawer()
self.viewBox = {"x":0, "y":0, "width":1000, "height":1000}
ui.button("Export SVG", on_click=self.export_svg)
with ui.row() as row: #.style("width: 100vw; height: 60vh; border: 1px solid black;") as row:
row.on("wheel", self.on_scroll)
row.on("mousedown", lambda e : setattr(self, "panning", True))
row.on("mouseup", lambda e : setattr(self, "panning", False))
row.on("mousemove", self.on_mousemove)
self.svg = SVG(1000, 1000, self.viewBox).style("border: 1px solid black; width: 100%; height: 100%;")
with self.svg:
self.create_svg_tree(svg_instance, 0, 0)
def on_mousemove(self, e):
e.args['preventDefault'] = False
if self.panning:
dx = e.args['movementX']
dy = e.args['movementY']
self.viewBox = {"x": self.viewBox["x"] - dx * self.scale, "y": self.viewBox["y"] - dy * self.scale, "width": self.viewBox["width"], "height": self.viewBox["height"]}
self.svg._props["viewBox"] = " ".join(([str(val) for val in self.viewBox.values()]))
self.svg.update()
def on_scroll(self, e):
width = self.viewBox["width"]
height = self.viewBox["height"]
mx = e.args['offsetX']
my = e.args['offsetY']
dw = width * 0.05 * (1 if e.args['deltaY'] > 0 else -1)
dh = height * 0.05 * (1 if e.args['deltaY'] > 0 else -1)
dx = dw * mx / 1000
dy = dh * my / 1000
self.viewBox = {"x": self.viewBox["x"] + dx, "y": self.viewBox["y"] + dy, "width": self.viewBox["width"] - dw, "height": self.viewBox["height"] - dh}
self.scale = 1000 / self.viewBox["width"]
self.svg._props["viewBox"] = " ".join(([str(val) for val in self.viewBox.values()]))
self.svg.update()
def export_svg(self):
print(svg_instance.__str__())
def create_svg_tree(self, node, indent, depth):
if type(node) == SVG:
group = Group(20 + indent*20, 20 + depth*30)
group.on("svg:pointerdown", lambda : self.open_sidedrawer(node), throttle=0.1)
with group:
Circle(20 + indent*20, 20 + depth*30, 20, fill="red", opacity=0.5)#.on("svg:pointerdown", lambda : open_sidedrawer(node), throttle=0.1)
Text(5 +indent*20, 20 + depth*30, "SVG", font_size=10, centered=True)#.on("svg:pointerdown", lambda : open_sidedrawer(node), throttle=0.1)
prev_depth = depth
for index, child in enumerate(node.children):
shapes, prev_depth = self.create_svg_tree(child, indent+1, prev_depth+1+index)
return group
#if it's a group, we must recurse
elif type(node) == Group:
group = Group(20 + indent*20, 20 + depth*30)
group.on("svg:pointerdown", lambda : self.open_sidedrawer(node), throttle=0.1)
with group:
Circle(20 + indent*20, 20 + depth*30, 20, fill="red", opacity=0.5)# .on("svg:pointerdown", lambda : open_sidedrawer(node), throttle=0.1)
Text(5 +indent*20, 20 + depth*30, "Group", font_size=10, centered=True)# .on("svg:pointerdown", lambda : open_sidedrawer(node), throttle=0.1)
for index, child in enumerate(node.children):
self.create_svg_tree(child, indent+1, depth+1+index)
return group, depth+len(node.children) - 1
#Case where it's not a group (ie, circle, elipse, rect, etc)
else:
group = Group(20 + indent*20, 20 + depth*30)
group.on("svg:pointerdown", lambda : self.open_sidedrawer(node), throttle=0.1)
with group:
Rectangle(indent*20, 5+ depth*30, 60, 30, fill="red", opacity=0.5)#.on("svg:pointerdown", lambda : open_sidedrawer(node), throttle=0.1)
Text(5 +indent*20, 20 + depth*30, str(node.__class__.__name__), font_size=10, centered=True)#.on("svg:pointerdown", lambda : open_sidedrawer(node), throttle=0.1)
return group, depth - 2
def update_props(self, node, prop, value):
node._props[prop] = value
setattr(node, prop, value)
node.update()
svg_instance.update()
print(node._props)
def open_sidedrawer(self, node):
print(node._props)
self.right_drawer.clear()
self.right_drawer.toggle()
with self.right_drawer as drawer:
for prop in node._props:
with ui.row():
ui.label(prop)
ui.input("prop value", value=node._props[prop]).on("keydown.enter", lambda e, prop=prop : self.update_props(node, prop, e.sender.value))
ui.button("Close", on_click=lambda : drawer.toggle())