Skip to content

Commit dbe5791

Browse files
committed
Load graph function fully working.
1 parent 1e3b740 commit dbe5791

File tree

5 files changed

+154
-32
lines changed

5 files changed

+154
-32
lines changed

examples/RPN_test.kv

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,33 @@ RootWidget:
1313
AtomWidget:
1414
pos: [769.0, 892.0]
1515
color: .800, .800, .800
16-
atom_name: 'a'
16+
text: 'a'
1717
AtomWidget:
1818
pos: [1010.0, 891.0]
1919
color: .800, .800, .800
20-
atom_name: 'b'
20+
text: 'b'
2121
EllipseWidget:
2222
pos: [606.0, 326.0]
2323
color: .800, .800, .800
2424
size: [629.0, 501.0000000000001]
2525
AtomWidget:
2626
pos: [749.0, 695.0]
27-
atom_name: 'c'
27+
text: 'c'
2828
SquareWidget:
2929
pos: [728.0, 452.0]
3030
size: [411.0, 141.0]
3131
AtomWidget:
3232
pos: [791.0, 513.0]
3333
color: .800, .800, .800
34-
atom_name: 'd'
34+
text: 'd'
3535
AtomWidget:
3636
pos: [972.0, 506.0]
3737
color: .800, .800, .800
38-
atom_name: 'e'
38+
text: 'e'
3939
SquareWidget:
4040
pos: [974.0, 626.0]
4141
size: [140.0, 113.0]
4242
AtomWidget:
4343
pos: [1011.0, 670.0]
4444
color: .800, .800, .800
45-
atom_name: 'f'
45+
text: 'f'

src/asp_graph.py

+87-8
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,12 @@ def get_tree(self, depth):
296296
if (depth % 2) <> 0:
297297
strl.append(' '*4*(depth+1) + 'color: .800, .800, .800')
298298
if isinstance(self, AtomWidget):
299+
strl.append(' '*4*(depth+1) + 'text: \'{0}\''.format(self.text))
299300
strl.append(' '*4*(depth+1) +
300-
'atom_name: \'{0}\''.format(self.text))
301+
'line_info: {0}'.format(self.get_line_info_str()))
302+
elif isinstance(self, NexusWidget):
303+
strl.append(' '*4*(depth+1) +
304+
'line_info: {0}'.format(self.get_line_info_str()))
301305
else:
302306
for ch in self.children:
303307
strl.append(ch.get_tree(depth+1))
@@ -578,20 +582,45 @@ def test_all_collisions(cls, widget):
578582
def get_all_lines(cls):
579583
return tuple(cls._lines)
580584

581-
def __init__(self, hook):
585+
@classmethod
586+
def clear_lines(cls):
587+
while cls._lines:
588+
cls._lines[0].delete()
589+
590+
@classmethod
591+
def build_from_graph(cls, graph, hook_list):
592+
line = cls()
593+
594+
def _build(node, seen):
595+
not_seen = set(graph[node]) - seen
596+
hook0 = hook_list[node]
597+
hook0.line = line
598+
for n in not_seen:
599+
hook1 = hook_list[n]
600+
line._add_segment(hook0, hook1)
601+
hook1.line = line
602+
_build(n, seen.union({node}))
603+
604+
_build(0, set())
605+
line.hook_list = hook_list
606+
line.grabbed_segment = None
607+
line._updated = False
608+
609+
def __init__(self, hook=None):
582610
self.segment_list = []
583-
self.hook_list = [hook]
611+
self.hook_list = [hook] if hook is not None else None
584612
self.grabbed_segment = None
585-
self.grabbed_hook = hook
613+
self.grabbed_hook = hook if hook is not None else None
586614
self.line_id = len(Line._lines)
587-
self._add_segment(hook, hook)
615+
if hook is not None:
616+
self._add_segment(hook, hook)
588617
Line._lines.append(self)
589618
# Graph
590619
self._updated = True
591620
self._graph = {0: []}
592621
self._components = [[0]]
593622
# TODO: remove this attribute if not necessary
594-
self._containers = [hook.get_container()]
623+
self._containers = [hook.get_container()] if hook is not None else []
595624

596625
def _add_segment(self, hook0, hook1):
597626
s = Segment(hook0, hook1)
@@ -652,6 +681,12 @@ def _update_graph(self):
652681
# if key in hacl}
653682
# return Line.connected_components(container_graph)
654683

684+
def get_full_graph(self):
685+
graph = {}
686+
for i, hook in enumerate(self.hook_list):
687+
graph[i] = [self._get_hook_index(h) for h in self._get_connected_hooks(hook)]
688+
return graph
689+
655690
def get_segment_ids(self, hook):
656691
l = []
657692
for i, s in enumerate(self.segment_list):
@@ -897,6 +932,7 @@ class NexusWidget(HookWidget):
897932

898933
def __init__(self, **kwargs):
899934
super(NexusWidget, self).__init__(**kwargs)
935+
self.line_info = []
900936
# Init position correction
901937
self.pos = (self.pos[0] - self.width/2, self.pos[1] - self.height/2)
902938

