Modules |
Files |
Inheritance Tree |
Inheritance Graph |
Name Index |
Config
File: Synopsis/UI/Qt/igraph.py
1| # $Id: igraph.py,v 1.3 2002/09/28 05:53:31 chalky Exp $
2| #
3| # This file is a part of Synopsis.
4| # Copyright (C) 2000, 2001 Stefan Seefeld
5| # Copyright (C) 2000, 2001 Stephen Davies
6| #
7| # Synopsis is free software; you can redistribute it and/or modify it
8| # under the terms of the GNU General Public License as published by
9| # the Free Software Foundation; either version 2 of the License, or
10| # (at your option) any later version.
11| #
12| # This program is distributed in the hope that it will be useful,
13| # but WITHOUT ANY WARRANTY; without even the implied warranty of
14| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15| # General Public License for more details.
16| #
17| # You should have received a copy of the GNU General Public License
18| # along with this program; if not, write to the Free Software
19| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20| # 02111-1307, USA.
21| #
22| # $Log: igraph.py,v $
23| # Revision 1.3 2002/09/28 05:53:31 chalky
24| # Refactored display into separate project and browser windows. Execute projects
25| # in the background
26| #
27| # Revision 1.2 2001/11/07 05:58:21 chalky
28| # Reorganised UI, opening a .syn file now builds a simple project to view it
29| #
30| # Revision 1.1 2001/11/05 06:52:11 chalky
31| # Major backside ui changes
32| #
33|
34|
35| import sys, pickle, Synopsis, cStringIO
36| from qt import *
37|
38| # In later versions of python-qt aka PyQt this is in a separate module
39| if not globals().has_key('QCanvasView'):
40| from qtcanvas import *
41|
42| from Synopsis.Core import AST, Util
43| from Synopsis.Core.Action import *
44| from Synopsis.Formatter.ASCII import ASCIIFormatter
45| from Synopsis.Formatter.ClassTree import ClassTree
46|
47|
48| class IGraphWindow (QCanvasView):
49| class Icon:
50| def __init__(self, canvas, classname, x, y):
51| self.classname = classname
52| name = Util.ccolonName(classname[-1:])
53| self.text = QCanvasText(name, canvas)
54| self.text.setZ(2)
55| self.text.show()
56| r = self.text.boundingRect()
57| self.border = QCanvasRectangle(x , y, r.width()+4,r.height()+4, canvas)
58| self.border.setBrush(QBrush(QColor(255,255,200)))
59| self.border.setZ(1)
60| self.border.show()
61| self.subs = []
62| self.supers = []
63| self.sub_lines = []
64| self.super_lines = []
65| self.move_to(x, y)
66| def move_to(self, x, y):
67| self.x = x
68| self.y = y
69| self.border.move(x, y)
70| self.text.move(x+2,y+2)
71| r = self.border.boundingRect()
72| self.mid_x = (r.left() + r.right())/2
73| self.max_x = r.right()
74| self.top_y = r.top()
75| self.mid_y = (r.top() + r.bottom())/2
76| self.max_y = r.bottom()
77| for sub in self.sub_lines: self.move_sub(sub)
78| for super in self.super_lines: self.move_super(super)
79| def width(self):
80| "returns the width of this icon"
81| return self.border.boundingRect().width()
82| def mid(self):
83| "Returns the x, y coords of the middle of this icon"
84| return self.mid_x, self.mid_y
85| def add_sub(self, sub, line):
86| self.subs.append(sub)
87| self.sub_lines.append(line)
88| self.move_sub(line)
89| def move_sub(self, line):
90| other = line.endPoint()
91| line.setPoints(self.mid_x, self.mid_y, other.x(), other.y())
92| def add_super(self, super, line):
93| self.supers.append(super)
94| self.super_lines.append(line)
95| self.move_super(line)
96| def move_super(self, line):
97| other = line.startPoint()
98| line.setPoints(other.x(), other.y(), self.mid_x, self.top_y)
99| def clear(self):
100| "Removes all references to other icons etc so they can be garbage collected"
101| del self.subs
102| del self.supers
103| del self.sub_lines
104| del self.super_lines
105| self.text.hide()
106| self.border.hide()
107|
108|
109| def __init__(self, parent, main_window, classTree):
110| self.__canvas = QCanvas()
111| self.main_window = main_window
112| self.classTree = classTree
113| QCanvasView.__init__(self, self.__canvas, parent)
114| self.canvas().resize(parent.width(), parent.height())
115|
116| # Empty icon list
117| self.icons = {}
118|
119| def set_class(self, classname):
120| self.mainclass = classname
121| graphs = self.classTree.graphs()
122| classes = None
123| for graph in graphs:
124| if classname in graph:
125| classes = graph
126| break
127| if not classes: return
128|
129| # Reset icons
130| for icon in self.icons.values():
131| icon.clear()
132| self.__canvas.setChanged(QRect(QPoint(0,0),self.__canvas.size()))
133| self.__canvas.update()
134|
135| # Create new icons
136| self.icons = {}
137| for clas in classes:
138| self.icons[clas]= self.Icon(self.__canvas, clas, 0,0)
139| if clas == classname:
140| self.icons[clas].text.setColor(Qt.red)
141|
142| # Now connect their supers and subs lists
143| for name, icon in self.icons.items():
144| for child in self.classTree.subclasses(name):
145| line = QCanvasLine(self.__canvas)
146| sub = self.icons[child]
147| icon.add_sub(sub, line)
148| sub.add_super(icon, line)
149| if name == classname or sub.classname == classname:
150| line.setPen(QPen(Qt.red))
151| line.show()
152|
153| self.organize()
154|
155| self.__canvas.update()
156|
157| #self.show()
158|
159| def organize(self):
160| # find a root
161| roots = filter(lambda x: not x.supers, self.icons.values())
162| if not roots: return # this is an error
163| root = roots[0]
164| self._organized = {}
165| self._ranks = {}
166| self.rank_down(root)
167| self.space_ranks()
168| self.sort_ranks()
169|
170| self.un_offset()
171|
172| def un_offset(self):
173| """Moves the graph to the top-left corner of the display"""
174| # Find top-left of graph
175| min_x, min_y = 0, 0
176| for node in self.icons.values():
177| if node.x < min_x: min_x = node.x
178| if node.y < min_y: min_y = node.y
179| # Move to top-left of display
180| min_x, min_y = min_x - 10, min_y - 10
181| max_x, max_y = 0, 0
182| for node in self.icons.values():
183| node.move_to(node.x - min_x, node.y - min_y)
184| if node.max_x > max_x: max_x = node.max_x
185| if node.max_y > max_y: max_y = node.max_y
186| self.__canvas.resize(max_x+10, max_y+10)
187|
188| def rank_down(self, node):
189| self._organized[node] = None
190| if not self._ranks.has_key(node.y): self._ranks[node.y] = []
191| self._ranks[node.y].append(node)
192| min_y = node.y + 30
193| for sub in node.subs:
194| if self._organized.has_key(sub) and min_y < sub.y: continue
195| sub.move_to(sub.x, node.y + 30)
196| self.rank_down(sub)
197|
198| for super in node.supers:
199| if self._organized.has_key(super): continue
200| super.move_to(super.x, node.y - 30)
201| self.rank_down(super)
202|
203| def space_ranks(self):
204| "Spaces out the ranks vertically"
205| ranks = self._ranks.keys()
206| ranks.sort()
207| space = 0
208| for rank in ranks:
209| nodes = self._ranks[rank]
210| space = space + len(nodes)
211| new_rank = rank + space
212|
213| del self._ranks[rank]
214| self._ranks[new_rank] = nodes
215|
216| for node in nodes:
217| node.move_to(node.x, new_rank)
218|
219|
220|
221| def sort_ranks(self):
222| ranks = self._ranks.keys()
223| ranks.sort()
224| for rank in ranks:
225| nodes = self._ranks[rank]
226| done = {}
227| order = []
228| rank_x = -1e6
229| for node in nodes:
230| for super in node.supers:
231| # Create a set of nodes that are childen of super at this rank
232| set = filter(lambda n, y=rank: n.y==y, super.subs)
233| # Eliminate already ordered nodes
234| set = filter(lambda n, d=done: not d.has_key(n), set)
235| # Sort the set relative to where their parents are
236| summer = lambda sum, num: (sum[0]+num, sum[1]+1)
237| def average(sum): return sum[1] and float(sum[0])/sum[1] or 0
238| super_order = lambda s, ds=node.supers: ds.index(s)
239| node_order = lambda n, summer=summer, super_order=super_order, average=average: \
240| average(reduce(summer, map(super_order, n.supers), (0,0)))
241| set.sort(lambda x,y,no=node_order: cmp(no(x), no(y)))
242| order.extend(set)
243| # Make the nodes as done
244| for s in set: done[s] = 1
245| # Calculate the width of the set
246| set_width = 0
247| for s in set: set_width = set_width + s.width() + 2
248| # Move nodes to their new order
249| x = super.mid()[0] - set_width/2
250| if x < rank_x: x = rank_x
251| for s in set:
252| s.move_to(x, s.y)
253| x = x + s.width() + 2
254| rank_x = x + 2
255| if not node.supers:
256| # Backup: no siblings to rank with so just use rank_x
257| if rank_x < -1e5: rank_x = 0
258| node.move_to(rank_x, node.y)
259| rank_x = rank_x + node.width() + 2 + 5*len(node.subs)
260| order.append(node)
261| done[node] = 1
262| #print rank, map(lambda n:'%s:%d'%(n.classname[-1],n.x), order)
263|
264|