From 75d2f3dd005376211e776e11949358db974c77d0 Mon Sep 17 00:00:00 2001 From: Serge Noiraud Date: Mon, 20 May 2019 11:16:10 +0200 Subject: [PATCH] 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 --- gramps/gui/widgets/fanchart.py | 1033 +++++++++++------ gramps/gui/widgets/fanchart2way.py | 407 ++++--- gramps/gui/widgets/fanchartdesc.py | 315 ++--- .../plugins/gramplet/fanchart2waygramplet.py | 29 +- .../plugins/gramplet/fanchartdescgramplet.py | 2 + gramps/plugins/gramplet/fanchartgramplet.py | 2 + gramps/plugins/view/fanchart2wayview.py | 233 ++-- gramps/plugins/view/fanchartdescview.py | 227 ++-- gramps/plugins/view/fanchartview.py | 217 ++-- 9 files changed, 1510 insertions(+), 955 deletions(-) diff --git a/gramps/gui/widgets/fanchart.py b/gramps/gui/widgets/fanchart.py index 58db5ed24..a0c5545f8 100644 --- a/gramps/gui/widgets/fanchart.py +++ b/gramps/gui/widgets/fanchart.py @@ -34,16 +34,15 @@ # Python modules # #------------------------------------------------------------------------- +import math +import colorsys +import pickle +from html import escape +import cairo from gi.repository import Pango 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 #------------------------------------------------------------------------- # @@ -55,14 +54,10 @@ from gramps.gen.display.name import displayer as name_displayer from gramps.gen.errors import WindowActiveError from gramps.gen.lib import ChildRef, Family, Name, Person, Surname from gramps.gen.lib.date import Today -from ..editors import EditPerson, EditFamily -from .reorderfam import Reorder -from ..utils import color_graph_box, hex_to_rgb, is_right_click -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, preset_name) +from gramps.gen.utils.db import (find_children, find_parents, get_timeperiod, + find_witnessed_people, get_age, preset_name) from gramps.gen.constfunc import is_quartz from gramps.gen.const import GRAMPS_LOCALE as glocale from gramps.gen.const import ( @@ -88,13 +83,16 @@ from gramps.gen.const import ( FORM_QUADRANT, COLLAPSED, NORMAL, - EXPANDED, - TYPE_BOX_NORMAL, - TYPE_BOX_FAMILY) -_ = glocale.translation.gettext + EXPANDED) +from .reorderfam import Reorder +from ..utils import color_graph_box, hex_to_rgb, is_right_click +from ..ddtargets import DdTargets +from ..editors import EditPerson, EditFamily from ..utilscairo import warpPath from gramps.gen.utils.symbols import Symbols +_ = glocale.translation.gettext + # following are used in name_displayer format def # (must not conflict with standard defs) TWO_LINE_FORMAT_1 = 100 @@ -121,7 +119,21 @@ class FanChartBaseWidget(Gtk.DrawingArea): self.textcolor = (0, 0, 0) self.dbstate = dbstate self.uistate = uistate + self.form = FORM_CIRCLE + self.generations = 8 + self.childring = 0 + self.childrenroot = [] + self.angle = {} + self.grad_start = '#0000FF' + self.grad_end = '#FF0000' + self.background = BACKGROUND_GRAD_GEN + self.filter = None + self.alpha_filter = 0.5 self.translating = False + self.showid = False + self.flipupsidedownname = True + self.dupcolor = None + self.twolinename = False self.surface = None self.goto = None self.on_popup = callback_popup @@ -148,9 +160,8 @@ class FanChartBaseWidget(Gtk.DrawingArea): Gdk.EventMask.KEY_PRESS_MASK) # Enable drag - self.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, - [], - Gdk.DragAction.COPY) + self.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [], + Gdk.DragAction.COPY) tglist = Gtk.TargetList.new([]) tglist.add(DdTargets.PERSON_LINK.atom_drag_type, DdTargets.PERSON_LINK.target_flags, @@ -162,22 +173,32 @@ class FanChartBaseWidget(Gtk.DrawingArea): self.connect("drag_begin", self.on_drag_begin) self.connect("drag_end", self.on_drag_end) # Enable drop - self.drag_dest_set(Gtk.DestDefaults.MOTION | - Gtk.DestDefaults.DROP, - [DdTargets.PERSON_LINK.target()], - Gdk.DragAction.COPY) + self.drag_dest_set(Gtk.DestDefaults.MOTION | Gtk.DestDefaults.DROP, + [DdTargets.PERSON_LINK.target()], + Gdk.DragAction.COPY) self.connect('drag_data_received', self.on_drag_data_received) self.uistate.connect('font-changed', self.reload_symbols) self._mouse_click = False self.rotate_value = 90 # degrees, initially, 1st gen male on right half - self.center_delta_xy = [0, 0] # translation of the center of the fan wrt canonical center + self.center_delta_xy = [0, 0] # translation of the center of the + # fan wrt canonical center self.center_xy = [0, 0] # coord of the center of the fan self.mouse_x = 0 self.mouse_y = 0 #(re)compute everything self.reset() self.set_size_request(120, 120) + self.maxperiod = 0 + self.minperiod = 0 + self.cstart_hsv = None + self.cend_hsv = None + self.colors = None + self.maincolor = None + self.gradval = None + self.gradcol = None + self.in_drag = False + self._mouse_click_cell_address = None self.symbols = Symbols() self.reload_symbols() @@ -251,22 +272,28 @@ class FanChartBaseWidget(Gtk.DrawingArea): """ raise NotImplementedError - def get_radiusinout_for_generation(self,generation): + def get_radiusinout_for_gen(self, generation): + """ + Get the in and out radius for descendant generation + """ raise NotImplementedError - def on_draw(self, widget, cr, scale=1.): + def on_draw(self, widget, ctx, scale=1.): """ callback to draw the fanchart """ + dummy_scale = scale + dummy_widget = widget if self.surface: - cr.set_source_surface(self.surface, 0, 0) - cr.paint() + ctx.set_source_surface(self.surface, 0, 0) + ctx.paint() - def prt_draw(self, widget, cr, scale=1.0): + def prt_draw(self, widget, ctx, scale=1.0): """ method to allow direct drawing to cairo context for printing """ - self.draw(cr=cr, scale=scale) + dummy_widget = widget + self.draw(ctx=ctx, scale=scale) def people_generator(self): """ @@ -295,6 +322,9 @@ class FanChartBaseWidget(Gtk.DrawingArea): userdata.append(period) def set_userdata_age(self, person, userdata): + """ + set the userdata as used by age + """ agecol = (1, 1, 1) # white if person: age = get_age(self.dbstate.db, person) @@ -307,9 +337,12 @@ class FanChartBaseWidget(Gtk.DrawingArea): #now determine fraction for gradient agefrac = age / MAX_AGE agecol = colorsys.hsv_to_rgb( - (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], + ((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)) @@ -321,24 +354,26 @@ class FanChartBaseWidget(Gtk.DrawingArea): cstart = hex_to_rgb(self.grad_start) cend = hex_to_rgb(self.grad_end) self.cstart_hsv = colorsys.rgb_to_hsv(cstart[0]/255, cstart[1]/255, - cstart[2]/255) + cstart[2]/255) self.cend_hsv = colorsys.rgb_to_hsv(cend[0]/255, cend[1]/255, - cend[2]/255) + cend[2]/255) if self.background in [BACKGROUND_GENDER, BACKGROUND_SINGLE_COLOR]: # nothing to precompute self.colors = None self.maincolor = cstart elif self.background == BACKGROUND_GRAD_GEN: #compute the colors, -1, 0, ..., maxgen - divs = [x/(maxgen-1) for x in range(maxgen)] if maxgen>1 else [0] + divs = [x/(maxgen-1) for x in range(maxgen)] if maxgen > 1 else [0] rgb_colors = [colorsys.hsv_to_rgb( - (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] + (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 * red, 255 * green, 255 * blue) + for red, green, blue in rgb_colors] elif self.background == BACKGROUND_GRAD_PERIOD: - # we fill in in the data structure what the period is, None if not found + # we fill in in the data structure what the period is, + # None if not found self.colors = None self.minperiod = 1e10 self.maxperiod = -1e10 @@ -349,7 +384,8 @@ class FanChartBaseWidget(Gtk.DrawingArea): gen_inner = self.innerpeople_generator() for child, userdata in gen_inner: self.set_userdata_timeperiod(child, userdata) - #now create gradient data, 5 values from min to max rounded to nearest 50 + # now create gradient data, 5 values from min to max rounded + # to nearest 50 if self.maxperiod < self.minperiod: self.maxperiod = self.minperiod = Today().get_year() rper = self.maxperiod // 50 @@ -359,18 +395,20 @@ class FanChartBaseWidget(Gtk.DrawingArea): 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] + 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] + (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 color age is, white if no age + # we fill in in the data structure what the color age is, + # white if no age self.colors = None gen_people = self.people_generator() for person, userdata in gen_people: @@ -388,10 +426,10 @@ class FanChartBaseWidget(Gtk.DrawingArea): 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] + (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 self.colors = GENCOLOR[self.background] @@ -402,8 +440,9 @@ class FanChartBaseWidget(Gtk.DrawingArea): which has gender gender, and is in ring generation """ if generation == 0 and self.background in [BACKGROUND_GENDER, - BACKGROUND_GRAD_GEN, BACKGROUND_SCHEME1, - BACKGROUND_SCHEME2]: + BACKGROUND_GRAD_GEN, + BACKGROUND_SCHEME1, + BACKGROUND_SCHEME2]: # white for center person: color = (255, 255, 255) elif self.background == BACKGROUND_GENDER: @@ -411,7 +450,7 @@ class FanChartBaseWidget(Gtk.DrawingArea): alive = probably_alive(person, self.dbstate.db) except RuntimeError: alive = False - backgr, border = color_graph_box(alive, person.gender) + backgr, dummy_border = color_graph_box(alive, person.gender) color = hex_to_rgb(backgr) elif self.background == BACKGROUND_SINGLE_COLOR: color = self.maincolor @@ -428,10 +467,13 @@ class FanChartBaseWidget(Gtk.DrawingArea): else: periodfrac = 0.5 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], - ) + ((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: @@ -440,7 +482,8 @@ class FanChartBaseWidget(Gtk.DrawingArea): if person.gender == Person.MALE: color = [x*.9 for x in color] # now we set transparency data - if self.filter and not self.filter.match(person.handle, self.dbstate.db): + if self.filter and not self.filter.match(person.handle, + self.dbstate.db): if self.background == BACKGROUND_SINGLE_COLOR: alpha = 0. # no color shown else: @@ -450,65 +493,70 @@ class FanChartBaseWidget(Gtk.DrawingArea): return color[0], color[1], color[2], alpha - def fontcolor(self, r, g, b, a): + def fontcolor(self, red, green, blue, alpha): """ return the font color based on the r, g, b of the background """ - if a == 0: + if alpha == 0: return self.textcolor try: - return self.cache_fontcolor[(r, g, b)] + return self.cache_fontcolor[(red, green, blue)] except KeyError: - hls = colorsys.rgb_to_hls(r/255, g/255, b/255) + hls = colorsys.rgb_to_hls(red / 255, green / 255, blue / 255) # we use the lightness value to determine white or black font if hls[1] > 0.4: - self.cache_fontcolor[(r, g, b)] = (0, 0, 0) + self.cache_fontcolor[(red, green, blue)] = (0, 0, 0) else: - self.cache_fontcolor[(r, g, b)] = (1, 1, 1) - return self.cache_fontcolor[(r, g, b)] + self.cache_fontcolor[(red, green, blue)] = (1, 1, 1) + return self.cache_fontcolor[(red, green, blue)] - def fontbold(self, a): + def fontbold(self, alpha): """ The font should be bold if no transparency and font is set. In that case, True is returned """ - if a >= 1. and self.filter: + if alpha >= 1. and self.filter: return True return False - def draw_radbox(self, cr, radiusin, radiusout, start_rad, stop_rad, color, + def draw_radbox(self, ctx, radiusin, radiusout, start_rad, stop_rad, color, thick=False): """ Procedure to draw a person box in the outter ring position """ - cr.move_to(radiusout * math.cos(start_rad), radiusout * math.sin(start_rad)) - cr.arc(0, 0, radiusout, start_rad, stop_rad) - cr.line_to(radiusin * math.cos(stop_rad), radiusin * math.sin(stop_rad)) - cr.arc_negative(0, 0, radiusin, stop_rad, start_rad) - cr.close_path() - ##path = cr.copy_path() # not working correct - cr.set_source_rgba(color[0], color[1], color[2], color[3]) - cr.fill() + ctx.move_to(radiusout * math.cos(start_rad), + radiusout * math.sin(start_rad)) + ctx.arc(0, 0, radiusout, start_rad, stop_rad) + ctx.line_to(radiusin * math.cos(stop_rad), + radiusin * math.sin(stop_rad)) + ctx.arc_negative(0, 0, radiusin, stop_rad, start_rad) + ctx.close_path() + ##path = ctx.copy_path() # not working correct + ctx.set_source_rgba(color[0], color[1], color[2], color[3]) + ctx.fill() #and again for the border - cr.move_to(radiusout * math.cos(start_rad), radiusout * math.sin(start_rad)) - cr.arc(0, 0, radiusout, start_rad, stop_rad) + ctx.move_to(radiusout * math.cos(start_rad), + radiusout * math.sin(start_rad)) + ctx.arc(0, 0, radiusout, start_rad, stop_rad) if (start_rad - stop_rad) % (2 * math.pi) > 1e-5: - radial_motion_type = cr.line_to + radial_motion_type = ctx.line_to else: - radial_motion_type = cr.move_to - radial_motion_type(radiusin * math.cos(stop_rad), radiusin * math.sin(stop_rad)) - cr.arc_negative(0, 0, radiusin, stop_rad, start_rad) - radial_motion_type(radiusout * math.cos(start_rad), radiusout * math.sin(start_rad)) - ##cr.append_path(path) # not working correct - cr.set_source_rgb(0, 0, 0) # black + radial_motion_type = ctx.move_to + radial_motion_type(radiusin * math.cos(stop_rad), + radiusin * math.sin(stop_rad)) + ctx.arc_negative(0, 0, radiusin, stop_rad, start_rad) + radial_motion_type(radiusout * math.cos(start_rad), + radiusout * math.sin(start_rad)) + ##ctx.append_path(path) # not working correct + ctx.set_source_rgb(0, 0, 0) # black if thick: - cr.set_line_width(3) + ctx.set_line_width(3) else: - cr.set_line_width(1) - cr.stroke() - cr.set_line_width(1) + ctx.set_line_width(1) + ctx.stroke() + ctx.set_line_width(1) - def draw_innerring(self, cr, person, userdata, start, inc): + def draw_innerring(self, ctx, person, userdata, start, inc): """ Procedure to draw a person in the inner ring position """ @@ -520,65 +568,69 @@ class FanChartBaseWidget(Gtk.DrawingArea): # add child to angle storage self.angle[-2].append([thetamin, thetamax, None]) #draw child now - cr.move_to(rmin*math.cos(thetamin), rmin*math.sin(thetamin)) - cr.arc(0, 0, rmin, thetamin, thetamax) - cr.line_to(rmax*math.cos(thetamax), rmax*math.sin(thetamax)) - cr.arc_negative(0, 0, rmax, thetamax, thetamin) - cr.close_path() - ##path = cr.copy_path() # not working correct - cr.set_source_rgb(0, 0, 0) # black - cr.set_line_width(1) - cr.stroke() + ctx.move_to(rmin*math.cos(thetamin), rmin*math.sin(thetamin)) + ctx.arc(0, 0, rmin, thetamin, thetamax) + ctx.line_to(rmax*math.cos(thetamax), rmax*math.sin(thetamax)) + ctx.arc_negative(0, 0, rmax, thetamax, thetamin) + ctx.close_path() + ##path = ctx.copy_path() # not working correct + ctx.set_source_rgb(0, 0, 0) # black + ctx.set_line_width(1) + ctx.stroke() #now again to fill if person: - r, g, b, a = self.background_box(person, -1, userdata) + red, green, blue, alpha = self.background_box(person, -1, userdata) else: - r=255; g=255; b=255; a=1 - cr.move_to(rmin*math.cos(thetamin), rmin*math.sin(thetamin)) - cr.arc(0, 0, rmin, thetamin, thetamax) - cr.line_to(rmax*math.cos(thetamax), rmax*math.sin(thetamax)) - cr.arc_negative(0, 0, rmax, thetamax, thetamin) - cr.close_path() - ##cr.append_path(path) # not working correct - cr.set_source_rgba(r/255., g/255., b/255., a) - cr.fill() + red = green = blue = 255 + alpha = 1 + ctx.move_to(rmin*math.cos(thetamin), rmin*math.sin(thetamin)) + ctx.arc(0, 0, rmin, thetamin, thetamax) + ctx.line_to(rmax*math.cos(thetamax), rmax*math.sin(thetamax)) + ctx.arc_negative(0, 0, rmax, thetamax, thetamin) + ctx.close_path() + ##ctx.append_path(path) # not working correct + ctx.set_source_rgba(red/255., green/255., blue/255., alpha) + ctx.fill() - def draw_person(self, cr, person, radiusin, radiusout, start_rad, stop_rad, - generation, dup, userdata, thick=False, has_moregen_indicator = False, - is_central_person=False): + def draw_person(self, ctx, person, radiusin, radiusout, start_rad, stop_rad, + generation, dup, userdata, thick=False, + has_moregen_indicator=False, is_central_person=False): """ Display the piece of pie for a given person. start_rad and stop_rad are in radians. """ - cr.save() + ctx.save() # If we need an indicator of more generations: if has_moregen_indicator: # draw an indicator - color=(1.0, 1.0, 1.0, 1.0) # white - self.draw_radbox(cr, radiusout, radiusout + BORDER_EDGE_WIDTH, start_rad, stop_rad, color, thick=False) + color = (1.0, 1.0, 1.0, 1.0) # white + self.draw_radbox(ctx, radiusout, radiusout + BORDER_EDGE_WIDTH, + start_rad, stop_rad, color, thick=False) # get the color of the background if not person: # if called on None, let's make a transparent box - r, g, b, a = (255, 255, 255, 0) + red, green, blue, alpha = (255, 255, 255, 0) elif dup: - r, g, b = self.dupcolor #duplicate color - a = 1.0 + red, green, blue = self.dupcolor #duplicate color + alpha = 1.0 else: - r, g, b, a = self.background_box(person, generation, userdata) - color=(r/255., g/255., b/255., a) + red, green, blue, alpha = self.background_box(person, generation, + userdata) + color = (red/255., green/255., blue/255., alpha) # now draw the person if not is_central_person: - self.draw_radbox(cr, radiusin, radiusout, start_rad, stop_rad, color, thick) + self.draw_radbox(ctx, radiusin, radiusout, start_rad, stop_rad, + color, thick) else: #special box for centrer pers - cr.arc(0, 0, radiusout, 0, 2 * math.pi) - if self.childring and len(self.childrenroot)>0: - cr.arc_negative(0, 0, radiusin, 2 * math.pi, 0) - cr.close_path() - cr.set_source_rgba(*color) - cr.fill() + ctx.arc(0, 0, radiusout, 0, 2 * math.pi) + if self.childring and self.childrenroot: + ctx.arc_negative(0, 0, radiusin, 2 * math.pi, 0) + ctx.close_path() + ctx.set_source_rgba(*color) + ctx.fill() if self.last_x is None or self.last_y is None: #we are not in a move, so draw text @@ -586,56 +638,98 @@ class FanChartBaseWidget(Gtk.DrawingArea): if self.radialtext: ## and generation >= 6: space_arc_text = (radiusin+radiusout)/2 * (stop_rad-start_rad) # is there more space to print it radial ? - radial= (space_arc_text < (radiusout-radiusin) * 1.1) - self.draw_person_text(cr, person, radiusin, radiusout, start_rad, stop_rad, - radial, self.fontcolor(r, g, b, a), self.fontbold(a), can_flip=not is_central_person) - cr.restore() + radial = (space_arc_text < (radiusout-radiusin) * 1.1) + self.draw_person_text(ctx, person, radiusin, radiusout, + start_rad, stop_rad, radial, + self.fontcolor(red, green, blue, alpha), + self.fontbold(alpha), + can_flip=not is_central_person) + ctx.restore() - def draw_person_text(self, cr, person, radiusin, radiusout, start, stop, - radial=False, fontcolor=(0, 0, 0), bold=False, can_flip = True): - if not person: return + def draw_person_text(self, ctx, person, radiusin, radiusout, start, stop, + radial=False, fontcolor=(0, 0, 0), bold=False, + can_flip=True): + """ + Draw the piece of pie for a given person. + """ + if not person: + return draw_radial = radial and self.radialtext try: alive = probably_alive(person, self.dbstate.db) except RuntimeError: alive = False if not self.twolinename: - name=name_displayer.display(person) + name = name_displayer.display(person) + if self.showid: + name += " (" + person.gramps_id + ")" if self.uistate.symbols and not alive: name = self.dth + ' ' + name - self.draw_text(cr, name, radiusin, radiusout, start, stop, draw_radial, - fontcolor, bold) + self.draw_text(ctx, name, radiusin, radiusout, start, stop, + draw_radial, fontcolor, bold) else: - text=name_displayer.display(person) - text_line1=name_displayer.display_format(person, TWO_LINE_FORMAT_1) - text_line2=name_displayer.display_format(person, TWO_LINE_FORMAT_2) + #text=name_displayer.display(person) + if self.showid: + text_line1 = "(" + person.gramps_id + ") " + else: + text_line1 = "" + text_line1 += name_displayer.display_format(person, + TWO_LINE_FORMAT_1) + text_line2 = name_displayer.display_format(person, + TWO_LINE_FORMAT_2) if draw_radial: - split_frac_line1=0.5 - flipped = can_flip and ((math.degrees((start+stop)/2.0) + self.rotate_value - 90) % 360 < 179 and self.flipupsidedownname) + split_frac_line1 = 0.5 + flipped = can_flip and ((math.degrees((start+stop)/2.0) + + self.rotate_value - 90) % 360 < 179 + and self.flipupsidedownname) if flipped: - middle=(start*split_frac_line1+stop*(1.0-split_frac_line1)) - (a11,a12,a21,a22)=(middle,stop,start,middle) + middle = (start * split_frac_line1 + + stop * (1.0 - split_frac_line1)) + (a11, a12, a21, a22) = (middle, stop, start, middle) else: - middle=(start*(1.0-split_frac_line1)+stop*split_frac_line1) - (a11,a12,a21,a22)=(start,middle,middle,stop) - written_textwidth=self.draw_text(cr, text_line1, radiusin, radiusout, a11, a12, draw_radial, fontcolor, bold=1, flipped=flipped) + middle = (start * (1.0 - split_frac_line1) + + stop * split_frac_line1) + (a11, a12, a21, a22) = (start, middle, middle, stop) + written_textwidth = self.draw_text(ctx, text_line1, radiusin, + radiusout, a11, a12, + draw_radial, fontcolor, + bold=1, flipped=flipped) if written_textwidth == 0 and text_line1 != "": #Not enought space for 2 line, fallback to 1 line - written_textwidth=self.draw_text(cr, text_line1, radiusin, radiusout, start, stop, draw_radial, fontcolor, bold=1, flipped=flipped) - self.draw_text(cr, text_line2, radiusin+written_textwidth+PAD_TEXT, radiusout, start, stop, draw_radial, fontcolor, bold, flipped) + written_textwidth = self.draw_text(ctx, text_line1, + radiusin, radiusout, + start, stop, draw_radial, + fontcolor, bold=1, + flipped=flipped) + self.draw_text(ctx, text_line2, + radiusin+written_textwidth+PAD_TEXT, + radiusout, start, stop, draw_radial, + fontcolor, bold, flipped) else: - self.draw_text(cr, text_line2, radiusin, radiusout, a21, a22, draw_radial, fontcolor, bold, flipped) + self.draw_text(ctx, text_line2, radiusin, radiusout, a21, + a22, draw_radial, fontcolor, bold, flipped) else: - middle=(radiusin*.5+radiusout*.5) - flipped = can_flip and ((math.degrees((start+stop)/2.0) + self.rotate_value) % 360 < 179 and self.flipupsidedownname) + middle = (radiusin * .5 + radiusout * .5) + flipped = can_flip and ((math.degrees((start+stop)/2.0) + + self.rotate_value) % 360 < 179 + and self.flipupsidedownname) if flipped: - self.draw_text(cr, text_line2, middle, radiusout, start, stop, draw_radial, fontcolor, bold=0, flipped=flipped) - self.draw_text(cr, text_line1, radiusin, middle, start, stop, draw_radial, fontcolor, bold=1, flipped=flipped) + self.draw_text(ctx, text_line2, middle, radiusout, + start, stop, draw_radial, fontcolor, + bold=0, flipped=flipped) + self.draw_text(ctx, text_line1, radiusin, middle, + start, stop, draw_radial, fontcolor, + bold=1, flipped=flipped) else: - self.draw_text(cr, text_line1, middle, radiusout, start, stop, draw_radial, fontcolor, bold=1, flipped=flipped) - self.draw_text(cr, text_line2, radiusin, middle, start, stop, draw_radial, fontcolor, bold=0, flipped=flipped) + self.draw_text(ctx, text_line1, middle, radiusout, + start, stop, draw_radial, fontcolor, + bold=1, flipped=flipped) + self.draw_text(ctx, text_line2, radiusin, middle, + start, stop, draw_radial, fontcolor, + bold=0, flipped=flipped) - def wrap_truncate_layout(self, layout, font, width_pixels, height_pixels, tryrescale=True): + def wrap_truncate_layout(self, layout, font, width_pixels, height_pixels, + tryrescale=True): """ Uses the layout to wrap and truncate its text to given width Returns: (w,h) as returned by layout.get_pixel_size() @@ -650,27 +744,30 @@ class FanChartBaseWidget(Gtk.DrawingArea): layout.set_text(layout.get_text(), layout.get_line(0).length) #2. we check if height is ok - w, h = layout.get_pixel_size() - if h > height_pixels: + dummy_width, height = layout.get_pixel_size() + if height > height_pixels: if tryrescale: #try to reduce the height - fontsize = max(height_pixels / h * font.get_size() /1.1, font.get_size()/2.0) + fontsize = max(height_pixels / height * font.get_size() /1.1, + font.get_size()/2.0) font.set_size(fontsize) - layout.set_text(all_text_backup, len(all_text_backup.encode('utf-8'))) # reducing the height allows for more characters + # reducing the height allows for more characters + layout.set_text(all_text_backup, + len(all_text_backup.encode('utf-8'))) layout.set_font_description(font) if layout.get_line_count() > 1: - layout.set_text(layout.get_text(), layout.get_line(0).length) - w, h = layout.get_pixel_size() + layout.set_text(layout.get_text(), + layout.get_line(0).length) + dummy_width, height = layout.get_pixel_size() # we check again if height is ok - if h > height_pixels: + if height > height_pixels: #we could not fix it, no text - layout.set_text("",0) + layout.set_text("", 0) layout.context_changed() return layout.get_pixel_size() - def draw_text(self, cr, text, radiusin, radiusout, start_rad, stop_rad, - radial=False, - fontcolor=(0, 0, 0), bold=False, flipped = False): + def draw_text(self, ctx, text, radiusin, radiusout, start_rad, stop_rad, + radial=False, fontcolor=(0, 0, 0), bold=False, flipped=False): """ Display text at a particular radius, between start_rad and stop_rad radians. @@ -680,15 +777,21 @@ class FanChartBaseWidget(Gtk.DrawingArea): font.set_size(fontsize * Pango.SCALE) if bold: font.set_weight(Pango.Weight.BOLD) - cr.save() - cr.set_source_rgb(*fontcolor) + ctx.save() + ctx.set_source_rgb(*fontcolor) if radial and self.radialtext: - self.draw_radial_text(cr, text, radiusin, radiusout, start_rad, stop_rad, font, flipped) + self.draw_radial_text(ctx, text, radiusin, radiusout, + start_rad, stop_rad, font, flipped) else: - self.draw_arc_text(cr, text, radiusin, radiusout, start_rad, stop_rad, font, flipped) - cr.restore() + self.draw_arc_text(ctx, text, radiusin, radiusout, + start_rad, stop_rad, font, flipped) + ctx.restore() - def draw_radial_text(self, cr, text, radiusin, radiusout, start_rad, stop_rad, font, flipped): + def draw_radial_text(self, ctx, text, radiusin, radiusout, + start_rad, stop_rad, font, flipped): + """ + Draw the text in the available portion. + """ layout = self.create_pango_layout(text) if is_quartz(): PangoCairo.context_set_resolution(layout.get_context(), 72) @@ -700,22 +803,26 @@ class FanChartBaseWidget(Gtk.DrawingArea): avail_height = (stop_rad - start_rad) * radiusin - 2.0 * PAD_TEXT avail_width = radiusout - radiusin - 2.0 * PAD_TEXT - w, h = self.wrap_truncate_layout(layout, font, avail_width, avail_height, tryrescale=True) + dummy_width, height = self.wrap_truncate_layout(layout, font, + avail_width, + avail_height, + tryrescale=True) # 2. now draw this text # offset for cairo-font system is 90 if flipped: - angle = (start_rad + stop_rad)/2 + (h / radiusin / 2) + math.pi + angle = (start_rad + stop_rad)/2 + (height / radiusin / 2) + math.pi start_pos = -radiusout + PAD_TEXT else: - angle = (start_rad + stop_rad)/2 - (h / radiusin / 2) + angle = (start_rad + stop_rad)/2 - (height / radiusin / 2) start_pos = radiusin + PAD_TEXT - cr.rotate(angle) + ctx.rotate(angle) layout.context_changed() - cr.move_to(start_pos, 0) - PangoCairo.show_layout(cr, layout) + ctx.move_to(start_pos, 0) + PangoCairo.show_layout(ctx, layout) - def draw_arc_text(self, cr, text, radiusin, radiusout, start_rad, stop_rad, font, bottom_is_outside): + def draw_arc_text(self, ctx, text, radiusin, radiusout, + start_rad, stop_rad, font, bottom_is_outside): """ Display text at a particular radius, between start and stop degrees, setting it up along the arc, center-justified. @@ -729,31 +836,36 @@ class FanChartBaseWidget(Gtk.DrawingArea): layout.set_wrap(Pango.WrapMode.WORD_CHAR) # get height of text: - textheight=layout.get_size()[1]/Pango.SCALE - radius_text=(radiusin+radiusout)/2.0 + textheight = layout.get_size()[1] / Pango.SCALE + radius_text = (radiusin + radiusout) / 2.0 # 1. compute available text space avail_height = radiusout - radiusin - 2.0 * PAD_TEXT avail_width = (stop_rad - start_rad) * radius_text - 2.0 * PAD_TEXT - w, h = self.wrap_truncate_layout(layout, font, avail_width, avail_height, tryrescale=True) + width, dummy_height = self.wrap_truncate_layout(layout, font, + avail_width, + avail_height, + tryrescale=True) # 2. Compute text position start angle mid_rad = (stop_rad + start_rad)/2 - pos_rad = mid_rad - (w/2/radius_text) - end_rad = pos_rad + (w/radius_text) + pos_rad = mid_rad - (width/2/radius_text) + end_rad = pos_rad + (width/radius_text) # 3. Use the layout to provide us the metrics of the text box - cr.new_path() - PangoCairo.layout_path(cr, layout) + ctx.new_path() + PangoCairo.layout_path(ctx, layout) # 4. The moment of truth: map the text box onto the sector, and render! - warpPath(cr, \ - self.create_map_rect_to_sector(radius_text, pos_rad, end_rad, textheight, bottom_is_outside )) - cr.fill() + warpPath(ctx, \ + self.create_map_rect_to_sector(radius_text, pos_rad, end_rad, + textheight, bottom_is_outside)) + ctx.fill() @staticmethod - def create_map_rect_to_sector(radius, start_rad, stop_rad, textheight, bottom_is_outside = False): + def create_map_rect_to_sector(radius, start_rad, stop_rad, textheight, + bottom_is_outside=False): """ Create a 2D-transform, mapping a rectangle onto a circle sector. @@ -761,48 +873,57 @@ class FanChartBaseWidget(Gtk.DrawingArea): :param start_rad: start radial angle of the sector, in radians :param stop_rad: stop radial angle of the sector, in radians :param textheight height of the text - :param bottom_is_outside flag defining if we write with the bottom toward outside + :param bottom_is_outside flag defining if we write with the + bottom toward outside :returns: a lambda (x,y)|->(xNew,yNew) to feed to warpPath. """ if bottom_is_outside: - def phi(x): - return -x/radius + stop_rad - def rho(y): - return radius + (y - textheight/2.0) + def phi(posx): + """ x in outside case """ + return -posx / radius + stop_rad + def rho(posy): + """ y in outside case """ + return radius + (posy - textheight / 2.0) else: - def phi(x): - return x/radius + start_rad - def rho(y): - return radius - ( y - textheight/2.0) - return lambda x, y: \ - (rho(y) * math.cos(phi(x)), rho(y) * math.sin(phi(x))) + def phi(posx): + """ x in inside case """ + return posx / radius + start_rad + def rho(posy): + """ y in inside case """ + return radius - (posy - textheight / 2.0) + return lambda posx, posy: (rho(posy) * math.cos(phi(posx)), + rho(posy) * math.sin(phi(posx))) - def draw_gradient_legend(self, cr, halfdist): + def draw_gradient_legend(self, ctx, halfdist): + """ + Draw the gradient legend + """ + dummy_halfdist = halfdist gradwidth = 10 gradheight = 10 starth = 15 startw = 5 - cr.save() + ctx.save() - cr.translate(-self.center_xy[0], -self.center_xy[1]) + ctx.translate(-self.center_xy[0], -self.center_xy[1]) font = Pango.FontDescription("") fontsize = self.fontsize font.set_size(fontsize * Pango.SCALE) for color, text in zip(self.gradcol, self.gradval): - cr.move_to(startw, starth) - cr.rectangle(startw, starth, gradwidth, gradheight) - cr.set_source_rgb(color[0], color[1], color[2]) - cr.fill() + ctx.move_to(startw, starth) + ctx.rectangle(startw, starth, gradwidth, gradheight) + ctx.set_source_rgb(color[0], color[1], color[2]) + ctx.fill() layout = self.create_pango_layout(text) if is_quartz(): PangoCairo.context_set_resolution(layout.get_context(), 72) layout.set_font_description(font) - cr.move_to(startw+gradwidth+4, starth) - cr.set_source_rgb(0, 0, 0) #black - PangoCairo.show_layout(cr, layout) + ctx.move_to(startw+gradwidth+4, starth) + ctx.set_source_rgb(0, 0, 0) #black + PangoCairo.show_layout(ctx, layout) starth = starth+gradheight - cr.restore() + ctx.restore() def cursor_on_tranlation_dot(self, curx, cury): """ @@ -813,27 +934,33 @@ class FanChartBaseWidget(Gtk.DrawingArea): radius = math.sqrt((fanxy[0]) ** 2 + (fanxy[1]) ** 2) return radius < TRANSLATE_PX - def cursor_to_polar(self, curx, cury, get_raw_rads = False): - # compute angle, radius in unrotated fan + def cursor_to_polar(self, curx, cury, get_raw_rads=False): + """ + Compute angle, radius in unrotated fan + """ fanxy = curx - self.center_xy[0], cury - self.center_xy[1] radius = math.sqrt((fanxy[0]) ** 2 + (fanxy[1]) ** 2) #angle before rotation: #children are in cairo angle (clockwise) from pi to 3 pi #rads however is clock 0 to 2 pi - raw_rads = math.atan2( fanxy[1], fanxy[0]) % (2 * math.pi) - rads = (raw_rads - math.radians(self.rotate_value) ) % (2 * math.pi) + raw_rads = math.atan2(fanxy[1], fanxy[0]) % (2 * math.pi) + rads = (raw_rads - math.radians(self.rotate_value)) % (2 * math.pi) if get_raw_rads: return radius, rads, raw_rads else: return radius, rads def radian_in_bounds(self, start_rad, rads, stop_rad): - assert(start_rad <= stop_rad) - # we compare (rads - start_rad) % (2.0 * math.pi) and (stop_rad - start_rad) - slice = stop_rad - start_rad + """ + We compare (rads - start_rad) % (2.0 * math.pi) and + (stop_rad - start_rad) + """ + assert start_rad <= stop_rad + portion = stop_rad - start_rad dist_rads_to_start_rads = (rads - start_rad) % (2.0 * math.pi) - #print start_rad, rads, stop_rad, ". (rads-start), slice :", dist_rads_to_start_rads, slice - return dist_rads_to_start_rads < slice + # print(start_rad, rads, stop_rad, ". (rads-start), portion :", + # dist_rads_to_start_rads, portion) + return dist_rads_to_start_rads < portion def cell_address_under_cursor(self, curx, cury): """ @@ -864,18 +991,21 @@ class FanChartBaseWidget(Gtk.DrawingArea): if person: for family_handle in person.get_family_handle_list(): family = self.dbstate.db.get_family_from_handle(family_handle) - if family and len(family.get_child_ref_list()) > 0: + if family and family.get_child_ref_list(): return True return False def on_key_press(self, widget, eventkey): """grab key press """ + dummy_widget = widget if self.mouse_x and self.mouse_y: - cell_address = self.cell_address_under_cursor(self.mouse_x, self.mouse_y) + cell_address = self.cell_address_under_cursor(self.mouse_x, + self.mouse_y) if cell_address is None: return False - person, family = self.person_at(cell_address), self.family_at(cell_address) + person, family = (self.person_at(cell_address), + self.family_at(cell_address)) if person and (Gdk.keyval_name(eventkey.keyval) == 'e'): # we edit the person self.edit_person_cb(None, person.handle) @@ -888,6 +1018,10 @@ class FanChartBaseWidget(Gtk.DrawingArea): return False def on_mouse_down(self, widget, event): + """ + What to do if we release a mouse button + """ + dummy_widget = widget self.translating = False # keep track of up/down/left/right movement if event.button == 1: @@ -918,7 +1052,8 @@ class FanChartBaseWidget(Gtk.DrawingArea): #right click on person, context menu # Do things based on state, event.get_state(), or button, event.button if is_right_click(event): - person, family = self.person_at(cell_address), self.family_at(cell_address) + person, family = (self.person_at(cell_address), + self.family_at(cell_address)) fhandle = None if family: fhandle = family.handle @@ -929,6 +1064,10 @@ class FanChartBaseWidget(Gtk.DrawingArea): return False def on_mouse_move(self, widget, event): + """ + What to do if we move the mouse + """ + dummy_widget = widget self._mouse_click = False if self.last_x is None or self.last_y is None: # while mouse is moving, we must update the tooltip based on person @@ -943,13 +1082,16 @@ class FanChartBaseWidget(Gtk.DrawingArea): #translate or rotate should happen if self.translating: - canonical_center = self.center_xy_from_delta([0,0]) - self.center_delta_xy = canonical_center[0]-event.x,canonical_center[1]-event.y + canonical_center = self.center_xy_from_delta([0, 0]) + self.center_delta_xy = (canonical_center[0] - event.x, + canonical_center[1] - event.y) self.center_xy = self.center_xy_from_delta() else: # get the angles of the two points from the center: - start_angle = math.atan2(event.y - self.center_xy[1], event.x - self.center_xy[0]) - end_angle = math.atan2(self.last_y - self.center_xy[1], self.last_x - self.center_xy[0]) + start_angle = math.atan2(event.y - self.center_xy[1], + event.x - self.center_xy[0]) + end_angle = math.atan2(self.last_y - self.center_xy[1], + self.last_x - self.center_xy[0]) # now look at change in angle: diff_angle = (end_angle - start_angle) % (math.pi * 2.0) self.rotate_value -= math.degrees(diff_angle) @@ -959,15 +1101,22 @@ class FanChartBaseWidget(Gtk.DrawingArea): return True def center_xy_from_delta(self, delta=None): + """ + return the x and y position for the center of the canvas + """ alloc = self.get_allocation() - x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height - if delta is None: delta = self.center_delta_xy + (dummy_x, dummy_y, + width, height) = alloc.x, alloc.y, alloc.width, alloc.height + if delta is None: + delta = self.center_delta_xy if self.form == FORM_CIRCLE: - canvas_xy = w/2 - delta[0], h/2 - delta[1] + canvas_xy = width / 2 - delta[0], height / 2 - delta[1] elif self.form == FORM_HALFCIRCLE: - canvas_xy = w/2 - delta[0], h - self.CENTER - PAD_PX - delta[1] + canvas_xy = (width / 2 - delta[0], + height - self.CENTER - PAD_PX - delta[1]) elif self.form == FORM_QUADRANT: - canvas_xy = self.CENTER + PAD_PX - delta[0], h - self.CENTER - PAD_PX - delta[1] + canvas_xy = (self.CENTER + PAD_PX - delta[0], + height - self.CENTER - PAD_PX - delta[1]) return canvas_xy def do_mouse_click(self): @@ -977,6 +1126,11 @@ class FanChartBaseWidget(Gtk.DrawingArea): pass def on_mouse_up(self, widget, event): + """ + What to do if we move the mouse + """ + dummy_widget = widget + dummy_event = event if self._mouse_click: self.do_mouse_click() return True @@ -996,11 +1150,15 @@ class FanChartBaseWidget(Gtk.DrawingArea): def on_drag_begin(self, widget, data): """Set up some inital conditions for drag. Set up icon.""" + dummy_widget = widget + dummy_data = data self.in_drag = True self.drag_source_set_icon_name('gramps-person') def on_drag_end(self, widget, data): """Set up some inital conditions for drag. Set up icon.""" + dummy_widget = widget + dummy_data = data self.in_drag = False def on_drag_data_get(self, widget, context, sel_data, info, time): @@ -1008,6 +1166,8 @@ class FanChartBaseWidget(Gtk.DrawingArea): Returned parameters after drag. Specified for 'person-link', for others return text info about person. """ + dummy_widget = widget + dummy_time = time tgs = [x.name() for x in context.list_targets()] person = self.person_at(self._mouse_click_cell_address) if person: @@ -1016,22 +1176,32 @@ class FanChartBaseWidget(Gtk.DrawingArea): id(self), person.get_handle(), 0) sel_data.set(sel_data.get_target(), 8, pickle.dumps(data)) elif ('TEXT' in tgs or 'text/plain' in tgs) and info == 0: - sel_data.set_text(self.format_helper.format_person(person, 11), -1) + sel_data.set_text(self.format_helper.format_person(person, + 11), -1) - def on_drag_data_received(self, widget, context, x, y, sel_data, info, time): + def on_drag_data_received(self, widget, context, pos_x, pos_y, + sel_data, info, time): """ Handle the standard gtk interface for drag_data_received. If the selection data is defined, extract the value from sel_data.data """ - radius, rads = self.cursor_to_polar(x, y) + dummy_context = context + dummy_widget = widget + dummy_info = info + dummy_time = time + radius, dummy_rads = self.cursor_to_polar(pos_x, pos_y) if radius < self.CENTER: if sel_data and sel_data.get_data(): - (drag_type, idval, handle, val) = pickle.loads(sel_data.get_data()) + (dummy_drag_type, dummy_idval, handle, + dummy_val) = pickle.loads(sel_data.get_data()) self.goto(self, handle) def edit_person_cb(self, obj, person_handle): + """ + Edit a person + """ person = self.dbstate.db.get_person_from_handle(person_handle) if person: try: @@ -1042,6 +1212,9 @@ class FanChartBaseWidget(Gtk.DrawingArea): return False def edit_fam_cb(self, obj, family_handle): + """ + Edit a family + """ fam = self.dbstate.db.get_family_from_handle(family_handle) if fam: try: @@ -1067,13 +1240,19 @@ class FanChartWidget(FanChartBaseWidget): Fan Chart Widget. Handles visualization of data in self.data. See main() of FanChartGramplet for example of model format. """ - self.set_values(None, 9, BACKGROUND_GRAD_GEN, True, True, True, True, 'Sans', '#0000FF', - '#FF0000', None, 0.5, FORM_CIRCLE) + self.angle = {} + self.childrenroot = [] + self.rootangle_rad = [] + self.menu = None + self.data = {} + self.set_values(None, 9, BACKGROUND_GRAD_GEN, True, True, True, True, + 'Sans', '#0000FF', '#FF0000', None, 0.5, FORM_CIRCLE, + False) FanChartBaseWidget.__init__(self, dbstate, uistate, callback_popup) def set_values(self, root_person_handle, maxgen, background, childring, - flipupsidedownname, twolinename, radialtext, fontdescr, - grad_start, grad_end, filter, alpha_filter, form): + flipupsidedownname, twolinename, radialtext, fontdescr, + grad_start, grad_end, filtr, alpha_filter, form, showid): """ Reset the values to be used: @@ -1083,15 +1262,17 @@ class FanChartWidget(FanChartBaseWidget): :type background: int :param childring: to show the center ring with children or not :param twolinename: uses two lines 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 flipupsidedownname: flip name on the left of the fanchart + for the display of person's name :param radialtext: try to use radial text or not :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: the alpha transparency value (0-1) to apply to filtered out data :param form: the ``FORM_`` constant for the fanchart + :param showid: to show the gramps_id or not """ self.rootpersonh = root_person_handle self.generations = maxgen @@ -1103,9 +1284,10 @@ class FanChartWidget(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.showid = showid def set_generations(self): """ @@ -1126,13 +1308,17 @@ class FanChartWidget(FanChartBaseWidget): self.data[i] = [(None,) * 4] * 2 ** i self.angle[i] = [] angle = self.rootangle_rad[0] - slice = 1/ (2 ** i) * (self.rootangle_rad[1] - self.rootangle_rad[0]) - for count in range(len(self.data[i])): + portion = 1/ (2 ** i) * (self.rootangle_rad[1] - + self.rootangle_rad[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): + """ + Initialise the data structures + """ self.set_generations() if not self.rootpersonh: return @@ -1155,9 +1341,10 @@ class FanChartWidget(FanChartBaseWidget): for current in range(1, self.generations): parent = 0 # 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 - 1: parents = self._have_parents(person) else: @@ -1174,9 +1361,9 @@ class FanChartWidget(FanChartBaseWidget): TODO: is there no util function for this """ if person: - m = self._get_parent(person, False) - f = self._get_parent(person, True) - return not m is f is None + mother = self._get_parent(person, False) + father = self._get_parent(person, True) + return not mother is father is None return False def _get_parent(self, person, father): @@ -1194,7 +1381,8 @@ class FanChartWidget(FanChartBaseWidget): else: person_handle = Family.get_mother_handle(family) if person_handle: - return self.dbstate.db.get_person_from_handle(person_handle) + fct = self.dbstate.db.get_person_from_handle + return fct(person_handle) return None def gen_pixels(self): @@ -1204,10 +1392,14 @@ class FanChartWidget(FanChartBaseWidget): return PIXELS_PER_GENERATION def nrgen(self): + """ + return the generation if we have a person to draw + """ #compute the number of generations present for generation in range(self.generations - 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 @@ -1216,22 +1408,24 @@ class FanChartWidget(FanChartBaseWidget): """ Compute the half radius of the circle """ - return PIXELS_PER_GENERATION * self.nrgen() + self.CENTER + BORDER_EDGE_WIDTH + return (PIXELS_PER_GENERATION * self.nrgen() + + self.CENTER + BORDER_EDGE_WIDTH) - def get_radiusinout_for_generation(self,generation): - outerradius=generation * PIXELS_PER_GENERATION + self.CENTER - innerradius=(generation-1) * PIXELS_PER_GENERATION + self.CENTER - if generation==0: - innerradius= CHILDRING_WIDTH + TRANSLATE_PX - return (innerradius,outerradius) + def get_radiusinout_for_gen(self, generation): + outerradius = generation * PIXELS_PER_GENERATION + self.CENTER + innerradius = (generation - 1) * PIXELS_PER_GENERATION + self.CENTER + if generation == 0: + innerradius = CHILDRING_WIDTH + TRANSLATE_PX + return (innerradius, outerradius) def people_generator(self): """ a generator over all people outside of the core person """ for generation in range(self.generations): - 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): @@ -1239,21 +1433,21 @@ class FanChartWidget(FanChartBaseWidget): a generator over all people inside of the core person """ for childdata in self.childrenroot: - (person, parents, child, userdata) = childdata + (person, dummy_parents, dummy_child, userdata) = childdata yield (person, 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: @@ -1269,57 +1463,68 @@ class FanChartWidget(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 + PIXELS_PER_GENERATION, halfdist else: self.center_xy = halfdist + PIXELS_PER_GENERATION, halfdist - cr.scale(scale, scale) - cr.translate(*self.center_xy) + ctx.scale(scale, scale) + ctx.translate(*self.center_xy) - cr.save() - cr.rotate(math.radians(self.rotate_value)) + ctx.save() + ctx.rotate(math.radians(self.rotate_value)) for generation in range(self.generations - 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, 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(generation) + (radiusin, + radiusout) = self.get_radiusinout_for_gen(generation) dup = False - self.draw_person(cr, person, radiusin, radiusout, start, stop, - generation, dup, userdata, thick=(state == EXPANDED), - has_moregen_indicator = (generation == self.generations - 1 and parents) ) - cr.restore() + indicator = (generation == self.generations - 1 + and parents) + self.draw_person(ctx, person, radiusin, radiusout, + start, stop, generation, dup, userdata, + thick=(state == EXPANDED), + has_moregen_indicator=indicator) + ctx.restore() # Draw center person: (person, parents, child, userdata) = self.data[0][0] if person: - radiusin, radiusout = self.get_radiusinout_for_generation(0) - if not child: radiusin = TRANSLATE_PX - self.draw_person(cr, person, radiusin, radiusout, math.pi/2, math.pi/2 + 2*math.pi, - 0, False, userdata, thick = False, has_moregen_indicator = False, is_central_person = True) + radiusin, radiusout = self.get_radiusinout_for_gen(0) + if not child: + radiusin = TRANSLATE_PX + self.draw_person(ctx, person, radiusin, radiusout, + math.pi/2, math.pi/2 + 2*math.pi, + 0, False, userdata, thick=False, + has_moregen_indicator=False, + is_central_person=True) #draw center disk 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 child: # has at least one child - cr.fill() + ctx.fill() else: - cr.stroke() + ctx.stroke() if child and self.childring: - self.draw_childring(cr) + self.draw_childring(ctx) if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]: - self.draw_gradient_legend(cr, halfdist) + self.draw_gradient_legend(ctx, halfdist) - def draw_childring(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_childring(self, ctx): + """ + Draw the ring for the children + """ + 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() nrchild = len(self.childrenroot) #Y axis is downward. positve angles are hence clockwise startangle = math.pi @@ -1328,40 +1533,54 @@ class FanChartWidget(FanChartBaseWidget): else: angleinc = 2 * math.pi / nrchild for childdata in self.childrenroot: - (person, parents, child, userdata) = childdata - self.draw_innerring(cr, person, userdata, startangle, angleinc) + (person, dummy_parents, dummy_child, userdata) = childdata + self.draw_innerring(ctx, person, userdata, startangle, angleinc) startangle += angleinc def expand_parents(self, generation, selected, current): - if generation >= self.generations: return + """ + Expand the parents + """ + if generation >= self.generations: + 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, + portion = (stop - start) * 2.0 + self.angle[generation][selected+1] = [current, current + portion, state] - self.expand_parents(generation + 1, selected+1, current) + self.expand_parents(generation + 1, selected + 1, current) - def show_parents(self, generation, selected, angle, slice): - if generation >= self.generations: return + def show_parents(self, generation, selected, angle, portion): + """ + Show the parents + """ + if generation >= self.generations: + 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: return + """ + Hide the parents + """ + if generation >= self.generations: + return selected = 2 * selected self.angle[generation][selected][0] = angle self.angle[generation][selected][1] = angle @@ -1373,23 +1592,30 @@ class FanChartWidget(FanChartBaseWidget): self.hide_parents(generation + 1, selected+1, angle) def shrink_parents(self, generation, selected, current): - if generation >= self.generations: return + """ + Shrink the parents + """ + if generation >= self.generations: + 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, + 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): + """ + Toggle the cell (expanded/shrinked) + """ generation, selected = cell_address if generation < 1: return @@ -1400,7 +1626,7 @@ class FanChartWidget(FanChartBaseWidget): stop = gstop + (gstop - gstart) self.angle[generation][selected] = [gstart, stop, EXPANDED] self.expand_parents(generation + 1, selected, gstart) - start, stop, state = self.angle[generation][selected+1] + start, stop, dummy_state = self.angle[generation][selected+1] self.angle[generation][selected+1] = [stop, stop, COLLAPSED] self.hide_parents(generation+1, selected+1, stop) else: @@ -1408,30 +1634,30 @@ class FanChartWidget(FanChartBaseWidget): start = gstart - (gstop - gstart) self.angle[generation][selected] = [start, gstop, EXPANDED] self.expand_parents(generation + 1, selected, start) - start, stop, state = self.angle[generation][selected-1] + start, stop, dummy_state = self.angle[generation][selected-1] self.angle[generation][selected-1] = [start, start, COLLAPSED] self.hide_parents(generation+1, selected-1, start) elif gstate == EXPANDED: # let's shrink if selected % 2 == 0: # shrink from right - slice = (gstop - gstart)/2.0 - stop = gstop - slice + portion = (gstop - gstart)/2.0 + stop = gstop - portion self.angle[generation][selected] = [gstart, stop, NORMAL] self.shrink_parents(generation+1, selected, gstart) self.angle[generation][selected+1][0] = stop # start - self.angle[generation][selected+1][1] = stop + slice # stop + self.angle[generation][selected+1][1] = stop + portion # stop self.angle[generation][selected+1][2] = NORMAL - self.show_parents(generation+1, selected+1, stop, slice/2.0) + self.show_parents(generation+1, selected+1, stop, portion/2.0) else: # shrink from left - slice = (gstop - gstart)/2.0 - start = gstop - slice + portion = (gstop - gstart)/2.0 + start = gstop - portion self.angle[generation][selected] = [start, gstop, NORMAL] self.shrink_parents(generation+1, selected, start) - start, stop, state = self.angle[generation][selected-1] - self.angle[generation][selected-1] = [start, start+slice, + start, stop, dummy_state = self.angle[generation][selected-1] + self.angle[generation][selected-1] = [start, start+portion, NORMAL] - self.show_parents(generation+1, selected-1, start, slice/2.0) + self.show_parents(generation+1, selected-1, start, portion/2.0) def cell_address_under_cursor(self, curx, cury): """ @@ -1439,33 +1665,34 @@ class FanChartWidget(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) # find out the generation if radius < TRANSLATE_PX: return None elif (self.childring and self.angle[-2] and - radius < TRANSLATE_PX + CHILDRING_WIDTH): + radius < TRANSLATE_PX + CHILDRING_WIDTH): generation = -2 # indication of one of the children elif radius < self.CENTER: generation = 0 else: generation = None for gen in range(self.generations): - radiusin,radiusout = self.get_radiusinout_for_generation(gen) + radiusin, radiusout = self.get_radiusinout_for_gen(gen) if radiusin <= radius <= radiusout: generation = gen break # find what person at this angle: 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) 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 @@ -1478,12 +1705,13 @@ class FanChartWidget(FanChartBaseWidget): if generation == 0: return 0 selected = None - for p in range(len(self.angle[generation])): - if self.data[generation][p][0]: # there is a person there - start, stop, state = self.angle[generation][p] - if state == COLLAPSED: continue + for idx in range(len(self.angle[generation])): + if self.data[generation][idx][0]: # there is a person there + start, stop, state = self.angle[generation][idx] + if state == COLLAPSED: + continue if self.radian_in_bounds(start, rads, stop): - selected = p + selected = idx break return selected @@ -1520,9 +1748,22 @@ class FanChartGrampsGUI: """ Common part of GUI that shows Fan Chart, needs to know what to do if one moves via Fan Ch def set_fan(self, fan):art to a new person - on_childmenu_changed: in popup, function called on moving to a new person + on_childmenu_changed: in popup, function called on moving + to a new person """ self.fan = None + self.menu = None + self.twolinename = False + self.radialtext = None + self.maxgen = 8 + self.childring = 0 + self.showid = False + self.grad_start = '#0000FF' + self.grad_end = '#FF0000' + self.form = FORM_CIRCLE + self.alpha_filter = 0.5 + self.filter = None + self.background = BACKGROUND_GRAD_GEN self.on_childmenu_changed = on_childmenu_changed self.format_helper = FormattingHelper(self.dbstate, self.uistate) self.uistate.connect('font-changed', self.reload_symbols) @@ -1545,9 +1786,10 @@ class FanChartGrampsGUI: """ root_person_handle = self.get_active('Person') self.fan.set_values(root_person_handle, self.maxgen, self.background, - self.childring, self.flipupsidedownname, self.twolinename, self.radialtext, self.fonttype, - self.grad_start, self.grad_end, - self.generic_filter, self.alpha_filter, self.form) + self.childring, self.flipupsidedownname, + self.twolinename, self.radialtext, self.fonttype, + self.grad_start, self.grad_end, self.generic_filter, + self.alpha_filter, self.form, self.showid) self.fan.reset() self.fan.draw() self.fan.queue_draw() @@ -1557,6 +1799,7 @@ class FanChartGrampsGUI: Builds the full menu (including Siblings, Spouses, Children, and Parents) with navigation. """ + dummy_obj = obj #store menu for GTK3 to avoid it being destroyed before showing self.menu = Gtk.Menu() menu = self.menu @@ -1649,8 +1892,8 @@ class FanChartGrampsGUI: pfam_list = person.get_parent_family_handle_list() siblings = [] step_siblings = [] - for f in pfam_list: - fam = self.dbstate.db.get_family_from_handle(f) + for fhdle in pfam_list: + fam = self.dbstate.db.get_family_from_handle(fhdle) sib_list = fam.get_child_ref_list() for sib_ref in sib_list: sib_id = sib_ref.ref @@ -1667,8 +1910,10 @@ class FanChartGrampsGUI: if fam_id not in pfam_list] for step_fam in other_families: fam_stepsiblings = [sib_ref.ref - for sib_ref in step_fam.get_child_ref_list() - if not (sib_ref.ref == person.get_handle())] + for sib_ref in + step_fam.get_child_ref_list() + if not sib_ref.ref == + person.get_handle()] if fam_stepsiblings: step_siblings.append(fam_stepsiblings) @@ -1683,17 +1928,20 @@ class FanChartGrampsGUI: sib = self.dbstate.db.get_person_from_handle(sib_id) if not sib: continue - if find_children(self.dbstate.db,sib): - label = Gtk.Label(label='%s' % escape(name_displayer.display(sib))) + if find_children(self.dbstate.db, sib): + thelabel = escape(name_displayer.display(sib)) + label = Gtk.Label(label='%s' % thelabel) else: - label = Gtk.Label(label=escape(name_displayer.display(sib))) + thelabel = escape(name_displayer.display(sib)) + label = Gtk.Label(label=thelabel) sib_item = Gtk.MenuItem() label.set_use_markup(True) label.show() - label.set_alignment(0,0) + label.set_alignment(0, 0) sib_item.add(label) linked_persons.append(sib_id) - sib_item.connect("activate", self.on_childmenu_changed, sib_id) + sib_item.connect("activate", self.on_childmenu_changed, + sib_id) sib_item.show() sib_menu.append(sib_item) if sibs.index(sib_group) < len(sibs)-1: @@ -1720,8 +1968,9 @@ class FanChartGrampsGUI: child_menu = item.get_submenu() child_menu.set_reserve_toggle_size(False) - if find_children(self.dbstate.db,child): - label = Gtk.Label(label='%s' % escape(name_displayer.display(child))) + if find_children(self.dbstate.db, child): + thelabel = escape(name_displayer.display(child)) + label = Gtk.Label(label='%s' % thelabel) else: label = Gtk.Label(label=escape(name_displayer.display(child))) @@ -1731,7 +1980,8 @@ class FanChartGrampsGUI: label.set_halign(Gtk.Align.START) child_item.add(label) linked_persons.append(child_handle) - child_item.connect("activate", self.on_childmenu_changed, child_handle) + child_item.connect("activate", self.on_childmenu_changed, + child_handle) child_item.show() child_menu.append(child_item) @@ -1746,7 +1996,7 @@ class FanChartGrampsGUI: par_menu = item.get_submenu() par_menu.set_reserve_toggle_size(False) no_parents = 1 - par_list = find_parents(self.dbstate.db,person) + par_list = find_parents(self.dbstate.db, person) for par_id in par_list: if not par_id: continue @@ -1757,8 +2007,9 @@ class FanChartGrampsGUI: if no_parents: no_parents = 0 - if find_parents(self.dbstate.db,par): - label = Gtk.Label(label='%s' % escape(name_displayer.display(par))) + if find_parents(self.dbstate.db, par): + thelabel = escape(name_displayer.display(par)) + label = Gtk.Label(label='%s' % thelabel) else: label = Gtk.Label(label=escape(name_displayer.display(par))) @@ -1785,7 +2036,7 @@ class FanChartGrampsGUI: # Go over parents and build their menu item = Gtk.MenuItem(label=_("Related")) no_related = 1 - for p_id in find_witnessed_people(self.dbstate.db,person): + for p_id in find_witnessed_people(self.dbstate.db, person): #if p_id in linked_persons: # continue # skip already listed family members @@ -1833,7 +2084,7 @@ class FanChartGrampsGUI: add_partner_item = Gtk.MenuItem() add_partner_item.set_label(_("Add partner to person")) add_partner_item.connect("activate", self.add_partner_to_pers_cb, - person_handle) + person_handle) add_partner_item.show() add_menu.append(add_partner_item) @@ -1849,6 +2100,9 @@ class FanChartGrampsGUI: return 1 def edit_person_cb(self, obj, person_handle): + """ + Edit a person + """ person = self.dbstate.db.get_person_from_handle(person_handle) if person: try: @@ -1859,6 +2113,9 @@ class FanChartGrampsGUI: return False def edit_fam_cb(self, obj, family_handle): + """ + Edit a family + """ fam = self.dbstate.db.get_family_from_handle(family_handle) if fam: try: @@ -1869,6 +2126,9 @@ class FanChartGrampsGUI: return False def reord_fam_cb(self, obj, person_handle): + """ + reorder a family + """ try: Reorder(self.dbstate, self.uistate, [], person_handle) except WindowActiveError: @@ -1889,6 +2149,9 @@ class FanChartGrampsGUI: pass def add_child_to_fam_cb(self, obj, family_handle): + """ + Add a child to a family + """ callback = lambda x: self.callback_add_child(x, family_handle) person = Person() name = Name() @@ -1897,7 +2160,7 @@ class FanChartGrampsGUI: name.set_primary_surname(0) family = self.dbstate.db.get_family_from_handle(family_handle) father = self.dbstate.db.get_person_from_handle( - family.get_father_handle()) + family.get_father_handle()) if father: preset_name(father, name) person.set_primary_name(name) @@ -1908,6 +2171,9 @@ class FanChartGrampsGUI: pass def callback_add_child(self, person, family_handle): + """ + Add a child + """ ref = ChildRef() ref.ref = person.get_handle() family = self.dbstate.db.get_family_from_handle(family_handle) @@ -1942,6 +2208,10 @@ class FanChartGrampsGUI: pass def on_add_parents(self, obj, person_handle): + """ + Add a family + """ + dummy_obj = obj family = Family() childref = ChildRef() childref.set_reference_handle(person_handle) @@ -1951,12 +2221,15 @@ class FanChartGrampsGUI: except WindowActiveError: return - def copy_person_to_clipboard_cb(self, obj,person_handle): - """Renders the person data into some lines of text and puts that into the clipboard""" + def copy_person_to_clipboard_cb(self, obj, person_handle): + """ + Renders the person data into some lines of text and puts that + into the clipboard + """ person = self.dbstate.db.get_person_from_handle(person_handle) if person: - cb = Gtk.Clipboard.get_for_display(Gdk.Display.get_default(), - Gdk.SELECTION_CLIPBOARD) - cb.set_text( self.format_helper.format_person(person,11), -1) + cbx = Gtk.Clipboard.get_for_display(Gdk.Display.get_default(), + Gdk.SELECTION_CLIPBOARD) + cbx.set_text(self.format_helper.format_person(person, 11), -1) return True return False diff --git a/gramps/gui/widgets/fanchart2way.py b/gramps/gui/widgets/fanchart2way.py index 38e790d2d..28aca7436 100644 --- a/gramps/gui/widgets/fanchart2way.py +++ b/gramps/gui/widgets/fanchart2way.py @@ -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() diff --git a/gramps/gui/widgets/fanchartdesc.py b/gramps/gui/widgets/fanchartdesc.py index 81ddbe355..1988d0ba8 100644 --- a/gramps/gui/widgets/fanchartdesc.py +++ b/gramps/gui/widgets/fanchartdesc.py @@ -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() diff --git a/gramps/plugins/gramplet/fanchart2waygramplet.py b/gramps/plugins/gramplet/fanchart2waygramplet.py index 3c3488fb1..2666a61ea 100644 --- a/gramps/plugins/gramplet/fanchart2waygramplet.py +++ b/gramps/plugins/gramplet/fanchart2waygramplet.py @@ -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 diff --git a/gramps/plugins/gramplet/fanchartdescgramplet.py b/gramps/plugins/gramplet/fanchartdescgramplet.py index ecb791a08..d07f5de69 100644 --- a/gramps/plugins/gramplet/fanchartdescgramplet.py +++ b/gramps/plugins/gramplet/fanchartdescgramplet.py @@ -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 diff --git a/gramps/plugins/gramplet/fanchartgramplet.py b/gramps/plugins/gramplet/fanchartgramplet.py index 27a7b75c2..9aad9daf9 100644 --- a/gramps/plugins/gramplet/fanchartgramplet.py +++ b/gramps/plugins/gramplet/fanchartgramplet.py @@ -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 diff --git a/gramps/plugins/view/fanchart2wayview.py b/gramps/plugins/view/fanchart2wayview.py index a3b611382..879b11b76 100644 --- a/gramps/plugins/view/fanchart2wayview.py +++ b/gramps/plugins/view/fanchart2wayview.py @@ -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() diff --git a/gramps/plugins/view/fanchartdescview.py b/gramps/plugins/view/fanchartdescview.py index 00ca8f735..34a346925 100644 --- a/gramps/plugins/view/fanchartdescview.py +++ b/gramps/plugins/view/fanchartdescview.py @@ -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() diff --git a/gramps/plugins/view/fanchartview.py b/gramps/plugins/view/fanchartview.py index ced026809..059b48d63 100644 --- a/gramps/plugins/view/fanchartview.py +++ b/gramps/plugins/view/fanchartview.py @@ -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()