@@ -981,6 +1017,10 @@ def get_as_text(self, connective='&'):
9811017
txt += '(' + first_var + ' = ' + var + ')'
9821018
return txt
9831019

1020+
def get_line_info_str(self):
1021+
return '[({0}, {1})]'.format(self.line.line_id,
1022+
self.line._get_hook_index(self))
1023+
9841024
class Atom(widget.Widget):
9851025

9861026
name = prop.StringProperty('')
@@ -1030,8 +1070,9 @@ def _deferred_init(self):
10301070
'''Special method to be called only when loading an AtomWidget from a
10311071
save file.
10321072
'''
1033-
# Only if self was loaded from a file, it will have an `atom_name` field
1034-
self.atom = NameManager.Instance().get(self.atom_name)
1073+
# Only if self was loaded from a file, it will have a valid text field
1074+
# equal to the name of the corresponding Atom
1075+
self.atom = NameManager.Instance().get(self.text)
10351076
self.text = self.atom.name
10361077
self.hook_points = self.atom.hook_points
10371078

@@ -1116,6 +1157,44 @@ def get_as_text(self):
11161157
txt += ')'
11171158
return txt
11181159

1160+
def get_line_info(self):
1161+
assert len(self.line_info) == len(filter(lambda x: x==True, self._hook_points))
1162+
i = 0
1163+
result = []
1164+
if self._hook_points[0]:
1165+
result.append((self.line_info[i][0], self.line_info[i][1],
1166+
self.ids['hook_left'].__self__))
1167+
i += 1
1168+
if self._hook_points[1]:
1169+
result.append((self.line_info[i][0], self.line_info[i][1],
1170+
self.ids['hook_right'].__self__))
1171+
i += 1
1172+
if self._hook_points[2]:
1173+
result.append((self.line_info[i][0], self.line_info[i][1],
1174+
self.ids['hook_top'].__self__))
1175+
i += 1
1176+
if self._hook_points[3]:
1177+
result.append((self.line_info[i][0], self.line_info[i][1],
1178+
self.ids['hook_bottom'].__self__))
1179+
i += 1
1180+
return result
1181+
1182+
def get_line_info_str(self):
1183+
result = []
1184+
if self._hook_points[0]:
1185+
hook = self.ids.hook_left.__self__
1186+
result.append((hook.line.line_id, hook.line._get_hook_index(hook)))
1187+
if self._hook_points[1]:
1188+
hook = self.ids.hook_right.__self__
1189+
result.append((hook.line.line_id, hook.line._get_hook_index(hook)))
1190+
if self._hook_points[2]:
1191+
hook = self.ids.hook_top.__self__
1192+
result.append((hook.line.line_id, hook.line._get_hook_index(hook)))
1193+
if self._hook_points[3]:
1194+
hook = self.ids.hook_bottom.__self__
1195+
result.append((hook.line.line_id, hook.line._get_hook_index(hook)))
1196+
return str(result)
1197+
11191198
class CustomLabel(label.Label):
11201199

11211200
def on_texture(self, instance, value):

src/main.kv

+2-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@
7878
# Width is computed by CustomLabel as a function of font_size and text,
7979
# both being determined by AtomWidget height and text, respectively.
8080

81-
text: 'atom'
81+
text: '__atom__'
82+
line_info: []
8283
width: customlabel.width
8384
height: 20
8485
CustomLabel:

src/main.py

+29-2
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
408408
self.show_load()
409409
elif keycode[1] == 'y':
410410
print self.name_manager.get_all()
411+
print asp.Line.get_all_lines()
411412
elif keycode[1] == 't':
412413
self.toggle_sidepanel()
413414
elif keycode[1] == 'tab':
@@ -428,9 +429,9 @@ def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
428429
return True
429430

430431
def on_resize(self, window, width, height):
431-
self.active_graph.size = self.active_graph.parent.size
432432
if self.show_sidepanel:
433433
self.ids.sidepanel.width = self.width * .15
434+
self.active_graph.size = self.ids.stencilview.size
434435

435436
def toggle_sidepanel(self):
436437
self.show_sidepanel = not self.show_sidepanel
@@ -514,8 +515,11 @@ def delete_atom(self):
514515
def clear_atoms(self):
515516
self.ids.name_list.clear_widgets()
516517
self.name_manager.clear()
518+
self.update_atom_editor('')
519+
asp.AtomWidget.active_atom = None
517520

518521
def new_graph(self):
522+
asp.Line.clear_lines()
519523
self.active_graph.delete_tree()
520524
self.clear_atoms()
521525
# TODO: Migrate to tab system
@@ -527,6 +531,7 @@ def new_graph(self):
527531

528532
def close_graph(self):
529533
if self.active_graph is not None:
534+
asp.Line.clear_lines()
530535
self.active_graph.delete_tree()
531536
self.active_graph.delete_root()
532537
self.clear_atoms()
@@ -585,11 +590,17 @@ def load(self, path, filename):
585590
self.close_graph()
586591

