Fanchart, last feature I wanted: time period coloring of the boxes
svn: r20353
This commit is contained in:
parent
e56fc52e95
commit
1ce04c4874
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user