Fanchart, last feature I wanted: time period coloring of the boxes

svn: r20353
This commit is contained in:
Benny Malengier 2012-09-08 12:17:20 +00:00
parent e56fc52e95
commit 1ce04c4874
3 changed files with 154 additions and 53 deletions

View File

@ -128,6 +128,37 @@ def get_age(db, person, fallback=True, calendar="gregorian"):
age = age.tuple()
return age
def get_timeperiod(db, person):
"""
Compute the timeperiod a person lived in
person : person handle or person object
Return: the year, None otherwise
"""
if isinstance(person, str):
# a handle is passed
person = db.get_person_from_handle(person)
# the period is the year of birth
birth = get_birth_or_fallback(db, person)
if birth is not None:
birth_date = birth.get_date_object().to_calendar("gregorian")
if (birth_date and birth_date.get_valid()):
return birth_date.get_year()
death = get_death_or_fallback(db, person)
# no birth, period is death - 20
if death is not None:
death_date = death.get_date_object().to_calendar("gregorian")
if (death_date and death_date.get_valid()):
return death_date.get_year() - 20
# no birth and death, look for another event date we can use
for event_ref in person.get_primary_event_ref_list():
if event_ref:
event = db.get_event_from_handle(event_ref.ref)
if event:
event_date = event.get_date_object().to_calendar("gregorian")
if (event_date and event_date.get_valid()):
return event_date.get_year()
return None
def get_event_ref(db, family, event_type):
"""
Return a reference to a primary family event of the given event type.

View File

@ -62,7 +62,7 @@ from gui.ddtargets import DdTargets
from gen.utils.alive import probably_alive
from gen.utils.libformatting import FormattingHelper
from gen.utils.db import (find_children, find_parents, find_witnessed_people,
get_age)
get_age, get_timeperiod)
#-------------------------------------------------------------------------
#
@ -98,6 +98,7 @@ BACKGROUND_WHITE = 3
BACKGROUND_GRAD_GEN = 4
BACKGROUND_GRAD_AGE = 5
BACKGROUND_SINGLE_COLOR = 6
BACKGROUND_GRAD_PERIOD = 7
GENCOLOR = {
BACKGROUND_SCHEME1: ((255, 63, 0),
(255,175, 15),
@ -418,31 +419,30 @@ class FanChartWidget(Gtk.DrawingArea):
# first do size request of what we will need
nrgen = self.nrgen()
halfdist = PIXELS_PER_GENERATION * nrgen + CENTER
if self.form == FORM_CIRCLE:
self.set_size_request(2 * halfdist, 2 * halfdist)
elif self.form == FORM_HALFCIRCLE:
self.set_size_request(2 * halfdist, halfdist + CENTER + PAD_PX)
elif self.form == FORM_QUADRANT:
self.set_size_request(halfdist + CENTER + PAD_PX, halfdist + CENTER + PAD_PX)
#obtain the allocation
alloc = self.get_allocation()
x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height
if widget is None: # printing, use the size we need
w = 2 * halfdist
h = 2 * halfdist
if widget:
if self.form == FORM_CIRCLE:
self.set_size_request(2 * halfdist, 2 * halfdist)
elif self.form == FORM_HALFCIRCLE:
self.set_size_request(2 * halfdist, halfdist + CENTER + PAD_PX)
elif self.form == FORM_QUADRANT:
self.set_size_request(halfdist + CENTER + PAD_PX, halfdist + CENTER + PAD_PX)
#obtain the allocation
alloc = self.get_allocation()
x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height
cr.scale(scale, scale)
if self.form == FORM_CIRCLE:
self.center_x = w/2 - self.center_xy[0]
self.center_y = h/2 - self.center_xy[1]
elif self.form == FORM_HALFCIRCLE:
self.center_x = w/2. - self.center_xy[0]
self.center_y = h - CENTER - PAD_PX- self.center_xy[1]
elif self.form == FORM_QUADRANT:
self.center_x = CENTER + PAD_PX - self.center_xy[0]
self.center_y = h - CENTER - PAD_PX - self.center_xy[1]
# when printing, we need not recalculate
if widget:
if self.form == FORM_CIRCLE:
self.center_x = w/2 - self.center_xy[0]
self.center_y = h/2 - self.center_xy[1]
elif self.form == FORM_HALFCIRCLE:
self.center_x = w/2. - self.center_xy[0]
self.center_y = h - CENTER - PAD_PX- self.center_xy[1]
elif self.form == FORM_QUADRANT:
self.center_x = CENTER + PAD_PX - self.center_xy[0]
self.center_y = h - CENTER - PAD_PX - self.center_xy[1]
cr.translate(self.center_x, self.center_y)
cr.save()
@ -488,7 +488,7 @@ class FanChartWidget(Gtk.DrawingArea):
cr.stroke()
if child and self.childring:
self.drawchildring(cr)
if self.background in [BACKGROUND_GRAD_AGE]:
if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]:
self.draw_gradient(cr, widget, halfdist)
def draw_person(self, cr, gender, name, start, stop, generation,
@ -746,10 +746,9 @@ class FanChartWidget(Gtk.DrawingArea):
alloc = self.get_allocation()
x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height
cr.save()
if widget:
cr.translate(-w/2. + self.center_xy[0], -h/2. + self.center_xy[1])
else:
cr.translate(-halfdist + self.center_xy[0], -halfdist + self.center_xy[1])
cr.translate(-self.center_x, -self.center_y)
font = Pango.FontDescription(self.fontdescr)
fontsize = self.fontsize
font.set_size(fontsize * Pango.SCALE)
@ -774,9 +773,9 @@ class FanChartWidget(Gtk.DrawingArea):
maxgen = self.generations
cstart = gui.utils.hex_to_rgb(self.grad_start)
cend = gui.utils.hex_to_rgb(self.grad_end)
cstart_hsv = colorsys.rgb_to_hsv(cstart[0]/255, cstart[1]/255,
self.cstart_hsv = colorsys.rgb_to_hsv(cstart[0]/255, cstart[1]/255,
cstart[2]/255)
cend_hsv = colorsys.rgb_to_hsv(cend[0]/255, cend[1]/255,
self.cend_hsv = colorsys.rgb_to_hsv(cend[0]/255, cend[1]/255,
cend[2]/255)
if self.background in [BACKGROUND_GENDER, BACKGROUND_SINGLE_COLOR]:
# nothing to precompute
@ -786,13 +785,63 @@ class FanChartWidget(Gtk.DrawingArea):
#compute the colors, -1, 0, ..., maxgen
divs = [x/(maxgen-1) for x in range(maxgen)]
rgb_colors = [colorsys.hsv_to_rgb(
(1-x) * cstart_hsv[0] + x * cend_hsv[0],
(1-x) * cstart_hsv[1] + x * cend_hsv[1],
(1-x) * cstart_hsv[2] + x * cend_hsv[2],
(1-x) * self.cstart_hsv[0] + x * self.cend_hsv[0],
(1-x) * self.cstart_hsv[1] + x * self.cend_hsv[1],
(1-x) * self.cstart_hsv[2] + x * self.cend_hsv[2],
) for x in divs]
self.colors = [(255*r, 255*g, 255*b) for r, g, b in rgb_colors]
elif self.background == BACKGROUND_GRAD_PERIOD:
# we fill in in the data structure what the period is, None if not found
self.colors = None
self.minperiod = 1e10
self.maxperiod = -1e10
for generation in range(self.generations):
for p in range(len(self.data[generation])):
period = None
(text, person, parents, child, userdata) = self.data[generation][p]
if person:
period = get_timeperiod(self.dbstate.db, person)
if period is not None:
if period > self.maxperiod:
self.maxperiod = period
if period < self.minperiod:
self.minperiod = period
userdata.append(period)
# same for child
for childdata in self.childrenroot:
period = None
child_handle, child_gender, has_child, userdata = childdata
child = self.dbstate.db.get_person_from_handle(child_handle)
period = get_timeperiod(self.dbstate.db, child)
if period is not None:
if period > self.maxperiod:
self.maxperiod = period
if period < self.minperiod:
self.minperiod = period
userdata.append(period)
#now create gradient data, 5 values from min to max rounded to nearest 50
if self.maxperiod < self.minperiod:
self.maxperiod = self.minperiod = gen.lib.date.Today().get_year()
rper = self.maxperiod // 50
if rper * 50 != self.maxperiod:
self.maxperiod = rper * 50 + 50
self.minperiod = 50 * (self.minperiod // 50)
periodrange = self.maxperiod - self.minperiod
steps = 2 * GRADIENTSCALE - 1
divs = [x/(steps-1) for x in range(steps)]
self.gradval = ['%d' % int(self.minperiod + x * periodrange) for x in divs]
for i in range(len(self.gradval)):
if i % 2 == 1:
self.gradval[i] = ''
self.gradcol = [colorsys.hsv_to_rgb(
(1-div) * self.cstart_hsv[0] + div * self.cend_hsv[0],
(1-div) * self.cstart_hsv[1] + div * self.cend_hsv[1],
(1-div) * self.cstart_hsv[2] + div * self.cend_hsv[2],
) for div in divs]
elif self.background == BACKGROUND_GRAD_AGE:
# we fill in in the data structure what the age is, None if no age
# we fill in in the data structure what the color age is, white if no age
self.colors = None
for generation in range(self.generations):
for p in range(len(self.data[generation])):
agecol = (255, 255, 255) # white
@ -808,9 +857,9 @@ class FanChartWidget(Gtk.DrawingArea):
#now determine fraction for gradient
agefrac = age / MAX_AGE
agecol = colorsys.hsv_to_rgb(
(1-agefrac) * cstart_hsv[0] + agefrac * cend_hsv[0],
(1-agefrac) * cstart_hsv[1] + agefrac * cend_hsv[1],
(1-agefrac) * cstart_hsv[2] + agefrac * cend_hsv[2],
(1-agefrac) * self.cstart_hsv[0] + agefrac * self.cend_hsv[0],
(1-agefrac) * self.cstart_hsv[1] + agefrac * self.cend_hsv[1],
(1-agefrac) * self.cstart_hsv[2] + agefrac * self.cend_hsv[2],
)
userdata.append((agecol[0]*255, agecol[1]*255, agecol[2]*255))
# same for child
@ -828,9 +877,9 @@ class FanChartWidget(Gtk.DrawingArea):
#now determine fraction for gradient
agefrac = age / MAX_AGE
agecol = colorsys.hsv_to_rgb(
(1-agefrac) * cstart_hsv[0] + agefrac * cend_hsv[0],
(1-agefrac) * cstart_hsv[1] + agefrac * cend_hsv[1],
(1-agefrac) * cstart_hsv[2] + agefrac * cend_hsv[2],
(1-agefrac) * self.cstart_hsv[0] + agefrac * self.cend_hsv[0],
(1-agefrac) * self.cstart_hsv[1] + agefrac * self.cend_hsv[1],
(1-agefrac) * self.cstart_hsv[2] + agefrac * self.cend_hsv[2],
)
userdata.append((agecol[0]*255, agecol[1]*255, agecol[2]*255))
#now create gradient data, 5 values from 0 to max
@ -842,9 +891,9 @@ class FanChartWidget(Gtk.DrawingArea):
if i % 2 == 1:
self.gradval[i] = ''
self.gradcol = [colorsys.hsv_to_rgb(
(1-div) * cstart_hsv[0] + div * cend_hsv[0],
(1-div) * cstart_hsv[1] + div * cend_hsv[1],
(1-div) * cstart_hsv[2] + div * cend_hsv[2],
(1-div) * self.cstart_hsv[0] + div * self.cend_hsv[0],
(1-div) * self.cstart_hsv[1] + div * self.cend_hsv[1],
(1-div) * self.cstart_hsv[2] + div * self.cend_hsv[2],
) for div in divs]
else:
# known colors per generation, set or compute them
@ -871,6 +920,19 @@ class FanChartWidget(Gtk.DrawingArea):
color = self.maincolor
elif self.background == BACKGROUND_GRAD_AGE:
color = userdata[0]
elif self.background == BACKGROUND_GRAD_PERIOD:
period = userdata[0]
if period is None:
color = (255, 255, 255) # white
else:
periodfrac = ((period - self.minperiod)
/ (self.maxperiod - self.minperiod))
periodcol = colorsys.hsv_to_rgb(
(1-periodfrac) * self.cstart_hsv[0] + periodfrac * self.cend_hsv[0],
(1-periodfrac) * self.cstart_hsv[1] + periodfrac * self.cend_hsv[1],
(1-periodfrac) * self.cstart_hsv[2] + periodfrac * self.cend_hsv[2],
)
color = (periodcol[0]*255, periodcol[1]*255, periodcol[2]*255)
else:
if self.background == BACKGROUND_GRAD_GEN and generation < 0:
generation = 0

View File

@ -180,7 +180,6 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
Method called when active person changes.
"""
# Reset everything but rotation angle (leave it as is)
print 'active changed'
self.update()
def _connect_db_signals(self):
@ -229,8 +228,15 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
Print or save the view that is currently shown
"""
widthpx = 2*(fanchart.PIXELS_PER_GENERATION * self.fan.nrgen()
+ self.fan.center)
prt = CairoPrintSave(widthpx, self.fan.on_draw, self.uistate.window)
+ fanchart.CENTER)
heightpx = widthpx
if self.form == fanchart.FORM_HALFCIRCLE:
heightpx = heightpx / 2 + fanchart.CENTER + fanchart.PAD_PX
elif self.form == fanchart.FORM_QUADRANT:
heightpx = heightpx / 2 + fanchart.CENTER + fanchart.PAD_PX
widthpx = heightpx
prt = CairoPrintSave(widthpx, heightpx, self.fan.on_draw, self.uistate.window)
prt.run()
def on_childmenu_changed(self, obj, person_handle):
@ -278,6 +284,7 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
(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')),
@ -288,7 +295,6 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
if curval == nr:
break
nrval += 1
print nrval
configdialog.add_combo(table,
_('Background'),
2, 'interface.fanview-background',
@ -407,12 +413,13 @@ class CairoPrintSave():
"""
def __init__(self, widthpx, drawfunc, parent):
def __init__(self, widthpx, heightpx, drawfunc, parent):
"""
This class provides the things needed so as to dump a cairo drawing on
a context to output
"""
self.widthpx = widthpx
self.heightpx = heightpx
self.drawfunc = drawfunc
self.parent = parent
@ -434,7 +441,7 @@ class CairoPrintSave():
paper_size = Gtk.PaperSize.new_custom("custom",
"Custom Size",
round(self.widthpx * 0.2646),
round(self.widthpx * 0.2646),
round(self.heightpx * 0.2646),
Gtk.Unit.MM)
page_setup = Gtk.PageSetup()
page_setup.set_paper_size(paper_size)
@ -471,9 +478,10 @@ class CairoPrintSave():
cr = context.get_cairo_context()
pxwidth = round(context.get_width())
pxheight = round(context.get_height())
dpi_x = context.get_dpi_x()
dpi_y = context.get_dpi_y()
self.drawfunc(None, cr, scale=pxwidth/self.widthpx)
scale = min(pxwidth/self.widthpx, pxheight/self.heightpx)
if scale > 1:
scale = 1
self.drawfunc(None, cr, scale=scale)
def on_paginate(self, operation, context):
"""Paginate the whole document in chunks.