Merge lp:~spud/spud/unused-schemas into lp:spud

Proposed by Fraser Waters
Status: Merged
Merged at revision: 483
Proposed branch: lp:~spud/spud/unused-schemas
Merge into: lp:spud
Diff against target: 416 lines (+291/-20)
7 files modified
diamond/diamond/choice.py (+4/-0)
diamond/diamond/interface.py (+5/-0)
diamond/diamond/schema.py (+11/-18)
diamond/diamond/schemauseage.py (+102/-0)
diamond/diamond/tree.py (+5/-1)
diamond/diamond/useview.py (+156/-0)
diamond/gui/gui.glade (+8/-1)
To merge this branch: bzr merge lp:~spud/spud/unused-schemas
Reviewer Review Type Date Requested Status
Simon Funke Approve
Patrick Farrell Pending
Review via email: mp+74390@code.launchpad.net

Description of the change

Compare multiple xml files to one schema to determine what parts of the schema are not used.

To post a comment you must log in.
lp:~spud/spud/unused-schemas updated
477. By Fraser Waters

Bug fix and don't show 'comments'

Revision history for this message
Simon Funke (simon-funke) wrote :

This is a very useful feature.
The code looks good;
The diff feature was successfully tested against all flml's in the fluidity test directory.
Documentation will be provided in a later merge.
=> good to go!

review: Approve
Revision history for this message
Patrick Farrell (pefarrell) wrote :

Fraser: I have a few comments.

Firstly, 'useage' should be spelled 'usage'.

Secondly, the "Input list" on the top of the gui window is a bit confusing: a list of what?

Thirdly, I expected it to open a directory to find all the flml files beneath, but I can't seem to do that: I can only open a file.

Fourth, it takes a very long time: I suspect that this is some algorithmic flaw. To be honest, I can't see any theoretical reason why this shouldn't take more than, say, 10 seconds; if it's taking longer than this it seems to me that we have the wrong algorithm implemented. Even if it does take very long, the UI freezes while it is working, which will lead users to think that diamond has crashed.

Fifth, can you please make it remember which directory you opened before, instead of defaulting to the cwd?

Finally, I ran it on 1material_shocktube.flml and got no schema usage window. Instead, I got a traceback on the shell:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/diamond/interface.py", line 779, in on_finduseage
    useview.UseView(self.s, self.filename)
  File "/usr/lib/python2.7/dist-packages/diamond/useview.py", line 49, in __init__
    self.__update(schema, paths)
  File "/usr/lib/python2.7/dist-packages/diamond/useview.py", line 144, in __update
    self.__set_useage(schemauseage.find_unusedset(schema, paths))
  File "/usr/lib/python2.7/dist-packages/diamond/schemauseage.py", line 81, in find_unusedset
    tree = schema.read(path)
  File "/usr/lib/python2.7/dist-packages/diamond/schema.py", line 504, in read
    doc = etree.parse(xmlfile)
  File "lxml.etree.pyx", line 2942, in lxml.etree.parse (src/lxml/lxml.etree.c:54187)
  File "parser.pxi", line 1528, in lxml.etree._parseDocument (src/lxml/lxml.etree.c:79485)
  File "parser.pxi", line 1557, in lxml.etree._parseDocumentFromURL (src/lxml/lxml.etree.c:79768)
  File "parser.pxi", line 1457, in lxml.etree._parseDocFromFile (src/lxml/lxml.etree.c:78843)
  File "parser.pxi", line 997, in lxml.etree._BaseParser._parseDocFromFile (src/lxml/lxml.etree.c:75698)
  File "parser.pxi", line 564, in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:71739)
  File "parser.pxi", line 645, in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:72614)
  File "parser.pxi", line 583, in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:71927)
IOError: Error reading file '<?xml version='1.0' encoding='utf-8'?>': failed to load external entity "<?xml version='1.0' encoding='utf-8'?>"

Revision history for this message
Patrick Farrell (pefarrell) wrote :

Simon: "The diff feature was successfully tested against all flml's in the fluidity test directory. "

The diff feature has nothing to do with this feature/merge request?

Revision history for this message
Fraser Waters (fraser-waters08) wrote :

Currently (and this is in the manual) the input file its asking for is a file with a list of paths to check. We figured this is slightly more adaptable than just selecting every .suffix file, but that can be done as well if needs be.

