Refractor so common base fan chart gramplet and view are shared
Fan chart view allows now configuration: max generation to set, 4 color schemes svn: r20307
This commit is contained in:
parent
455d077c2c
commit
c86f383122
@ -218,6 +218,7 @@ src/gen/utils/debug.py
|
||||
src/gen/utils/file.py
|
||||
src/gen/utils/id.py
|
||||
src/gen/utils/image.py
|
||||
src/gen/utils/libformatting.py
|
||||
|
||||
# gen.utils.docgen
|
||||
src/gen/utils/docgen/__init__.py
|
||||
@ -316,7 +317,6 @@ src/plugins/sidebar/sidebar.gpr.py
|
||||
src/plugins/docgen/pdfdoc.py
|
||||
|
||||
# plugins/lib directory
|
||||
src/plugins/lib/libformatting.py
|
||||
src/plugins/lib/libgrampsxml.py
|
||||
src/plugins/lib/libhtml.py
|
||||
src/plugins/lib/libhtmlbackend.py
|
||||
|
@ -40,7 +40,8 @@ from cgi import escape
|
||||
import gen.lib
|
||||
import gen.datehandler
|
||||
from gen.display.name import displayer as name_displayer
|
||||
from gen.utils.db import get_birth_or_fallback, get_death_or_fallback, get_marriage_or_fallback
|
||||
from gen.utils.db import (get_birth_or_fallback, get_death_or_fallback,
|
||||
get_marriage_or_fallback)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -198,3 +199,4 @@ class FormattingHelper(object):
|
||||
"""
|
||||
self._text_cache = {}
|
||||
self._markup_cache = {}
|
||||
|
@ -1110,7 +1110,7 @@ class GrampsPreferences(ConfigureDialog):
|
||||
self.add_spinner(table,
|
||||
_('Date before range'),
|
||||
2, 'behavior.date-before-range', (1, 80))
|
||||
self.add_spinner(table,
|
||||
self.add_spinner(table,
|
||||
_('Maximum age probably alive'),
|
||||
3, 'behavior.max-age-prob-alive', (80, 140))
|
||||
self.add_spinner(table,
|
||||
|
@ -42,6 +42,7 @@ from gi.repository import Gtk
|
||||
from gi.repository import PangoCairo
|
||||
import math
|
||||
import cPickle as pickle
|
||||
from cgi import escape
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -53,6 +54,8 @@ import gen.lib
|
||||
import gui.utils
|
||||
from gui.ddtargets import DdTargets
|
||||
from gen.utils.alive import probably_alive
|
||||
from gen.utils.libformatting import FormattingHelper
|
||||
from gen.utils.db import (find_children, find_parents, find_witnessed_people)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -78,17 +81,37 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
Interactive Fan Chart Widget.
|
||||
"""
|
||||
BORDER_EDGE_WIDTH = 10
|
||||
GENCOLOR = ((229,191,252),
|
||||
(191,191,252),
|
||||
(191,222,252),
|
||||
(183,219,197),
|
||||
(206,246,209))
|
||||
TRANSLATE_PX = 10
|
||||
|
||||
BACKGROUND_SCHEME1 = 0
|
||||
BACKGROUND_SCHEME2 = 1
|
||||
BACKGROUND_GENDER = 2
|
||||
BACKGROUND_WHITE = 3
|
||||
GENCOLOR = {
|
||||
BACKGROUND_SCHEME1: ((255, 63, 0),
|
||||
(255,175, 15),
|
||||
(255,223, 87),
|
||||
(255,255,111),
|
||||
(159,255,159),
|
||||
(111,215,255),
|
||||
( 79,151,255),
|
||||
(231, 23,255),
|
||||
(231, 23,121),
|
||||
(210,170,124),
|
||||
(189,153,112)),
|
||||
BACKGROUND_SCHEME2: ((229,191,252),
|
||||
(191,191,252),
|
||||
(191,222,252),
|
||||
(183,219,197),
|
||||
(206,246,209)),
|
||||
BACKGROUND_WHITE: ((255,255,255),),
|
||||
}
|
||||
|
||||
COLLAPSED = 0
|
||||
NORMAL = 1
|
||||
EXPANDED = 2
|
||||
|
||||
def __init__(self, generations, dbstate, context_popup_callback=None):
|
||||
def __init__(self, dbstate, callback_popup=None):
|
||||
"""
|
||||
Fan Chart Widget. Handles visualization of data in self.data.
|
||||
See main() of FanChartGramplet for example of model format.
|
||||
@ -96,6 +119,7 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
GObject.GObject.__init__(self)
|
||||
self.dbstate = dbstate
|
||||
self.translating = False
|
||||
self.on_popup = callback_popup
|
||||
self.last_x, self.last_y = None, None
|
||||
self.connect("button_release_event", self.on_mouse_up)
|
||||
self.connect("motion_notify_event", self.on_mouse_move)
|
||||
@ -104,7 +128,6 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
self.connect("drag_data_get", self.on_drag_data_get)
|
||||
self.connect("drag_begin", self.on_drag_begin)
|
||||
self.connect("drag_end", self.on_drag_end)
|
||||
self.context_popup_callback = context_popup_callback
|
||||
self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK |
|
||||
Gdk.EventMask.BUTTON_RELEASE_MASK |
|
||||
Gdk.EventMask.POINTER_MOTION_MASK)
|
||||
@ -122,22 +145,26 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
self.pixels_per_generation = 50 # size of radius for generation
|
||||
## gotten from experiments with "sans serif 8":
|
||||
self.degrees_per_radius = .80
|
||||
self._mouse_click = False
|
||||
## Other fonts will have different settings. Can you compute that
|
||||
## from the font size? I have no idea.
|
||||
self.generations = generations
|
||||
self._mouse_click = False
|
||||
self.rotate_value = 90 # degrees, initially, 1st gen male on right half
|
||||
self.center_xy = [0, 0] # distance from center (x, y)
|
||||
self.set_generations(self.generations)
|
||||
self.center = 50 # pixel radius of center
|
||||
self.gen_color = True
|
||||
#default values
|
||||
self.reset(9, self.BACKGROUND_SCHEME1)
|
||||
self.set_size_request(120, 120)
|
||||
|
||||
def reset_generations(self):
|
||||
def reset(self, maxgen, background):
|
||||
"""
|
||||
Reset all of the data on where slices appear, and if they are expanded.
|
||||
Reset all of the data on where/how slices appear, and if they are expanded.
|
||||
"""
|
||||
self.set_generations(self.generations)
|
||||
self.background = background
|
||||
if self.background == self.BACKGROUND_GENDER:
|
||||
self.colors = None
|
||||
else:
|
||||
self.colors = self.GENCOLOR[self.background]
|
||||
self.set_generations(maxgen)
|
||||
|
||||
def set_generations(self, generations):
|
||||
"""
|
||||
@ -269,19 +296,19 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height
|
||||
start_rad = start * math.pi/180
|
||||
stop_rad = stop * math.pi/180
|
||||
if self.gen_color:
|
||||
r,g,b = self.GENCOLOR[generation % len(self.GENCOLOR)]
|
||||
if gender == gen.lib.Person.MALE:
|
||||
r *= .9
|
||||
g *= .9
|
||||
b *= .9
|
||||
else:
|
||||
if self.background == self.BACKGROUND_GENDER:
|
||||
try:
|
||||
alive = probably_alive(person, self.dbstate.db)
|
||||
except RuntimeError:
|
||||
alive = False
|
||||
backgr, border = gui.utils.color_graph_box(alive, person.gender)
|
||||
r, g, b = gui.utils.hex_to_rgb(backgr)
|
||||
else:
|
||||
r,g,b = self.colors[generation % len(self.colors)]
|
||||
if gender == gen.lib.Person.MALE:
|
||||
r *= .9
|
||||
g *= .9
|
||||
b *= .9
|
||||
radius = generation * self.pixels_per_generation + self.center
|
||||
# If max generation, and they have parents:
|
||||
if generation == self.generations - 1 and parents:
|
||||
@ -572,8 +599,8 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
# Do things based on state, event.get_state(), or button, event.button
|
||||
if gui.utils.is_right_click(event):
|
||||
text, person, parents, child = self.data[generation][selected]
|
||||
if person and self.context_popup_callback:
|
||||
self.context_popup_callback(widget, event, person.handle)
|
||||
if person and self.on_popup:
|
||||
self.on_popup(widget, event, person.handle)
|
||||
return True
|
||||
|
||||
return False
|
||||
@ -619,3 +646,358 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
sel_data.set(sel_data.get_target(), 8, pickle.dumps(data))
|
||||
elif ('TEXT' in tgs or 'text/plain' in tgs) and info == 0L:
|
||||
sel_data.set_text(self.format_helper.format_person(person, 11), -1)
|
||||
|
||||
class FanChartGrampsGUI(object):
|
||||
""" class for functions fanchart GUI elements will need in Gramps
|
||||
"""
|
||||
def __init__(self, maxgen, background, on_childmenu_changed):
|
||||
"""
|
||||
Common part of GUI that shows Fan Chart, needs to know what to do if
|
||||
one moves via Fan Chart to a new person
|
||||
on_childmenu_changed: in popup, function called on moving to a new person
|
||||
"""
|
||||
self.fan = None
|
||||
self.on_childmenu_changed = on_childmenu_changed
|
||||
self.format_helper = FormattingHelper(self.dbstate)
|
||||
|
||||
self.maxgen = maxgen
|
||||
self.background = background
|
||||
|
||||
def have_parents(self, person):
|
||||
"""
|
||||
Returns True if a person has parents.
|
||||
"""
|
||||
if person:
|
||||
m = self.get_parent(person, False)
|
||||
f = self.get_parent(person, True)
|
||||
return not m is f is None
|
||||
return False
|
||||
|
||||
def have_children(self, person):
|
||||
"""
|
||||
Returns True if a person has children.
|
||||
"""
|
||||
if person:
|
||||
for family_handle in person.get_family_handle_list():
|
||||
family = self.dbstate.db.get_family_from_handle(family_handle)
|
||||
if family and len(family.get_child_ref_list()) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_parent(self, person, father):
|
||||
"""
|
||||
Get the father of the family if father == True, otherwise mother
|
||||
"""
|
||||
if person:
|
||||
parent_handle_list = person.get_parent_family_handle_list()
|
||||
if parent_handle_list:
|
||||
family_id = parent_handle_list[0]
|
||||
family = self.dbstate.db.get_family_from_handle(family_id)
|
||||
if family:
|
||||
if father:
|
||||
person_handle = gen.lib.Family.get_father_handle(family)
|
||||
else:
|
||||
person_handle = gen.lib.Family.get_mother_handle(family)
|
||||
if person_handle:
|
||||
return self.dbstate.db.get_person_from_handle(person_handle)
|
||||
return None
|
||||
|
||||
def set_fan(self, fan):
|
||||
"""
|
||||
Set the fanchartwidget to work on
|
||||
"""
|
||||
self.fan = fan
|
||||
self.fan.format_helper = self.format_helper
|
||||
|
||||
def main(self):
|
||||
"""
|
||||
Fill the data structures with the active data. This initializes all
|
||||
data.
|
||||
"""
|
||||
self.fan.reset(self.maxgen, self.background)
|
||||
person = self.dbstate.db.get_person_from_handle(self.get_active('Person'))
|
||||
if not person:
|
||||
name = None
|
||||
else:
|
||||
name = name_displayer.display(person)
|
||||
parents = self.have_parents(person)
|
||||
child = self.have_children(person)
|
||||
self.fan.data[0][0] = (name, person, parents, child)
|
||||
for current in range(1, self.maxgen):
|
||||
parent = 0
|
||||
# name, person, parents, children
|
||||
for (n,p,q,c) in self.fan.data[current - 1]:
|
||||
# Get father's details:
|
||||
person = self.get_parent(p, True)
|
||||
if person:
|
||||
name = name_displayer.display(person)
|
||||
else:
|
||||
name = None
|
||||
if current == self.maxgen - 1:
|
||||
parents = self.have_parents(person)
|
||||
else:
|
||||
parents = None
|
||||
self.fan.data[current][parent] = (name, person, parents, None)
|
||||
if person is None:
|
||||
# start,stop,male/right,state
|
||||
self.fan.angle[current][parent][3] = self.fan.COLLAPSED
|
||||
parent += 1
|
||||
# Get mother's details:
|
||||
person = self.get_parent(p, False)
|
||||
if person:
|
||||
name = name_displayer.display(person)
|
||||
else:
|
||||
name = None
|
||||
if current == self.maxgen - 1:
|
||||
parents = self.have_parents(person)
|
||||
else:
|
||||
parents = None
|
||||
self.fan.data[current][parent] = (name, person, parents, None)
|
||||
if person is None:
|
||||
# start,stop,male/right,state
|
||||
self.fan.angle[current][parent][3] = self.fan.COLLAPSED
|
||||
parent += 1
|
||||
self.fan.queue_draw()
|
||||
|
||||
def on_popup(self, obj, event, person_handle):
|
||||
"""
|
||||
Builds the full menu (including Siblings, Spouses, Children,
|
||||
and Parents) with navigation. Copied from PedigreeView.
|
||||
"""
|
||||
#store menu for GTK3 to avoid it being destroyed before showing
|
||||
self.menu = Gtk.Menu()
|
||||
menu = self.menu
|
||||
menu.set_title(_('People Menu'))
|
||||
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if not person:
|
||||
return 0
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO,Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
go_item = Gtk.ImageMenuItem(name_displayer.display(person))
|
||||
go_item.set_image(go_image)
|
||||
go_item.connect("activate",self.on_childmenu_changed,person_handle)
|
||||
go_item.show()
|
||||
menu.append(go_item)
|
||||
|
||||
edit_item = Gtk.ImageMenuItem.new_from_stock(stock_id=Gtk.STOCK_EDIT, accel_group=None)
|
||||
edit_item.connect("activate", self.edit_person_cb, person_handle)
|
||||
edit_item.show()
|
||||
menu.append(edit_item)
|
||||
|
||||
clipboard_item = Gtk.ImageMenuItem.new_from_stock(stock_id=Gtk.STOCK_COPY, accel_group=None)
|
||||
clipboard_item.connect("activate",self.copy_person_to_clipboard_cb,person_handle)
|
||||
clipboard_item.show()
|
||||
menu.append(clipboard_item)
|
||||
|
||||
# collect all spouses, parents and children
|
||||
linked_persons = []
|
||||
|
||||
# Go over spouses and build their menu
|
||||
item = Gtk.MenuItem(label=_("Spouses"))
|
||||
fam_list = person.get_family_handle_list()
|
||||
no_spouses = 1
|
||||
for fam_id in fam_list:
|
||||
family = self.dbstate.db.get_family_from_handle(fam_id)
|
||||
if family.get_father_handle() == person.get_handle():
|
||||
sp_id = family.get_mother_handle()
|
||||
else:
|
||||
sp_id = family.get_father_handle()
|
||||
spouse = self.dbstate.db.get_person_from_handle(sp_id)
|
||||
if not spouse:
|
||||
continue
|
||||
|
||||
if no_spouses:
|
||||
no_spouses = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
sp_menu = item.get_submenu()
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
sp_item = Gtk.ImageMenuItem(name_displayer.display(spouse))
|
||||
sp_item.set_image(go_image)
|
||||
linked_persons.append(sp_id)
|
||||
sp_item.connect("activate",self.on_childmenu_changed, sp_id)
|
||||
sp_item.show()
|
||||
sp_menu.append(sp_item)
|
||||
|
||||
if no_spouses:
|
||||
item.set_sensitive(0)
|
||||
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over siblings and build their menu
|
||||
item = Gtk.MenuItem(label=_("Siblings"))
|
||||
pfam_list = person.get_parent_family_handle_list()
|
||||
no_siblings = 1
|
||||
for f in pfam_list:
|
||||
fam = self.dbstate.db.get_family_from_handle(f)
|
||||
sib_list = fam.get_child_ref_list()
|
||||
for sib_ref in sib_list:
|
||||
sib_id = sib_ref.ref
|
||||
if sib_id == person.get_handle():
|
||||
continue
|
||||
sib = self.dbstate.db.get_person_from_handle(sib_id)
|
||||
if not sib:
|
||||
continue
|
||||
|
||||
if no_siblings:
|
||||
no_siblings = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
sib_menu = item.get_submenu()
|
||||
|
||||
if find_children(self.dbstate.db,sib):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(sib)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(sib)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
sib_item = Gtk.ImageMenuItem(None)
|
||||
sib_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
sib_item.add(label)
|
||||
linked_persons.append(sib_id)
|
||||
sib_item.connect("activate", self.on_childmenu_changed, sib_id)
|
||||
sib_item.show()
|
||||
sib_menu.append(sib_item)
|
||||
|
||||
if no_siblings:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over children and build their menu
|
||||
item = Gtk.MenuItem(label=_("Children"))
|
||||
no_children = 1
|
||||
childlist = find_children(self.dbstate.db, person)
|
||||
for child_handle in childlist:
|
||||
child = self.dbstate.db.get_person_from_handle(child_handle)
|
||||
if not child:
|
||||
continue
|
||||
|
||||
if no_children:
|
||||
no_children = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
child_menu = item.get_submenu()
|
||||
|
||||
if find_children(self.dbstate.db,child):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(child)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(child)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
child_item = Gtk.ImageMenuItem(None)
|
||||
child_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
child_item.add(label)
|
||||
linked_persons.append(child_handle)
|
||||
child_item.connect("activate", self.on_childmenu_changed, child_handle)
|
||||
child_item.show()
|
||||
child_menu.append(child_item)
|
||||
|
||||
if no_children:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over parents and build their menu
|
||||
item = Gtk.MenuItem(label=_("Parents"))
|
||||
no_parents = 1
|
||||
par_list = find_parents(self.dbstate.db,person)
|
||||
for par_id in par_list:
|
||||
par = self.dbstate.db.get_person_from_handle(par_id)
|
||||
if not par:
|
||||
continue
|
||||
|
||||
if no_parents:
|
||||
no_parents = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
par_menu = item.get_submenu()
|
||||
|
||||
if find_parents(self.dbstate.db,par):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(par)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(par)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
par_item = Gtk.ImageMenuItem(None)
|
||||
par_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
par_item.add(label)
|
||||
linked_persons.append(par_id)
|
||||
par_item.connect("activate",self.on_childmenu_changed, par_id)
|
||||
par_item.show()
|
||||
par_menu.append(par_item)
|
||||
|
||||
if no_parents:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over parents and build their menu
|
||||
item = Gtk.MenuItem(label=_("Related"))
|
||||
no_related = 1
|
||||
for p_id in find_witnessed_people(self.dbstate.db,person):
|
||||
#if p_id in linked_persons:
|
||||
# continue # skip already listed family members
|
||||
|
||||
per = self.dbstate.db.get_person_from_handle(p_id)
|
||||
if not per:
|
||||
continue
|
||||
|
||||
if no_related:
|
||||
no_related = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
per_menu = item.get_submenu()
|
||||
|
||||
label = Gtk.Label(label=escape(name_displayer.display(per)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
per_item = Gtk.ImageMenuItem(None)
|
||||
per_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0, 0)
|
||||
per_item.add(label)
|
||||
per_item.connect("activate", self.on_childmenu_changed, p_id)
|
||||
per_item.show()
|
||||
per_menu.append(per_item)
|
||||
|
||||
if no_related:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
return 1
|
||||
|
||||
def edit_person_cb(self, obj,person_handle):
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
try:
|
||||
EditPerson(self.dbstate, self.uistate, [], person)
|
||||
except WindowActiveError:
|
||||
pass
|
||||
return True
|
||||
return False
|
||||
|
||||
def copy_person_to_clipboard_cb(self, obj,person_handle):
|
||||
"""Renders the person data into some lines of text and puts that into the clipboard"""
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
cb = Gtk.Clipboard.get_for_display(Gdk.Display.get_default(),
|
||||
Gdk.SELECTION_CLIPBOARD)
|
||||
cb.set_text( self.format_helper.format_person(person,11))
|
||||
return True
|
||||
return False
|
||||
|
@ -38,7 +38,6 @@ from gi.repository import Pango
|
||||
from gi.repository import Gtk
|
||||
import math
|
||||
from gi.repository import Gdk
|
||||
from cgi import escape
|
||||
try:
|
||||
import cairo
|
||||
except ImportError:
|
||||
@ -49,33 +48,31 @@ except ImportError:
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gen.display.name import displayer as name_displayer
|
||||
from gen.ggettext import gettext as _
|
||||
from gen.plug import Gramplet
|
||||
from gen.utils.db import (find_children, find_parents, find_witnessed_people)
|
||||
from libformatting import FormattingHelper
|
||||
import gen.lib
|
||||
from gen.errors import WindowActiveError
|
||||
from gui.editors import EditPerson
|
||||
import gui.utils
|
||||
from gui.widgets.fanchart import FanChartWidget
|
||||
from gui.widgets.fanchart import FanChartWidget, FanChartGrampsGUI
|
||||
|
||||
class FanChartGramplet(Gramplet):
|
||||
class FanChartGramplet(FanChartGrampsGUI, Gramplet):
|
||||
"""
|
||||
The Gramplet code that realizes the FanChartWidget.
|
||||
"""
|
||||
def init(self):
|
||||
self.set_tooltip(_("Click to expand/contract person\nRight-click for options\nClick and drag in open area to rotate"))
|
||||
self.generations = 6
|
||||
self.format_helper = FormattingHelper(self.dbstate)
|
||||
self.gui.fan = FanChartWidget(self.generations, self.dbstate,
|
||||
context_popup_callback=self.on_popup)
|
||||
self.gui.fan.format_helper = self.format_helper
|
||||
|
||||
def __init__(self, gui, nav_group=0):
|
||||
Gramplet.__init__(self, gui, nav_group)
|
||||
FanChartGrampsGUI.__init__(self, 6, 0, self.on_childmenu_changed)
|
||||
self.set_fan(FanChartWidget(self.dbstate, self.on_popup))
|
||||
# Replace the standard textview with the fan chart widget:
|
||||
self.gui.get_container_widget().remove(self.gui.textview)
|
||||
self.gui.get_container_widget().add_with_viewport(self.gui.fan)
|
||||
self.gui.get_container_widget().add_with_viewport(self.fan)
|
||||
# Make sure it is visible:
|
||||
self.gui.fan.show()
|
||||
self.fan.show()
|
||||
|
||||
def init(self):
|
||||
self.set_tooltip(_("Click to expand/contract person\nRight-click for options\nClick and drag in open area to rotate"))
|
||||
|
||||
def active_changed(self, handle):
|
||||
"""
|
||||
@ -84,340 +81,8 @@ class FanChartGramplet(Gramplet):
|
||||
# Reset everything but rotation angle (leave it as is)
|
||||
self.update()
|
||||
|
||||
def have_parents(self, person):
|
||||
"""
|
||||
Returns True if a person has parents.
|
||||
"""
|
||||
if person:
|
||||
m = self.get_parent(person, "female")
|
||||
f = self.get_parent(person, "male")
|
||||
return not m is f is None
|
||||
return False
|
||||
|
||||
def have_children(self, person):
|
||||
"""
|
||||
Returns True if a person has children.
|
||||
"""
|
||||
if person:
|
||||
for family_handle in person.get_family_handle_list():
|
||||
family = self.dbstate.db.get_family_from_handle(family_handle)
|
||||
if family and len(family.get_child_ref_list()) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_parent(self, person, gender):
|
||||
"""
|
||||
Get the father if gender == "male", or get mother otherwise.
|
||||
"""
|
||||
if person:
|
||||
parent_handle_list = person.get_parent_family_handle_list()
|
||||
if parent_handle_list:
|
||||
family_id = parent_handle_list[0]
|
||||
family = self.dbstate.db.get_family_from_handle(family_id)
|
||||
if family:
|
||||
if gender == "male":
|
||||
person_handle = gen.lib.Family.get_father_handle(family)
|
||||
else:
|
||||
person_handle = gen.lib.Family.get_mother_handle(family)
|
||||
if person_handle:
|
||||
return self.dbstate.db.get_person_from_handle(person_handle)
|
||||
return None
|
||||
|
||||
def main(self):
|
||||
"""
|
||||
Fill the data structures with the active data. This initializes all
|
||||
data.
|
||||
"""
|
||||
self.gui.fan.reset_generations()
|
||||
active_handle = self.get_active('Person')
|
||||
person = self.dbstate.db.get_person_from_handle(active_handle)
|
||||
if not person:
|
||||
name = None
|
||||
else:
|
||||
name = name_displayer.display(person)
|
||||
parents = self.have_parents(person)
|
||||
child = self.have_children(person)
|
||||
self.gui.fan.data[0][0] = (name, person, parents, child)
|
||||
for current in range(1, self.generations):
|
||||
parent = 0
|
||||
# name, person, parents, children
|
||||
for (n,p,q,c) in self.gui.fan.data[current - 1]:
|
||||
# Get father's details:
|
||||
person = self.get_parent(p, "male")
|
||||
if person:
|
||||
name = name_displayer.display(person)
|
||||
else:
|
||||
name = None
|
||||
if current == self.generations - 1:
|
||||
parents = self.have_parents(person)
|
||||
else:
|
||||
parents = None
|
||||
self.gui.fan.data[current][parent] = (name, person, parents, None)
|
||||
if person is None:
|
||||
# start,stop,male/right,state
|
||||
self.gui.fan.angle[current][parent][3] = self.gui.fan.COLLAPSED
|
||||
parent += 1
|
||||
# Get mother's details:
|
||||
person = self.get_parent(p, "female")
|
||||
if person:
|
||||
name = name_displayer.display(person)
|
||||
else:
|
||||
name = None
|
||||
if current == self.generations - 1:
|
||||
parents = self.have_parents(person)
|
||||
else:
|
||||
parents = None
|
||||
self.gui.fan.data[current][parent] = (name, person, parents, None)
|
||||
if person is None:
|
||||
# start,stop,male/right,state
|
||||
self.gui.fan.angle[current][parent][3] = self.gui.fan.COLLAPSED
|
||||
parent += 1
|
||||
self.gui.fan.queue_draw()
|
||||
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
self.set_active('Person', person_handle)
|
||||
return True
|
||||
|
||||
def edit_person_cb(self, obj,person_handle):
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
try:
|
||||
EditPerson(self.dbstate, self.uistate, [], person)
|
||||
except WindowActiveError:
|
||||
pass
|
||||
return True
|
||||
return False
|
||||
|
||||
def copy_person_to_clipboard_cb(self, obj,person_handle):
|
||||
"""Renders the person data into some lines of text and puts that into the clipboard"""
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
cb = Gtk.Clipboard.get_for_display(Gdk.Display.get_default(),
|
||||
Gdk.SELECTION_CLIPBOARD)
|
||||
cb.set_text( self.format_helper.format_person(person,11))
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_popup(self, obj, event, person_handle):
|
||||
"""
|
||||
Builds the full menu (including Siblings, Spouses, Children,
|
||||
and Parents) with navigation. Copied from PedigreeView.
|
||||
"""
|
||||
|
||||
menu = Gtk.Menu()
|
||||
menu.set_title(_('People Menu'))
|
||||
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if not person:
|
||||
return 0
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO,Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
go_item = Gtk.ImageMenuItem(name_displayer.display(person))
|
||||
go_item.set_image(go_image)
|
||||
go_item.connect("activate",self.on_childmenu_changed,person_handle)
|
||||
go_item.show()
|
||||
menu.append(go_item)
|
||||
|
||||
edit_item = Gtk.ImageMenuItem(Gtk.STOCK_EDIT)
|
||||
edit_item.connect("activate",self.edit_person_cb,person_handle)
|
||||
edit_item.show()
|
||||
menu.append(edit_item)
|
||||
|
||||
clipboard_item = Gtk.ImageMenuItem(Gtk.STOCK_COPY)
|
||||
clipboard_item.connect("activate",self.copy_person_to_clipboard_cb,person_handle)
|
||||
clipboard_item.show()
|
||||
menu.append(clipboard_item)
|
||||
|
||||
# collect all spouses, parents and children
|
||||
linked_persons = []
|
||||
|
||||
# Go over spouses and build their menu
|
||||
item = Gtk.MenuItem(label=_("Spouses"))
|
||||
fam_list = person.get_family_handle_list()
|
||||
no_spouses = 1
|
||||
for fam_id in fam_list:
|
||||
family = self.dbstate.db.get_family_from_handle(fam_id)
|
||||
if family.get_father_handle() == person.get_handle():
|
||||
sp_id = family.get_mother_handle()
|
||||
else:
|
||||
sp_id = family.get_father_handle()
|
||||
spouse = self.dbstate.db.get_person_from_handle(sp_id)
|
||||
if not spouse:
|
||||
continue
|
||||
|
||||
if no_spouses:
|
||||
no_spouses = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
sp_menu = item.get_submenu()
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO,Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
sp_item = Gtk.ImageMenuItem(name_displayer.display(spouse))
|
||||
sp_item.set_image(go_image)
|
||||
linked_persons.append(sp_id)
|
||||
sp_item.connect("activate",self.on_childmenu_changed,sp_id)
|
||||
sp_item.show()
|
||||
sp_menu.append(sp_item)
|
||||
|
||||
if no_spouses:
|
||||
item.set_sensitive(0)
|
||||
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over siblings and build their menu
|
||||
item = Gtk.MenuItem(label=_("Siblings"))
|
||||
pfam_list = person.get_parent_family_handle_list()
|
||||
no_siblings = 1
|
||||
for f in pfam_list:
|
||||
fam = self.dbstate.db.get_family_from_handle(f)
|
||||
sib_list = fam.get_child_ref_list()
|
||||
for sib_ref in sib_list:
|
||||
sib_id = sib_ref.ref
|
||||
if sib_id == person.get_handle():
|
||||
continue
|
||||
sib = self.dbstate.db.get_person_from_handle(sib_id)
|
||||
if not sib:
|
||||
continue
|
||||
|
||||
if no_siblings:
|
||||
no_siblings = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
sib_menu = item.get_submenu()
|
||||
|
||||
if find_children(self.dbstate.db,sib):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(sib)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(sib)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO,Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
sib_item = Gtk.ImageMenuItem(None)
|
||||
sib_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
sib_item.add(label)
|
||||
linked_persons.append(sib_id)
|
||||
sib_item.connect("activate",self.on_childmenu_changed,sib_id)
|
||||
sib_item.show()
|
||||
sib_menu.append(sib_item)
|
||||
|
||||
if no_siblings:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over children and build their menu
|
||||
item = Gtk.MenuItem(label=_("Children"))
|
||||
no_children = 1
|
||||
childlist = find_children(self.dbstate.db,person)
|
||||
for child_handle in childlist:
|
||||
child = self.dbstate.db.get_person_from_handle(child_handle)
|
||||
if not child:
|
||||
continue
|
||||
|
||||
if no_children:
|
||||
no_children = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
child_menu = item.get_submenu()
|
||||
|
||||
if find_children(self.dbstate.db,child):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(child)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(child)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO,Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
child_item = Gtk.ImageMenuItem(None)
|
||||
child_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
child_item.add(label)
|
||||
linked_persons.append(child_handle)
|
||||
child_item.connect("activate",self.on_childmenu_changed,child_handle)
|
||||
child_item.show()
|
||||
child_menu.append(child_item)
|
||||
|
||||
if no_children:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over parents and build their menu
|
||||
item = Gtk.MenuItem(label=_("Parents"))
|
||||
no_parents = 1
|
||||
par_list = find_parents(self.dbstate.db,person)
|
||||
for par_id in par_list:
|
||||
par = self.dbstate.db.get_person_from_handle(par_id)
|
||||
if not par:
|
||||
continue
|
||||
|
||||
if no_parents:
|
||||
no_parents = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
par_menu = item.get_submenu()
|
||||
|
||||
if find_parents(self.dbstate.db,par):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(par)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(par)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO,Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
par_item = Gtk.ImageMenuItem(None)
|
||||
par_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
par_item.add(label)
|
||||
linked_persons.append(par_id)
|
||||
par_item.connect("activate",self.on_childmenu_changed,par_id)
|
||||
par_item.show()
|
||||
par_menu.append(par_item)
|
||||
|
||||
if no_parents:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over parents and build their menu
|
||||
item = Gtk.MenuItem(label=_("Related"))
|
||||
no_related = 1
|
||||
for p_id in find_witnessed_people(self.dbstate.db,person):
|
||||
#if p_id in linked_persons:
|
||||
# continue # skip already listed family members
|
||||
|
||||
per = self.dbstate.db.get_person_from_handle(p_id)
|
||||
if not per:
|
||||
continue
|
||||
|
||||
if no_related:
|
||||
no_related = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
per_menu = item.get_submenu()
|
||||
|
||||
label = Gtk.Label(label=escape(name_displayer.display(per)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO,Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
per_item = Gtk.ImageMenuItem(None)
|
||||
per_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
per_item.add(label)
|
||||
per_item.connect("activate",self.on_childmenu_changed,p_id)
|
||||
per_item.show()
|
||||
per_menu.append(per_item)
|
||||
|
||||
if no_related:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
return 1
|
||||
|
@ -146,4 +146,3 @@ class QuickViewGramplet(Gramplet):
|
||||
qv_list = get_quick_report_list(code_map[qv_option.get_value()])
|
||||
for pdata in qv_list:
|
||||
list_option.add_item(pdata.id, pdata.name)
|
||||
|
||||
|
@ -17,7 +17,6 @@ SUBDIRS = maps
|
||||
|
||||
pkgpython_PYTHON = \
|
||||
libcairodoc.py\
|
||||
libformatting.py\
|
||||
libgedcom.py\
|
||||
libgrampsxml.py\
|
||||
libgrdb.py\
|
||||
|
@ -40,24 +40,6 @@ authors_email = ["http://gramps-project.org"],
|
||||
#load_on_reg = True
|
||||
)
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# libformatting
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
register(GENERAL,
|
||||
id = 'libformatting',
|
||||
name = "FormattingHelper lib",
|
||||
description = _("Provides a FormattingHelper class for common strings"),
|
||||
version = '1.0',
|
||||
gramps_target_version = '4.0',
|
||||
status = STABLE,
|
||||
fname = 'libformatting.py',
|
||||
authors = ["The Gramps project"],
|
||||
authors_email = ["http://gramps-project.org"],
|
||||
#load_on_reg = True
|
||||
)
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# libgedcom
|
||||
|
@ -50,7 +50,7 @@ from gi.repository import GdkPixbuf
|
||||
import gen.lib
|
||||
from gen.display.name import displayer as _nd
|
||||
from gui.views.navigationview import NavigationView
|
||||
from libformatting import FormattingHelper
|
||||
from gen.utils.libformatting import FormattingHelper
|
||||
from gen.errors import WindowActiveError
|
||||
from gen.const import HOME_DIR, ROOT_DIR
|
||||
from gui.managedwindow import ManagedWindow
|
||||
|
@ -35,7 +35,6 @@
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
import cairo
|
||||
from cgi import escape
|
||||
from gen.ggettext import gettext as _
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -43,37 +42,40 @@ from gen.ggettext import gettext as _
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gen.display.name import displayer as name_displayer
|
||||
from gen.utils.db import (find_children, find_parents, find_witnessed_people)
|
||||
from libformatting import FormattingHelper
|
||||
import gen.lib
|
||||
from gui.widgets.fanchart import FanChartWidget
|
||||
from gui.widgets.fanchart import FanChartWidget, FanChartGrampsGUI
|
||||
from gui.views.navigationview import NavigationView
|
||||
from gen.errors import WindowActiveError
|
||||
from gui.views.bookmarks import PersonBookmarks
|
||||
from gui.editors import EditPerson
|
||||
|
||||
|
||||
# the print settings to remember between print sessions
|
||||
PRINT_SETTINGS = None
|
||||
|
||||
class FanChartView(NavigationView):
|
||||
class FanChartView(FanChartGrampsGUI, NavigationView):
|
||||
"""
|
||||
The Gramplet code that realizes the FanChartWidget.
|
||||
"""
|
||||
#settings in the config file
|
||||
CONFIGSETTINGS = (
|
||||
('interface.fanview-maxgen', 9),
|
||||
('interface.fanview-background', 0),
|
||||
)
|
||||
def __init__(self, pdata, dbstate, uistate, nav_group=0):
|
||||
self.dbstate = dbstate
|
||||
self.uistate = uistate
|
||||
NavigationView.__init__(self, _('Fan Chart'),
|
||||
pdata, dbstate, uistate,
|
||||
dbstate.db.get_bookmarks(),
|
||||
PersonBookmarks,
|
||||
nav_group)
|
||||
nav_group)
|
||||
FanChartGrampsGUI.__init__(self,
|
||||
self._config.get('interface.fanview-maxgen'),
|
||||
self._config.get('interface.fanview-background'),
|
||||
self.on_childmenu_changed)
|
||||
|
||||
dbstate.connect('active-changed', self.active_changed)
|
||||
dbstate.connect('database-changed', self.change_db)
|
||||
self.dbstate = dbstate
|
||||
self.uistate = uistate
|
||||
self.generations = 9
|
||||
self.format_helper = FormattingHelper(self.dbstate)
|
||||
|
||||
self.additional_uis.append(self.additional_ui())
|
||||
|
||||
@ -81,9 +83,7 @@ class FanChartView(NavigationView):
|
||||
return 'Person'
|
||||
|
||||
def build_widget(self):
|
||||
self.fan = FanChartWidget(self.generations, self.dbstate,
|
||||
context_popup_callback=self.on_popup)
|
||||
self.fan.format_helper = self.format_helper
|
||||
self.set_fan(FanChartWidget(self.dbstate, self.on_popup))
|
||||
self.scrolledwindow = Gtk.ScrolledWindow(None, None)
|
||||
self.scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC,
|
||||
Gtk.PolicyType.AUTOMATIC)
|
||||
@ -188,110 +188,10 @@ class FanChartView(NavigationView):
|
||||
self.change_active(handle)
|
||||
self.main()
|
||||
|
||||
def have_parents(self, person):
|
||||
def get_active(self, object):
|
||||
"""overrule get_active, to support call as in Gramplets
|
||||
"""
|
||||
Returns True if a person has parents.
|
||||
"""
|
||||
if person:
|
||||
m = self.get_parent(person, False)
|
||||
f = self.get_parent(person, True)
|
||||
return not m is f is None
|
||||
return False
|
||||
|
||||
def have_children(self, person):
|
||||
"""
|
||||
Returns True if a person has children.
|
||||
"""
|
||||
if person:
|
||||
for family_handle in person.get_family_handle_list():
|
||||
family = self.dbstate.db.get_family_from_handle(family_handle)
|
||||
if family and len(family.get_child_ref_list()) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_parent(self, person, father):
|
||||
"""
|
||||
Get the father of the family if father == True, otherwise mother
|
||||
"""
|
||||
if person:
|
||||
parent_handle_list = person.get_parent_family_handle_list()
|
||||
if parent_handle_list:
|
||||
family_id = parent_handle_list[0]
|
||||
family = self.dbstate.db.get_family_from_handle(family_id)
|
||||
if family:
|
||||
if father:
|
||||
person_handle = gen.lib.Family.get_father_handle(family)
|
||||
else:
|
||||
person_handle = gen.lib.Family.get_mother_handle(family)
|
||||
if person_handle:
|
||||
return self.dbstate.db.get_person_from_handle(person_handle)
|
||||
return None
|
||||
|
||||
def main(self):
|
||||
"""
|
||||
Fill the data structures with the active data. This initializes all
|
||||
data.
|
||||
"""
|
||||
self.fan.reset_generations()
|
||||
person = self.dbstate.db.get_person_from_handle(self.get_active())
|
||||
if not person:
|
||||
name = None
|
||||
else:
|
||||
name = name_displayer.display(person)
|
||||
parents = self.have_parents(person)
|
||||
child = self.have_children(person)
|
||||
self.fan.data[0][0] = (name, person, parents, child)
|
||||
for current in range(1, self.generations):
|
||||
parent = 0
|
||||
# name, person, parents, children
|
||||
for (n,p,q,c) in self.fan.data[current - 1]:
|
||||
# Get father's details:
|
||||
person = self.get_parent(p, True)
|
||||
if person:
|
||||
name = name_displayer.display(person)
|
||||
else:
|
||||
name = None
|
||||
if current == self.generations - 1:
|
||||
parents = self.have_parents(person)
|
||||
else:
|
||||
parents = None
|
||||
self.fan.data[current][parent] = (name, person, parents, None)
|
||||
if person is None:
|
||||
# start,stop,male/right,state
|
||||
self.fan.angle[current][parent][3] = self.fan.COLLAPSED
|
||||
parent += 1
|
||||
# Get mother's details:
|
||||
person = self.get_parent(p, False)
|
||||
if person:
|
||||
name = name_displayer.display(person)
|
||||
else:
|
||||
name = None
|
||||
if current == self.generations - 1:
|
||||
parents = self.have_parents(person)
|
||||
else:
|
||||
parents = None
|
||||
self.fan.data[current][parent] = (name, person, parents, None)
|
||||
if person is None:
|
||||
# start,stop,male/right,state
|
||||
self.fan.angle[current][parent][3] = self.fan.COLLAPSED
|
||||
parent += 1
|
||||
self.fan.queue_draw()
|
||||
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
self.change_active(person_handle)
|
||||
return True
|
||||
|
||||
def edit_person_cb(self, obj,person_handle):
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
try:
|
||||
EditPerson(self.dbstate, self.uistate, [], person)
|
||||
except WindowActiveError:
|
||||
pass
|
||||
return True
|
||||
return False
|
||||
return NavigationView.get_active(self)
|
||||
|
||||
def person_rebuild(self, *args):
|
||||
self.update()
|
||||
@ -301,239 +201,6 @@ class FanChartView(NavigationView):
|
||||
self.person_rebuild()
|
||||
if self.active:
|
||||
self.bookmarks.redraw()
|
||||
|
||||
def copy_person_to_clipboard_cb(self, obj, person_handle):
|
||||
"""Renders the person data into some lines of text and puts that into the clipboard"""
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
cb = Gtk.Clipboard.get_for_display(Gdk.Display.get_default(),
|
||||
Gdk.SELECTION_CLIPBOARD)
|
||||
cb.set_text( self.format_helper.format_person(person,11), -1)
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_popup(self, obj, event, person_handle):
|
||||
"""
|
||||
Builds the full menu (including Siblings, Spouses, Children,
|
||||
and Parents) with navigation. Copied from PedigreeView.
|
||||
"""
|
||||
#store menu for GTK3 to avoid it being destroyed before showing
|
||||
self.menu = Gtk.Menu()
|
||||
menu = self.menu
|
||||
menu.set_title(_('People Menu'))
|
||||
|
||||
person = self.dbstate.db.get_person_from_handle(person_handle)
|
||||
if not person:
|
||||
return 0
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO,Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
go_item = Gtk.ImageMenuItem(name_displayer.display(person))
|
||||
go_item.set_image(go_image)
|
||||
go_item.connect("activate",self.on_childmenu_changed,person_handle)
|
||||
go_item.show()
|
||||
menu.append(go_item)
|
||||
|
||||
edit_item = Gtk.ImageMenuItem.new_from_stock(stock_id=Gtk.STOCK_EDIT, accel_group=None)
|
||||
edit_item.connect("activate", self.edit_person_cb, person_handle)
|
||||
edit_item.show()
|
||||
menu.append(edit_item)
|
||||
|
||||
clipboard_item = Gtk.ImageMenuItem.new_from_stock(stock_id=Gtk.STOCK_COPY, accel_group=None)
|
||||
clipboard_item.connect("activate",self.copy_person_to_clipboard_cb,person_handle)
|
||||
clipboard_item.show()
|
||||
menu.append(clipboard_item)
|
||||
|
||||
# collect all spouses, parents and children
|
||||
linked_persons = []
|
||||
|
||||
# Go over spouses and build their menu
|
||||
item = Gtk.MenuItem(label=_("Spouses"))
|
||||
fam_list = person.get_family_handle_list()
|
||||
no_spouses = 1
|
||||
for fam_id in fam_list:
|
||||
family = self.dbstate.db.get_family_from_handle(fam_id)
|
||||
if family.get_father_handle() == person.get_handle():
|
||||
sp_id = family.get_mother_handle()
|
||||
else:
|
||||
sp_id = family.get_father_handle()
|
||||
spouse = self.dbstate.db.get_person_from_handle(sp_id)
|
||||
if not spouse:
|
||||
continue
|
||||
|
||||
if no_spouses:
|
||||
no_spouses = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
sp_menu = item.get_submenu()
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
sp_item = Gtk.ImageMenuItem(name_displayer.display(spouse))
|
||||
sp_item.set_image(go_image)
|
||||
linked_persons.append(sp_id)
|
||||
sp_item.connect("activate",self.on_childmenu_changed,sp_id)
|
||||
sp_item.show()
|
||||
sp_menu.append(sp_item)
|
||||
|
||||
if no_spouses:
|
||||
item.set_sensitive(0)
|
||||
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over siblings and build their menu
|
||||
item = Gtk.MenuItem(label=_("Siblings"))
|
||||
pfam_list = person.get_parent_family_handle_list()
|
||||
no_siblings = 1
|
||||
for f in pfam_list:
|
||||
fam = self.dbstate.db.get_family_from_handle(f)
|
||||
sib_list = fam.get_child_ref_list()
|
||||
for sib_ref in sib_list:
|
||||
sib_id = sib_ref.ref
|
||||
if sib_id == person.get_handle():
|
||||
continue
|
||||
sib = self.dbstate.db.get_person_from_handle(sib_id)
|
||||
if not sib:
|
||||
continue
|
||||
|
||||
if no_siblings:
|
||||
no_siblings = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
sib_menu = item.get_submenu()
|
||||
|
||||
if find_children(self.dbstate.db,sib):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(sib)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(sib)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
sib_item = Gtk.ImageMenuItem(None)
|
||||
sib_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
sib_item.add(label)
|
||||
linked_persons.append(sib_id)
|
||||
sib_item.connect("activate", self.on_childmenu_changed, sib_id)
|
||||
sib_item.show()
|
||||
sib_menu.append(sib_item)
|
||||
|
||||
if no_siblings:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over children and build their menu
|
||||
item = Gtk.MenuItem(label=_("Children"))
|
||||
no_children = 1
|
||||
childlist = find_children(self.dbstate.db, person)
|
||||
for child_handle in childlist:
|
||||
child = self.dbstate.db.get_person_from_handle(child_handle)
|
||||
if not child:
|
||||
continue
|
||||
|
||||
if no_children:
|
||||
no_children = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
child_menu = item.get_submenu()
|
||||
|
||||
if find_children(self.dbstate.db,child):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(child)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(child)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
child_item = Gtk.ImageMenuItem(None)
|
||||
child_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
child_item.add(label)
|
||||
linked_persons.append(child_handle)
|
||||
child_item.connect("activate", self.on_childmenu_changed, child_handle)
|
||||
child_item.show()
|
||||
child_menu.append(child_item)
|
||||
|
||||
if no_children:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over parents and build their menu
|
||||
item = Gtk.MenuItem(label=_("Parents"))
|
||||
no_parents = 1
|
||||
par_list = find_parents(self.dbstate.db,person)
|
||||
for par_id in par_list:
|
||||
par = self.dbstate.db.get_person_from_handle(par_id)
|
||||
if not par:
|
||||
continue
|
||||
|
||||
if no_parents:
|
||||
no_parents = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
par_menu = item.get_submenu()
|
||||
|
||||
if find_parents(self.dbstate.db,par):
|
||||
label = Gtk.Label(label='<b><i>%s</i></b>' % escape(name_displayer.display(par)))
|
||||
else:
|
||||
label = Gtk.Label(label=escape(name_displayer.display(par)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
par_item = Gtk.ImageMenuItem(None)
|
||||
par_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0,0)
|
||||
par_item.add(label)
|
||||
linked_persons.append(par_id)
|
||||
par_item.connect("activate",self.on_childmenu_changed,par_id)
|
||||
par_item.show()
|
||||
par_menu.append(par_item)
|
||||
|
||||
if no_parents:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
|
||||
# Go over parents and build their menu
|
||||
item = Gtk.MenuItem(label=_("Related"))
|
||||
no_related = 1
|
||||
for p_id in find_witnessed_people(self.dbstate.db,person):
|
||||
#if p_id in linked_persons:
|
||||
# continue # skip already listed family members
|
||||
|
||||
per = self.dbstate.db.get_person_from_handle(p_id)
|
||||
if not per:
|
||||
continue
|
||||
|
||||
if no_related:
|
||||
no_related = 0
|
||||
item.set_submenu(Gtk.Menu())
|
||||
per_menu = item.get_submenu()
|
||||
|
||||
label = Gtk.Label(label=escape(name_displayer.display(per)))
|
||||
|
||||
go_image = Gtk.Image.new_from_stock(Gtk.STOCK_JUMP_TO, Gtk.IconSize.MENU)
|
||||
go_image.show()
|
||||
per_item = Gtk.ImageMenuItem(None)
|
||||
per_item.set_image(go_image)
|
||||
label.set_use_markup(True)
|
||||
label.show()
|
||||
label.set_alignment(0, 0)
|
||||
per_item.add(label)
|
||||
per_item.connect("activate", self.on_childmenu_changed, p_id)
|
||||
per_item.show()
|
||||
per_menu.append(per_item)
|
||||
|
||||
if no_related:
|
||||
item.set_sensitive(0)
|
||||
item.show()
|
||||
menu.append(item)
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
return 1
|
||||
|
||||
def printview(self, obj):
|
||||
"""
|
||||
@ -544,6 +211,62 @@ class FanChartView(NavigationView):
|
||||
prt = CairoPrintSave(widthpx, self.fan.on_draw, self.uistate.window)
|
||||
prt.run()
|
||||
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
self.change_active(person_handle)
|
||||
return True
|
||||
|
||||
def can_configure(self):
|
||||
"""
|
||||
See :class:`~gui.views.pageview.PageView
|
||||
:return: bool
|
||||
"""
|
||||
return True
|
||||
|
||||
def _get_configure_page_funcs(self):
|
||||
"""
|
||||
Return a list of functions that create gtk elements to use in the
|
||||
notebook pages of the Configure dialog
|
||||
|
||||
:return: list of functions
|
||||
"""
|
||||
return [self.config_panel]
|
||||
|
||||
def config_panel(self, configdialog):
|
||||
"""
|
||||
Function that builds the widget in the configuration dialog
|
||||
"""
|
||||
table = Gtk.Table(3, 2)
|
||||
table.set_border_width(12)
|
||||
table.set_col_spacings(6)
|
||||
table.set_row_spacings(6)
|
||||
|
||||
configdialog.add_spinner(table, _("Max generations"), 0,
|
||||
'interface.fanview-maxgen', (1, 11),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_combo(table,
|
||||
_('Background'),
|
||||
4, 'interface.fanview-background',
|
||||
((0, _('Color Scheme 1')),
|
||||
(1, _('Color Scheme 2')),
|
||||
(2, _('Gender Colors')),
|
||||
(3, _('White'))),
|
||||
callback=self.cb_update_background)
|
||||
|
||||
return _('Layout'), table
|
||||
|
||||
def cb_update_maxgen(self, spinbtn, constant):
|
||||
self.maxgen = spinbtn.get_value_as_int()
|
||||
self._config.set(constant, self.maxgen)
|
||||
self.update()
|
||||
|
||||
def cb_update_background(self, obj, constant):
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, entry)
|
||||
self.background = int(entry)
|
||||
self.update()
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# CairoPrintSave class
|
||||
|
@ -60,7 +60,7 @@ from gen.display.name import displayer as name_displayer
|
||||
from gen.utils.alive import probably_alive
|
||||
from gen.utils.file import media_path_full
|
||||
from gen.utils.db import find_children, find_parents, find_witnessed_people
|
||||
from libformatting import FormattingHelper
|
||||
from gen.utils.libformatting import FormattingHelper
|
||||
from gui.thumbnails import get_thumbnail_path
|
||||
from gen.errors import WindowActiveError
|
||||
from gui.editors import EditPerson, EditFamily
|
||||
|
Loading…
Reference in New Issue
Block a user