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:
Serge Noiraud 2019-05-20 11:16:10 +02:00 committed by Nick Hall
parent aefa262cd1
commit 75d2f3dd00
9 changed files with 1510 additions and 955 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()