Fanchart: New feature, a childring can be added to indicate the children
This prepares drag and drop to move, so will allow fast navigation svn: r20314
This commit is contained in:
parent
5f74964ec5
commit
87a5da6135
@ -81,6 +81,7 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
Interactive Fan Chart Widget.
|
||||
"""
|
||||
BORDER_EDGE_WIDTH = 10
|
||||
CHILDRING_WIDTH = 12
|
||||
TRANSLATE_PX = 10
|
||||
|
||||
BACKGROUND_SCHEME1 = 0
|
||||
@ -104,7 +105,8 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
(191,222,252),
|
||||
(183,219,197),
|
||||
(206,246,209)),
|
||||
BACKGROUND_WHITE: ((255,255,255),),
|
||||
BACKGROUND_WHITE: ((255,255,255),
|
||||
(255,255,255),),
|
||||
}
|
||||
|
||||
COLLAPSED = 0
|
||||
@ -152,13 +154,14 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
self.center_xy = [0, 0] # distance from center (x, y)
|
||||
self.center = 50 # pixel radius of center
|
||||
#default values
|
||||
self.reset(9, self.BACKGROUND_SCHEME1)
|
||||
self.reset(9, self.BACKGROUND_SCHEME1, True)
|
||||
self.set_size_request(120, 120)
|
||||
|
||||
def reset(self, maxgen, background):
|
||||
def reset(self, maxgen, background, childring):
|
||||
"""
|
||||
Reset all of the data on where/how slices appear, and if they are expanded.
|
||||
"""
|
||||
self.childring = childring
|
||||
self.background = background
|
||||
if self.background == self.BACKGROUND_GENDER:
|
||||
self.colors = None
|
||||
@ -173,6 +176,7 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
self.generations = generations
|
||||
self.angle = {}
|
||||
self.data = {}
|
||||
self.childrenroot = []
|
||||
for i in range(self.generations):
|
||||
# name, person, parents?, children?
|
||||
self.data[i] = [(None,) * 4] * 2 ** i
|
||||
@ -278,12 +282,16 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
name = name_displayer.display(person)
|
||||
self.draw_text(cr, name, self.center - 10, 95, 455)
|
||||
cr.restore()
|
||||
#draw center to move chart
|
||||
cr.set_source_rgb(0, 0, 0) # black
|
||||
cr.move_to(self.TRANSLATE_PX, 0)
|
||||
cr.arc(0, 0, self.TRANSLATE_PX, 0, 2 * math.pi)
|
||||
if child: # has at least one child
|
||||
cr.set_source_rgb(0, 0, 0) # black
|
||||
cr.move_to(0, 0)
|
||||
cr.arc(0, 0, self.TRANSLATE_PX, 0, 2 * math.pi)
|
||||
cr.move_to(0,0)
|
||||
cr.fill()
|
||||
else:
|
||||
cr.stroke()
|
||||
if child and self.childring:
|
||||
self.drawchildring(cr)
|
||||
|
||||
def draw_person(self, cr, gender, name, start, stop, generation,
|
||||
state, parents, child, person):
|
||||
@ -292,8 +300,9 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
are in degrees. Gender is indication of father position or mother
|
||||
position in the chart
|
||||
"""
|
||||
alloc = self.get_allocation()
|
||||
x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height
|
||||
#alloc = self.get_allocation()
|
||||
#x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height
|
||||
cr.save()
|
||||
start_rad = start * math.pi/180
|
||||
stop_rad = stop * math.pi/180
|
||||
if self.background == self.BACKGROUND_GENDER:
|
||||
@ -341,6 +350,64 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
if self.last_x is None or self.last_y is None:
|
||||
self.draw_text(cr, name, radius - self.pixels_per_generation/2,
|
||||
start, stop)
|
||||
cr.restore()
|
||||
|
||||
def drawchildring(self, cr):
|
||||
cr.move_to(self.TRANSLATE_PX + self.CHILDRING_WIDTH, 0)
|
||||
cr.set_source_rgb(0, 0, 0) # black
|
||||
cr.set_line_width(1)
|
||||
cr.arc(0, 0, self.TRANSLATE_PX + self.CHILDRING_WIDTH, 0, 2 * math.pi)
|
||||
cr.stroke()
|
||||
nrchild = len(self.childrenroot)
|
||||
#Y axis is downward. positve angles are hence clockwise
|
||||
startangle = math.pi
|
||||
if nrchild <= 4:
|
||||
angleinc = math.pi/2
|
||||
else:
|
||||
angleinc = 2 * math.pi / nrchild
|
||||
self.angle[-2] = []
|
||||
for child in self.childrenroot:
|
||||
self.drawchild(cr, child, startangle, angleinc)
|
||||
startangle += angleinc
|
||||
|
||||
def drawchild(self, cr, childdata, start, inc):
|
||||
child_handle, child_gender, has_child = childdata
|
||||
# in polar coordinates what is to draw
|
||||
rmin = self.TRANSLATE_PX
|
||||
rmax = self.TRANSLATE_PX + self.CHILDRING_WIDTH
|
||||
thetamin = start
|
||||
thetamax = start + inc
|
||||
# add child to angle storage
|
||||
self.angle[-2].append([thetamin, thetamax, child_gender, None])
|
||||
def _childpath(cr):
|
||||
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.line_to(rmin*math.cos(thetamin), rmin*math.sin(thetamin))
|
||||
_childpath(cr)
|
||||
cr.set_source_rgb(0, 0, 0) # black
|
||||
cr.set_line_width(1)
|
||||
cr.stroke()
|
||||
#now fill
|
||||
if self.background == self.BACKGROUND_GENDER:
|
||||
person = self.dbstate.db.get_person_from_handle(child_handle)
|
||||
try:
|
||||
alive = probably_alive(person, self.dbstate.db)
|
||||
except RuntimeError:
|
||||
alive = False
|
||||
backgr, border = gui.utils.color_graph_box(alive, child_gender)
|
||||
r, g, b = gui.utils.hex_to_rgb(backgr)
|
||||
else:
|
||||
#children in color as parents
|
||||
r,g,b = self.colors[1]
|
||||
if child_gender == gen.lib.Person.MALE:
|
||||
r *= .9
|
||||
g *= .9
|
||||
b *= .9
|
||||
_childpath(cr)
|
||||
cr.set_source_rgb(r/255., g/255., b/255.)
|
||||
cr.fill()
|
||||
|
||||
def text_degrees(self, text, radius):
|
||||
"""
|
||||
@ -367,8 +434,6 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
# center text:
|
||||
# offset for cairo-font system is 90:
|
||||
pos = start + ((stop - start) - self.text_degrees(text,radius))/2.0 + 90
|
||||
alloc = self.get_allocation()
|
||||
x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height
|
||||
cr.save()
|
||||
# Create a PangoLayout, set the font and text
|
||||
# Draw the layout N_WORDS times in a circle
|
||||
@ -500,10 +565,14 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
# while mouse is moving, we must update the tooltip based on person
|
||||
generation, selected = self.person_under_cursor(event.x, event.y)
|
||||
tooltip = ""
|
||||
if selected is not None:
|
||||
person = None
|
||||
if selected is not None and generation >= 0:
|
||||
text, person, parents, child = self.data[generation][selected]
|
||||
if person:
|
||||
tooltip = self.format_helper.format_person(person, 11)
|
||||
elif selected is not None and generation == -2:
|
||||
child_handle, child_gender, has_child = self.childrenroot[selected]
|
||||
person = self.dbstate.db.get_person_from_handle(child_handle)
|
||||
if person:
|
||||
tooltip = self.format_helper.format_person(person, 11)
|
||||
self.set_tooltip_text(tooltip)
|
||||
return False
|
||||
|
||||
@ -544,6 +613,9 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
radius = math.sqrt((curx - cx) ** 2 + (cury - cy) ** 2)
|
||||
if radius < self.TRANSLATE_PX:
|
||||
generation = -1
|
||||
elif (self.childring and self.childrenroot and
|
||||
radius < self.TRANSLATE_PX + self.CHILDRING_WIDTH):
|
||||
generation = -2 # indication of one of the children
|
||||
elif radius < self.center:
|
||||
generation = 0
|
||||
else:
|
||||
@ -554,6 +626,10 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
if rads < 0: # second half of unit circle
|
||||
rads = math.pi + (math.pi + rads)
|
||||
pos = ((rads/(math.pi * 2) - self.rotate_value/360.) * 360.0) % 360
|
||||
#children are in cairo angle (clockwise) from pi to 3 pi
|
||||
#rads however is clock 0 to 2 pi
|
||||
if rads < math.pi:
|
||||
rads += 2 * math.pi
|
||||
# if generation is in expand zone:
|
||||
# FIXME: add a way of expanding
|
||||
# find what person is in this position:
|
||||
@ -568,6 +644,13 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
break
|
||||
elif generation == 0:
|
||||
selected = 0
|
||||
elif generation == -2:
|
||||
for p in range(len(self.angle[generation])):
|
||||
start, stop, male, state = self.angle[generation][p]
|
||||
if start <= rads <= stop:
|
||||
selected = p
|
||||
break
|
||||
|
||||
return generation, selected
|
||||
|
||||
def on_mouse_down(self, widget, event):
|
||||
@ -598,7 +681,11 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
#right click on person, context menu
|
||||
# Do things based on state, event.get_state(), or button, event.button
|
||||
if gui.utils.is_right_click(event):
|
||||
text, person, parents, child = self.data[generation][selected]
|
||||
if generation == -2:
|
||||
child_handle, child_gender, has_child = self.childrenroot[selected]
|
||||
person = self.dbstate.db.get_person_from_handle(child_handle)
|
||||
else:
|
||||
text, person, parents, child = self.data[generation][selected]
|
||||
if person and self.on_popup:
|
||||
self.on_popup(widget, event, person.handle)
|
||||
return True
|
||||
@ -650,7 +737,8 @@ class FanChartWidget(Gtk.DrawingArea):
|
||||
class FanChartGrampsGUI(object):
|
||||
""" class for functions fanchart GUI elements will need in Gramps
|
||||
"""
|
||||
def __init__(self, maxgen, background, on_childmenu_changed):
|
||||
def __init__(self, maxgen, background, childring,
|
||||
on_childmenu_changed):
|
||||
"""
|
||||
Common part of GUI that shows Fan Chart, needs to know what to do if
|
||||
one moves via Fan Chart to a new person
|
||||
@ -662,6 +750,7 @@ class FanChartGrampsGUI(object):
|
||||
|
||||
self.maxgen = maxgen
|
||||
self.background = background
|
||||
self.childring = childring
|
||||
|
||||
def have_parents(self, person):
|
||||
"""
|
||||
@ -714,7 +803,7 @@ class FanChartGrampsGUI(object):
|
||||
Fill the data structures with the active data. This initializes all
|
||||
data.
|
||||
"""
|
||||
self.fan.reset(self.maxgen, self.background)
|
||||
self.fan.reset(self.maxgen, self.background, self.childring)
|
||||
person = self.dbstate.db.get_person_from_handle(self.get_active('Person'))
|
||||
if not person:
|
||||
name = None
|
||||
@ -723,6 +812,17 @@ class FanChartGrampsGUI(object):
|
||||
parents = self.have_parents(person)
|
||||
child = self.have_children(person)
|
||||
self.fan.data[0][0] = (name, person, parents, child)
|
||||
self.fan.childrenroot = []
|
||||
if child:
|
||||
childlist = find_children(self.dbstate.db, person)
|
||||
for child_handle in childlist:
|
||||
child = self.dbstate.db.get_person_from_handle(child_handle)
|
||||
if not child:
|
||||
continue
|
||||
else:
|
||||
self.fan.childrenroot.append((child_handle,
|
||||
child.get_gender(),
|
||||
self.have_children(child)))
|
||||
for current in range(1, self.maxgen):
|
||||
parent = 0
|
||||
# name, person, parents, children
|
||||
|
@ -63,7 +63,7 @@ class FanChartGramplet(FanChartGrampsGUI, Gramplet):
|
||||
|
||||
def __init__(self, gui, nav_group=0):
|
||||
Gramplet.__init__(self, gui, nav_group)
|
||||
FanChartGrampsGUI.__init__(self, 6, 0, self.on_childmenu_changed)
|
||||
FanChartGrampsGUI.__init__(self, 6, 0, True, self.on_childmenu_changed)
|
||||
self.set_fan(FanChartWidget(self.dbstate, self.on_popup))
|
||||
# Replace the standard textview with the fan chart widget:
|
||||
self.gui.get_container_widget().remove(self.gui.textview)
|
||||
|
@ -60,6 +60,7 @@ class FanChartView(FanChartGrampsGUI, NavigationView):
|
||||
CONFIGSETTINGS = (
|
||||
('interface.fanview-maxgen', 9),
|
||||
('interface.fanview-background', 0),
|
||||
('interface.fanview-childrenring', True),
|
||||
)
|
||||
def __init__(self, pdata, dbstate, uistate, nav_group=0):
|
||||
self.dbstate = dbstate
|
||||
@ -72,6 +73,7 @@ class FanChartView(FanChartGrampsGUI, NavigationView):
|
||||
FanChartGrampsGUI.__init__(self,
|
||||
self._config.get('interface.fanview-maxgen'),
|
||||
self._config.get('interface.fanview-background'),
|
||||
self._config.get('interface.fanview-childrenring'),
|
||||
self.on_childmenu_changed)
|
||||
|
||||
dbstate.connect('active-changed', self.active_changed)
|
||||
@ -245,9 +247,12 @@ class FanChartView(FanChartGrampsGUI, NavigationView):
|
||||
configdialog.add_spinner(table, _("Max generations"), 0,
|
||||
'interface.fanview-maxgen', (1, 11),
|
||||
callback=self.cb_update_maxgen)
|
||||
configdialog.add_checkbox(table,
|
||||
_('Show children ring'),
|
||||
1, 'interface.fanview-childrenring')
|
||||
configdialog.add_combo(table,
|
||||
_('Background'),
|
||||
4, 'interface.fanview-background',
|
||||
2, 'interface.fanview-background',
|
||||
((0, _('Color Scheme 1')),
|
||||
(1, _('Color Scheme 2')),
|
||||
(2, _('Gender Colors')),
|
||||
@ -256,6 +261,15 @@ class FanChartView(FanChartGrampsGUI, NavigationView):
|
||||
|
||||
return _('Layout'), table
|
||||
|
||||
def config_connect(self):
|
||||
"""
|
||||
Overwriten from :class:`~gui.views.pageview.PageView method
|
||||
This method will be called after the ini file is initialized,
|
||||
use it to monitor changes in the ini file
|
||||
"""
|
||||
self._config.connect('interface.fanview-childrenring',
|
||||
self.cb_update_childrenring)
|
||||
|
||||
def cb_update_maxgen(self, spinbtn, constant):
|
||||
self.maxgen = spinbtn.get_value_as_int()
|
||||
self._config.set(constant, self.maxgen)
|
||||
@ -267,6 +281,16 @@ class FanChartView(FanChartGrampsGUI, NavigationView):
|
||||
self.background = int(entry)
|
||||
self.update()
|
||||
|
||||
def cb_update_childrenring(self, client, cnxn_id, entry, data):
|
||||
"""
|
||||
Called when the configuration menu changes the childrenring setting.
|
||||
"""
|
||||
if entry == 'True':
|
||||
self.childring = True
|
||||
else:
|
||||
self.childring = False
|
||||
self.update()
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# CairoPrintSave class
|
||||
|
Loading…
Reference in New Issue
Block a user