587592
f = os.path.join(path, filename[0])
593+
# Temporal line storage. Its contents are arranged as follows:
594+
# { line_id: (graph, hook_list) , ... }
595+
lines = {}
588596
with open(f, 'r') as stream:
589597
for line in stream:
590598
if line.startswith(NameParser.TOKENS['name']):
591-
name, hooks = NameParser.parse_line(line)
599+
name, hooks = NameParser.parse_name(line)
592600
self.register_atom(name, hooks)
601+
if line.startswith(NameParser.TOKENS['line']):
602+
line_id, graph = NameParser.parse_line(line)
603+
lines[line_id] = (graph, [None] * len(graph))
593604

594605
new_graph = lang.Builder.load_file(f)
595606
self.ids.stencilview.add_widget(new_graph)
@@ -599,6 +610,19 @@ def load(self, path, filename):
599610
for w in self.active_graph.walk(restrict=True):
600611
if isinstance(w, asp.AtomWidget):
601612
w._deferred_init()
613+
for i in w.get_line_info():
614+
line_id = i[0]
615+
hook_index = i[1]
616+
lines[line_id][1][hook_index] = i[2]
617+
elif isinstance(w, asp.NexusWidget):
618+
for i in w.line_info:
619+
line_id = i[0]
620+
hook_index = i[1]
621+
lines[line_id][1][hook_index] = w
622+
623+
for line, info in lines.iteritems():
624+
print line, info
625+
asp.Line.build_from_graph(info[0], info[1])
602626

603627
self.set_mode(self.modestr)
604628
self.set_item(self.itemstr)
@@ -609,6 +633,9 @@ def save(self, path, filename):
609633
stream.write('#:kivy 1.0.9\n\n')
610634
for (name, atom) in self.name_manager.get_all():
611635
stream.write(NameParser.get_name_str(name, atom.hook_points))
636+
for line in asp.Line.get_all_lines():
637+
stream.write(NameParser.get_line_str(line.line_id,
638+
line.get_full_graph()))
612639
stream.write('\n')
613640
stream.write(self.active_graph.get_tree(0))
614641
self.dismiss_popup()

src/name_manager.py

+30-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# -*- coding: utf-8 -*-
22

3+
from ast import literal_eval
4+
35
class Singleton:
46
"""A non-thread-safe helper class to implement singletons.
57
It should be used as a decorator (not a metaclass) to the
@@ -78,14 +80,16 @@ def get_all(self, namespace_idx=0):
7880
return []
7981

8082
class NameParser:
81-
"""Class for parsing a list of names from a loaded file or string.
82-
The name must be in the format:
83+
"""Class for parsing a list of names or lines from a loaded file or string.
84+
Specifications must be in the format:
8385
8486
#name: 'AtomName', [Hook0Value, Hook1Value, Hook2Value, Hook3Value]
8587
88+
#line: LineId, {Hook0Id: [HookIds, ...], Hook2Id: [HookIds, ...], ...}
8689
"""
8790

88-
TOKENS = {'name': '#name:'}
91+
TOKENS = {'name': '#name:',
92+
'line': '#line:'}
8993

9094
@staticmethod
9195
def str_to_bool(s):
@@ -97,29 +101,40 @@ def str_to_bool(s):
97101
raise ValueError("Cannot covert {} to a bool".format(s))
98102

99103
@classmethod
100-
def parse_line(cls, line):
104+
def parse_name(cls, line):
101105
name = ''
102106
hooks = []
103107

104-
parts = line.split("'")
105-
if len(parts) == 3:
106-
name = parts[1]
107-
108-
open_bracket = line.find("[")
109-
close_bracket = line.find("]")
110-
parts = line[(open_bracket+1):(close_bracket)].split(',')
111-
for p in parts:
112-
print p
113-
boolstring = p.translate(None, ' ')
114-
hooks.append(cls.str_to_bool(boolstring))
108+
data = line.lstrip(cls.TOKENS['name'])
109+
data = data.lstrip()
110+
name, hooks = literal_eval(data)
115111

116112
if (name == '') or (len(hooks) != 4):
117113
raise AssertionError("Wrong name specification: {}".format(line))
118114

119115
return name, hooks
120116

117+
@classmethod
118+
def parse_line(cls, line):
119+
line_id = None
120+
graph = None
121+
122+
data = line.lstrip(cls.TOKENS['line'])
123+
data = data.lstrip()
124+
line_id, graph = literal_eval(data)
125+
126+
if (line_id is None) or (graph is None):
127+
raise AssertionError("Wrong line specification: {}".format(line))
128+
129+
return line_id, graph
130+
121131
@classmethod
122132
def get_name_str(cls, name, hooks):
123133
s = cls.TOKENS['name'] + " '{0}', [{1}, {2}, {3}, {4}]\n".format(
124134
name, str(hooks[0]), str(hooks[1]), str(hooks[2]), str(hooks[3]))
125135
return s
136+
137+
@classmethod
138+
def get_line_str(cls, line_id, graph):
139+
s = cls.TOKENS['line'] + ' ' + str(line_id) + ', ' + str(graph) + '\n'
140+
return s

0 commit comments

Comments
 (0)