Question four, the algorithm is pretty simple. Make a set of every element in the schema, then open each file and for every element in that file remove it from the set.
(This is slight more efficent than building a new set from each file and than calculating set differences) I can't think of any way this can be improved we HAVE to look at every schema element and we HAVE to look at every element in every file.
The UI freeze is also mentioned in the manual. We'll have to pull that off to another thread and then callback to UI to fix that, can probably be done but I'll have to look up how to do threads and callbacks first.

Final question, that's not the input it was expecting but there should be error handeling around it so that it at least doesn't crash.

Revision history for this message
Patrick Farrell (pefarrell) wrote :

Ah, well in that case it is good that I didn't read the manual first, so that I could find where it was counterintuitive :-)

I think we should take in a directory. In practice, this will only ever be run on all the flml files in the tests/ or longtests/ directories, so it makes sense to do it that way.

I made a file

[pef@aislinn:~/src/spud]$ cat /tmp/files
/data/pfarrell/fluidity/trunk/tests/python_diagnostic_field/python.flml

and ran it on that. I got:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/diamond/interface.py", line 779, in on_finduseage
    useview.UseView(self.s, self.filename)
  File "/usr/lib/python2.7/dist-packages/diamond/useview.py", line 49, in __init__
    self.__update(schema, paths)
  File "/usr/lib/python2.7/dist-packages/diamond/useview.py", line 144, in __update
    self.__set_useage(schemauseage.find_unusedset(schema, paths))
  File "/usr/lib/python2.7/dist-packages/diamond/useview.py", line 116, in __set_useage
    iter = self.treestore.get_iter(self.mapping[xpath])
KeyError: '/*/*[278]/*/*'

Revision history for this message
Fraser Waters (fraser-waters08) wrote :

Could I have that file please? If it's not in the mapping it means somehow usage traversal found it but useview traversal didn't...

If it's always going to be every .suffix in a directory I can change it to that easy enough, give me a few minutes.

Revision history for this message
Patrick Farrell (pefarrell) wrote :

While file? I pasted the contents of the list, and the referenced flml file lives in the fluidity trunk (tests/python_diagnostic_field/python.flml).

Revision history for this message
Fraser Waters (fraser-waters08) wrote :

Ah I see, sorry not enough coffee in the blood yet. I'll check it out.

Revision history for this message
Fraser Waters (fraser-waters08) wrote :

Oki that's fixed, it was just a problem with comment nodes (we were ignoring them at one point and trying to look them up later)

Revision history for this message
Patrick Farrell (pefarrell) wrote :

Great. It now works when I call it on fluidity's tests directory. And the results are /very/ interesting. Fantastic work!

Quick query: is it possible to offer a command-line option that only does the schema usage on a specified directory (i.e. doesn't bring up the main interface)?

Revision history for this message
Patrick Farrell (pefarrell) wrote :

I did some profiling where I ran it on the tests/ directory, and the results are pretty interesting.

About 2/3 of the runtime is spent in xpath lookups. As a simple first optimisation, I memoised schema.element_children. The runtime of on_finduseage went from 577s to 359s. Not bad for 7 lines of code! And of the 366 seconds of runtime, over 191s of those are still in xpath lookups.

Exercise for the interested reader (i.e. Fraser): identify all the other points where xpath lookups are happening and cache it. I tried memoising schema.tree.xpath, but because the class is written in C, I can't overwrite the function (unfortunately). Caching the usages at

/usr/lib/python2.7/dist-packages/diamond/schemausage.py:31
and
/usr/lib/python2.7/dist-packages/diamond/useview.py:78

would knock off about another ~170s or so.

Here's the profile for the r492:

{method 'xpath' of 'lxml.etree._ElementTree' objects} <- 32 0.051 0.051 /usr/lib/python2.7/dist-packages/diamond/preprocess.py:26(preprocess)
                                                                             1823 0.726 0.726 /usr/lib/python2.7/dist-packages/diamond/schema.py:79(element_children)
                                                                           120235 6.166 6.166 /usr/lib/python2.7/dist-packages/diamond/schema.py:123(valid_children)
                                                                                1 0.000 0.000 /usr/lib/python2.7/dist-packages/diamond/schemausage.py:26(find_fullset)
                                                                           231826 91.364 91.364 /usr/lib/python2.7/dist-packages/diamond/schemausage.py:31(traverse)
                                                                           231826 93.463 93.463 /usr/lib/python2.7/dist-packages/diamond/useview.py:78(__set_treestore)
                                                                                1 0.000 0.000 /usr/lib/python2.7/dist-packages/diamond/useview.py:140(__update)
{method 'xpath' of 'lxml.etree._Element' objects} <- 425764 12.282 12.282 /usr/lib/python2.7/dist-packages/diamond/schema.py:691(assign_xml_nodes)

Revision history for this message
Fraser Waters (fraser-waters08) wrote :

command-line option: yes, how about -u [DIR]?

I'm not suprised at those profile results, xpath lookups was my first guess.

Revision history for this message
Patrick Farrell (pefarrell) wrote :

Yes, -u is fine. I have got the time down from 577s to 268s -- hopefully you can make further improvements. I've been running it like this:

python -m cProfile -o statfile /usr/bin/diamond test.flml

and then visualising the stats with RunSnakeRun (.deb packages in /scratch/pfarrell)

Revision history for this message
Fraser Waters (fraser-waters08) wrote :

-u is in. Also added some caching in other places. The bulk of time is now spent in schema.Read
Next up, async UseView and DiffView and change the manual to match the new UseView behaviour

Revision history for this message
Fraser Waters (fraser-waters08) wrote :

And that's all in, DiffView and UseView won't freeze Diamond anymore

Revision history for this message
Patrick Farrell (pefarrell) wrote :

"Parseing schema" should be "Parsing schema".

Nice work! Down from 577s to 160s. I told you you could make it faster :-)

