Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLionel LASKE <llaske@c2s.fr>2011-12-03 14:48:39 (GMT)
committer Lionel LASKE <llaske@c2s.fr>2011-12-03 14:48:39 (GMT)
commit0be87a54defeab50ea772c30ee78b9e1c473822b (patch)
tree5376b4800e5ab8475b1cd3e66226cbe7a480b781
parent6cdf287b3b5fe153ea7ccd9d852cda4d88fe7859 (diff)
Handle add/delete button, fix zoom and detail view
-rw-r--r--MANIFEST2
-rw-r--r--activity.py266
-rw-r--r--src/person.py25
-rw-r--r--src/tree.py103
-rw-r--r--src/union.py7
5 files changed, 315 insertions, 88 deletions
diff --git a/MANIFEST b/MANIFEST
index a8478f6..77479da 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,12 +1,12 @@
sugardummy.py
setup.py
activity.py
-MANIFEST
COPYING
activity/roots-icon.svg
activity/activity.info
src/union.py
src/person.py
+src/tree.py
src/partialdate.py
src/guid.py
src/const.py
diff --git a/activity.py b/activity.py
index 5573f58..91d2e24 100644
--- a/activity.py
+++ b/activity.py
@@ -5,8 +5,9 @@ from gettext import gettext as _
# Local import
from src.person import Person
-from src.person import persons
from src.union import Union
+from src.tree import Tree
+from src.tree import sample_family1, sample_family2
import src.const as const
@@ -49,15 +50,10 @@ class RootsActivity(activity.Activity):
self.set_toolbox(toolbox)
toolbox.show()
- # Create sample data
- (self.initx, self.inity, self.initzoom) = (500, 50, 0)
- self.tree = self.init_family()
- self.tree.compute_draw(self.initx, self.inity)
-
# Create drawing area
- self.zoomlevel = 1
+ self.zoomlevel = 0
self.area = gtk.DrawingArea()
- self.area.set_size_request(600, 300)
+ self.area.set_size_request(750, 600)
self.area.set_events(gtk.gdk.BUTTON_PRESS_MASK|gtk.gdk.BUTTON_RELEASE_MASK|gtk.gdk.BUTTON_MOTION_MASK|gtk.gdk.POINTER_MOTION_MASK)
self.area.connect("expose_event", self.area_expose_cb)
self.area.connect("button_press_event", self.press_button)
@@ -65,104 +61,162 @@ class RootsActivity(activity.Activity):
self.area.connect("motion_notify_event", self.mouse_move)
self.moving = False
- # Draw
- self.box = gtk.VBox(False)
+ # Create detail view
+ self.fixed = gtk.VBox()
+ self.fixed.set_size_request(200, 300)
+ self.fixed.pack_start(gtk.Label("Name:"), False, False, 0)
+ self.detail_name = gtk.TextView()
+ self.detail_name.set_cursor_visible(True)
+ self.detail_name.get_buffer().connect("changed", self.detail_changed)
+ self.fixed.pack_start(self.detail_name, False, False, 0)
+ self.detail_chkmale = gtk.RadioButton(None, "Male")
+ self.detail_chkmale.connect("toggled", self.sexradio_checked, 'M')
+ self.fixed.pack_start(self.detail_chkmale, False, False, 0)
+ self.detail_chkfemale = gtk.RadioButton(self.detail_chkmale, "Female")
+ self.detail_chkfemale.connect("toggled", self.sexradio_checked, 'F')
+ self.fixed.pack_start(self.detail_chkfemale, False, False, 0)
+ self.detail_btnaddunion = gtk.Button("Add union")
+ self.detail_btnaddunion.connect("clicked", self.addunion_clicked)
+ self.detail_btnaddchild = gtk.Button("Add child")
+ self.detail_btnaddchild.connect("clicked", self.addchild_clicked)
+ self.detail_btndelete = gtk.Button("Delete")
+ self.detail_btndelete.connect("clicked", self.delete_clicked)
+ self.fixed.pack_start(self.detail_btnaddunion, False, False, 0)
+ self.fixed.pack_start(self.detail_btnaddchild, False, False, 0)
+ self.fixed.pack_start(self.detail_btndelete, False, False, 0)
+ self.box = gtk.HBox(False)
+ self.box.pack_start(self.fixed, True, True, 0)
self.box.pack_start(self.area, True, True, 0)
self.set_canvas(self.box)
+
+ # Create sample data
+ (self.initx, self.inity) = (500, 50)
+ self.tree = sample_family2()
+ self.tree.root.compute_draw(self.initx, self.inity)
+ self.show_detail(None)
+
+ # Show all
self.show_all()
self.area.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW))
-
- def init_family(self):
- "Init a sample tree"
- l = Person("Lucien", "M")
- a = Person("Annick", "F")
- u = Union(l, a)
-
- d = Person("Dominique", "M")
- u.append_child(d)
- au = Person("Anne", "F")
- u.append_child(au)
- m = Person("Madeleine", "F")
- u.append_child(m)
-
- jp = Person("Jean-Pierre", "M")
- j = Person("Julie", "F")
- up = Union(jp, j)
- up.append_child(l)
- c = Person("Christian", "M")
- up.append_child(c)
-
- ub = Union(Person("Jonathan", "M"), j)
- ub.append_child(Person("Charlie", "F"))
-
- rs = Person("Renee", "F")
- vm = Person("Vivien", "M")
- urv = Union(vm, rs)
- urv.append_child(j)
-
- jr = Person("Jean-Rene", "M")
- ua = Union(jr, Person("Micheline", "F"))
- ua.append_child(a)
- i = Person("Irene", "F")
- ua.append_child(i)
- ui = Union(Person("Nathan", "M"), i)
- ui.append_child(Person("Marie", "F"))
- ui.append_child(Person("Noel", "M"))
- ui.append_child(Person("Thierry", "M"))
-
- uc = Union(c, Person("Clarah", "F"))
- uc.append_child(Person("Sandrine", "F"))
- uc2 = Union(c, Person("Vivianne", "F"))
- uc2.append_child(Person("Pierre", "M"))
- uc2.append_child(Person("Camille", "F"))
-
- return vm
-
+
+ def set_center(self, person):
+ "Set the center of the draw on a person"
+ rect = self.area.allocation
+ dx = (rect.width/2) - self.tree.root.x0 - (person.x0 - self.tree.root.x0) - (person.x1 - person.x0)/2
+ dy = (rect.height/2) - self.tree.root.y0 - (person.y0 - self.tree.root.y0) - (person.y1 - person.y0)/2
+ self.translate(dx, dy)
+
+
+ def show_detail(self, person):
+ "Show detail information for a person"
+
+ # Change selection
+ self.selected = person
+
+ # No selection
+ if person == None:
+ self.detail_name.set_sensitive(False)
+ self.detail_name.get_buffer().set_text("Click one a node to select it")
+ self.detail_chkmale.set_active(True)
+ self.detail_chkmale.set_sensitive(False)
+ self.detail_chkfemale.set_sensitive(False)
+ self.detail_btnaddunion.set_sensitive(False)
+ self.detail_btnaddchild.set_sensitive(False)
+ self.detail_btndelete.set_sensitive(False)
+ return
+
+ # A node is selected
+ self.detail_name.set_sensitive(True)
+ self.detail_name.grab_focus()
+ self.detail_name.get_buffer().set_text(person.name)
+ if person.sex == 'M':
+ self.detail_chkmale.set_active(True)
+ else:
+ self.detail_chkfemale.set_active(True)
+
+ # Compute button status
+ checkable = (len(person.unions) == 0)
+ self.detail_chkmale.set_sensitive(checkable)
+ self.detail_chkfemale.set_sensitive(checkable)
+ self.detail_btnaddchild.set_sensitive(len(person.unions) > 0)
+ unionscount = len(person.unions)
+ childcount = person.child_count()
+ isdescendant = self.tree.is_descendant(person)
+ if person == self.tree.root:
+ deletable = False
+ elif unionscount == 0:
+ deletable = True
+ elif unionscount > 1:
+ deletable = False
+ elif unionscount == 1 and isdescendant:
+ deletable = False
+ elif childcount > 0:
+ deletable = False
+ else:
+ deletable = True
+ self.detail_btndelete.set_sensitive(deletable)
+ self.detail_btnaddunion.set_sensitive(isdescendant)
+
def area_expose_cb(self, area, event):
"Draw tree event"
+
+ # Create context then draw tree inside
gc = self.area.window.cairo_create()
pc = self.create_pango_context()
- self.tree.draw(gc, pc)
+ self.tree.root.draw(gc, pc)
gc.stroke()
+
+ # Draw cross in middle to debug position
+ rect = self.area.allocation
+ gc.move_to((rect.width/2)-10, (rect.height/2))
+ gc.line_to((rect.width/2)+10, (rect.height/2))
+ gc.move_to((rect.width/2), (rect.height/2)-10)
+ gc.line_to((rect.width/2), (rect.height/2)+10)
+ gc.set_source_rgb(255, 255, 255)
+ gc.stroke()
def press_button(self, widget, event):
"Mouse button clicked, detail a person or start the moving mode"
# Look if click in a person
- for p in persons:
+ for p in self.tree.persons:
# Found one, show detail
if p.point_inside(event.x, event.y):
- print p.name
+ self.set_center(p)
+ self.show_detail(p)
self.moving = False
return
# Not found, pass in moving mode
+ self.show_detail(None)
self.movingStart = (event.x, event.y)
self.moving = True
self.area.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
-
+
+ def translate(self, dx, dy):
+ # Translate all the tree from deltax, deltay
+ (self.initx, self.inity) = (self.initx+dx, self.inity+dy)
+ for p in self.tree.persons:
+ p.translate(dx, dy)
+ self.redraw()
+
+
def mouse_move(self, widget, event):
"Mouse move event, in moving mode translate draw if in moving mode else change cursor on person"
# In moving mode ?
if self.moving:
# Compute translation
- (dx, dy) = (event.x-self.movingStart[0], event.y-self.movingStart[1])
- (self.initx, self.inity) = (self.initx+dx, self.inity+dy)
- for p in persons:
- p.translate(dx, dy)
+ self.translate(event.x-self.movingStart[0], event.y-self.movingStart[1])
self.movingStart = (event.x, event.y)
-
- # Redraw
- self.redraw()
else:
# Look if a person is under the cursor
- for p in persons:
+ for p in self.tree.persons:
# Found one, change cursor
if p.point_inside(event.x, event.y):
self.area.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1))
@@ -181,21 +235,85 @@ class RootsActivity(activity.Activity):
def zoom_in(self, event):
"ToolBar button zoom in clicked"
- self.initzoom = self.initzoom + 20
+ self.zoomlevel = self.zoomlevel + 1
+ for p in self.tree.persons:
+ p.scale(20)
self.redraw()
def zoom_out(self, event):
"ToolBar button zoom out clicked"
- self.initzoom = self.initzoom - 20
+ if self.zoomlevel == -3:
+ return
+ self.zoomlevel = self.zoomlevel - 1
+ for p in self.tree.persons:
+ p.scale(-20)
+ self.redraw()
+
+
+ def detail_changed(self, event):
+ "Textfield for the detail has changed, change the matching person name"
+ if self.selected == None:
+ self.redraw()
+ return
+ buffer = self.detail_name.get_buffer()
+ self.selected.name = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
+ self.redraw()
+
+
+ def sexradio_checked(self, widget, data):
+ "Radio button for sex checked, change sex for the selected person"
+ if self.selected is not None:
+ self.selected.sex = data
self.redraw()
+
+ def addchild_clicked(self, event):
+ "Add child clicked"
+ # TODO: Child add to the first union
+ newchild = self.tree.Person("", "M")
+ self.selected.unions[0].append_child(newchild)
+ self.tree.root.compute_draw(self.tree.root.x0, self.tree.root.y0)
+ self.show_detail(newchild)
+
+
+ def addunion_clicked(self, event):
+ "Add union clicked"
+ if self.selected.sex == "M":
+ male = self.selected
+ female = newunion = self.tree.Person("", "F")
+ else:
+ male = newunion = self.tree.Person("", "M")
+ female = self.selected
+ self.tree.Union(male, female)
+ self.tree.root.compute_draw(self.tree.root.x0, self.tree.root.y0)
+ self.show_detail(newunion)
+
+
+ def delete_clicked(self, event):
+ "Delete button clicked"
+ # Remove from union
+ for u in self.selected.unions:
+ self.tree.unions.remove(u)
+ if u.dad == self.selected:
+ u.mum.unions.remove(u)
+ else:
+ u.dad.unions.remove(u)
+
+ # Remove from parent child
+ if self.selected.parents is not None:
+ self.selected.parents.childs.remove(self.selected)
+ # Remove from tree
+ self.tree.persons.remove(self.selected)
+
+ # Recompute and redraw
+ self.tree.root.compute_draw(self.tree.root.x0, self.tree.root.y0)
+ self.show_detail(None)
+
+
def redraw(self):
"Redraw area"
- self.tree.compute_draw(self.initx, self.inity)
- for p in persons:
- p.scale(self.initzoom)
self.area.queue_draw_area(0, 0, self.area.allocation.width, self.area.allocation.height)
diff --git a/src/person.py b/src/person.py
index 094eeb8..75115f5 100644
--- a/src/person.py
+++ b/src/person.py
@@ -15,10 +15,6 @@ const._person_font = 'Arial'
const._person_fontsize = 8
-# Set of person
-persons = []
-
-
# Person class
class Person:
@@ -37,7 +33,6 @@ class Person:
self.parents = None
self.unions = []
(self.x0, self.y0, self.x1, self.y1, self.fontsize) = (-1, -1, -1, -1, const._person_fontsize)
- persons.append(self)
def append_union(self, union):
@@ -47,19 +42,19 @@ class Person:
def tostring(self, level=1):
"Translate to a formatted string, tree is horizontal"
- str = 'P{0}\n'.format(self.id)
+ buf = 'P'+str(self.id)+'\n'
if self.unions == []:
- str += '\t'*level + self.name + " " + self.sex
+ buf += '\t'*level + self.name + " " + self.sex
else:
for u in self.unions:
if self.sex == 'M':
opposite = u.mum
else:
opposite = u.dad
- str += '\t'*level + self.name + " " + self.sex + '\n+' + '\t'*level + opposite.name + " " + opposite.sex + '\n'
- str += u.tostring(level)
+ buf += '\t'*level + self.name + " " + self.sex + '\n+' + '\t'*level + opposite.name + " " + opposite.sex + '\n'
+ buf += u.tostring(level)
- return str
+ return buf
def size_to_draw(self):
@@ -77,6 +72,13 @@ class Person:
return x >= self.x0 and x <= self.x1 and y >= self.y0 and y <= self.y1
+ def child_count(self):
+ totchild = 0
+ for u in self.unions:
+ totchild = totchild + len(u.childs)
+ return totchild
+
+
def draw_node(self, gc, pc):
"Draw a person"
@@ -105,6 +107,7 @@ class Person:
# Set person position
(self.x0, self.y0) = (x, y)
(self.x1, self.y1) = (x+const._person_width, y+const._person_height)
+ self.fontsize = const._person_fontsize
if self.unions != []:
# Set union position
wmargin = const._person_width / const._person_wmargin_ratio
@@ -145,7 +148,7 @@ class Person:
self.y0 = self.y0 + ((self.y0 * scalerate) / 100)
self.x1 = self.x1 + ((self.x1 * scalerate) / 100)
self.y1 = self.y1 + ((self.y1 * scalerate) / 100)
- self.fontsize = const._person_fontsize + (scalerate / 10)
+ self.fontsize = self.fontsize + (scalerate / 10)
def draw(self, gc, pc):
diff --git a/src/tree.py b/src/tree.py
new file mode 100644
index 0000000..1dfa8c9
--- /dev/null
+++ b/src/tree.py
@@ -0,0 +1,103 @@
+
+from person import Person
+from union import Union
+
+
+# Tree class
+class Tree:
+ "Class to represent a tree: a sum of persons and unions"
+
+ def __init__(self):
+ "Constructor, init fields to None"
+ self.root = None
+ self.persons = []
+ self.unions = []
+
+
+ def Person(self, name, sex):
+ "Add a new person"
+ p = Person(name, sex)
+ self.persons.append(p)
+ return p
+
+
+ def Union(self, dad, mum):
+ "Add a new union"
+ u = Union(dad, mum)
+ self.unions.append(u)
+ return u
+
+ def is_descendant(self, person, root=None):
+ "Look is person is a descendant of the tree"
+ start = root
+ if start is None:
+ start = self.root
+ for u in start.unions:
+ for c in u.childs:
+ if c == person:
+ return True
+ if self.is_descendant(person, c):
+ return True
+ return False
+
+
+
+# Create the samples family
+def sample_family1():
+
+ tree = Tree()
+ tree.root = tree.Person("", "M")
+
+ return tree
+
+
+def sample_family2():
+
+ tree = Tree()
+
+ l = tree.Person("Lucien", "M")
+ a = tree.Person("Annick", "F")
+ u = tree.Union(l, a)
+
+ d = tree.Person("Dominique", "M")
+ u.append_child(d)
+ au = tree.Person("Anne", "F")
+ u.append_child(au)
+ m = tree.Person("Madeleine", "F")
+ u.append_child(m)
+
+ jp = tree.Person("Jean-Pierre", "M")
+ j = tree.Person("Julie", "F")
+ up = tree.Union(jp, j)
+ up.append_child(l)
+ c = tree.Person("Christian", "M")
+ up.append_child(c)
+
+ ub = tree.Union(tree.Person("Jonathan", "M"), j)
+ ub.append_child(tree.Person("Charlie", "F"))
+
+ rs = tree.Person("Renee", "F")
+ vm = tree.Person("Vivien", "M")
+ urv = tree.Union(vm, rs)
+ urv.append_child(j)
+
+ jr = tree.Person("Jean-Rene", "M")
+ ua = tree.Union(jr, tree.Person("Micheline", "F"))
+ ua.append_child(a)
+ i = tree.Person("Irene", "F")
+ ua.append_child(i)
+ ui = tree.Union(tree.Person("Nathan", "M"), i)
+ ui.append_child(tree.Person("Marie", "F"))
+ ui.append_child(tree.Person("Noel", "M"))
+ ui.append_child(tree.Person("Thierry", "M"))
+
+ uc = tree.Union(c, tree.Person("Clarah", "F"))
+ sa = tree.Person("Sandrine", "F")
+ uc.append_child(sa)
+ uc2 = tree.Union(c, tree.Person("Vivianne", "F"))
+ uc2.append_child(tree.Person("Pierre", "M"))
+ uc2.append_child(tree.Person("Camille", "F"))
+
+ tree.root = vm
+
+ return tree
diff --git a/src/union.py b/src/union.py
index 05c3a2d..96016c9 100644
--- a/src/union.py
+++ b/src/union.py
@@ -49,6 +49,10 @@ class Union:
def compute_draw(self, x, y):
"Compute union subtree position"
+
+ # Reinit size
+ self.dad.fontsize = const._person_fontsize
+ self.mum.fontsize = const._person_fontsize
# Compute childs size
size = 0
@@ -62,9 +66,8 @@ class Union:
else:
x = x - (size*const._person_width+(size-1)*wmargin)/2
- # Draw each child
+ # Compute draw for childs
for c in self.childs:
- # Draw the child
c.compute_draw(x, y)
size = c.size_to_draw()
x = x + (size*const._person_width+size*wmargin)