Add option to show the Gramps ID in parenthesis in the fan chart
Add option to show gramps id. Improve pylint score for the modified files. Fixes #11045
This commit is contained in:
parent
aefa262cd1
commit
75d2f3dd00
File diff suppressed because it is too large
Load Diff
@ -34,35 +34,25 @@
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Pango
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import PangoCairo
|
||||
import cairo
|
||||
import math
|
||||
import colorsys
|
||||
import sys
|
||||
import pickle
|
||||
from html import escape
|
||||
import cairo
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.display.name import displayer as name_displayer
|
||||
from gramps.gen.errors import WindowActiveError
|
||||
from ..editors import EditPerson, EditFamily
|
||||
from ..utils import hex_to_rgb
|
||||
from ..ddtargets import DdTargets
|
||||
from gramps.gen.utils.alive import probably_alive
|
||||
from gramps.gen.utils.libformatting import FormattingHelper
|
||||
from gramps.gen.utils.db import (find_children, find_parents, find_witnessed_people,
|
||||
get_age, get_timeperiod)
|
||||
from gramps.gen.plug.report.utils import find_spouse
|
||||
from .fanchart import *
|
||||
from .fanchartdesc import *
|
||||
from .fanchart import (FanChartWidget, PIXELS_PER_GENERATION, BORDER_EDGE_WIDTH)
|
||||
from .fanchartdesc import (FanChartBaseWidget,
|
||||
FanChartDescWidget,
|
||||
FanChartGrampsGUI,
|
||||
NORMAL, EXPANDED, COLLAPSED,
|
||||
TRANSLATE_PX, CHILDRING_WIDTH,
|
||||
BACKGROUND_GRAD_GEN,
|
||||
BACKGROUND_GRAD_AGE,
|
||||
BACKGROUND_GRAD_PERIOD,
|
||||
FORM_CIRCLE, TYPE_BOX_NORMAL, TYPE_BOX_FAMILY)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -70,12 +60,14 @@ from .fanchartdesc import *
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
PIXELS_PER_GENPERSON_RATIO = 0.55 # ratio of generation radius for person (rest for partner)
|
||||
PIXELS_PER_GENPERSON_RATIO = 0.55 # ratio of generation radius for person
|
||||
# (rest for partner)
|
||||
PIXELS_PER_GEN_SMALL = 80
|
||||
PIXELS_PER_GEN_LARGE = 160
|
||||
N_GEN_SMALL = 4
|
||||
PIXELS_PER_GENFAMILY = 25 # size of radius for family
|
||||
PIXELS_PER_RECLAIM = 4 # size of the radius of pixels taken from family to reclaim space
|
||||
PIXELS_PER_RECLAIM = 4 # size of the radius of pixels taken from family
|
||||
# to reclaim space
|
||||
PIXELS_PARTNER_GAP = 0 # Padding between someone and his partner
|
||||
PIXELS_CHILDREN_GAP = 5 # Padding between generations
|
||||
PARENTRING_WIDTH = 12 # width of the parent ring inside the person
|
||||
@ -103,8 +95,14 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
Fan Chart Widget. Handles visualization of data in self.data.
|
||||
See main() of FanChartGramplet for example of model format.
|
||||
"""
|
||||
self.set_values(None, 6, 5, True, True, BACKGROUND_GRAD_GEN, True, 'Sans', '#0000FF',
|
||||
'#FF0000', None, 0.5, ANGLE_WEIGHT, '#888a85')
|
||||
self.gen2people = {}
|
||||
self.gen2fam = {}
|
||||
self.rootangle_rad_desc = [math.radians(275), math.radians(275 + 170)]
|
||||
self.rootangle_rad_asc = [math.radians(90), math.radians(270)]
|
||||
self.data = {}
|
||||
self.set_values(None, 6, 5, True, True, BACKGROUND_GRAD_GEN, True,
|
||||
'Sans', '#0000FF', '#FF0000', None, 0.5, ANGLE_WEIGHT,
|
||||
'#888a85', False)
|
||||
FanChartBaseWidget.__init__(self, dbstate, uistate, callback_popup)
|
||||
|
||||
def reset(self):
|
||||
@ -118,30 +116,35 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
self._fill_data_structures()
|
||||
|
||||
# prepare the colors for the boxes
|
||||
self.prepare_background_box(self.generations_asc + self.generations_desc - 1)
|
||||
self.prepare_background_box(self.generations_asc +
|
||||
self.generations_desc - 1)
|
||||
|
||||
def set_values(self, root_person_handle, maxgen_asc, maxgen_desc, flipupsidedownname, twolinename, background,
|
||||
background_gradient, fontdescr, grad_start, grad_end,
|
||||
filter, alpha_filter, angle_algo, dupcolor):
|
||||
def set_values(self, root_person_handle, maxgen_asc, maxgen_desc,
|
||||
flipupsidedownname, twolinename, background,
|
||||
background_gradient, fontdescr, grad_start, grad_end,
|
||||
filtr, alpha_filter, angle_algo, dupcolor, showid):
|
||||
"""
|
||||
Reset the values to be used:
|
||||
|
||||
:param root_person_handle: person to show
|
||||
:param maxgen_asc: maximum of ascendant generations to show
|
||||
:param maxgen_desc: maximum of descendant generations to show
|
||||
:param flipupsidedownname: flip name on the left of the fanchart for the display of person's name
|
||||
:param flipupsidedownname: flip name on the left of the fanchart
|
||||
for the display of person's name
|
||||
:param background: config setting of which background procedure to use
|
||||
:type background: int
|
||||
:param background_gradient: option to add an overall gradient for distinguishing Asc/Desc
|
||||
:param background_gradient: option to add an overall gradient
|
||||
for distinguishing Asc/Desc
|
||||
:param fontdescr: string describing the font to use
|
||||
:param grad_start: colors to use for background procedure
|
||||
:param grad_end: colors to use for background procedure
|
||||
:param filter: the person filter to apply to the people in the chart
|
||||
:param filtr: the person filter to apply to the people in the chart
|
||||
:param alpha_filter: the alpha transparency value (0-1) to apply to
|
||||
filtered out data
|
||||
:param angle_algo: alorithm to use to calculate the sizes of the boxes
|
||||
:param dupcolor: color to use for people or families that occur a second
|
||||
or more time
|
||||
:param showid: option to show the gramps id or not
|
||||
"""
|
||||
self.rootpersonh = root_person_handle
|
||||
self.generations_asc = maxgen_asc
|
||||
@ -151,7 +154,7 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
self.fontdescr = fontdescr
|
||||
self.grad_start = grad_start
|
||||
self.grad_end = grad_end
|
||||
self.filter = filter
|
||||
self.filter = filtr
|
||||
self.form = FORM_CIRCLE
|
||||
self.alpha_filter = alpha_filter
|
||||
self.anglealgo = angle_algo
|
||||
@ -159,6 +162,7 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
self.childring = False
|
||||
self.flipupsidedownname = flipupsidedownname
|
||||
self.twolinename = twolinename
|
||||
self.showid = showid
|
||||
|
||||
def set_generations(self):
|
||||
"""
|
||||
@ -172,12 +176,14 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
self.handle2fam = {}
|
||||
self.gen2people = {}
|
||||
self.gen2fam = {}
|
||||
self.gen2people[0] = [(None, False, 0, 2 * math.pi, 0, 0, [], NORMAL)] # no center person
|
||||
# no center person
|
||||
self.gen2people[0] = [(None, False, 0, 2 * math.pi, 0, 0, [], NORMAL)]
|
||||
self.gen2fam[0] = [] # no families
|
||||
for i in range(1, self.generations_desc):
|
||||
self.gen2fam[i] = []
|
||||
self.gen2people[i] = []
|
||||
self.gen2people[self.generations_desc] = [] # indication of more children
|
||||
# indication of more children
|
||||
self.gen2people[self.generations_desc] = []
|
||||
|
||||
# Ascendance part
|
||||
self.angle = {}
|
||||
@ -187,13 +193,17 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
self.data[i] = [(None,) * 4] * 2 ** i
|
||||
self.angle[i] = []
|
||||
angle = self.rootangle_rad_asc[0]
|
||||
slice = 1 / (2 ** i) * (self.rootangle_rad_asc[1] - self.rootangle_rad_asc[0])
|
||||
for count in range(len(self.data[i])):
|
||||
portion = 1 / (2 ** i) * (self.rootangle_rad_asc[1] -
|
||||
self.rootangle_rad_asc[0])
|
||||
for dummy_count in range(len(self.data[i])):
|
||||
# start, stop, state
|
||||
self.angle[i].append([angle, angle + slice, NORMAL])
|
||||
angle += slice
|
||||
self.angle[i].append([angle, angle + portion, NORMAL])
|
||||
angle += portion
|
||||
|
||||
def _fill_data_structures(self):
|
||||
"""
|
||||
Initialize the data structures
|
||||
"""
|
||||
self.set_generations()
|
||||
if not self.rootpersonh:
|
||||
return
|
||||
@ -221,9 +231,10 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
for current in range(1, self.generations_asc):
|
||||
parent = 0
|
||||
# name, person, parents, children
|
||||
for (p, q, c, d) in self.data[current - 1]:
|
||||
for (pers, dummy_q, dummy_c, dummy_d) in self.data[current - 1]:
|
||||
# Get father's and mother's details:
|
||||
for person in [self._get_parent(p, True), self._get_parent(p, False)]:
|
||||
for person in [self._get_parent(pers, True),
|
||||
self._get_parent(pers, False)]:
|
||||
if current == self.generations_asc - 1:
|
||||
parents = self._have_parents(person)
|
||||
else:
|
||||
@ -235,17 +246,22 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
parent += 1
|
||||
|
||||
def nrgen_desc(self):
|
||||
# compute the number of generations present
|
||||
"""
|
||||
compute the number of generations present
|
||||
"""
|
||||
for gen in range(self.generations_desc - 1, 0, -1):
|
||||
if len(self.gen2people[gen]) > 0:
|
||||
if self.gen2people[gen]:
|
||||
return gen + 1
|
||||
return 1
|
||||
|
||||
def nrgen_asc(self):
|
||||
# compute the number of generations present
|
||||
"""
|
||||
compute the number of generations present
|
||||
"""
|
||||
for generation in range(self.generations_asc - 1, 0, -1):
|
||||
for p in range(len(self.data[generation])):
|
||||
(person, parents, child, userdata) = self.data[generation][p]
|
||||
for idx in range(len(self.data[generation])):
|
||||
(person, dummy_parents, dummy_child,
|
||||
dummy_userdata) = self.data[generation][idx]
|
||||
if person:
|
||||
return generation
|
||||
return 1
|
||||
@ -254,59 +270,73 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
"""
|
||||
Compute the current half radius of the ascendant circle
|
||||
"""
|
||||
radiusin, radius_asc = self.get_radiusinout_for_generation_asc(generation)
|
||||
(dummy_radiusin,
|
||||
radius_asc) = self.get_radiusinout_for_gen_asc(generation)
|
||||
return radius_asc + BORDER_EDGE_WIDTH
|
||||
|
||||
def maxradius_desc(self,generation):
|
||||
def maxradius_desc(self, generation):
|
||||
"""
|
||||
Compute the current radius of the descendant circle
|
||||
"""
|
||||
radiusin_pers, radiusout_pers, radiusin_partner, radius_desc = self.get_radiusinout_for_generation_pair(generation-1)
|
||||
(dummy_radiusin_pers, dummy_radiusout_pers, dummy_radiusin_partner,
|
||||
radius_desc) = self.get_radiusinout_for_gen_pair(generation-1)
|
||||
return radius_desc + BORDER_EDGE_WIDTH
|
||||
|
||||
def halfdist(self):
|
||||
"""
|
||||
Compute the current max half radius of the circle
|
||||
"""
|
||||
return max(self.maxradius_desc(self.nrgen_desc()), self.maxradius_asc(self.nrgen_asc()))
|
||||
return max(self.maxradius_desc(self.nrgen_desc()),
|
||||
self.maxradius_asc(self.nrgen_asc()))
|
||||
|
||||
def get_radiusinout_for_generation_desc(self, generation):
|
||||
def get_radiusinout_for_gen_desc(self, generation):
|
||||
"""
|
||||
Get the in and out radius for descendant generation (starting with center pers = 0)
|
||||
Get the in and out radius for descendant generation
|
||||
(starting with center pers = 0)
|
||||
"""
|
||||
radius_first_gen = self.CENTER - (1 - PIXELS_PER_GENPERSON_RATIO) * PIXELS_PER_GEN_SMALL
|
||||
radius_first_gen = (self.CENTER -
|
||||
(1 - PIXELS_PER_GENPERSON_RATIO) *
|
||||
PIXELS_PER_GEN_SMALL)
|
||||
if generation < N_GEN_SMALL:
|
||||
radius_start = PIXELS_PER_GEN_SMALL * generation + radius_first_gen
|
||||
return (radius_start, radius_start + PIXELS_PER_GEN_SMALL)
|
||||
else:
|
||||
radius_start = PIXELS_PER_GEN_SMALL * N_GEN_SMALL + PIXELS_PER_GEN_LARGE \
|
||||
* (generation - N_GEN_SMALL) + radius_first_gen
|
||||
radius_start = (PIXELS_PER_GEN_SMALL * N_GEN_SMALL +
|
||||
PIXELS_PER_GEN_LARGE * (generation - N_GEN_SMALL) +
|
||||
radius_first_gen)
|
||||
return (radius_start, radius_start + PIXELS_PER_GEN_LARGE)
|
||||
|
||||
def get_radiusinout_for_generation_asc(self, generation):
|
||||
def get_radiusinout_for_gen_asc(self, generation):
|
||||
"""
|
||||
Get the in and out radius for ascendant generation (starting with center pers = 0)
|
||||
Get the in and out radius for ascendant generation
|
||||
(starting with center pers = 0)
|
||||
"""
|
||||
radiusin, radius_first_gen = self.get_radiusinout_for_generation_desc(0)
|
||||
dummy_radiusin, radius_first_gen = self.get_radiusinout_for_gen_desc(0)
|
||||
outerradius = generation * PIXELS_PER_GENERATION + radius_first_gen
|
||||
innerradius = (generation - 1) * PIXELS_PER_GENERATION + radius_first_gen
|
||||
innerradius = ((generation - 1) * PIXELS_PER_GENERATION +
|
||||
radius_first_gen)
|
||||
if generation == 0:
|
||||
innerradius = CHILDRING_WIDTH + TRANSLATE_PX
|
||||
return (innerradius, outerradius)
|
||||
|
||||
def get_radiusinout_for_generation_pair(self, generation):
|
||||
def get_radiusinout_for_gen_pair(self, generation):
|
||||
"""
|
||||
Get the in and out radius for descendant generation pair (starting with center pers = 0)
|
||||
:return: (radiusin_pers, radiusout_pers, radiusin_partner, radiusout_partner)
|
||||
Get the in and out radius for descendant generation pair
|
||||
(starting with center pers = 0)
|
||||
:return: (radiusin_pers, radiusout_pers,
|
||||
radiusin_partner, radiusout_partner)
|
||||
"""
|
||||
radiusin, radiusout = self.get_radiusinout_for_generation_desc(generation)
|
||||
radius_spread = radiusout - radiusin - PIXELS_CHILDREN_GAP - PIXELS_PARTNER_GAP
|
||||
radiusin, radiusout = self.get_radiusinout_for_gen_desc(generation)
|
||||
radius_spread = (radiusout - radiusin -
|
||||
PIXELS_CHILDREN_GAP - PIXELS_PARTNER_GAP)
|
||||
|
||||
radiusin_pers = radiusin + PIXELS_CHILDREN_GAP
|
||||
radiusout_pers = radiusin_pers + PIXELS_PER_GENPERSON_RATIO * radius_spread
|
||||
radiusout_pers = (radiusin_pers +
|
||||
PIXELS_PER_GENPERSON_RATIO * radius_spread)
|
||||
radiusin_partner = radiusout_pers + PIXELS_PARTNER_GAP
|
||||
radiusout_partner = radiusout
|
||||
return (radiusin_pers, radiusout_pers, radiusin_partner, radiusout_partner)
|
||||
return (radiusin_pers, radiusout_pers,
|
||||
radiusin_partner, radiusout_partner)
|
||||
|
||||
def people_generator(self):
|
||||
"""
|
||||
@ -319,8 +349,9 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
for data in self.gen2fam[generation]:
|
||||
yield (data[7], data[6])
|
||||
for generation in range(self.generations_asc):
|
||||
for p in range(len(self.data[generation])):
|
||||
(person, parents, child, userdata) = self.data[generation][p]
|
||||
for idx in range(len(self.data[generation])):
|
||||
(person, dummy_parents, dummy_child,
|
||||
userdata) = self.data[generation][idx]
|
||||
yield (person, userdata)
|
||||
|
||||
def innerpeople_generator(self):
|
||||
@ -330,47 +361,57 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
if False:
|
||||
yield
|
||||
|
||||
def draw_background(self, cr):
|
||||
cr.save()
|
||||
def draw_background(self, ctx):
|
||||
"""
|
||||
Draw the background
|
||||
"""
|
||||
ctx.save()
|
||||
|
||||
cr.rotate(math.radians(self.rotate_value))
|
||||
delta = (self.rootangle_rad_asc[0] - self.rootangle_rad_desc[1]) / 2.0 % math.pi
|
||||
ctx.rotate(math.radians(self.rotate_value))
|
||||
delta = (self.rootangle_rad_asc[0] -
|
||||
self.rootangle_rad_desc[1]) / 2.0 % math.pi
|
||||
|
||||
cr.move_to(0, 0)
|
||||
ctx.move_to(0, 0)
|
||||
radius_gradient_asc = 1.5 * self.maxradius_asc(self.generations_asc)
|
||||
gradient_asc = cairo.RadialGradient(0, 0, self.CENTER, 0, 0, radius_gradient_asc)
|
||||
gradient_asc = cairo.RadialGradient(0, 0, self.CENTER,
|
||||
0, 0, radius_gradient_asc)
|
||||
color = hex_to_rgb(self.grad_end)
|
||||
gradient_asc.add_color_stop_rgba(0.0, color[0]/255, color[1]/255, color[2]/255, 0.5)
|
||||
gradient_asc.add_color_stop_rgba(0.0, color[0] / 255, color[1] / 255,
|
||||
color[2] / 255, 0.5)
|
||||
gradient_asc.add_color_stop_rgba(1.0, 1, 1, 1, 0.0)
|
||||
start_rad, stop_rad = self.rootangle_rad_asc[0] - delta, self.rootangle_rad_asc[1] + delta
|
||||
cr.set_source(gradient_asc)
|
||||
cr.arc(0, 0, radius_gradient_asc, start_rad, stop_rad)
|
||||
cr.fill()
|
||||
start_rad, stop_rad = (self.rootangle_rad_asc[0] - delta,
|
||||
self.rootangle_rad_asc[1] + delta)
|
||||
ctx.set_source(gradient_asc)
|
||||
ctx.arc(0, 0, radius_gradient_asc, start_rad, stop_rad)
|
||||
ctx.fill()
|
||||
|
||||
cr.move_to(0, 0)
|
||||
ctx.move_to(0, 0)
|
||||
radius_gradient_desc = 1.5 * self.maxradius_desc(self.generations_desc)
|
||||
gradient_desc = cairo.RadialGradient(0, 0, self.CENTER, 0, 0, radius_gradient_desc)
|
||||
gradient_desc = cairo.RadialGradient(0, 0, self.CENTER,
|
||||
0, 0, radius_gradient_desc)
|
||||
color = hex_to_rgb(self.grad_start)
|
||||
gradient_desc.add_color_stop_rgba(0.0, color[0]/255, color[1]/255, color[2]/255, 0.5)
|
||||
gradient_desc.add_color_stop_rgba(0.0, color[0] / 255, color[1] / 255,
|
||||
color[2] / 255, 0.5)
|
||||
gradient_desc.add_color_stop_rgba(1.0, 1, 1, 1, 0.0)
|
||||
start_rad, stop_rad = self.rootangle_rad_desc[0] - delta, self.rootangle_rad_desc[1] + delta
|
||||
cr.set_source(gradient_desc)
|
||||
cr.arc(0, 0, radius_gradient_desc, start_rad, stop_rad)
|
||||
cr.fill()
|
||||
cr.restore()
|
||||
start_rad, stop_rad = (self.rootangle_rad_desc[0] - delta,
|
||||
self.rootangle_rad_desc[1] + delta)
|
||||
ctx.set_source(gradient_desc)
|
||||
ctx.arc(0, 0, radius_gradient_desc, start_rad, stop_rad)
|
||||
ctx.fill()
|
||||
ctx.restore()
|
||||
|
||||
def draw(self, cr=None, scale=1.0):
|
||||
def draw(self, ctx=None, scale=1.0):
|
||||
"""
|
||||
The main method to do the drawing.
|
||||
If cr is given, we assume we draw draw raw on the cairo context cr
|
||||
To draw in GTK3 and use the allocation, set cr=None.
|
||||
If ctx is given, we assume we draw draw raw on the cairo context ctx
|
||||
To draw in GTK3 and use the allocation, set ctx=None.
|
||||
Note: when drawing for display, to counter a Gtk issue with scrolling
|
||||
or resizing the drawing window, we draw on a surface, then copy to the
|
||||
drawing context when the Gtk 'draw' signal arrives.
|
||||
"""
|
||||
# first do size request of what we will need
|
||||
halfdist = self.halfdist()
|
||||
if not cr: # Display
|
||||
if not ctx: # Display
|
||||
size_w = size_h = 2 * halfdist
|
||||
|
||||
size_w_a = self.get_allocated_width()
|
||||
@ -380,81 +421,101 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
size_h = self.get_allocated_height()
|
||||
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
|
||||
size_w, size_h)
|
||||
cr = cairo.Context(self.surface)
|
||||
ctx = cairo.Context(self.surface)
|
||||
self.center_xy = self.center_xy_from_delta()
|
||||
cr.translate(*self.center_xy)
|
||||
ctx.translate(*self.center_xy)
|
||||
else: # printing
|
||||
self.center_xy = halfdist, halfdist
|
||||
cr.scale(scale, scale)
|
||||
cr.translate(halfdist, halfdist)
|
||||
ctx.scale(scale, scale)
|
||||
ctx.translate(halfdist, halfdist)
|
||||
|
||||
cr.save()
|
||||
ctx.save()
|
||||
# Draw background
|
||||
if self.background_gradient:
|
||||
self.draw_background(cr)
|
||||
self.draw_background(ctx)
|
||||
# Draw center person:
|
||||
(person, dup, start, slice, parentfampos, nrfam, userdata, status) \
|
||||
= self.gen2people[0][0]
|
||||
(person, dup, start, portion, dummy_parentfampos, dummy_nrfam,
|
||||
userdata, status) = self.gen2people[0][0]
|
||||
if not person:
|
||||
return
|
||||
gen_remapped = self.generations_desc - 1 # remapped generation
|
||||
if gen_remapped == 0: gen_remapped = (self.generations_desc + self.generations_asc - 1) # remapped generation
|
||||
if gen_remapped == 0:
|
||||
# remapped generation
|
||||
gen_remapped = (self.generations_desc + self.generations_asc - 1)
|
||||
radiusin_pers, radiusout_pers, radiusin_partner, radiusout_partner = \
|
||||
self.get_radiusinout_for_generation_pair(0)
|
||||
self.get_radiusinout_for_gen_pair(0)
|
||||
radiusin = TRANSLATE_PX
|
||||
radiusout = radiusout_pers
|
||||
self.draw_person(cr, person, radiusin, radiusout, math.pi / 2, math.pi / 2 + 2 * math.pi,
|
||||
gen_remapped, False, userdata, is_central_person=True)
|
||||
self.draw_person(ctx, person, radiusin, radiusout,
|
||||
math.pi / 2, math.pi / 2 + 2 * math.pi, gen_remapped,
|
||||
False, userdata, is_central_person=True)
|
||||
# draw center to move chart
|
||||
cr.set_source_rgb(0, 0, 0) # black
|
||||
cr.move_to(TRANSLATE_PX, 0)
|
||||
cr.arc(0, 0, TRANSLATE_PX, 0, 2 * math.pi)
|
||||
cr.fill()
|
||||
ctx.set_source_rgb(0, 0, 0) # black
|
||||
ctx.move_to(TRANSLATE_PX, 0)
|
||||
ctx.arc(0, 0, TRANSLATE_PX, 0, 2 * math.pi)
|
||||
ctx.fill()
|
||||
|
||||
cr.rotate(math.radians(self.rotate_value))
|
||||
ctx.rotate(math.radians(self.rotate_value))
|
||||
# Ascendance
|
||||
for generation in range(self.generations_asc - 1, 0, -1):
|
||||
for p in range(len(self.data[generation])):
|
||||
(person, parents, child, userdata) = self.data[generation][p]
|
||||
for idx in range(len(self.data[generation])):
|
||||
(person, parents, dummy_child,
|
||||
userdata) = self.data[generation][idx]
|
||||
if person:
|
||||
start, stop, state = self.angle[generation][p]
|
||||
start, stop, state = self.angle[generation][idx]
|
||||
if state in [NORMAL, EXPANDED]:
|
||||
radiusin, radiusout = self.get_radiusinout_for_generation_asc(generation)
|
||||
fct = self.get_radiusinout_for_gen_asc
|
||||
radiusin, radiusout = fct(generation)
|
||||
dup = False
|
||||
gen_remapped = generation + self.generations_desc - 1 # remapped generation
|
||||
self.draw_person(cr, person, radiusin, radiusout, start, stop,
|
||||
gen_remapped, dup, userdata, thick=(state == EXPANDED),
|
||||
has_moregen_indicator=(generation == self.generations_asc - 1 and parents))
|
||||
# remapped generation
|
||||
gen_remapped = generation + self.generations_desc - 1
|
||||
indicator = (generation == self.generations_asc - 1
|
||||
and parents)
|
||||
self.draw_person(ctx, person, radiusin, radiusout,
|
||||
start, stop, gen_remapped, dup,
|
||||
userdata, thick=(state == EXPANDED),
|
||||
has_moregen_indicator=indicator)
|
||||
|
||||
# Descendance
|
||||
for gen in range(self.generations_desc):
|
||||
radiusin_pers, radiusout_pers, radiusin_partner, radiusout_partner = \
|
||||
self.get_radiusinout_for_generation_pair(gen)
|
||||
(radiusin_pers, radiusout_pers, radiusin_partner,
|
||||
radiusout_partner) = self.get_radiusinout_for_gen_pair(gen)
|
||||
gen_remapped = (self.generations_desc - gen - 1)
|
||||
if gen_remapped == 0: gen_remapped = (self.generations_desc + self.generations_asc - 1) # remapped generation
|
||||
if gen_remapped == 0:
|
||||
# remapped generation
|
||||
gen_remapped = (self.generations_desc +
|
||||
self.generations_asc - 1)
|
||||
if gen > 0:
|
||||
for pdata in self.gen2people[gen]:
|
||||
# person, duplicate or not, start angle, slice size,
|
||||
# parent pos in fam, nrfam, userdata, status
|
||||
pers, dup, start, slice, pospar, nrfam, userdata, status = pdata
|
||||
(pers, dup, start, portion, dummy_pospar, dummy_nrfam,
|
||||
userdata, status) = pdata
|
||||
if status != COLLAPSED:
|
||||
self.draw_person(cr, pers, radiusin_pers, radiusout_pers,
|
||||
start, start + slice, gen_remapped, dup, userdata,
|
||||
self.draw_person(ctx, pers, radiusin_pers,
|
||||
radiusout_pers,
|
||||
start, start + portion,
|
||||
gen_remapped, dup, userdata,
|
||||
thick=status != NORMAL)
|
||||
#if gen < self.generations_desc - 1:
|
||||
for famdata in self.gen2fam[gen]:
|
||||
# family, duplicate or not, start angle, slice size,
|
||||
# spouse pos in gen, nrchildren, userdata, status
|
||||
fam, dup, start, slice, posfam, nrchild, userdata, partner, status = famdata
|
||||
(fam, dup, start, portion, dummy_posfam, dummy_nrchild,
|
||||
userdata, partner, status) = famdata
|
||||
if status != COLLAPSED:
|
||||
more_pers_flag = (gen == self.generations_desc - 1
|
||||
and len(fam.get_child_ref_list()) > 0)
|
||||
self.draw_person(cr, partner, radiusin_partner, radiusout_partner, start, start + slice,
|
||||
gen_remapped, dup, userdata, thick=(status != NORMAL), has_moregen_indicator=more_pers_flag)
|
||||
cr.restore()
|
||||
and fam.get_child_ref_list())
|
||||
self.draw_person(ctx, partner,
|
||||
radiusin_partner, radiusout_partner,
|
||||
start, start + portion,
|
||||
gen_remapped, dup, userdata,
|
||||
thick=(status != NORMAL),
|
||||
has_moregen_indicator=more_pers_flag)
|
||||
ctx.restore()
|
||||
|
||||
if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]:
|
||||
self.draw_gradient_legend(cr, halfdist)
|
||||
self.draw_gradient_legend(ctx, halfdist)
|
||||
|
||||
def cell_address_under_cursor(self, curx, cury):
|
||||
"""
|
||||
@ -462,19 +523,23 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
position x and y.
|
||||
None if outside of diagram
|
||||
"""
|
||||
radius, rads, raw_rads = self.cursor_to_polar(curx, cury, get_raw_rads=True)
|
||||
radius, rads, dummy_raw_rads = self.cursor_to_polar(curx, cury,
|
||||
get_raw_rads=True)
|
||||
|
||||
if radius < TRANSLATE_PX:
|
||||
return None
|
||||
radius_parents = self.get_radiusinout_for_generation_asc(0)[1]
|
||||
if (radius < radius_parents) or \
|
||||
(self.radian_in_bounds(self.rootangle_rad_desc[0], rads, self.rootangle_rad_desc[1])):
|
||||
radius_parents = self.get_radiusinout_for_gen_asc(0)[1]
|
||||
if ((radius < radius_parents) or
|
||||
self.radian_in_bounds(self.rootangle_rad_desc[0], rads,
|
||||
self.rootangle_rad_desc[1])):
|
||||
cell_address = self.cell_address_under_cursor_desc(rads, radius)
|
||||
if cell_address is not None:
|
||||
return (TYPE_DESCENDANCE,) + cell_address
|
||||
elif self.radian_in_bounds(self.rootangle_rad_asc[0], rads, self.rootangle_rad_asc[1]):
|
||||
elif self.radian_in_bounds(self.rootangle_rad_asc[0], rads,
|
||||
self.rootangle_rad_asc[1]):
|
||||
cell_address = self.cell_address_under_cursor_asc(rads, radius)
|
||||
if cell_address and cell_address[0]==0: return None # There is a gap before first parents
|
||||
if cell_address and cell_address[0] == 0:
|
||||
return None # There is a gap before first parents
|
||||
if cell_address is not None:
|
||||
return (TYPE_ASCENDANCE,) + cell_address
|
||||
|
||||
@ -488,8 +553,8 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
"""
|
||||
generation, selected, btype = None, None, TYPE_BOX_NORMAL
|
||||
for gen in range(self.generations_desc):
|
||||
radiusin_pers, radiusout_pers, radiusin_partner, radiusout_partner \
|
||||
= self.get_radiusinout_for_generation_pair(gen)
|
||||
(radiusin_pers, radiusout_pers, radiusin_partner,
|
||||
radiusout_partner) = self.get_radiusinout_for_gen_pair(gen)
|
||||
if radiusin_pers <= radius <= radiusout_pers:
|
||||
generation, btype = gen, TYPE_BOX_NORMAL
|
||||
break
|
||||
@ -497,8 +562,9 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
generation, btype = gen, TYPE_BOX_FAMILY
|
||||
break
|
||||
# find what person is in this position:
|
||||
if not (generation is None) and 0 <= generation:
|
||||
selected = FanChartDescWidget.personpos_at_angle(self, generation, rads, btype)
|
||||
if not (generation is None) and generation > 0:
|
||||
selected = FanChartDescWidget.personpos_at_angle(self, generation,
|
||||
rads, btype)
|
||||
|
||||
if (generation is None or selected is None):
|
||||
return None
|
||||
@ -514,13 +580,13 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
|
||||
generation, selected = None, None
|
||||
for gen in range(self.generations_asc):
|
||||
radiusin, radiusout = self.get_radiusinout_for_generation_asc(gen)
|
||||
radiusin, radiusout = self.get_radiusinout_for_gen_asc(gen)
|
||||
if radiusin <= radius <= radiusout:
|
||||
generation = gen
|
||||
break
|
||||
|
||||
# find what person is in this position:
|
||||
if not (generation is None) and 0 <= generation:
|
||||
if not (generation is None) and generation > 0:
|
||||
selected = FanChartWidget.personpos_at_angle(self, generation, rads)
|
||||
if (generation is None or selected is None):
|
||||
return None
|
||||
@ -556,35 +622,40 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
self.queue_draw()
|
||||
|
||||
def expand_parents(self, generation, selected, current):
|
||||
if generation >= self.generations_asc: return
|
||||
if generation >= self.generations_asc:
|
||||
return
|
||||
selected = 2 * selected
|
||||
start, stop, state = self.angle[generation][selected]
|
||||
if state in [NORMAL, EXPANDED]:
|
||||
slice = (stop - start) * 2.0
|
||||
self.angle[generation][selected] = [current, current + slice, state]
|
||||
portion = (stop - start) * 2.0
|
||||
self.angle[generation][selected] = [current, current + portion,
|
||||
state]
|
||||
self.expand_parents(generation + 1, selected, current)
|
||||
current += slice
|
||||
current += portion
|
||||
start, stop, state = self.angle[generation][selected + 1]
|
||||
if state in [NORMAL, EXPANDED]:
|
||||
slice = (stop - start) * 2.0
|
||||
self.angle[generation][selected + 1] = [current, current + slice,
|
||||
state]
|
||||
portion = (stop - start) * 2.0
|
||||
self.angle[generation][selected + 1] = [current, current + portion,
|
||||
state]
|
||||
self.expand_parents(generation + 1, selected + 1, current)
|
||||
|
||||
def show_parents(self, generation, selected, angle, slice):
|
||||
if generation >= self.generations_asc: return
|
||||
def show_parents(self, generation, selected, angle, portion):
|
||||
if generation >= self.generations_asc:
|
||||
return
|
||||
selected *= 2
|
||||
self.angle[generation][selected][0] = angle
|
||||
self.angle[generation][selected][1] = angle + slice
|
||||
self.angle[generation][selected][1] = angle + portion
|
||||
self.angle[generation][selected][2] = NORMAL
|
||||
self.show_parents(generation + 1, selected, angle, slice / 2.0)
|
||||
self.angle[generation][selected + 1][0] = angle + slice
|
||||
self.angle[generation][selected + 1][1] = angle + slice + slice
|
||||
self.show_parents(generation + 1, selected, angle, portion / 2.0)
|
||||
self.angle[generation][selected + 1][0] = angle + portion
|
||||
self.angle[generation][selected + 1][1] = angle + portion + portion
|
||||
self.angle[generation][selected + 1][2] = NORMAL
|
||||
self.show_parents(generation + 1, selected + 1, angle + slice, slice / 2.0)
|
||||
self.show_parents(generation + 1, selected + 1,
|
||||
angle + portion, portion / 2.0)
|
||||
|
||||
def hide_parents(self, generation, selected, angle):
|
||||
if generation >= self.generations_asc: return
|
||||
if generation >= self.generations_asc:
|
||||
return
|
||||
selected = 2 * selected
|
||||
self.angle[generation][selected][0] = angle
|
||||
self.angle[generation][selected][1] = angle
|
||||
@ -596,20 +667,21 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
self.hide_parents(generation + 1, selected + 1, angle)
|
||||
|
||||
def shrink_parents(self, generation, selected, current):
|
||||
if generation >= self.generations_asc: return
|
||||
if generation >= self.generations_asc:
|
||||
return
|
||||
selected = 2 * selected
|
||||
start, stop, state = self.angle[generation][selected]
|
||||
if state in [NORMAL, EXPANDED]:
|
||||
slice = (stop - start) / 2.0
|
||||
self.angle[generation][selected] = [current, current + slice,
|
||||
portion = (stop - start) / 2.0
|
||||
self.angle[generation][selected] = [current, current + portion,
|
||||
state]
|
||||
self.shrink_parents(generation + 1, selected, current)
|
||||
current += slice
|
||||
current += portion
|
||||
start, stop, state = self.angle[generation][selected + 1]
|
||||
if state in [NORMAL, EXPANDED]:
|
||||
slice = (stop - start) / 2.0
|
||||
self.angle[generation][selected + 1] = [current, current + slice,
|
||||
state]
|
||||
portion = (stop - start) / 2.0
|
||||
self.angle[generation][selected + 1] = [current, current + portion,
|
||||
state]
|
||||
self.shrink_parents(generation + 1, selected + 1, current)
|
||||
|
||||
def toggle_cell_state(self, cell_address):
|
||||
@ -630,10 +702,13 @@ class FanChart2WayGrampsGUI(FanChartGrampsGUI):
|
||||
data.
|
||||
"""
|
||||
root_person_handle = self.get_active('Person')
|
||||
self.fan.set_values(root_person_handle, self.generations_asc, self.generations_desc, self.flipupsidedownname, self.twolinename, self.background,
|
||||
self.background_gradient, self.fonttype, self.grad_start, self.grad_end,
|
||||
self.generic_filter, self.alpha_filter,
|
||||
self.angle_algo, self.dupcolor)
|
||||
self.fan.set_values(root_person_handle, self.generations_asc,
|
||||
self.generations_desc, self.flipupsidedownname,
|
||||
self.twolinename, self.background,
|
||||
self.background_gradient, self.fonttype,
|
||||
self.grad_start, self.grad_end, self.generic_filter,
|
||||
self.alpha_filter, self.angle_algo, self.dupcolor,
|
||||
self.showid)
|
||||
self.fan.reset()
|
||||
self.fan.draw()
|
||||
self.fan.queue_draw()
|
||||
|
@ -33,50 +33,40 @@
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Pango
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import PangoCairo
|
||||
import cairo
|
||||
import math
|
||||
import colorsys
|
||||
import pickle
|
||||
from html import escape
|
||||
import cairo
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.display.name import displayer as name_displayer
|
||||
from gramps.gen.errors import WindowActiveError
|
||||
from ..editors import EditPerson, EditFamily
|
||||
from ..utils import hex_to_rgb
|
||||
from ..ddtargets import DdTargets
|
||||
from gramps.gen.utils.alive import probably_alive
|
||||
from gramps.gen.utils.libformatting import FormattingHelper
|
||||
from gramps.gen.utils.db import (find_children, find_parents, find_witnessed_people,
|
||||
get_age, get_timeperiod)
|
||||
from gramps.gen.plug.report.utils import find_spouse
|
||||
from .fanchart import *
|
||||
from ..utils import hex_to_rgb
|
||||
from .fanchart import (FanChartBaseWidget, FanChartGrampsGUI,
|
||||
PAD_PX, TRANSLATE_PX,
|
||||
FORM_CIRCLE, FORM_HALFCIRCLE, FORM_QUADRANT,
|
||||
NORMAL, EXPANDED, COLLAPSED,
|
||||
BACKGROUND_GRAD_GEN, BACKGROUND_GRAD_AGE,
|
||||
BACKGROUND_GRAD_PERIOD, CHILDRING_WIDTH)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
pi = math.pi
|
||||
|
||||
PIXELS_PER_GENPERSON_RATIO = 0.55 # ratio of generation radius for person (rest for partner)
|
||||
PIXELS_PER_GENPERSON_RATIO = 0.55 # ratio of generation radius for person
|
||||
# (rest for partner)
|
||||
PIXELS_PER_GEN_SMALL = 80
|
||||
PIXELS_PER_GEN_LARGE = 160
|
||||
N_GEN_SMALL = 4
|
||||
PIXELS_PER_GENFAMILY = 25 # size of radius for family
|
||||
PIXELS_PER_RECLAIM = 4 # size of the radius of pixels taken from family to reclaim space
|
||||
PIXELS_PER_RECLAIM = 4 # size of the radius of pixels taken from family
|
||||
# to reclaim space
|
||||
PIXELS_PARTNER_GAP = 0 # Padding between someone and his partner
|
||||
PIXELS_CHILDREN_GAP = 5 # Padding between generations
|
||||
PARENTRING_WIDTH = 12 # width of the parent ring inside the person
|
||||
PARENTRING_WIDTH = 12 # width of the parent ring inside the person
|
||||
|
||||
ANGLE_CHEQUI = 0 #Algorithm with homogeneous children distribution
|
||||
ANGLE_WEIGHT = 1 #Algorithm for angle computation based on nr of descendants
|
||||
@ -94,38 +84,49 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
"""
|
||||
Interactive Fan Chart Widget.
|
||||
"""
|
||||
CENTER = 60 # we require a larger center as CENTER includes the 1st partner
|
||||
CENTER = 60 # we require a larger center as CENTER includes the 1st partner
|
||||
|
||||
def __init__(self, dbstate, uistate, callback_popup=None):
|
||||
"""
|
||||
Fan Chart Widget. Handles visualization of data in self.data.
|
||||
See main() of FanChartGramplet for example of model format.
|
||||
"""
|
||||
self.set_values(None, 9, True, True, BACKGROUND_GRAD_GEN, 'Sans', '#0000FF',
|
||||
'#FF0000', None, 0.5, FORM_CIRCLE, ANGLE_WEIGHT, '#888a85')
|
||||
self.gen2people = {}
|
||||
self.gen2fam = {}
|
||||
self.rootangle_rad = []
|
||||
self.handle2desc = {}
|
||||
self.famhandle2desc = {}
|
||||
self.handle2fam = {}
|
||||
self.innerring = []
|
||||
self.angle = {}
|
||||
self.set_values(None, 9, True, True, BACKGROUND_GRAD_GEN, 'Sans',
|
||||
'#0000FF', '#FF0000', None, 0.5,
|
||||
FORM_CIRCLE, ANGLE_WEIGHT, '#888a85', False)
|
||||
FanChartBaseWidget.__init__(self, dbstate, uistate, callback_popup)
|
||||
|
||||
def set_values(self, root_person_handle, maxgen, flipupsidedownname, twolinename, background,
|
||||
fontdescr, grad_start, grad_end,
|
||||
filter, alpha_filter, form, angle_algo, dupcolor):
|
||||
def set_values(self, root_person_handle, maxgen, flipupsidedownname,
|
||||
twolinename, background, fontdescr, grad_start, grad_end,
|
||||
filtr, alpha_filter, form, angle_algo, dupcolor, showid):
|
||||
"""
|
||||
Reset the values to be used:
|
||||
|
||||
:param root_person_handle: person to show
|
||||
:param maxgen: maximum generations to show
|
||||
:param flipupsidedownname: flip name on the left of the fanchart for the display of person's name
|
||||
:param flipupsidedownname: flip name on the left of the fanchart
|
||||
for the display of person's name
|
||||
:param background: config setting of which background procedure to use
|
||||
:type background: int
|
||||
:param fontdescr: string describing the font to use
|
||||
:param grad_start: colors to use for background procedure
|
||||
:param grad_end: colors to use for background procedure
|
||||
:param filter: the person filter to apply to the people in the chart
|
||||
:param filtr: the person filter to apply to the people in the chart
|
||||
:param alpha_filter: the alpha transparency value (0-1) to apply to
|
||||
filtered out data
|
||||
:param form: the ``FORM_`` constant for the fanchart
|
||||
:param angle_algo: alorithm to use to calculate the sizes of the boxes
|
||||
:param dupcolor: color to use for people or families that occur a second
|
||||
or more time
|
||||
:param showid: to show the gramps_id or not
|
||||
"""
|
||||
self.rootpersonh = root_person_handle
|
||||
self.generations = maxgen
|
||||
@ -133,7 +134,7 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
self.fontdescr = fontdescr
|
||||
self.grad_start = grad_start
|
||||
self.grad_end = grad_end
|
||||
self.filter = filter
|
||||
self.filter = filtr
|
||||
self.alpha_filter = alpha_filter
|
||||
self.form = form
|
||||
self.anglealgo = angle_algo
|
||||
@ -141,6 +142,7 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
self.childring = False
|
||||
self.flipupsidedownname = flipupsidedownname
|
||||
self.twolinename = twolinename
|
||||
self.showid = showid
|
||||
|
||||
def set_generations(self):
|
||||
"""
|
||||
@ -160,7 +162,8 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
self.gen2people = {}
|
||||
self.gen2fam = {}
|
||||
self.innerring = []
|
||||
self.gen2people[0] = [(None, False, 0, 2*pi, 0, 0, [], NORMAL)] #no center person
|
||||
self.gen2people[0] = [(None, False, 0, 2*math.pi, 0, 0,
|
||||
[], NORMAL)] #no center person
|
||||
self.gen2fam[0] = [] #no families
|
||||
self.angle = {}
|
||||
self.angle[-2] = []
|
||||
@ -180,7 +183,7 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
|
||||
# person, duplicate or not, start angle, slice size,
|
||||
# text, parent pos in fam, nrfam, userdata, status
|
||||
self.gen2people[0] = [[person, False, 0, 2*pi, 0, 0, [], NORMAL]]
|
||||
self.gen2people[0] = [[person, False, 0, 2*math.pi, 0, 0, [], NORMAL]]
|
||||
self.handle2desc[self.rootpersonh] = 0
|
||||
# fill in data for the parents
|
||||
self.innerring = []
|
||||
@ -191,7 +194,8 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
family = self.dbstate.db.get_family_from_handle(family_handle)
|
||||
if not family:
|
||||
continue
|
||||
for hparent in [family.get_father_handle(), family.get_mother_handle()]:
|
||||
for hparent in [family.get_father_handle(),
|
||||
family.get_mother_handle()]:
|
||||
if hparent and hparent not in handleparents:
|
||||
parent = self.dbstate.db.get_person_from_handle(hparent)
|
||||
if parent:
|
||||
@ -224,26 +228,31 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
fam_duplicate = family_handle in self.famhandle2desc
|
||||
# family, duplicate or not, start angle, slice size,
|
||||
# spouse pos in gen, nrchildren, userdata, parnter, status
|
||||
self.gen2fam[gen].append([family, fam_duplicate, 0, 0, pos, 0, [], spouse, NORMAL])
|
||||
self.gen2fam[gen].append([family, fam_duplicate, 0, 0,
|
||||
pos, 0, [], spouse, NORMAL])
|
||||
posfam = len(self.gen2fam[gen]) - 1
|
||||
|
||||
if not fam_duplicate and gen < maxgen-1:
|
||||
nrchild = len(family.get_child_ref_list())
|
||||
self.gen2fam[gen][posfam][5] = nrchild
|
||||
for child_ref in family.get_child_ref_list():
|
||||
child = self.dbstate.db.get_person_from_handle(child_ref.ref)
|
||||
chld = self.dbstate.db.get_person_from_handle(child_ref.ref)
|
||||
child_dup = child_ref.ref in self.handle2desc
|
||||
if not child_dup:
|
||||
self.handle2desc[child_ref.ref] = 0 # mark this child as processed
|
||||
# mark this child as processed
|
||||
self.handle2desc[child_ref.ref] = 0
|
||||
# person, duplicate or not, start angle, slice size,
|
||||
# parent pos in fam, nrfam, userdata, status
|
||||
self.gen2people[gen+1].append([child, child_dup, 0, 0, posfam, 0, [], NORMAL])
|
||||
self.gen2people[gen+1].append([chld, child_dup, 0, 0,
|
||||
posfam, 0, [], NORMAL])
|
||||
totdescfam += 1 #add this person as descendant
|
||||
pospers = len(self.gen2people[gen+1]) - 1
|
||||
pospers = len(self.gen2people[gen + 1]) - 1
|
||||
if not child_dup:
|
||||
nrdesc = self._rec_fill_data(gen+1, child, pospers, maxgen)
|
||||
nrdesc = self._rec_fill_data(gen + 1, chld,
|
||||
pospers, maxgen)
|
||||
self.handle2desc[child_ref.ref] += nrdesc
|
||||
totdescfam += nrdesc # add children of him as descendants
|
||||
# add children of him as descendants
|
||||
totdescfam += nrdesc
|
||||
if not fam_duplicate:
|
||||
self.famhandle2desc[family_handle] = totdescfam
|
||||
totdesc += totdescfam
|
||||
@ -255,13 +264,13 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
"""
|
||||
#first we compute the size of the slice.
|
||||
#set angles root person
|
||||
start, slice = start_rad, stop_rad - start_rad
|
||||
start, portion = start_rad, stop_rad - start_rad
|
||||
nr_gen = len(self.gen2people)-1
|
||||
# Fill in central person angles
|
||||
gen = 0
|
||||
data = self.gen2people[gen][0]
|
||||
data[2] = start
|
||||
data[3] = slice
|
||||
data[3] = portion
|
||||
for gen in range(0, nr_gen):
|
||||
prevpartnerdatahandle = None
|
||||
offset = 0
|
||||
@ -281,40 +290,42 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
#partner of a new person: reset the offset
|
||||
offset = 0
|
||||
prevpartnerdatahandle = persondata[0].handle
|
||||
slice = personslice/(nrdescperson+nrfam)*(nrdescfam+1)
|
||||
portion = personslice/(nrdescperson+nrfam)*(nrdescfam+1)
|
||||
if data_fam[8] == COLLAPSED:
|
||||
slice = 0
|
||||
portion = 0
|
||||
elif data_fam[8] == EXPANDED:
|
||||
slice = personslice
|
||||
portion = personslice
|
||||
|
||||
data_fam[2] = personstart + offset
|
||||
data_fam[3] = slice
|
||||
offset += slice
|
||||
data_fam[3] = portion
|
||||
offset += portion
|
||||
|
||||
## if nrdescperson == 0:
|
||||
## #no offspring, draw as large as fraction of
|
||||
## #nr families
|
||||
## nrfam = persondata[6]
|
||||
## slice = personslice/nrfam
|
||||
## portion = personslice/nrfam
|
||||
## data_fam[2] = personstart + offset
|
||||
## data_fam[3] = slice
|
||||
## offset += slice
|
||||
## data_fam[3] = portion
|
||||
## offset += portion
|
||||
## elif nrdescfam == 0:
|
||||
## #no offspring this family, but there is another
|
||||
## #family. We draw this as a weight of 1
|
||||
## nrfam = persondata[6]
|
||||
## slice = personslice/(nrdescperson + nrfam - 1)*(nrdescfam+1)
|
||||
## portion = (personslice/(nrdescperson + nrfam - 1) *
|
||||
## (nrdescfam + 1))
|
||||
## data_fam[2] = personstart + offset
|
||||
## data_fam[3] = slice
|
||||
## offset += slice
|
||||
## data_fam[3] = portion
|
||||
## offset += portion
|
||||
## else:
|
||||
## #this family has offspring. We give it space for it's
|
||||
## #weight in offspring
|
||||
## nrfam = persondata[6]
|
||||
## slice = personslice/(nrdescperson + nrfam - 1)*(nrdescfam+1)
|
||||
## portion = (personslice/(nrdescperson + nrfam - 1) *
|
||||
## (nrdescfam + 1))
|
||||
## data_fam[2] = personstart + offset
|
||||
## data_fam[3] = slice
|
||||
## offset += slice
|
||||
## data_fam[3] = portion
|
||||
## offset += portion
|
||||
|
||||
prevfamdatahandle = None
|
||||
offset = 0
|
||||
@ -333,27 +344,30 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
#now we divide this slice to the weight of children,
|
||||
#adding one for every child
|
||||
if self.anglealgo == ANGLE_CHEQUI:
|
||||
slice = famslice / nrchild
|
||||
portion = famslice / nrchild
|
||||
elif self.anglealgo == ANGLE_WEIGHT:
|
||||
slice = famslice/(nrdescfam) * (nrdesc + 1)
|
||||
portion = famslice/(nrdescfam) * (nrdesc + 1)
|
||||
else:
|
||||
raise NotImplementedError('Unknown angle algorithm %d' % self.anglealgo)
|
||||
raise NotImplementedError('Unknown angle algorithm %d' %
|
||||
self.anglealgo)
|
||||
if prevfamdatahandle != parentfamdata[0].handle:
|
||||
#reset the offset
|
||||
offset = 0
|
||||
prevfamdatahandle = parentfamdata[0].handle
|
||||
if persondata[7] == COLLAPSED:
|
||||
slice = 0
|
||||
portion = 0
|
||||
elif persondata[7] == EXPANDED:
|
||||
slice = famslice
|
||||
portion = famslice
|
||||
persondata[2] = famstart + offset
|
||||
persondata[3] = slice
|
||||
offset += slice
|
||||
persondata[3] = portion
|
||||
offset += portion
|
||||
|
||||
def nrgen(self):
|
||||
#compute the number of generations present
|
||||
"""
|
||||
compute the number of generations present
|
||||
"""
|
||||
for gen in range(self.generations - 1, 0, -1):
|
||||
if len(self.gen2people[gen]) > 0:
|
||||
if self.gen2people[gen]:
|
||||
return gen + 1
|
||||
return 1
|
||||
|
||||
@ -363,25 +377,35 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
"""
|
||||
return self.get_radiusinout_for_generation(self.nrgen())[1]
|
||||
|
||||
def get_radiusinout_for_generation(self,generation):
|
||||
def get_radiusinout_for_generation(self, generation):
|
||||
"""
|
||||
Get the in and out radius for the generation
|
||||
"""
|
||||
radius_first_gen = 14 # fudged to make inner circle a bit tighter
|
||||
if generation < N_GEN_SMALL:
|
||||
radius_start = PIXELS_PER_GEN_SMALL * generation + radius_first_gen
|
||||
return (radius_start,radius_start + PIXELS_PER_GEN_SMALL)
|
||||
return (radius_start, radius_start + PIXELS_PER_GEN_SMALL)
|
||||
else:
|
||||
radius_start = PIXELS_PER_GEN_SMALL * N_GEN_SMALL + PIXELS_PER_GEN_LARGE \
|
||||
* ( generation - N_GEN_SMALL ) + radius_first_gen
|
||||
return (radius_start,radius_start + PIXELS_PER_GEN_LARGE)
|
||||
radius_start = (PIXELS_PER_GEN_SMALL * N_GEN_SMALL +
|
||||
PIXELS_PER_GEN_LARGE * (generation - N_GEN_SMALL)
|
||||
+ radius_first_gen)
|
||||
return (radius_start, radius_start + PIXELS_PER_GEN_LARGE)
|
||||
|
||||
def get_radiusinout_for_generation_pair(self,generation):
|
||||
def get_radiusinout_for_gen_pair(self, generation):
|
||||
"""
|
||||
Get the in and out radius for the father and mother
|
||||
"""
|
||||
radiusin, radiusout = self.get_radiusinout_for_generation(generation)
|
||||
radius_spread = radiusout - radiusin - PIXELS_CHILDREN_GAP - PIXELS_PARTNER_GAP
|
||||
radius_spread = (radiusout - radiusin -
|
||||
PIXELS_CHILDREN_GAP - PIXELS_PARTNER_GAP)
|
||||
|
||||
radiusin_pers = radiusin + PIXELS_CHILDREN_GAP
|
||||
radiusout_pers = radiusin_pers + PIXELS_PER_GENPERSON_RATIO * radius_spread
|
||||
radiusout_pers = (radiusin_pers +
|
||||
PIXELS_PER_GENPERSON_RATIO * radius_spread)
|
||||
radiusin_partner = radiusout_pers + PIXELS_PARTNER_GAP
|
||||
radiusout_partner = radiusout
|
||||
return (radiusin_pers,radiusout_pers,radiusin_partner,radiusout_partner)
|
||||
return (radiusin_pers, radiusout_pers,
|
||||
radiusin_partner, radiusout_partner)
|
||||
|
||||
def people_generator(self):
|
||||
"""
|
||||
@ -402,18 +426,18 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
parent, userdata = parentdata
|
||||
yield (parent, userdata)
|
||||
|
||||
def draw(self, cr=None, scale=1.0):
|
||||
def draw(self, ctx=None, scale=1.0):
|
||||
"""
|
||||
The main method to do the drawing.
|
||||
If cr is given, we assume we draw draw raw on the cairo context cr
|
||||
To draw in GTK3 and use the allocation, set cr=None.
|
||||
If ctx is given, we assume we draw draw raw on the cairo context ctx
|
||||
To draw in GTK3 and use the allocation, set ctx=None.
|
||||
Note: when drawing for display, to counter a Gtk issue with scrolling
|
||||
or resizing the drawing window, we draw on a surface, then copy to the
|
||||
drawing context when the Gtk 'draw' signal arrives.
|
||||
"""
|
||||
# first do size request of what we will need
|
||||
halfdist = self.halfdist()
|
||||
if not cr: # Display
|
||||
if not ctx: # Display
|
||||
if self.form == FORM_CIRCLE:
|
||||
size_w = size_h = 2 * halfdist
|
||||
elif self.form == FORM_HALFCIRCLE:
|
||||
@ -429,67 +453,73 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
size_h = self.get_allocated_height()
|
||||
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
|
||||
size_w, size_h)
|
||||
cr = cairo.Context(self.surface)
|
||||
ctx = cairo.Context(self.surface)
|
||||
self.center_xy = self.center_xy_from_delta()
|
||||
cr.translate(*self.center_xy)
|
||||
ctx.translate(*self.center_xy)
|
||||
else: # printing
|
||||
if self.form == FORM_QUADRANT:
|
||||
self.center_xy = self.CENTER, halfdist
|
||||
else:
|
||||
self.center_xy = halfdist, halfdist
|
||||
cr.scale(scale, scale)
|
||||
cr.translate(*self.center_xy)
|
||||
ctx.scale(scale, scale)
|
||||
ctx.translate(*self.center_xy)
|
||||
|
||||
cr.save()
|
||||
ctx.save()
|
||||
# Draw center person:
|
||||
(person, dup, start, slice, parentfampos, nrfam, userdata, status) \
|
||||
= self.gen2people[0][0]
|
||||
(person, dup, start, portion, dummy_parentfampos, dummy_nrfam,
|
||||
userdata, status) = self.gen2people[0][0]
|
||||
if person:
|
||||
r, g, b, a = self.background_box(person, 0, userdata)
|
||||
radiusin_pers,radiusout_pers,radiusin_partner,radiusout_partner = \
|
||||
self.get_radiusinout_for_generation_pair(0)
|
||||
if not self.innerring: radiusin_pers = TRANSLATE_PX
|
||||
self.draw_person(cr, person, radiusin_pers, radiusout_pers, math.pi/2, math.pi/2 + 2*math.pi,
|
||||
0, False, userdata, is_central_person =True)
|
||||
#r, g, b, a = self.background_box(person, 0, userdata)
|
||||
(radiusin_pers, radiusout_pers, radiusin_partner,
|
||||
radiusout_partner) = self.get_radiusinout_for_gen_pair(0)
|
||||
if not self.innerring:
|
||||
radiusin_pers = TRANSLATE_PX
|
||||
self.draw_person(ctx, person, radiusin_pers, radiusout_pers,
|
||||
math.pi / 2, math.pi / 2 + 2 * math.pi,
|
||||
0, False, userdata, is_central_person=True)
|
||||
#draw center to move chart
|
||||
cr.set_source_rgb(0, 0, 0) # black
|
||||
cr.move_to(TRANSLATE_PX, 0)
|
||||
cr.arc(0, 0, TRANSLATE_PX, 0, 2 * math.pi)
|
||||
ctx.set_source_rgb(0, 0, 0) # black
|
||||
ctx.move_to(TRANSLATE_PX, 0)
|
||||
ctx.arc(0, 0, TRANSLATE_PX, 0, 2 * math.pi)
|
||||
if self.innerring: # has at least one parent
|
||||
cr.fill()
|
||||
self.draw_innerring_people(cr)
|
||||
ctx.fill()
|
||||
self.draw_innerring_people(ctx)
|
||||
else:
|
||||
cr.stroke()
|
||||
ctx.stroke()
|
||||
#now write all the families and children
|
||||
cr.rotate(self.rotate_value * math.pi/180)
|
||||
ctx.rotate(self.rotate_value * math.pi/180)
|
||||
for gen in range(self.generations):
|
||||
radiusin_pers,radiusout_pers,radiusin_partner,radiusout_partner = \
|
||||
self.get_radiusinout_for_generation_pair(gen)
|
||||
(radiusin_pers, radiusout_pers, radiusin_partner,
|
||||
radiusout_partner) = self.get_radiusinout_for_gen_pair(gen)
|
||||
if gen > 0:
|
||||
for pdata in self.gen2people[gen]:
|
||||
# person, duplicate or not, start angle, slice size,
|
||||
# parent pos in fam, nrfam, userdata, status
|
||||
pers, dup, start, slice, pospar, nrfam, userdata, status = \
|
||||
pdata
|
||||
(pers, dup, start, portion, dummy_pospar, dummy_nrfam,
|
||||
userdata, status) = pdata
|
||||
if status != COLLAPSED:
|
||||
self.draw_person(cr, pers, radiusin_pers, radiusout_pers,
|
||||
start, start + slice, gen, dup, userdata,
|
||||
self.draw_person(ctx, pers, radiusin_pers,
|
||||
radiusout_pers, start, start + portion,
|
||||
gen, dup, userdata,
|
||||
thick=status != NORMAL)
|
||||
#if gen < self.generations-1:
|
||||
for famdata in self.gen2fam[gen]:
|
||||
# family, duplicate or not, start angle, slice size,
|
||||
# spouse pos in gen, nrchildren, userdata, status
|
||||
fam, dup, start, slice, posfam, nrchild, userdata,\
|
||||
partner, status = famdata
|
||||
(fam, dup, start, portion, dummy_posfam, dummy_nrchild,
|
||||
userdata, partner, status) = famdata
|
||||
if status != COLLAPSED:
|
||||
more_pers_flag = (gen == self.generations - 1
|
||||
and len(fam.get_child_ref_list()) > 0)
|
||||
self.draw_person(cr, partner, radiusin_partner, radiusout_partner, start, start + slice,
|
||||
gen, dup, userdata, thick = (status != NORMAL), has_moregen_indicator = more_pers_flag )
|
||||
cr.restore()
|
||||
and fam.get_child_ref_list())
|
||||
self.draw_person(ctx, partner, radiusin_partner,
|
||||
radiusout_partner, start, start + portion,
|
||||
gen, dup, userdata,
|
||||
thick=(status != NORMAL),
|
||||
has_moregen_indicator=more_pers_flag)
|
||||
ctx.restore()
|
||||
|
||||
if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]:
|
||||
self.draw_gradient_legend(cr, halfdist)
|
||||
self.draw_gradient_legend(ctx, halfdist)
|
||||
|
||||
def cell_address_under_cursor(self, curx, cury):
|
||||
"""
|
||||
@ -497,21 +527,22 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
position x and y.
|
||||
None if outside of diagram
|
||||
"""
|
||||
radius, rads, raw_rads = self.cursor_to_polar(curx, cury, get_raw_rads=True)
|
||||
radius, rads, raw_rads = self.cursor_to_polar(curx, cury,
|
||||
get_raw_rads=True)
|
||||
|
||||
btype = TYPE_BOX_NORMAL
|
||||
if radius < TRANSLATE_PX:
|
||||
return None
|
||||
elif (self.innerring and self.angle[-2] and
|
||||
radius < CHILDRING_WIDTH + TRANSLATE_PX):
|
||||
radius < CHILDRING_WIDTH + TRANSLATE_PX):
|
||||
generation = -2 # indication of one of the children
|
||||
elif radius < self.CENTER:
|
||||
generation = 0
|
||||
else:
|
||||
generation = None
|
||||
for gen in range(self.generations):
|
||||
radiusin_pers,radiusout_pers,radiusin_partner,radiusout_partner \
|
||||
= self.get_radiusinout_for_generation_pair(gen)
|
||||
(radiusin_pers, radiusout_pers, radiusin_partner,
|
||||
radiusout_partner) = self.get_radiusinout_for_gen_pair(gen)
|
||||
if radiusin_pers <= radius <= radiusout_pers:
|
||||
generation, btype = gen, TYPE_BOX_NORMAL
|
||||
break
|
||||
@ -521,24 +552,27 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
|
||||
# find what person is in this position:
|
||||
selected = None
|
||||
if not (generation is None) and 0 <= generation:
|
||||
if not (generation is None) and generation > 0:
|
||||
selected = self.personpos_at_angle(generation, rads, btype)
|
||||
elif generation == -2:
|
||||
for p in range(len(self.angle[generation])):
|
||||
start, stop, state = self.angle[generation][p]
|
||||
for idx in range(len(self.angle[generation])):
|
||||
start, stop, dummy_state = self.angle[generation][idx]
|
||||
if self.radian_in_bounds(start, raw_rads, stop):
|
||||
selected = p
|
||||
selected = idx
|
||||
break
|
||||
if (generation is None or selected is None):
|
||||
return None
|
||||
return generation, selected, btype
|
||||
|
||||
def draw_innerring_people(self, cr):
|
||||
cr.move_to(TRANSLATE_PX + CHILDRING_WIDTH, 0)
|
||||
cr.set_source_rgb(0, 0, 0) # black
|
||||
cr.set_line_width(1)
|
||||
cr.arc(0, 0, TRANSLATE_PX + CHILDRING_WIDTH, 0, 2 * math.pi)
|
||||
cr.stroke()
|
||||
def draw_innerring_people(self, ctx):
|
||||
"""
|
||||
Draw the innerring person
|
||||
"""
|
||||
ctx.move_to(TRANSLATE_PX + CHILDRING_WIDTH, 0)
|
||||
ctx.set_source_rgb(0, 0, 0) # black
|
||||
ctx.set_line_width(1)
|
||||
ctx.arc(0, 0, TRANSLATE_PX + CHILDRING_WIDTH, 0, 2 * math.pi)
|
||||
ctx.stroke()
|
||||
nrparent = len(self.innerring)
|
||||
#Y axis is downward. positve angles are hence clockwise
|
||||
startangle = math.pi
|
||||
@ -549,7 +583,7 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
else:
|
||||
angleinc = 2 * math.pi / nrparent
|
||||
for data in self.innerring:
|
||||
self.draw_innerring(cr, data[0], data[1], startangle, angleinc)
|
||||
self.draw_innerring(ctx, data[0], data[1], startangle, angleinc)
|
||||
startangle += angleinc
|
||||
|
||||
def personpos_at_angle(self, generation, rads, btype):
|
||||
@ -559,19 +593,19 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
selected = None
|
||||
datas = None
|
||||
if btype == TYPE_BOX_NORMAL:
|
||||
if generation==0:
|
||||
if generation == 0:
|
||||
return 0 # central person is always ok !
|
||||
datas = self.gen2people[generation]
|
||||
elif btype == TYPE_BOX_FAMILY:
|
||||
datas = self.gen2fam[generation]
|
||||
else:
|
||||
return None
|
||||
for p, pdata in enumerate(datas):
|
||||
for idx, pdata in enumerate(datas):
|
||||
# person, duplicate or not, start angle, slice size,
|
||||
# parent pos in fam, nrfam, userdata, status
|
||||
start, stop = pdata[2], pdata[2] + pdata[3]
|
||||
if self.radian_in_bounds(start, rads, stop):
|
||||
selected = p
|
||||
selected = idx
|
||||
break
|
||||
return selected
|
||||
|
||||
@ -581,7 +615,7 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
"""
|
||||
generation, pos, btype = cell_address
|
||||
if generation == -2:
|
||||
person, userdata = self.innerring[pos]
|
||||
person, dummy_userdata = self.innerring[pos]
|
||||
elif btype == TYPE_BOX_NORMAL:
|
||||
# person, duplicate or not, start angle, slice size,
|
||||
# parent pos in fam, nrfam, userdata, status
|
||||
@ -602,7 +636,9 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
return self.gen2fam[generation][pos][0]
|
||||
|
||||
def do_mouse_click(self):
|
||||
# no drag occured, expand or collapse the section
|
||||
"""
|
||||
no drag occured, expand or collapse the section
|
||||
"""
|
||||
self.toggle_cell_state(self._mouse_click_cell_address)
|
||||
self._compute_angles(*self.rootangle_rad)
|
||||
self._mouse_click = False
|
||||
@ -610,6 +646,9 @@ class FanChartDescWidget(FanChartBaseWidget):
|
||||
self.queue_draw()
|
||||
|
||||
def toggle_cell_state(self, cell_address):
|
||||
"""
|
||||
Toggle the person cell (EXPAND/COLLAPSE)
|
||||
"""
|
||||
generation, selected, btype = cell_address
|
||||
if generation < 1:
|
||||
return
|
||||
@ -654,10 +693,12 @@ class FanChartDescGrampsGUI(FanChartGrampsGUI):
|
||||
data.
|
||||
"""
|
||||
root_person_handle = self.get_active('Person')
|
||||
self.fan.set_values(root_person_handle, self.maxgen, self.flipupsidedownname, self.twolinename, self.background,
|
||||
self.fonttype, self.grad_start, self.grad_end,
|
||||
self.generic_filter, self.alpha_filter, self.form,
|
||||
self.angle_algo, self.dupcolor)
|
||||
self.fan.set_values(root_person_handle, self.maxgen,
|
||||
self.flipupsidedownname, self.twolinename,
|
||||
self.background, self.fonttype, self.grad_start,
|
||||
self.grad_end, self.generic_filter,
|
||||
self.alpha_filter, self.form, self.angle_algo,
|
||||
self.dupcolor, self.showid)
|
||||
self.fan.reset()
|
||||
self.fan.draw()
|
||||
self.fan.queue_draw()
|
||||
|
@ -25,16 +25,6 @@
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Pango
|
||||
from gi.repository import Gtk
|
||||
import math
|
||||
from gi.repository import Gdk
|
||||
try:
|
||||
import cairo
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -42,14 +32,14 @@ except ImportError:
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.plug import Gramplet
|
||||
from gramps.gen.errors import WindowActiveError
|
||||
from gramps.gui.editors import EditPerson
|
||||
from gramps.gui.widgets.fanchart2way import (FanChart2WayWidget, FanChart2WayGrampsGUI,
|
||||
ANGLE_WEIGHT)
|
||||
from gramps.gui.widgets.fanchart2way import (FanChart2WayWidget,
|
||||
FanChart2WayGrampsGUI,
|
||||
ANGLE_WEIGHT)
|
||||
from gramps.gui.widgets.fanchart import FORM_HALFCIRCLE, BACKGROUND_SCHEME1
|
||||
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
class FanChart2WayGramplet(FanChart2WayGrampsGUI, Gramplet):
|
||||
"""
|
||||
The Gramplet code that realizes the FanChartWidget.
|
||||
@ -71,11 +61,13 @@ class FanChart2WayGramplet(FanChart2WayGrampsGUI, Gramplet):
|
||||
self.angle_algo = ANGLE_WEIGHT
|
||||
self.flipupsidedownname = True
|
||||
self.twolinename = True
|
||||
self.showid = False
|
||||
self.childring = False
|
||||
self.background_gradient = True
|
||||
#self.filter = filter
|
||||
|
||||
self.set_fan(FanChart2WayWidget(self.dbstate, self.uistate, self.on_popup))
|
||||
self.set_fan(FanChart2WayWidget(self.dbstate, self.uistate,
|
||||
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.fan)
|
||||
@ -83,7 +75,9 @@ class FanChart2WayGramplet(FanChart2WayGrampsGUI, Gramplet):
|
||||
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"))
|
||||
self.set_tooltip(_("Click to expand/contract person\n"
|
||||
"Right-click for options\n"
|
||||
"Click and drag in open area to rotate"))
|
||||
|
||||
def active_changed(self, handle):
|
||||
"""
|
||||
@ -95,5 +89,6 @@ class FanChart2WayGramplet(FanChart2WayGrampsGUI, Gramplet):
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
dummy_obj = obj
|
||||
self.set_active('Person', person_handle)
|
||||
return True
|
||||
|
@ -52,6 +52,7 @@ class FanChartDescGramplet(FanChartDescGrampsGUI, Gramplet):
|
||||
self.angle_algo = ANGLE_WEIGHT
|
||||
self.flipupsidedownname = True
|
||||
self.twolinename = True
|
||||
self.showid = False
|
||||
self.set_fan(FanChartDescWidget(self.dbstate, self.uistate,
|
||||
self.on_popup))
|
||||
# Replace the standard textview with the fan chart widget:
|
||||
@ -75,5 +76,6 @@ class FanChartDescGramplet(FanChartDescGrampsGUI, Gramplet):
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
dummy_obj = obj
|
||||
self.set_active('Person', person_handle)
|
||||
return True
|
||||
|
@ -56,6 +56,7 @@ class FanChartGramplet(FanChartGrampsGUI, Gramplet):
|
||||
self.generic_filter = None
|
||||
self.alpha_filter = 0.2
|
||||
self.form = FORM_HALFCIRCLE
|
||||
self.showid = False
|
||||
self.set_fan(FanChartWidget(self.dbstate, self.uistate, self.on_popup))
|
||||
# Replace the standard textview with the fan chart widget:
|
||||
self.gui.get_container_widget().remove(self.gui.textview)
|
||||
@ -77,5 +78,6 @@ class FanChartGramplet(FanChartGrampsGUI, Gramplet):
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
dummy_obj = obj
|
||||
self.set_active('Person', person_handle)
|
||||
return True
|
||||
|
@ -31,11 +31,9 @@
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
import cairo
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -51,6 +49,7 @@ from gramps.plugins.view.fanchartview import FanChartView
|
||||
|
||||
# the print settings to remember between print sessions
|
||||
PRINT_SETTINGS = None
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
"""
|
||||
@ -67,6 +66,7 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
('interface.fanview-flipupsidedownname', True),
|
||||
('interface.fanview-font', 'Sans'),
|
||||
('interface.fanview-form', fanchart.FORM_CIRCLE),
|
||||
('interface.fanview-showid', False),
|
||||
('interface.color-start-grad', '#ef2929'),
|
||||
('interface.color-end-grad', '#3d37e9'),
|
||||
('interface.angle-algorithm', fanchart2way.ANGLE_WEIGHT),
|
||||
@ -77,27 +77,30 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
self.uistate = uistate
|
||||
|
||||
NavigationView.__init__(self, _('2-Way Fan Chart'),
|
||||
pdata, dbstate, uistate,
|
||||
PersonBookmarks,
|
||||
nav_group)
|
||||
fanchart2way.FanChart2WayGrampsGUI.__init__(self, self.on_childmenu_changed)
|
||||
pdata, dbstate, uistate,
|
||||
PersonBookmarks, nav_group)
|
||||
fanchart2way.FanChart2WayGrampsGUI.__init__(self,
|
||||
self.on_childmenu_changed)
|
||||
#set needed values
|
||||
self.generations_asc = self._config.get('interface.fanview-maxgen-asc')
|
||||
self.generations_desc = self._config.get('interface.fanview-maxgen-desc')
|
||||
self.background = self._config.get('interface.fanview-background')
|
||||
self.background_gradient = self._config.get('interface.fanview-background-gradient')
|
||||
self.radialtext = self._config.get('interface.fanview-radialtext')
|
||||
self.twolinename = self._config.get('interface.fanview-twolinename')
|
||||
self.flipupsidedownname = self._config.get('interface.fanview-flipupsidedownname')
|
||||
self.fonttype = self._config.get('interface.fanview-font')
|
||||
scg = self._config.get
|
||||
self.generations_asc = scg('interface.fanview-maxgen-asc')
|
||||
self.generations_desc = scg('interface.fanview-maxgen-desc')
|
||||
self.background = scg('interface.fanview-background')
|
||||
self.background_gradient = scg('interface.fanview-background-gradient')
|
||||
self.radialtext = scg('interface.fanview-radialtext')
|
||||
self.twolinename = scg('interface.fanview-twolinename')
|
||||
self.flipupsidedownname = scg('interface.fanview-flipupsidedownname')
|
||||
self.fonttype = scg('interface.fanview-font')
|
||||
|
||||
self.grad_start = self._config.get('interface.color-start-grad')
|
||||
self.grad_end = self._config.get('interface.color-end-grad')
|
||||
self.grad_start = scg('interface.color-start-grad')
|
||||
self.grad_end = scg('interface.color-end-grad')
|
||||
self.form = fanchart.FORM_CIRCLE
|
||||
self.angle_algo = self._config.get('interface.angle-algorithm')
|
||||
self.dupcolor = self._config.get('interface.duplicate-color')
|
||||
self.showid = scg('interface.fanview-showid')
|
||||
self.angle_algo = scg('interface.angle-algorithm')
|
||||
self.dupcolor = scg('interface.duplicate-color')
|
||||
self.generic_filter = None
|
||||
self.alpha_filter = 0.2
|
||||
self.scrolledwindow = None
|
||||
|
||||
dbstate.connect('active-changed', self.active_changed)
|
||||
dbstate.connect('database-changed', self.change_db)
|
||||
@ -131,7 +134,7 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
self.set_fan(fanchart2way.FanChart2WayWidget(self.dbstate, self.uistate,
|
||||
self.on_popup))
|
||||
self.scrolledwindow = Gtk.ScrolledWindow(hadjustment=None,
|
||||
vadjustment=None)
|
||||
vadjustment=None)
|
||||
self.scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC,
|
||||
Gtk.PolicyType.AUTOMATIC)
|
||||
self.fan.show_all()
|
||||
@ -174,6 +177,7 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
"""
|
||||
Method called when active person changes.
|
||||
"""
|
||||
dummy_handle = handle
|
||||
# Reset everything but rotation angle (leave it as is)
|
||||
self.update()
|
||||
|
||||
@ -191,28 +195,43 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
self._add_db_signal('family-rebuild', self.person_rebuild)
|
||||
|
||||
def change_db(self, db):
|
||||
"""
|
||||
We selected a new database
|
||||
"""
|
||||
self._change_db(db)
|
||||
if self.active:
|
||||
self.bookmarks.redraw()
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Redraw the fan chart
|
||||
"""
|
||||
self.main()
|
||||
|
||||
def goto_handle(self, handle):
|
||||
"""
|
||||
Draw the fan chart for the active person
|
||||
"""
|
||||
self.change_active(handle)
|
||||
self.main()
|
||||
|
||||
def get_active(self, object):
|
||||
def get_active(self, obj):
|
||||
"""overrule get_active, to support call as in Gramplets
|
||||
"""
|
||||
dummy_obj = obj
|
||||
return NavigationView.get_active(self)
|
||||
|
||||
def person_rebuild(self, *args):
|
||||
"""
|
||||
Redraw the fan chart for the person
|
||||
"""
|
||||
dummy_args = args
|
||||
self.update()
|
||||
|
||||
def person_rebuild_bm(self, *args):
|
||||
"""Large change to person database"""
|
||||
dummy_args = args
|
||||
self.person_rebuild()
|
||||
if self.active:
|
||||
self.bookmarks.redraw()
|
||||
@ -221,6 +240,7 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
"""
|
||||
Print or save the view that is currently shown
|
||||
"""
|
||||
dummy_obj = obj
|
||||
widthpx = 2 * self.fan.halfdist()
|
||||
heightpx = widthpx
|
||||
|
||||
@ -231,6 +251,7 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
dummy_obj = obj
|
||||
self.change_active(person_handle)
|
||||
return True
|
||||
|
||||
@ -254,78 +275,76 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
"""
|
||||
Function that builds the widget in the configuration dialog
|
||||
"""
|
||||
nrentry = 9
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
grid.set_column_spacing(6)
|
||||
grid.set_row_spacing(6)
|
||||
|
||||
configdialog.add_spinner(grid, _("Max ancestor generations"), 0,
|
||||
'interface.fanview-maxgen-asc', (1, 11),
|
||||
callback=self.cb_update_maxgen)
|
||||
'interface.fanview-maxgen-asc', (1, 11),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_spinner(grid, _("Max descendant generations"), 1,
|
||||
'interface.fanview-maxgen-desc', (1, 11),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_combo(grid,
|
||||
_('Text Font'),
|
||||
2, 'interface.fanview-font',
|
||||
self.allfonts, callback=self.cb_update_font, valueactive=True)
|
||||
'interface.fanview-maxgen-desc', (1, 11),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_combo(grid, _('Text Font'), 2,
|
||||
'interface.fanview-font',
|
||||
self.allfonts, callback=self.cb_update_font,
|
||||
valueactive=True)
|
||||
backgrvals = (
|
||||
(fanchart.BACKGROUND_GENDER, _('Gender colors')),
|
||||
(fanchart.BACKGROUND_GRAD_GEN, _('Generation based gradient')),
|
||||
(fanchart.BACKGROUND_GRAD_AGE, _('Age (0-100) based gradient')),
|
||||
(fanchart.BACKGROUND_SINGLE_COLOR,
|
||||
_('Single main (filter) color')),
|
||||
(fanchart.BACKGROUND_GRAD_PERIOD, _('Time period based gradient')),
|
||||
(fanchart.BACKGROUND_WHITE, _('White')),
|
||||
(fanchart.BACKGROUND_SCHEME1, _('Color scheme classic report')),
|
||||
(fanchart.BACKGROUND_SCHEME2, _('Color scheme classic view')),
|
||||
)
|
||||
(fanchart.BACKGROUND_GENDER, _('Gender colors')),
|
||||
(fanchart.BACKGROUND_GRAD_GEN, _('Generation based gradient')),
|
||||
(fanchart.BACKGROUND_GRAD_AGE, _('Age (0-100) based gradient')),
|
||||
(fanchart.BACKGROUND_SINGLE_COLOR, _('Single main (filter) color')),
|
||||
(fanchart.BACKGROUND_GRAD_PERIOD, _('Time period based gradient')),
|
||||
(fanchart.BACKGROUND_WHITE, _('White')),
|
||||
(fanchart.BACKGROUND_SCHEME1, _('Color scheme classic report')),
|
||||
(fanchart.BACKGROUND_SCHEME2, _('Color scheme classic view')),
|
||||
)
|
||||
curval = self._config.get('interface.fanview-background')
|
||||
nrval = 0
|
||||
for nr, val in backgrvals:
|
||||
if curval == nr:
|
||||
for nbr, dummy_val in backgrvals:
|
||||
if curval == nbr:
|
||||
break
|
||||
nrval += 1
|
||||
configdialog.add_combo(grid,
|
||||
_('Background'),
|
||||
3, 'interface.fanview-background',
|
||||
backgrvals,
|
||||
callback=self.cb_update_background, valueactive=False,
|
||||
setactive=nrval
|
||||
)
|
||||
configdialog.add_combo(grid, _('Background'), 3,
|
||||
'interface.fanview-background', backgrvals,
|
||||
callback=self.cb_update_background,
|
||||
valueactive=False, setactive=nrval)
|
||||
|
||||
# show names one two line
|
||||
configdialog.add_checkbox(grid,
|
||||
_('Add global background colored gradient'),
|
||||
4, 'interface.fanview-background-gradient')
|
||||
_('Add global background colored gradient'),
|
||||
4, 'interface.fanview-background-gradient')
|
||||
|
||||
#colors, stored as hex values
|
||||
configdialog.add_color(grid, _('Start gradient/Main color'), 5,
|
||||
'interface.color-start-grad', col=1)
|
||||
'interface.color-start-grad', col=1)
|
||||
configdialog.add_color(grid, _('End gradient/2nd color'), 6,
|
||||
'interface.color-end-grad', col=1)
|
||||
'interface.color-end-grad', col=1)
|
||||
configdialog.add_color(grid, _('Color for duplicates'), 7,
|
||||
'interface.duplicate-color', col=1)
|
||||
'interface.duplicate-color', col=1)
|
||||
# algo for the fan angle distribution
|
||||
configdialog.add_combo(grid, _('Fan chart distribution'), 8,
|
||||
'interface.angle-algorithm',
|
||||
((fanchart2way.ANGLE_CHEQUI,
|
||||
_('Homogeneous children distribution')),
|
||||
(fanchart2way.ANGLE_WEIGHT,
|
||||
_('Size proportional to number of descendants')),
|
||||
),
|
||||
callback=self.cb_update_anglealgo)
|
||||
'interface.angle-algorithm',
|
||||
((fanchart2way.ANGLE_CHEQUI,
|
||||
_('Homogeneous children distribution')),
|
||||
(fanchart2way.ANGLE_WEIGHT,
|
||||
_('Size proportional to number'
|
||||
' of descendants')),
|
||||
),
|
||||
callback=self.cb_update_anglealgo)
|
||||
|
||||
# show names one two line
|
||||
configdialog.add_checkbox(grid,
|
||||
_('Show names on two lines'),
|
||||
9, 'interface.fanview-twolinename')
|
||||
configdialog.add_checkbox(grid, _('Show names on two lines'),
|
||||
9, 'interface.fanview-twolinename')
|
||||
|
||||
# Flip names
|
||||
configdialog.add_checkbox(grid,
|
||||
_('Flip name on the left of the fan'),
|
||||
10, 'interface.fanview-flipupsidedownname')
|
||||
configdialog.add_checkbox(grid, _('Flip name on the left of the fan'),
|
||||
10, 'interface.fanview-flipupsidedownname')
|
||||
|
||||
# Show gramps id
|
||||
configdialog.add_checkbox(grid, _('Show the gramps id'),
|
||||
11, 'interface.fanview-showid')
|
||||
|
||||
return _('Layout'), grid
|
||||
|
||||
@ -336,22 +355,28 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
use it to monitor changes in the ini file
|
||||
"""
|
||||
self._config.connect('interface.color-start-grad',
|
||||
self.cb_update_color)
|
||||
self.cb_update_color)
|
||||
self._config.connect('interface.color-end-grad',
|
||||
self.cb_update_color)
|
||||
self.cb_update_color)
|
||||
self._config.connect('interface.duplicate-color',
|
||||
self.cb_update_color)
|
||||
self.cb_update_color)
|
||||
self._config.connect('interface.fanview-flipupsidedownname',
|
||||
self.cb_update_flipupsidedownname)
|
||||
self.cb_update_flipupsidedownname)
|
||||
self._config.connect('interface.fanview-twolinename',
|
||||
self.cb_update_twolinename)
|
||||
self.cb_update_twolinename)
|
||||
self._config.connect('interface.fanview-background-gradient',
|
||||
self.cb_update_background_gradient)
|
||||
self.cb_update_background_gradient)
|
||||
self._config.connect('interface.fanview-showid',
|
||||
self.cb_update_showid)
|
||||
|
||||
def cb_update_maxgen(self, spinbtn, constant):
|
||||
"""
|
||||
The maximum generations in the fanchart
|
||||
"""
|
||||
self._config.set(constant, spinbtn.get_value_as_int())
|
||||
self.generations_asc = int(self._config.get('interface.fanview-maxgen-asc'))
|
||||
self.generations_desc = int(self._config.get('interface.fanview-maxgen-desc'))
|
||||
scg = self._config.get
|
||||
self.generations_asc = int(scg('interface.fanview-maxgen-asc'))
|
||||
self.generations_desc = int(scg('interface.fanview-maxgen-desc'))
|
||||
self.update()
|
||||
|
||||
def cb_update_twolinename(self, client, cnxn_id, entry, data):
|
||||
@ -361,11 +386,21 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
self.twolinename = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_showid(self, client, cnxn_id, entry, data):
|
||||
"""
|
||||
Called when the configuration menu changes the showid setting.
|
||||
"""
|
||||
self.showid = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_background(self, obj, constant):
|
||||
"""
|
||||
The background selected
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
Gtk.TreePath.new_from_string('%d' % entry)
|
||||
val = int(obj.get_model().get_value(
|
||||
obj.get_model().get_iter_from_string('%d' % entry), 0))
|
||||
obj.get_model().get_iter_from_string('%d' % entry), 0))
|
||||
self._config.set(constant, val)
|
||||
self.background = val
|
||||
self.update()
|
||||
@ -378,12 +413,18 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
self.update()
|
||||
|
||||
def cb_update_form(self, obj, constant):
|
||||
"""
|
||||
Update the fanchart form: CIRCLE, HALFCIRCLE or QUADRANT
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, entry)
|
||||
self.form = entry
|
||||
self.update()
|
||||
|
||||
def cb_update_anglealgo(self, obj, constant):
|
||||
"""
|
||||
Update the angle algorythm : homogeneous children distribution or not
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, entry)
|
||||
self.angle_algo = entry
|
||||
@ -400,12 +441,16 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
|
||||
|
||||
def cb_update_flipupsidedownname(self, client, cnxn_id, entry, data):
|
||||
"""
|
||||
Called when the configuration menu changes the flipupsidedownname setting.
|
||||
Called when the configuration menu changes the
|
||||
flipupsidedownname setting.
|
||||
"""
|
||||
self.flipupsidedownname = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_font(self, obj, constant):
|
||||
"""
|
||||
Update the choosed font
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, self.allfonts[entry][1])
|
||||
self.fonttype = self.allfonts[entry][1]
|
||||
@ -440,6 +485,8 @@ class CairoPrintSave():
|
||||
self.heightpx = heightpx
|
||||
self.drawfunc = drawfunc
|
||||
self.parent = parent
|
||||
self.preview = None
|
||||
self.previewopr = None
|
||||
|
||||
def run(self):
|
||||
"""Create the physical output from the meta document.
|
||||
@ -473,7 +520,8 @@ class CairoPrintSave():
|
||||
# run print dialog
|
||||
while True:
|
||||
self.preview = None
|
||||
res = operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, self.parent)
|
||||
res = operation.run(Gtk.PrintOperationAction.PRINT_DIALOG,
|
||||
self.parent)
|
||||
if self.preview is None: # cancel or print
|
||||
break
|
||||
# set up printing again; can't reuse PrintOperation?
|
||||
@ -493,20 +541,24 @@ class CairoPrintSave():
|
||||
def on_draw_page(self, operation, context, page_nr):
|
||||
"""Draw a page on a Cairo context.
|
||||
"""
|
||||
cr = context.get_cairo_context()
|
||||
dummy_operation = operation
|
||||
dummy_page_nr = page_nr
|
||||
ctx = context.get_cairo_context()
|
||||
pxwidth = round(context.get_width())
|
||||
pxheight = round(context.get_height())
|
||||
scale = min(pxwidth/self.widthpx, pxheight/self.heightpx)
|
||||
self.drawfunc(None, cr, scale=scale)
|
||||
self.drawfunc(None, ctx, scale=scale)
|
||||
|
||||
def on_paginate(self, operation, context):
|
||||
"""Paginate the whole document in chunks.
|
||||
We don't need this as there is only one page, however,
|
||||
we provide a dummy holder here, because on_preview crashes if no
|
||||
default application is set with gir 3.3.2 (typically evince not installed)!
|
||||
default application is set with gir 3.3.2
|
||||
(typically evince not installed)!
|
||||
It will provide the start of the preview dialog, which cannot be
|
||||
started in on_preview
|
||||
"""
|
||||
dummy_context = context
|
||||
finished = True
|
||||
# update page number
|
||||
operation.set_n_pages(1)
|
||||
@ -520,13 +572,15 @@ class CairoPrintSave():
|
||||
def on_preview(self, operation, preview, context, parent):
|
||||
"""Implement custom print preview functionality.
|
||||
We provide a dummy holder here, because on_preview crashes if no
|
||||
default application is set with gir 3.3.2 (typically evince not installed)!
|
||||
default application is set with gir 3.3.2
|
||||
(typically evince not installed)!
|
||||
"""
|
||||
dummy_preview = preview
|
||||
dlg = Gtk.MessageDialog(parent,
|
||||
flags=Gtk.DialogFlags.MODAL,
|
||||
type=Gtk.MessageType.WARNING,
|
||||
buttons=Gtk.ButtonsType.CLOSE,
|
||||
message_format=_('No preview available'))
|
||||
flags=Gtk.DialogFlags.MODAL,
|
||||
type=Gtk.MessageType.WARNING,
|
||||
buttons=Gtk.ButtonsType.CLOSE,
|
||||
message_format=_('No preview available'))
|
||||
self.preview = dlg
|
||||
self.previewopr = operation
|
||||
#dlg.format_secondary_markup(msg2)
|
||||
@ -543,11 +597,16 @@ class CairoPrintSave():
|
||||
except ValueError:
|
||||
height = 0
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
||||
cr = cairo.Context(surface)
|
||||
context.set_cairo_context(cr, 72.0, 72.0)
|
||||
ctx = cairo.Context(surface)
|
||||
context.set_cairo_context(ctx, 72.0, 72.0)
|
||||
|
||||
return True
|
||||
|
||||
def previewdestroy(self, dlg, res):
|
||||
"""
|
||||
Destroy the preview page
|
||||
"""
|
||||
dummy_dlg = dlg
|
||||
dummy_res = res
|
||||
self.preview.destroy()
|
||||
self.previewopr.end_preview()
|
||||
|
@ -30,11 +30,9 @@
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
import cairo
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -50,6 +48,7 @@ from gramps.plugins.view.fanchartview import FanChartView
|
||||
|
||||
# the print settings to remember between print sessions
|
||||
PRINT_SETTINGS = None
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
"""
|
||||
@ -67,32 +66,36 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
('interface.color-start-grad', '#ef2929'),
|
||||
('interface.color-end-grad', '#3d37e9'),
|
||||
('interface.angle-algorithm', fanchartdesc.ANGLE_WEIGHT),
|
||||
('interface.duplicate-color', '#888a85')
|
||||
('interface.duplicate-color', '#888a85'),
|
||||
('interface.fanview-showid', False)
|
||||
)
|
||||
def __init__(self, pdata, dbstate, uistate, nav_group=0):
|
||||
self.dbstate = dbstate
|
||||
self.uistate = uistate
|
||||
|
||||
NavigationView.__init__(self, _('Descendant Fan Chart'),
|
||||
pdata, dbstate, uistate,
|
||||
PersonBookmarks,
|
||||
nav_group)
|
||||
fanchartdesc.FanChartDescGrampsGUI.__init__(self, self.on_childmenu_changed)
|
||||
pdata, dbstate, uistate,
|
||||
PersonBookmarks, nav_group)
|
||||
fanchartdesc.FanChartDescGrampsGUI.__init__(self,
|
||||
self.on_childmenu_changed)
|
||||
#set needed values
|
||||
self.maxgen = self._config.get('interface.fanview-maxgen')
|
||||
self.background = self._config.get('interface.fanview-background')
|
||||
self.radialtext = self._config.get('interface.fanview-radialtext')
|
||||
self.twolinename = self._config.get('interface.fanview-twolinename')
|
||||
self.flipupsidedownname = self._config.get('interface.fanview-flipupsidedownname')
|
||||
self.fonttype = self._config.get('interface.fanview-font')
|
||||
scg = self._config.get
|
||||
self.maxgen = scg('interface.fanview-maxgen')
|
||||
self.background = scg('interface.fanview-background')
|
||||
self.radialtext = scg('interface.fanview-radialtext')
|
||||
self.twolinename = scg('interface.fanview-twolinename')
|
||||
self.flipupsidedownname = scg('interface.fanview-flipupsidedownname')
|
||||
self.fonttype = scg('interface.fanview-font')
|
||||
|
||||
self.grad_start = self._config.get('interface.color-start-grad')
|
||||
self.grad_end = self._config.get('interface.color-end-grad')
|
||||
self.form = self._config.get('interface.fanview-form')
|
||||
self.angle_algo = self._config.get('interface.angle-algorithm')
|
||||
self.dupcolor = self._config.get('interface.duplicate-color')
|
||||
self.grad_start = scg('interface.color-start-grad')
|
||||
self.grad_end = scg('interface.color-end-grad')
|
||||
self.form = scg('interface.fanview-form')
|
||||
self.angle_algo = scg('interface.angle-algorithm')
|
||||
self.dupcolor = scg('interface.duplicate-color')
|
||||
self.showid = scg('interface.fanview-showid')
|
||||
self.generic_filter = None
|
||||
self.alpha_filter = 0.2
|
||||
self.scrolledwindow = None
|
||||
|
||||
dbstate.connect('active-changed', self.active_changed)
|
||||
dbstate.connect('database-changed', self.change_db)
|
||||
@ -170,6 +173,7 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
Method called when active person changes.
|
||||
"""
|
||||
# Reset everything but rotation angle (leave it as is)
|
||||
dummy_handle = handle
|
||||
self.update()
|
||||
|
||||
def _connect_db_signals(self):
|
||||
@ -186,28 +190,43 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
self._add_db_signal('family-rebuild', self.person_rebuild)
|
||||
|
||||
def change_db(self, db):
|
||||
"""
|
||||
We selected a new database
|
||||
"""
|
||||
self._change_db(db)
|
||||
if self.active:
|
||||
self.bookmarks.redraw()
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Redraw the fan chart
|
||||
"""
|
||||
self.main()
|
||||
|
||||
def goto_handle(self, handle):
|
||||
"""
|
||||
Draw the fan chart for the active person
|
||||
"""
|
||||
self.change_active(handle)
|
||||
self.main()
|
||||
|
||||
def get_active(self, object):
|
||||
def get_active(self, obj):
|
||||
"""overrule get_active, to support call as in Gramplets
|
||||
"""
|
||||
dummy_obj = obj
|
||||
return NavigationView.get_active(self)
|
||||
|
||||
def person_rebuild(self, *args):
|
||||
"""
|
||||
Redraw the fan chart for the person
|
||||
"""
|
||||
dummy_args = args
|
||||
self.update()
|
||||
|
||||
def person_rebuild_bm(self, *args):
|
||||
"""Large change to person database"""
|
||||
dummy_args = args
|
||||
self.person_rebuild()
|
||||
if self.active:
|
||||
self.bookmarks.redraw()
|
||||
@ -216,6 +235,7 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
"""
|
||||
Print or save the view that is currently shown
|
||||
"""
|
||||
dummy_obj = obj
|
||||
widthpx = 2 * self.fan.halfdist()
|
||||
heightpx = widthpx
|
||||
if self.form == fanchart.FORM_HALFCIRCLE:
|
||||
@ -231,6 +251,7 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
dummy_obj = obj
|
||||
self.change_active(person_handle)
|
||||
return True
|
||||
|
||||
@ -254,76 +275,75 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
"""
|
||||
Function that builds the widget in the configuration dialog
|
||||
"""
|
||||
nrentry = 9
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
grid.set_column_spacing(6)
|
||||
grid.set_row_spacing(6)
|
||||
|
||||
configdialog.add_spinner(grid, _("Max generations"), 0,
|
||||
'interface.fanview-maxgen', (2, 16),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_combo(grid,
|
||||
_('Text Font'),
|
||||
1, 'interface.fanview-font',
|
||||
self.allfonts, callback=self.cb_update_font, valueactive=True)
|
||||
'interface.fanview-maxgen', (2, 16),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_combo(grid, _('Text Font'),
|
||||
1, 'interface.fanview-font',
|
||||
self.allfonts, callback=self.cb_update_font,
|
||||
valueactive=True)
|
||||
backgrvals = (
|
||||
(fanchart.BACKGROUND_GENDER, _('Gender colors')),
|
||||
(fanchart.BACKGROUND_GRAD_GEN, _('Generation based gradient')),
|
||||
(fanchart.BACKGROUND_GRAD_AGE, _('Age (0-100) based gradient')),
|
||||
(fanchart.BACKGROUND_SINGLE_COLOR,
|
||||
_('Single main (filter) color')),
|
||||
(fanchart.BACKGROUND_GRAD_PERIOD, _('Time period based gradient')),
|
||||
(fanchart.BACKGROUND_WHITE, _('White')),
|
||||
(fanchart.BACKGROUND_SCHEME1, _('Color scheme classic report')),
|
||||
(fanchart.BACKGROUND_SCHEME2, _('Color scheme classic view')),
|
||||
)
|
||||
(fanchart.BACKGROUND_GENDER, _('Gender colors')),
|
||||
(fanchart.BACKGROUND_GRAD_GEN, _('Generation based gradient')),
|
||||
(fanchart.BACKGROUND_GRAD_AGE, _('Age (0-100) based gradient')),
|
||||
(fanchart.BACKGROUND_SINGLE_COLOR,
|
||||
_('Single main (filter) color')),
|
||||
(fanchart.BACKGROUND_GRAD_PERIOD, _('Time period based gradient')),
|
||||
(fanchart.BACKGROUND_WHITE, _('White')),
|
||||
(fanchart.BACKGROUND_SCHEME1, _('Color scheme classic report')),
|
||||
(fanchart.BACKGROUND_SCHEME2, _('Color scheme classic view')),
|
||||
)
|
||||
curval = self._config.get('interface.fanview-background')
|
||||
nrval = 0
|
||||
for nr, val in backgrvals:
|
||||
if curval == nr:
|
||||
for nbr, dummy_val in backgrvals:
|
||||
if curval == nbr:
|
||||
break
|
||||
nrval += 1
|
||||
configdialog.add_combo(grid,
|
||||
_('Background'),
|
||||
2, 'interface.fanview-background',
|
||||
backgrvals,
|
||||
callback=self.cb_update_background, valueactive=False,
|
||||
setactive=nrval
|
||||
)
|
||||
configdialog.add_combo(grid, _('Background'),
|
||||
2, 'interface.fanview-background',
|
||||
backgrvals, callback=self.cb_update_background,
|
||||
valueactive=False, setactive=nrval)
|
||||
#colors, stored as hex values
|
||||
configdialog.add_color(grid, _('Start gradient/Main color'), 3,
|
||||
'interface.color-start-grad', col=1)
|
||||
'interface.color-start-grad', col=1)
|
||||
configdialog.add_color(grid, _('End gradient/2nd color'), 4,
|
||||
'interface.color-end-grad', col=1)
|
||||
'interface.color-end-grad', col=1)
|
||||
configdialog.add_color(grid, _('Color for duplicates'), 5,
|
||||
'interface.duplicate-color', col=1)
|
||||
'interface.duplicate-color', col=1)
|
||||
# form of the fan
|
||||
configdialog.add_combo(grid, _('Fan chart type'), 6,
|
||||
'interface.fanview-form',
|
||||
((fanchart.FORM_CIRCLE, _('Full Circle')),
|
||||
(fanchart.FORM_HALFCIRCLE, _('Half Circle')),
|
||||
(fanchart.FORM_QUADRANT, _('Quadrant'))),
|
||||
callback=self.cb_update_form)
|
||||
'interface.fanview-form',
|
||||
((fanchart.FORM_CIRCLE, _('Full Circle')),
|
||||
(fanchart.FORM_HALFCIRCLE, _('Half Circle')),
|
||||
(fanchart.FORM_QUADRANT, _('Quadrant'))),
|
||||
callback=self.cb_update_form)
|
||||
# algo for the fan angle distribution
|
||||
configdialog.add_combo(grid, _('Fan chart distribution'), 7,
|
||||
'interface.angle-algorithm',
|
||||
((fanchartdesc.ANGLE_CHEQUI,
|
||||
_('Homogeneous children distribution')),
|
||||
(fanchartdesc.ANGLE_WEIGHT,
|
||||
_('Size proportional to number of descendants')),
|
||||
),
|
||||
callback=self.cb_update_anglealgo)
|
||||
'interface.angle-algorithm',
|
||||
((fanchartdesc.ANGLE_CHEQUI,
|
||||
_('Homogeneous children distribution')),
|
||||
(fanchartdesc.ANGLE_WEIGHT,
|
||||
_('Size proportional to number'
|
||||
' of descendants')),
|
||||
),
|
||||
callback=self.cb_update_anglealgo)
|
||||
|
||||
# show names one two line
|
||||
configdialog.add_checkbox(grid,
|
||||
_('Show names on two lines'),
|
||||
8, 'interface.fanview-twolinename')
|
||||
configdialog.add_checkbox(grid, _('Show names on two lines'),
|
||||
8, 'interface.fanview-twolinename')
|
||||
|
||||
# Flip names
|
||||
configdialog.add_checkbox(grid,
|
||||
_('Flip name on the left of the fan'),
|
||||
9, 'interface.fanview-flipupsidedownname')
|
||||
configdialog.add_checkbox(grid, _('Flip name on the left of the fan'),
|
||||
9, 'interface.fanview-flipupsidedownname')
|
||||
|
||||
# show gramps_id
|
||||
configdialog.add_checkbox(grid, _('Show gramps id'),
|
||||
10, 'interface.fanview-showid')
|
||||
|
||||
return _('Layout'), grid
|
||||
|
||||
@ -334,17 +354,22 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
use it to monitor changes in the ini file
|
||||
"""
|
||||
self._config.connect('interface.color-start-grad',
|
||||
self.cb_update_color)
|
||||
self.cb_update_color)
|
||||
self._config.connect('interface.color-end-grad',
|
||||
self.cb_update_color)
|
||||
self.cb_update_color)
|
||||
self._config.connect('interface.duplicate-color',
|
||||
self.cb_update_color)
|
||||
self.cb_update_color)
|
||||
self._config.connect('interface.fanview-flipupsidedownname',
|
||||
self.cb_update_flipupsidedownname)
|
||||
self.cb_update_flipupsidedownname)
|
||||
self._config.connect('interface.fanview-twolinename',
|
||||
self.cb_update_twolinename)
|
||||
self.cb_update_twolinename)
|
||||
self._config.connect('interface.fanview-showid',
|
||||
self.cb_update_showid)
|
||||
|
||||
def cb_update_maxgen(self, spinbtn, constant):
|
||||
"""
|
||||
The maximum generations in the fanchart
|
||||
"""
|
||||
self.maxgen = spinbtn.get_value_as_int()
|
||||
self._config.set(constant, self.maxgen)
|
||||
self.update()
|
||||
@ -357,21 +382,30 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
self.update()
|
||||
|
||||
def cb_update_background(self, obj, constant):
|
||||
"""
|
||||
Change the background
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
Gtk.TreePath.new_from_string('%d' % entry)
|
||||
val = int(obj.get_model().get_value(
|
||||
obj.get_model().get_iter_from_string('%d' % entry), 0))
|
||||
obj.get_model().get_iter_from_string('%d' % entry), 0))
|
||||
self._config.set(constant, val)
|
||||
self.background = val
|
||||
self.update()
|
||||
|
||||
def cb_update_form(self, obj, constant):
|
||||
"""
|
||||
Update the fanchart form: CIRCLE, HALFCIRCLE or QUADRANT
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, entry)
|
||||
self.form = entry
|
||||
self.update()
|
||||
|
||||
def cb_update_anglealgo(self, obj, constant):
|
||||
"""
|
||||
Update the angle algorythm : homogeneous children distribution or not
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, entry)
|
||||
self.angle_algo = entry
|
||||
@ -388,12 +422,23 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
|
||||
|
||||
def cb_update_flipupsidedownname(self, client, cnxn_id, entry, data):
|
||||
"""
|
||||
Called when the configuration menu changes the flipupsidedownname setting.
|
||||
Called when the configuration menu changes the
|
||||
flipupsidedownname setting.
|
||||
"""
|
||||
self.flipupsidedownname = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_showid(self, client, cnxn_id, entry, data):
|
||||
"""
|
||||
Called when the configuration menu changes the showid setting.
|
||||
"""
|
||||
self.showid = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_font(self, obj, constant):
|
||||
"""
|
||||
Change the font
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, self.allfonts[entry][1])
|
||||
self.fonttype = self.allfonts[entry][1]
|
||||
@ -428,6 +473,8 @@ class CairoPrintSave:
|
||||
self.heightpx = heightpx
|
||||
self.drawfunc = drawfunc
|
||||
self.parent = parent
|
||||
self.preview = None
|
||||
self.previewopr = None
|
||||
|
||||
def run(self):
|
||||
"""Create the physical output from the meta document.
|
||||
@ -461,7 +508,8 @@ class CairoPrintSave:
|
||||
# run print dialog
|
||||
while True:
|
||||
self.preview = None
|
||||
res = operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, self.parent)
|
||||
res = operation.run(Gtk.PrintOperationAction.PRINT_DIALOG,
|
||||
self.parent)
|
||||
if self.preview is None: # cancel or print
|
||||
break
|
||||
# set up printing again; can't reuse PrintOperation?
|
||||
@ -481,20 +529,24 @@ class CairoPrintSave:
|
||||
def on_draw_page(self, operation, context, page_nr):
|
||||
"""Draw a page on a Cairo context.
|
||||
"""
|
||||
cr = context.get_cairo_context()
|
||||
dummy_operation = operation
|
||||
dummy_page_nr = page_nr
|
||||
ctx = context.get_cairo_context()
|
||||
pxwidth = round(context.get_width())
|
||||
pxheight = round(context.get_height())
|
||||
scale = min(pxwidth/self.widthpx, pxheight/self.heightpx)
|
||||
self.drawfunc(None, cr, scale=scale)
|
||||
self.drawfunc(None, ctx, scale=scale)
|
||||
|
||||
def on_paginate(self, operation, context):
|
||||
"""Paginate the whole document in chunks.
|
||||
We don't need this as there is only one page, however,
|
||||
we provide a dummy holder here, because on_preview crashes if no
|
||||
default application is set with gir 3.3.2 (typically evince not installed)!
|
||||
default application is set with gir 3.3.2
|
||||
(typically evince not installed)!
|
||||
It will provide the start of the preview dialog, which cannot be
|
||||
started in on_preview
|
||||
"""
|
||||
dummy_context = context
|
||||
finished = True
|
||||
# update page number
|
||||
operation.set_n_pages(1)
|
||||
@ -508,13 +560,15 @@ class CairoPrintSave:
|
||||
def on_preview(self, operation, preview, context, parent):
|
||||
"""Implement custom print preview functionality.
|
||||
We provide a dummy holder here, because on_preview crashes if no
|
||||
default application is set with gir 3.3.2 (typically evince not installed)!
|
||||
default application is set with gir 3.3.2
|
||||
(typically evince not installed)!
|
||||
"""
|
||||
dummy_preview = preview
|
||||
dlg = Gtk.MessageDialog(parent,
|
||||
flags=Gtk.DialogFlags.MODAL,
|
||||
type=Gtk.MessageType.WARNING,
|
||||
buttons=Gtk.ButtonsType.CLOSE,
|
||||
message_format=_('No preview available'))
|
||||
flags=Gtk.DialogFlags.MODAL,
|
||||
type=Gtk.MessageType.WARNING,
|
||||
buttons=Gtk.ButtonsType.CLOSE,
|
||||
message_format=_('No preview available'))
|
||||
self.preview = dlg
|
||||
self.previewopr = operation
|
||||
#dlg.format_secondary_markup(msg2)
|
||||
@ -531,11 +585,16 @@ class CairoPrintSave:
|
||||
except ValueError:
|
||||
height = 0
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
||||
cr = cairo.Context(surface)
|
||||
context.set_cairo_context(cr, 72.0, 72.0)
|
||||
ctx = cairo.Context(surface)
|
||||
context.set_cairo_context(ctx, 72.0, 72.0)
|
||||
|
||||
return True
|
||||
|
||||
def previewdestroy(self, dlg, res):
|
||||
"""
|
||||
Destroy the preview page
|
||||
"""
|
||||
dummy_dlg = dlg
|
||||
dummy_res = res
|
||||
self.preview.destroy()
|
||||
self.previewopr.end_preview()
|
||||
|
@ -30,11 +30,9 @@
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
import cairo
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.sgettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -48,6 +46,7 @@ from gramps.gui.utils import SystemFonts
|
||||
|
||||
# the print settings to remember between print sessions
|
||||
PRINT_SETTINGS = None
|
||||
_ = glocale.translation.sgettext
|
||||
|
||||
class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
"""
|
||||
@ -63,6 +62,7 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
('interface.fanview-flipupsidedownname', True),
|
||||
('interface.fanview-font', 'Sans'),
|
||||
('interface.fanview-form', fanchart.FORM_CIRCLE),
|
||||
('interface.fanview-showid', False),
|
||||
('interface.color-start-grad', '#ef2929'),
|
||||
('interface.color-end-grad', '#3d37e9'),
|
||||
)
|
||||
@ -71,24 +71,26 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
self.uistate = uistate
|
||||
|
||||
NavigationView.__init__(self, _('Fan Chart'),
|
||||
pdata, dbstate, uistate,
|
||||
PersonBookmarks,
|
||||
nav_group)
|
||||
pdata, dbstate, uistate,
|
||||
PersonBookmarks, nav_group)
|
||||
fanchart.FanChartGrampsGUI.__init__(self, self.on_childmenu_changed)
|
||||
#set needed values
|
||||
self.maxgen = self._config.get('interface.fanview-maxgen')
|
||||
self.background = self._config.get('interface.fanview-background')
|
||||
self.childring = self._config.get('interface.fanview-childrenring')
|
||||
self.radialtext = self._config.get('interface.fanview-radialtext')
|
||||
self.twolinename = self._config.get('interface.fanview-twolinename')
|
||||
self.flipupsidedownname = self._config.get('interface.fanview-flipupsidedownname')
|
||||
self.fonttype = self._config.get('interface.fanview-font')
|
||||
scg = self._config.get
|
||||
self.maxgen = scg('interface.fanview-maxgen')
|
||||
self.background = scg('interface.fanview-background')
|
||||
self.childring = scg('interface.fanview-childrenring')
|
||||
self.radialtext = scg('interface.fanview-radialtext')
|
||||
self.twolinename = scg('interface.fanview-twolinename')
|
||||
self.flipupsidedownname = scg('interface.fanview-flipupsidedownname')
|
||||
self.fonttype = scg('interface.fanview-font')
|
||||
|
||||
self.grad_start = self._config.get('interface.color-start-grad')
|
||||
self.grad_end = self._config.get('interface.color-end-grad')
|
||||
self.form = self._config.get('interface.fanview-form')
|
||||
self.grad_start = scg('interface.color-start-grad')
|
||||
self.grad_end = scg('interface.color-end-grad')
|
||||
self.form = scg('interface.fanview-form')
|
||||
self.showid = scg('interface.fanview-showid')
|
||||
self.generic_filter = None
|
||||
self.alpha_filter = 0.2
|
||||
self.scrolledwindow = None
|
||||
|
||||
dbstate.connect('active-changed', self.active_changed)
|
||||
dbstate.connect('database-changed', self.change_db)
|
||||
@ -119,7 +121,8 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
return None
|
||||
|
||||
def build_widget(self):
|
||||
self.set_fan(fanchart.FanChartWidget(self.dbstate, self.uistate, self.on_popup))
|
||||
self.set_fan(fanchart.FanChartWidget(self.dbstate, self.uistate,
|
||||
self.on_popup))
|
||||
self.scrolledwindow = Gtk.ScrolledWindow(hadjustment=None,
|
||||
vadjustment=None)
|
||||
self.scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC,
|
||||
@ -266,6 +269,7 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
"""
|
||||
Method called when active person changes.
|
||||
"""
|
||||
dummy_handle = handle
|
||||
# Reset everything but rotation angle (leave it as is)
|
||||
self.update()
|
||||
|
||||
@ -283,28 +287,43 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
self._add_db_signal('family-rebuild', self.person_rebuild)
|
||||
|
||||
def change_db(self, db):
|
||||
"""
|
||||
We selected a new database
|
||||
"""
|
||||
self._change_db(db)
|
||||
if self.active:
|
||||
self.bookmarks.redraw()
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Redraw the fan chart
|
||||
"""
|
||||
self.main()
|
||||
|
||||
def goto_handle(self, handle):
|
||||
"""
|
||||
Draw the fan chart for the active person
|
||||
"""
|
||||
self.change_active(handle)
|
||||
self.main()
|
||||
|
||||
def get_active(self, object):
|
||||
def get_active(self, obj):
|
||||
"""overrule get_active, to support call as in Gramplets
|
||||
"""
|
||||
dummy_obj = obj
|
||||
return NavigationView.get_active(self)
|
||||
|
||||
def person_rebuild(self, *args):
|
||||
"""
|
||||
Redraw the fan chart for the person
|
||||
"""
|
||||
dummy_args = args
|
||||
self.update()
|
||||
|
||||
def person_rebuild_bm(self, *args):
|
||||
"""Large change to person database"""
|
||||
dummy_args = args
|
||||
self.person_rebuild()
|
||||
if self.active:
|
||||
self.bookmarks.redraw()
|
||||
@ -313,6 +332,7 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
"""
|
||||
Print or save the view that is currently shown
|
||||
"""
|
||||
dummy_obj = obj
|
||||
widthpx = 2 * self.fan.halfdist()
|
||||
heightpx = widthpx
|
||||
if self.form == fanchart.FORM_HALFCIRCLE:
|
||||
@ -328,6 +348,7 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
def on_childmenu_changed(self, obj, person_handle):
|
||||
"""Callback for the pulldown menu selection, changing to the person
|
||||
attached with menu item."""
|
||||
dummy_obj = obj
|
||||
self.change_active(person_handle)
|
||||
return True
|
||||
|
||||
@ -351,68 +372,66 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
"""
|
||||
Function that builds the widget in the configuration dialog
|
||||
"""
|
||||
nrentry = 9
|
||||
nrentry = 10
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
grid.set_column_spacing(6)
|
||||
grid.set_row_spacing(6)
|
||||
|
||||
configdialog.add_spinner(grid, _("Max generations"), 0,
|
||||
'interface.fanview-maxgen', (1, 16),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_combo(grid,
|
||||
_('Text Font'),
|
||||
1, 'interface.fanview-font',
|
||||
self.allfonts, callback=self.cb_update_font, valueactive=True)
|
||||
'interface.fanview-maxgen', (1, 16),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_combo(grid, _('Text Font'),
|
||||
1, 'interface.fanview-font',
|
||||
self.allfonts, callback=self.cb_update_font,
|
||||
valueactive=True)
|
||||
backgrvals = (
|
||||
(fanchart.BACKGROUND_GENDER, _('Gender colors')),
|
||||
(fanchart.BACKGROUND_GRAD_GEN, _('Generation based gradient')),
|
||||
(fanchart.BACKGROUND_GRAD_AGE, _('Age (0-100) based gradient')),
|
||||
(fanchart.BACKGROUND_SINGLE_COLOR,
|
||||
_('Single main (filter) color')),
|
||||
(fanchart.BACKGROUND_GRAD_PERIOD, _('Time period based gradient')),
|
||||
(fanchart.BACKGROUND_WHITE, _('White')),
|
||||
(fanchart.BACKGROUND_SCHEME1, _('Color scheme classic report')),
|
||||
(fanchart.BACKGROUND_SCHEME2, _('Color scheme classic view')),
|
||||
)
|
||||
(fanchart.BACKGROUND_GENDER, _('Gender colors')),
|
||||
(fanchart.BACKGROUND_GRAD_GEN, _('Generation based gradient')),
|
||||
(fanchart.BACKGROUND_GRAD_AGE, _('Age (0-100) based gradient')),
|
||||
(fanchart.BACKGROUND_SINGLE_COLOR, _('Single main (filter) color')),
|
||||
(fanchart.BACKGROUND_GRAD_PERIOD, _('Time period based gradient')),
|
||||
(fanchart.BACKGROUND_WHITE, _('White')),
|
||||
(fanchart.BACKGROUND_SCHEME1, _('Color scheme classic report')),
|
||||
(fanchart.BACKGROUND_SCHEME2, _('Color scheme classic view')),
|
||||
)
|
||||
curval = self._config.get('interface.fanview-background')
|
||||
nrval = 0
|
||||
for nr, val in backgrvals:
|
||||
if curval == nr:
|
||||
for nbr, dummy_val in backgrvals:
|
||||
if curval == nbr:
|
||||
break
|
||||
nrval += 1
|
||||
configdialog.add_combo(grid,
|
||||
_('Background'),
|
||||
2, 'interface.fanview-background',
|
||||
backgrvals,
|
||||
callback=self.cb_update_background, valueactive=False, setactive=nrval
|
||||
)
|
||||
configdialog.add_combo(grid, _('Background'),
|
||||
2, 'interface.fanview-background', backgrvals,
|
||||
callback=self.cb_update_background,
|
||||
valueactive=False, setactive=nrval)
|
||||
#colors, stored as hex values
|
||||
configdialog.add_color(grid, _('Start gradient/Main color'), 3,
|
||||
'interface.color-start-grad', col=1)
|
||||
'interface.color-start-grad', col=1)
|
||||
configdialog.add_color(grid, _('End gradient/2nd color'), 4,
|
||||
'interface.color-end-grad', col=1)
|
||||
'interface.color-end-grad', col=1)
|
||||
# form of the fan
|
||||
configdialog.add_combo(grid, _('Fan chart type'), 5,
|
||||
'interface.fanview-form',
|
||||
((0, _('Full Circle')), (1,_('Half Circle')),
|
||||
(2, _('Quadrant'))),
|
||||
callback=self.cb_update_form)
|
||||
'interface.fanview-form',
|
||||
((0, _('Full Circle')), (1, _('Half Circle')),
|
||||
(2, _('Quadrant'))),
|
||||
callback=self.cb_update_form)
|
||||
|
||||
# show names one two line
|
||||
configdialog.add_checkbox(grid,
|
||||
_('Show names on two lines'),
|
||||
6, 'interface.fanview-twolinename')
|
||||
configdialog.add_checkbox(grid, _('Show names on two lines'),
|
||||
6, 'interface.fanview-twolinename')
|
||||
|
||||
# Flip names
|
||||
configdialog.add_checkbox(grid,
|
||||
_('Flip name on the left of the fan'),
|
||||
7, 'interface.fanview-flipupsidedownname')
|
||||
configdialog.add_checkbox(grid, _('Flip name on the left of the fan'),
|
||||
7, 'interface.fanview-flipupsidedownname')
|
||||
|
||||
# options users should not change:
|
||||
configdialog.add_checkbox(grid,
|
||||
_('Show children ring'),
|
||||
nrentry-1, 'interface.fanview-childrenring')
|
||||
configdialog.add_checkbox(grid, _('Show children ring'),
|
||||
nrentry-2, 'interface.fanview-childrenring')
|
||||
|
||||
# Show the gramps_id
|
||||
configdialog.add_checkbox(grid, _('Show gramps id'),
|
||||
nrentry-1, 'interface.fanview-showid')
|
||||
# options we don't show on the dialog
|
||||
##configdialog.add_checkbox(table,
|
||||
## _('Allow radial text'),
|
||||
@ -427,33 +446,44 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
use it to monitor changes in the ini file
|
||||
"""
|
||||
self._config.connect('interface.fanview-childrenring',
|
||||
self.cb_update_childrenring)
|
||||
self.cb_update_childrenring)
|
||||
self._config.connect('interface.fanview-twolinename',
|
||||
self.cb_update_twolinename)
|
||||
self.cb_update_twolinename)
|
||||
self._config.connect('interface.fanview-flipupsidedownname',
|
||||
self.cb_update_flipupsidedownname)
|
||||
self.cb_update_flipupsidedownname)
|
||||
self._config.connect('interface.fanview-radialtext',
|
||||
self.cb_update_radialtext)
|
||||
self.cb_update_radialtext)
|
||||
self._config.connect('interface.fanview-showid',
|
||||
self.cb_update_showid)
|
||||
self._config.connect('interface.color-start-grad',
|
||||
self.cb_update_color)
|
||||
self.cb_update_color)
|
||||
self._config.connect('interface.color-end-grad',
|
||||
self.cb_update_color)
|
||||
self.cb_update_color)
|
||||
|
||||
def cb_update_maxgen(self, spinbtn, constant):
|
||||
"""
|
||||
The maximum generations in the fanchart
|
||||
"""
|
||||
self.maxgen = spinbtn.get_value_as_int()
|
||||
self._config.set(constant, self.maxgen)
|
||||
self.update()
|
||||
|
||||
def cb_update_background(self, obj, constant):
|
||||
"""
|
||||
Change the background
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
Gtk.TreePath.new_from_string('%d' % entry)
|
||||
val = int(obj.get_model().get_value(
|
||||
obj.get_model().get_iter_from_string('%d' % entry), 0))
|
||||
obj.get_model().get_iter_from_string('%d' % entry), 0))
|
||||
self._config.set(constant, val)
|
||||
self.background = val
|
||||
self.update()
|
||||
|
||||
def cb_update_form(self, obj, constant):
|
||||
"""
|
||||
Update the fanchart form: CIRCLE, HALFCIRCLE or QUADRANT
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, entry)
|
||||
self.form = entry
|
||||
@ -463,20 +493,21 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
"""
|
||||
Called when the configuration menu changes the childrenring setting.
|
||||
"""
|
||||
if entry == 'True':
|
||||
self.childring = True
|
||||
else:
|
||||
self.childring = False
|
||||
self.childring = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_radialtext(self, client, cnxn_id, entry, data):
|
||||
"""
|
||||
Called when the configuration menu changes the childrenring setting.
|
||||
"""
|
||||
if entry == 'True':
|
||||
self.radialtext = True
|
||||
else:
|
||||
self.radialtext = False
|
||||
self.radialtext = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_showid(self, client, cnxn_id, entry, data):
|
||||
"""
|
||||
Called when the configuration menu changes the showid setting.
|
||||
"""
|
||||
self.showid = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_color(self, client, cnxn_id, entry, data):
|
||||
@ -496,12 +527,16 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
|
||||
|
||||
def cb_update_flipupsidedownname(self, client, cnxn_id, entry, data):
|
||||
"""
|
||||
Called when the configuration menu changes the flipupsidedownname setting.
|
||||
Called when the configuration menu changes the
|
||||
flipupsidedownname setting.
|
||||
"""
|
||||
self.flipupsidedownname = (entry == 'True')
|
||||
self.update()
|
||||
|
||||
def cb_update_font(self, obj, constant):
|
||||
"""
|
||||
Change the font
|
||||
"""
|
||||
entry = obj.get_active()
|
||||
self._config.set(constant, self.allfonts[entry][1])
|
||||
self.fonttype = self.allfonts[entry][1]
|
||||
@ -536,6 +571,8 @@ class CairoPrintSave:
|
||||
self.heightpx = heightpx
|
||||
self.drawfunc = drawfunc
|
||||
self.parent = parent
|
||||
self.preview = None
|
||||
self.previewopr = None
|
||||
|
||||
def run(self):
|
||||
"""Create the physical output from the meta document.
|
||||
@ -569,7 +606,8 @@ class CairoPrintSave:
|
||||
# run print dialog
|
||||
while True:
|
||||
self.preview = None
|
||||
res = operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, self.parent)
|
||||
res = operation.run(Gtk.PrintOperationAction.PRINT_DIALOG,
|
||||
self.parent)
|
||||
if self.preview is None: # cancel or print
|
||||
break
|
||||
# set up printing again; can't reuse PrintOperation?
|
||||
@ -589,20 +627,24 @@ class CairoPrintSave:
|
||||
def on_draw_page(self, operation, context, page_nr):
|
||||
"""Draw a page on a Cairo context.
|
||||
"""
|
||||
cr = context.get_cairo_context()
|
||||
dummy_operation = operation
|
||||
dummy_page_nr = page_nr
|
||||
ctx = context.get_cairo_context()
|
||||
pxwidth = round(context.get_width())
|
||||
pxheight = round(context.get_height())
|
||||
scale = min(pxwidth/self.widthpx, pxheight/self.heightpx)
|
||||
self.drawfunc(None, cr, scale=scale)
|
||||
self.drawfunc(None, ctx, scale=scale)
|
||||
|
||||
def on_paginate(self, operation, context):
|
||||
"""Paginate the whole document in chunks.
|
||||
We don't need this as there is only one page, however,
|
||||
we provide a dummy holder here, because on_preview crashes if no
|
||||
default application is set with gir 3.3.2 (typically evince not installed)!
|
||||
default application is set with gir 3.3.2
|
||||
(typically evince not installed)!
|
||||
It will provide the start of the preview dialog, which cannot be
|
||||
started in on_preview
|
||||
"""
|
||||
dummy_context = context
|
||||
finished = True
|
||||
# update page number
|
||||
operation.set_n_pages(1)
|
||||
@ -616,13 +658,15 @@ class CairoPrintSave:
|
||||
def on_preview(self, operation, preview, context, parent):
|
||||
"""Implement custom print preview functionality.
|
||||
We provide a dummy holder here, because on_preview crashes if no
|
||||
default application is set with gir 3.3.2 (typically evince not installed)!
|
||||
default application is set with gir 3.3.2
|
||||
(typically evince not installed)!
|
||||
"""
|
||||
dummy_preview = preview
|
||||
dlg = Gtk.MessageDialog(parent,
|
||||
flags=Gtk.DialogFlags.MODAL,
|
||||
type=Gtk.MessageType.WARNING,
|
||||
buttons=Gtk.ButtonsType.CLOSE,
|
||||
message_format=_('No preview available'))
|
||||
flags=Gtk.DialogFlags.MODAL,
|
||||
type=Gtk.MessageType.WARNING,
|
||||
buttons=Gtk.ButtonsType.CLOSE,
|
||||
message_format=_('No preview available'))
|
||||
self.preview = dlg
|
||||
self.previewopr = operation
|
||||
#dlg.format_secondary_markup(msg2)
|
||||
@ -639,11 +683,16 @@ class CairoPrintSave:
|
||||
except ValueError:
|
||||
height = 0
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
||||
cr = cairo.Context(surface)
|
||||
context.set_cairo_context(cr, 72.0, 72.0)
|
||||
ctx = cairo.Context(surface)
|
||||
context.set_cairo_context(ctx, 72.0, 72.0)
|
||||
|
||||
return True
|
||||
|
||||
def previewdestroy(self, dlg, res):
|
||||
"""
|
||||
Destroy the preview page
|
||||
"""
|
||||
dummy_dlg = dlg
|
||||
dummy_res = res
|
||||
self.preview.destroy()
|
||||
self.previewopr.end_preview()
|
||||
|
Loading…
Reference in New Issue
Block a user