Now, for the coup-de-grace: Can you add a -ub (-u, batch) option that doesn't open any GUI or gtk window, but just makes diamond exit with 0 (if all the schema is used) and 1 (if any part of the schema is unused)? I want to add a test that all of the schema is used ... :-)

Revision history for this message
Patrick Farrell (pefarrell) wrote :

And while I'm adding last-minute feature requests: can -ub print out to the terminal some readable description of the schema entries that are not used by the tests?

Thanks for all the hard work you have done over the 10 weeks; the whole group and I are very impressed.

Revision history for this message
Fraser Waters (fraser-waters08) wrote :

Readable, do you want the schemaname (the /*/*//[58] format) or some kind of named path (Like element (fludity_options)/element *(material_phase)/choice/element *(scalar_field) ).
The later will be better for humans the former for machines.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'diamond/diamond/choice.py'
2--- diamond/diamond/choice.py 2011-07-29 09:18:28 +0000
3+++ diamond/diamond/choice.py 2011-09-07 14:03:34 +0000
4@@ -58,6 +58,10 @@
5 def _on_set_attr(self, node, attr, value):
6 self.emit("on-set-attr", attr, value)
7
8+ def get_attrs(self):
9+ """Get all attributes"""
10+ return self.get_current_tree().get_attrs()
11+
12 def set_default_active(self):
13 self.active = True
14 if self.cardinality == '?' or self.cardinality == '*':
15
16=== modified file 'diamond/diamond/interface.py'
17--- diamond/diamond/interface.py 2011-09-07 12:31:36 +0000
18+++ diamond/diamond/interface.py 2011-09-07 14:03:34 +0000
19@@ -50,6 +50,7 @@
20 import datawidget
21 import diffview
22 import sliceview
23+import useview
24
25 from lxml import etree
26
27@@ -144,6 +145,7 @@
28 "on_slice": self.on_slice,
29 "on_diff": self.on_diff,
30 "on_diffsave": self.on_diffsave,
31+ "on_finduseage": self.on_finduseage,
32 "on_group": self.on_group,
33 "on_ungroup": self.on_ungroup}
34
35@@ -773,6 +775,9 @@
36 else:
37 dialogs.error(self.main_window, "No save to diff against.")
38
39+ def on_finduseage(self, widget = None):
40+ useview.UseView(self.s, self.filename)
41+
42 def on_slice(self, widget = None):
43 if not self.selected_node.is_sliceable():
44 self.statusbar.set_statusbar("Cannot slice on this element.")
45
46=== modified file 'diamond/diamond/schema.py'
47--- diamond/diamond/schema.py 2011-08-02 11:29:07 +0000
48+++ diamond/diamond/schema.py 2011-09-07 14:03:34 +0000
49@@ -55,17 +55,17 @@
50 'interleave': self.cb_group,
51 'name': self.cb_name,
52 'text': self.cb_text,
53- 'anyName' : self.cb_anyname,
54- 'nsName' : self.cb_nsname,
55- 'except' : self.cb_except,
56+ 'anyName' : self.cb_anyname,
57+ 'nsName' : self.cb_nsname,
58+ 'except' : self.cb_except,
59 'ignore' : self.cb_ignore,
60 'notAllowed' : self.cb_notallowed}
61-
62+
63 self.lost_eles = []
64 self.added_eles = []
65 self.lost_attrs = []
66 self.added_attrs = []
67-
68+
69 return
70
71 def element_children(self, element):
72@@ -145,19 +145,12 @@
73 if isinstance(eid, tree.Tree) or isinstance(eid, choice.Choice):
74 eidtree = eid
75 eid = eid.schemaname
76-
77- if eid == ":start":
78- try:
79- node = self.tree.xpath('/t:grammar/t:start', namespaces={'t': 'http://relaxng.org/ns/structure/1.0'})[0]
80- except:
81- debug.deprint("No valid start node found. Are you using a library Relax-NG file like spud_base.rng?", 0)
82- sys.exit(0)
83- else:
84- xpath = self.tree.xpath(eid)
85- if len(xpath) == 0:
86- debug.deprint("Warning: no element with XPath %s" % eid)
87- return None
88- node = xpath[0]
89+
90+ xpath = self.tree.xpath(eid)
91+ if len(xpath) == 0:
92+ debug.deprint("Warning: no element with XPath %s" % eid)
93+ return None
94+ node = xpath[0]
95
96 node = self.to_tree(node)
97
98
99=== added file 'diamond/diamond/schemauseage.py'
100--- diamond/diamond/schemauseage.py 1970-01-01 00:00:00 +0000
101+++ diamond/diamond/schemauseage.py 2011-09-07 14:03:34 +0000
102@@ -0,0 +1,102 @@
103+#!/usr/bin/env python
104+
105+# This file is part of Diamond.
106+#
107+# Diamond is free software: you can redistribute it and/or modify
108+# it under the terms of the GNU General Public License as published by
109+# the Free Software Foundation, either version 3 of the License, or
110+# (at your option) any later version.
111+#
112+# Diamond is distributed in the hope that it will be useful,
113+# but WITHOUT ANY WARRANTY; without even the implied warranty of
114+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
115+# GNU General Public License for more details.
116+#
117+# You should have received a copy of the GNU General Public License
118+# along with Diamond. If not, see <http://www.gnu.org/licenses/>.
119+
120+from cStringIO import StringIO
121+from lxml import etree
122+
123+from schema import Schema
124+
125+RELAXNGNS = "http://relaxng.org/ns/structure/1.0"
126+RELAXNG = "{" + RELAXNGNS + "}"
127+
128+def find_fullset(tree):
129+ """
130+ Given a schema tree pulls out xpaths for every element.
131+ """
132+
133+ def traverse(node):
134+
135+ if node.tag == RELAXNG + "element" or (node.tag == RELAXNG + "choice" and all(n.tag != RELAXNG + "value" for n in node)):
136+ fullset.add(tree.getpath(node))
137+
138+ elif node.tag == RELAXNG + "ref":
139+ node = tree.xpath('/t:grammar/t:define[@name="' + node.get("name") + '"]', namespaces={'t': RELAXNGNS})[0]
140+
141+ for child in node:
142+ traverse(child)
143+
144+ start = tree.xpath("/t:grammar/t:start", namespaces={'t': RELAXNGNS})[0]
145+
146+ root = start[0]
147+
148+ fullset = set()
149+ traverse(root)
150+ return fullset
151+
152+def find_useset(tree):
153+ """
154+ Given a diamond xml tree pulls out scehama paths for every element and attribute.
155+ """
156+
157+ def traverse(node):
158+ if node.active:
159+ useset.add(node.schemaname)
160+
161+ for child in node.get_children():
162+ traverse(child)
163+
164+ useset = set()
165+ traverse(tree)
166+ return useset
167+
168+def find_unusedset(schema, paths):
169+ """
170+ Given the a diamond schema and a list of paths to xml files
171+ find the unused xpaths.
172+ """
173+ def traverse(node):
174+ if node.active:
175+ unusedset.discard(node.schemaname)
176+
177+ for child in node.get_children():
178+ traverse(child)
179+
180+ unusedset = find_fullset(schema.tree)
181+
182+ for path in paths:
183+ tree = schema.read(path)
184+ traverse(tree)
185+
186+ return unusedset
187+
188+def strip(tag):
189+ return tag[tag.index("}") + 1:]
190+
191+def node_name(node):
192+ """
193+ Returns a name for this node.
194+ """
195+ tagname = node.get("name") if "name" in node.keys() else strip(node.tag)
196+ name = None
197+
198+ for child in node:
199+ if child.tag == RELAXNG + "attribute":
200+ if "name" in child.keys() and child.get("name") == "name":
201+ for grandchild in child:
202+ if grandchild.tag == RELAXNG + "value":
203+ name = " (" + grandchild.text + ")"
204+ return tagname + (name if name else "")
205
206=== modified file 'diamond/diamond/tree.py'
207--- diamond/diamond/tree.py 2011-07-30 04:36:33 +0000
208+++ diamond/diamond/tree.py 2011-09-07 14:03:34 +0000
209@@ -53,7 +53,7 @@
210
211 # Any children?
212 if children is None:
213- self.children = copy.copy([])
214+ self.children = []
215 else:
216 self.children = children
217
218@@ -114,6 +114,10 @@
219 (datatype, curval) = self.attrs[attr]
220 return curval
221
222+ def get_attrs(self):
223+ """Get all attributes"""
224+ return self.attrs
225+
226 def set_data(self, data):
227 (invalid, data) = self.valid_data(self.datatype, data)
228 if invalid:
229
230=== added file 'diamond/diamond/useview.py'
231--- diamond/diamond/useview.py 1970-01-01 00:00:00 +0000
232+++ diamond/diamond/useview.py 2011-09-07 14:03:34 +0000
233@@ -0,0 +1,156 @@
234+#!/usr/bin/env python
235+
236+# This file is part of Diamond.
237+#
238+# Diamond is free software: you can redistribute it and/or modify
239+# it under the terms of the GNU General Public License as published by
240+# the Free Software Foundation, either version 3 of the License, or
241+# (at your option) any later version.
242+#
243+# Diamond is distributed in the hope that it will be useful,
244+# but WITHOUT ANY WARRANTY; without even the implied warranty of
245+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
246+# GNU General Public License for more details.
247+#
248+# You should have received a copy of the GNU General Public License
249+# along with Diamond. If not, see <http://www.gnu.org/licenses/>.
250+
251+import gobject
252+import gtk
253+
254+import schemauseage
255+
256+RELAXNGNS = "http://relaxng.org/ns/structure/1.0"
257+RELAXNG = "{" + RELAXNGNS + "}"
258+
259+class UseView(gtk.Window):
260+ def __init__(self, schema, path):
261+ gtk.Window.__init__(self)
262+ self.__add_controls()
263+
264+ dialog = gtk.FileChooserDialog(title = "Input list",
265+ action = gtk.FILE_CHOOSER_ACTION_OPEN,
266+ buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
267+
268+ if path:
269+ dialog.set_current_folder(path)
270+ response = dialog.run()
271+ if response != gtk.RESPONSE_OK:
272+ dialog.destroy()
273+ return
274+
275+ filename = dialog.get_filename()
276+ dialog.destroy()
277+
278+ paths = [line.strip() for line in open(filename) if line.strip()]
279+ if not paths:
280+ return
281+
282+ self.__update(schema, paths)
283+ self.show_all()
284+
285+ def __add_controls(self):
286+ self.set_title("Unused schema entries")
287+ self.set_default_size(800, 600)
288+
289+ scrolledwindow = gtk.ScrolledWindow()
290+ scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
291+
292+ self.treeview = gtk.TreeView()
293+
294+ self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
295+
296+ # Node column
297+ celltext = gtk.CellRendererText()
298+ column = gtk.TreeViewColumn("Node", celltext)
299+ column.set_cell_data_func(celltext, self.set_celltext)
300+
301+ self.treeview.append_column(column)
302+
303+ # 0: The node tag
304+ # 1: Used (0 == Not used, 1 = Child not used, 2 = Used)
305+ self.treestore = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)
306+ self.treeview.set_model(self.treestore)
307+ self.treeview.set_enable_search(False)
308+
309+ scrolledwindow.add(self.treeview)
310+ self.add(scrolledwindow)
311+
312+ def __set_treestore(self, node, iter = None, type = None):
313+ if node.tag == RELAXNG + "element":
314+ name = schemauseage.node_name(node)
315+ if name == "comment":
316+ return #early out to skip comment nodes
317+
318+ tag = name + (type if type else "")
319+ child_iter = self.treestore.append(iter, [tag, 2])
320+ self.mapping[self.tree.getpath(node)] = self.treestore.get_path(child_iter)
321+ type = None
322+ elif node.tag == RELAXNG + "choice" and all(n.tag != RELAXNG + "value" for n in node):
323+ tag = "choice" + (type if type else "")
324+ child_iter = self.treestore.append(iter, [tag, 2])
325+ self.mapping[self.tree.getpath(node)] = self.treestore.get_path(child_iter)
326+ type = None
327+ elif node.tag == RELAXNG + "optional":
328+ child_iter = iter
329+ type = " ?"
330+ elif node.tag == RELAXNG + "oneOrMore":
331+ child_iter = iter
332+ type = " +"
333+ elif node.tag == RELAXNG + "zeroOrMore":
334+ child_iter = iter
335+ type = " *"
336+ elif node.tag == RELAXNG + "ref":
337+ node = self.tree.xpath('/t:grammar/t:define[@name="' + node.get("name") + '"]', namespaces={'t': RELAXNGNS})[0]
338+ child_iter = iter
339+ elif node.tag == RELAXNG + "group" or node.tag == RELAXNG + "interleave":
340+ child_iter = iter
341+ else:
342+ return
343+
344+ for child in node:
345+ self.__set_treestore(child, child_iter, type)
346+
347+ def __set_useage(self, useage):
348+ for xpath in useage:
349+ iter = self.treestore.get_iter(self.mapping[xpath])
350+ self.treestore.set_value(iter, 1, 0)
351+
352+ def __floodfill(self, iter, parent = 2):
353+ """
354+ Floodfill the tree with the correct useage.
355+ """
356+ if parent == 0: #parent is not used
357+ self.treestore.set_value(iter, 1, 0) #color us not used
358+
359+ useage = self.treestore.get_value(iter, 1)
360+
361+ child = self.treestore.iter_children(iter)
362+ while child is not None:
363+ change = self.__floodfill(child, useage)
364+ if change != 2 and useage == 2:
365+ self.treestore.set(iter, 1, 1)
366+ child = self.treestore.iter_next(child)
367+
368+ return self.treestore.get_value(iter, 1)
369+
370+
371+ def __update(self, schema, paths):
372+ self.tree = schema.tree
373+ self.start = self.tree.xpath('/t:grammar/t:start', namespaces={'t': RELAXNGNS})[0]
374+ self.mapping = {}
375+
376+ self.__set_treestore(self.start[0])
377+ self.__set_useage(schemauseage.find_unusedset(schema, paths))
378+ self.__floodfill(self.treestore.get_iter_root())
379+
380+ def set_celltext(self, column, cell, model, iter):
381+ tag, useage = model.get(iter, 0, 1)
382+ cell.set_property("text", tag)
383+
384+ if useage == 0:
385+ cell.set_property("foreground", "red")
386+ elif useage == 1:
387+ cell.set_property("foreground", "indianred")
388+ else:
389+ cell.set_property("foreground", "black")
390
391=== modified file 'diamond/gui/gui.glade'
392--- diamond/gui/gui.glade 2011-08-23 12:37:37 +0000
393+++ diamond/gui/gui.glade 2011-09-07 14:03:34 +0000
394@@ -227,7 +227,7 @@
395 <signal name="activate" handler="on_diff"/>
396 </widget>
397 </child>
398- <child>
399+ <child>
400 <widget class="GtkMenuItem" id="menuitemDiffSave">
401 <property name="visible">True</property>
402 <property name="label" translatable="yes">Diff against last save</property>
403@@ -235,6 +235,13 @@
404 </widget>
405 </child>
406 <child>
407+ <widget class="GtkMenuItem" id="menuitemFindUseage">
408+ <property name="visible">True</property>
409+ <property name="label" translatable="yes">Find schema useage</property>
410+ <signal name="activate" handler="on_finduseage"/>
411+ </widget>
412+ </child>
413+ <child>
414 <widget class="GtkCheckMenuItem" id="display_properties">
415 <property name="visible">True</property>
416 <property name="tooltip" translatable="yes">Display the option properties on the right hand side of the main window.</property>

Subscribers

People subscribed via source and target branches