integrated object selector into edit family

svn: r5804
This commit is contained in:
Richard Taylor 2006-01-20 10:30:35 +00:00
parent c5fc889410
commit 6fee6a5f79
16 changed files with 405 additions and 39 deletions

View File

@ -1,3 +1,14 @@
2006-01-20 Richard Taylor <rjt-gramps@thegrindstone.me.uk>
* src/gramps.py: stoped Ctrl-C causing an error report
* src/EditFamily.py: integrated ObjectSelector
* src/ObjectSelector/_Factories.py, src/ObjectSelector/_FamilyFilterFrame.py,
src/ObjectSelector/_FamilyFrame.py, src/ObjectSelector/_FamilyPreviewFrame.py,
src/ObjectSelector/_FilterFrameBase.py, src/ObjectSelector/_ObjectSelectorWindow.py,
src/ObjectSelector/_PersonFilterFrame.py, src/ObjectSelector/_PersonFrame.py,
src/ObjectSelector/_PersonPreviewFrame.py, src/ObjectSelector/_FilterSpec.py,
src/ObjectSelector/_FilterSpecBase.py, src/ObjectSelector/_PersonFilterSpec.py,
src/ObjectSelector/__init__.py: implemented filter specs and setup package exports
2006-01-19 Don Allingham <don@gramps-project.org>
* src/DisplayModels.py: use dirty flag to only draw when requested
* src/PageView.py: use dirty flag to only draw when requested

View File

@ -62,6 +62,7 @@ from DdTargets import DdTargets
from WindowUtils import GladeIf
from DisplayTabs import *
from GrampsWidgets import *
from ObjectSelector import PersonSelector,PersonFilterSpec
#-------------------------------------------------------------------------
#
@ -153,7 +154,21 @@ class ChildEmbedList(EmbeddedList):
return [(1,0),(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(0,8),(0,9)]
def add_button_clicked(self,obj):
print "Add Button Clicked"
# we could workout the death years of the parents here and
# set a suitable filter_spec on the PersonSelector
# we might also be able to set a filter that only includes
# people that are not already listed as children in another
# family.
selector = PersonSelector(self.dbstate,self.uistate,self.track)
# this need the window handle of the main EditFamily window
# to make the PersonSelector transient to it. I am not sure
# want the best way is to get that handle from here.
#selector.set_transient_for(self.window)
# Connect this to the method used to add a new child.
#selector.connect('add-object',self.on_add_child)
def del_button_clicked(self,obj):
handle = self.get_selected()
@ -327,13 +342,72 @@ class EditFamily(DisplayState.ManagedWindow):
def update_mother(self,handle):
self.load_parent(handle, self.mbox, self.mbirth, self.mdeath, self.mbutton)
def on_change_mother(self, selector_window, select_result):
if select_result.is_person():
try:
self.update_mother(
self.dbstate.db.get_person_from_gramps_id(
select_result.get_gramps_id()).get_handle())
except:
log.warn("Failed to update mother: \n"
"gramps_id returned from selector was: %s\n"
"person returned from get_person_from_gramps_id: %s"
% (select_result.get_gramps_id(),
repr(self.dbstate.db.get_person_from_gramps_id(
select_result.get_gramps_id()))))
raise
else:
log.warn("Object selector returned a result of type = %s, it should "
"have been of type PERSON." % (str(select_result.get_object_type())))
selector_window.close()
def mother_clicked(self,obj):
handle = self.family.get_mother_handle()
if handle:
self.family.set_mother_handle(None)
self.update_mother(None)
else:
print "Call person selector"
filter_spec = PersonFilterSpec()
filter_spec.set_gender(RelLib.Person.FEMALE)
child_birth_years = []
for person_handle in self.family.get_child_handle_list():
person = self.dbstate.db.get_person_from_handle(person_handle)
event_handle = person.get_birth_handle()
if event_handle:
event = self.dbstate.db.get_event_from_handle(event_handle)
child_birth_years.append(event.get_date_object().get_year())
if len(child_birth_years) > 0:
filter_spec.set_birth_year(min(child_birth_years))
filter_spec.set_birth_criteria(PersonFilterSpec.BEFORE)
selector = PersonSelector(self.dbstate,self.uistate,self.track,filter_spec=filter_spec)
selector.set_transient_for(self.window)
selector.connect('add-object',self.on_change_mother)
def on_change_father(self, selector_window, select_result):
if select_result.is_person():
try:
self.update_father(
self.dbstate.db.get_person_from_gramps_id(
select_result.get_gramps_id()).get_handle())
except:
log.warn("Failed to update father: \n"
"gramps_id returned from selector was: %s\n"
"person returned from get_person_from_gramps_id: %s"
% (select_result.get_gramps_id(),
repr(self.dbstate.db.get_person_from_gramps_id(
select_result.get_gramps_id()))))
raise
else:
log.warn("Object selector returned a result of type = %s, it should "
"have been of type PERSON." % (str(select_result.get_object_type())))
selector_window.close()
def father_clicked(self,obj):
handle = self.family.get_father_handle()
@ -341,7 +415,25 @@ class EditFamily(DisplayState.ManagedWindow):
self.family.set_father_handle(None)
self.update_father(None)
else:
print "Call person selector"
filter_spec = PersonFilterSpec()
filter_spec.set_gender(RelLib.Person.MALE)
child_birth_years = []
for person_handle in self.family.get_child_handle_list():
person = self.dbstate.db.get_person_from_handle(person_handle)
event_handle = person.get_birth_handle()
if event_handle:
event = self.dbstate.db.get_event_from_handle(event_handle)
child_birth_years.append(event.get_date_object().get_year())
if len(child_birth_years) > 0:
filter_spec.set_birth_year(min(child_birth_years))
filter_spec.set_birth_criteria(PersonFilterSpec.BEFORE)
selector = PersonSelector(self.dbstate,self.uistate,self.track,filter_spec=filter_spec)
selector.set_transient_for(self.window)
selector.connect('add-object',self.on_change_father)
def edit_person(self,obj,event,handle):
if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1:

View File

@ -8,7 +8,7 @@ class ObjectFrameFactory(object):
__frame_creators = {ObjectTypes.PERSON: PersonFrame,
ObjectTypes.FAMILY: FamilyFrame}
def get_frame(self,object_type,dbstate,uistate):
return self.__class__.__frame_creators[object_type](dbstate,uistate)
def get_frame(self,object_type,dbstate,uistate,filter_spec=None):
return self.__class__.__frame_creators[object_type](dbstate,uistate,filter_spec)

View File

@ -13,8 +13,8 @@ class FamilyFilterFrame(FilterFrameBase):
__default_border_width = 5
def __init__(self,dbstate,label="Filter"):
FilterFrameBase.__init__(self,label)
def __init__(self,dbstate,filter_spec=None,label="Filter"):
FilterFrameBase.__init__(self,filter_spec,label)
# Gramps ID
id_check = gtk.CheckButton()
@ -115,6 +115,12 @@ class FamilyFilterFrame(FilterFrameBase):
self._table.attach(filter_label,self._label_col,self._label_col+1,current_row,current_row+1,xoptions=gtk.FILL,yoptions=False)
self._table.attach(filter_combo,self._control_col,self._control_col+1,current_row,current_row+1,xoptions=gtk.EXPAND|gtk.FILL,yoptions=False)
if filter_spec is not None:
self._set_filter(filter_spec)
def _set_filter(filter_spec):
pass
def on_apply(self,button):
pass

View File

@ -29,12 +29,13 @@ class FamilyFrame(ObjectFrameBase):
def __init__(self,
dbstate,
uistate):
uistate,
filter_spec=None):
ObjectFrameBase.__init__(self,
dbstate=dbstate,
uistate=uistate,
filter_frame = FamilyFilterFrame(dbstate),
filter_frame = FamilyFilterFrame(filter_spec=filter_spec),
preview_frame = FamilyPreviewFrame(dbstate),
tree_frame = FamilyTreeFrame(dbstate))

View File

@ -1,9 +1,11 @@
import os.path
import gtk
import gobject
from logging import getLogger
log = getLogger(".ObjectSelector")
import ImgManip
import const
class FamilyPreviewFrame(gtk.Frame):
@ -79,16 +81,16 @@ class FamilyPreviewFrame(gtk.Frame):
image[image_no].set_from_pixbuf(pixbuf)
image_no += 1
else:
self._image_l.set_from_file("../person.svg")
self._image_r.set_from_file("../person.svg")
self._image_l.set_from_file(os.path.join(const.rootDir,"person.svg"))
self._image_r.set_from_file(os.path.join(const.rootDir,"person.svg"))
except:
log.warn("Failed to generate preview for family", exc_info=True)
self.clear_object()
def clear_object(self):
self._image_l.set_from_file("../person.svg")
self._image_r.set_from_file("../person.svg")
self._image_l.set_from_file(os.path.join(const.rootDir,"person.svg"))
self._image_r.set_from_file(os.path.join(const.rootDir,"person.svg"))
if gtk.pygtk_version < (2,8,0):

View File

@ -16,9 +16,11 @@ class FilterFrameBase(gtk.Frame):
__default_border_width = 5
def __init__(self,dbstate,label="Filter"):
def __init__(self,filter_spec=None,label="Filter"):
gtk.Frame.__init__(self,label)
self._filter_spec = filter_spec
align = gtk.Alignment()
# table layout
@ -54,6 +56,7 @@ class FilterFrameBase(gtk.Frame):
self.add(align)
def on_apply(self,button):
"""Build a GenericFilter object from the settings in the filter controls and
emit a 'apply-filter' signal with the GenericFilter object as the parameter."""

View File

@ -0,0 +1,14 @@
class FilterSpecBase(object):
def __init__(self):
self._gramps_id = None
def set_gramps_id(self,gramps_id):
self._gramps_id = gramps_id
def get_gramps_id(self):
return self._gramps_id
def include_gramps_id(self):
return self._gramps_id is not None

View File

@ -0,0 +1,14 @@
class FilterSpecBase(object):
def __init__(self):
self._gramps_id = None
def set_gramps_id(self,gramps_id):
self._gramps_id = gramps_id
def get_gramps_id(self):
return self._gramps_id
def include_gramps_id(self):
return self._gramps_id is not None

View File

@ -4,13 +4,18 @@ sys.path.append("..")
sys.path.append(".")
sys.path.append("ObjectSelector")
import os.path
import gtk
import gobject
from gettext import gettext as _
import _Factories
from _Constants import ObjectTypes
from _ObjectSelectorResult import ObjectSelectorResult
from DisplayState import ManagedWindow
import const
class _ObjectTypeWidgets(object):
def __init__(self):
@ -37,7 +42,7 @@ OBJECT_LIST = [ObjectTypes.PERSON, ObjectTypes.FAMILY,
ObjectTypes.MEDIA, ObjectTypes.PLACE,
ObjectTypes.REPOSITORY]
class ObjectSelectorWindow(gtk.Window):
class ObjectSelectorWindow(gtk.Window,ManagedWindow):
__gproperties__ = {}
@ -54,13 +59,21 @@ class ObjectSelectorWindow(gtk.Window):
def __init__(self,
dbstate,
uistate,
track,
title = _("Select Object"),
filter_spec = None,
default_object_type = ObjectTypes.PERSON,
object_list = OBJECT_LIST):
# Init the display manager
ManagedWindow.__init__(self,uistate,track,self)
# Init the Window
gtk.Window.__init__(self)
self._dbstate = dbstate
self._uistate = dbstate
self._track = track
self._object_list = object_list
self._current_object_type = None
@ -70,7 +83,7 @@ class ObjectSelectorWindow(gtk.Window):
for object_type in object_list:
self._object_frames[object_type] = _ObjectTypeWidgets()
self.set_title("Add Person")
self.set_title(title)
# Selected object label
@ -122,8 +135,8 @@ class ObjectSelectorWindow(gtk.Window):
self.__class__.__default_border_width)
person_pixbuf = gtk.gdk.pixbuf_new_from_file("./person.svg")
flist_pixbuf = gtk.gdk.pixbuf_new_from_file("./flist.svg")
person_pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(const.rootDir,"person.svg"))
flist_pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(const.rootDir,"flist.svg"))
self._tool_list = gtk.ListStore(gtk.gdk.Pixbuf, str,int)
@ -178,7 +191,7 @@ class ObjectSelectorWindow(gtk.Window):
for object_type in object_list:
self._object_frames[object_type].frame = \
_Factories.ObjectFrameFactory().get_frame(object_type,dbstate,uistate)
_Factories.ObjectFrameFactory().get_frame(object_type,dbstate,uistate,filter_spec)
# connect signals
self._object_frames[object_type].frame.connect(
@ -246,6 +259,8 @@ class ObjectSelectorWindow(gtk.Window):
self._set_object_type(default_object_type)
self.set_default_size(700,300)
self.show()
def _set_object_type(self,selected_object_type):
# enable selected object type

View File

@ -19,8 +19,8 @@ class PersonFilterFrame(FilterFrameBase):
__default_border_width = 5
def __init__(self,dbstate,label="Filter"):
FilterFrameBase.__init__(self,label)
def __init__(self,filter_spec=None,label="Filter"):
FilterFrameBase.__init__(self,filter_spec,label)
# Gramps ID
self._id_check = gtk.CheckButton()
@ -86,12 +86,12 @@ class PersonFilterFrame(FilterFrameBase):
self._b_unknown = gtk.CheckButton("Include Unknown")
self._b_unknown.set_sensitive(False)
self._b_unknown.set_active(True)
self._b_unknown.set_active(False)
self._birth_check.connect('toggled',lambda b: self._b_edit.set_sensitive(self._birth_check.get_active()))
self._birth_check.connect('toggled',lambda b: self._b_before.set_sensitive(self._birth_check.get_active()))
self._birth_check.connect('toggled',lambda b: self._b_after.set_sensitive(self._birth_check.get_active()))
self._birth_check.connect('toggled',lambda b: self._b_unknown.set_sensitive(self._birth_check.get_active()))
#self._birth_check.connect('toggled',lambda b: self._b_unknown.set_sensitive(self._birth_check.get_active()))
self._b_inner_box = gtk.HBox()
self._b_inner_box.pack_start(self._b_before)
@ -118,12 +118,12 @@ class PersonFilterFrame(FilterFrameBase):
self._d_unknown = gtk.CheckButton("Include Unknown")
self._d_unknown.set_sensitive(False)
self._d_unknown.set_active(True)
self._d_unknown.set_active(False)
self._death_check.connect('toggled',lambda b: self._d_edit.set_sensitive(self._death_check.get_active()))
self._death_check.connect('toggled',lambda b: self._d_before.set_sensitive(self._death_check.get_active()))
self._death_check.connect('toggled',lambda b: self._d_after.set_sensitive(self._death_check.get_active()))
self._death_check.connect('toggled',lambda b: self._d_unknown.set_sensitive(self._death_check.get_active()))
#self._death_check.connect('toggled',lambda b: self._d_unknown.set_sensitive(self._death_check.get_active()))
d_inner_box = gtk.HBox()
d_inner_box.pack_start(self._d_before)
@ -257,7 +257,68 @@ class PersonFilterFrame(FilterFrameBase):
self._table.attach(self._filter_combo,self._control_col,self._control_col+1,
current_row,current_row+1,xoptions=gtk.EXPAND|gtk.FILL,yoptions=False)
def on_apply(self,button):
if filter_spec is not None:
self._set_filter(filter_spec)
def _set_filter(self,filter_spec):
if filter_spec.include_gramps_id():
self._id_check.set_active(True)
self._id_edit.set_text(filter_spec.get_gramps_id())
else:
self._id_check.set_active(False)
self._id_edit.set_text("")
if filter_spec.include_name():
self._name_check.set_active(True)
self._name_edit.set_text(filter_spec.get_name())
else:
self._name_check.set_active(False)
self._name_edit.set_text("")
if filter_spec.include_gender():
self._gender_check.set_active(True)
store = self._gender_list
it = store.get_iter_first()
while it:
if store.get(it, 1)[0] == filter_spec.get_gender():
break
it = store.iter_next(it)
if it != None:
self._gender_combo.set_active_iter(it)
else:
self._gender_check.set_active(False)
if filter_spec.include_birth():
self._birth_check.set_active(True)
self._b_edit.set_text(filter_spec.get_birth_year())
if filter_spec.get_birth_criteria() == filter_spec.__class__.BEFORE:
self._b_before.set_active(True)
self._b_after.set_active(False)
else:
self._b_before.set_active(False)
self._b_after.set_active(True)
else:
self._birth_check.set_active(False)
self._b_edit.set_text("")
if filter_spec.include_death():
self._death_check.set_active(True)
self._d_edit.set_text(filter_spec.get_death_year())
if filter_spec.get_death_criteria() == filter_spec.__class__.BEFORE:
self._d_before.set_active(True)
self._d_after.set_active(False)
else:
self._d_before.set_active(False)
self._d_after.set_active(True)
else:
self._death_check.set_active(False)
self._d_edit.set_text("")
def on_apply(self,button=None):
filter = GenericFilter.GenericFilter()
if self._id_check.get_active():

View File

@ -0,0 +1,65 @@
from _FilterSpecBase import FilterSpecBase
class PersonFilterSpec(FilterSpecBase):
BEFORE = 1
AFTER = 2
def __init__(self):
FilterSpecBase.__init__(self)
self._name = None
self._gender = None
self._birth_year = None
self._birth_criteria = self.__class__.BEFORE
self._death_year = None
self._death_criteria = self.__class__.BEFORE
def set_name(self,name):
self._name = name
def get_name(self):
return self._name
def include_name(self):
return self._name is not None
def set_gender(self,gender):
self._gender = gender
def get_gender(self):
return self._gender
def include_gender(self):
return self._gender is not None
def set_birth_year(self,year):
self._birth_year = str(year)
def get_birth_year(self):
return self._birth_year
def include_birth(self):
return self._birth_year is not None
def set_birth_criteria(self,birth_criteria):
self._birth_criteria = birth_criteria
def get_birth_criteria(self):
return self._birth_criteria
def set_death_year(self,year):
self._death_year = str(year)
def get_death_year(self):
return self._death_year
def include_death(self):
return self._death_year is not None
def set_death_criteria(self,death_criteria):
self._death_criteria = death_criteria
def get_death_criteria(self):
return self._death_criteria

View File

@ -33,12 +33,13 @@ class PersonFrame(ObjectFrameBase):
def __init__(self,
dbstate,
uistate):
uistate,
filter_spec = None):
ObjectFrameBase.__init__(self,
dbstate=dbstate,
uistate=uistate,
filter_frame = PersonFilterFrame(dbstate),
filter_frame = PersonFilterFrame(filter_spec=filter_spec),
preview_frame = PersonPreviewFrame(dbstate),
tree_frame = PersonTreeFrame(dbstate))
@ -59,6 +60,11 @@ class PersonFrame(ObjectFrameBase):
self._filter_frame.connect('apply-filter',lambda w,m: self._tree_frame.set_model(m))
# Now that the filter is connected we need to tell it to apply any
# filter_spec that may have been passed to it. We can't apply the filter
# until the connections have been made.
self._filter_frame.on_apply()
def _on_row_activated(self,widget,path,col):
(model, iter) = widget.get_selection().get_selected()
if iter and model.get_value(iter,self.__class__.__person_id_field):

View File

@ -1,9 +1,22 @@
import os.path
from xml.sax.saxutils import escape
import gtk
import gobject
from logging import getLogger
log = getLogger(".ObjectSelector")
import ImgManip
import const
from ToolTips import PersonTip
import DateHandler
def short(val,size=60):
if len(val) > size:
return "%s..." % val[0:size]
else:
return val
class PersonPreviewFrame(gtk.Frame):
@ -25,17 +38,14 @@ class PersonPreviewFrame(gtk.Frame):
self._image = gtk.Image()
# test image
self._image.set_from_file("../person.svg")
self._image.set_from_file(os.path.join(const.rootDir,"person.svg"))
# Text
label = gtk.Label()
label.set_use_markup(True)
label.set_line_wrap(True)
label.set_justify(gtk.JUSTIFY_LEFT)
label.set_alignment(xalign=0.1,yalign=0.1)
label.set_markup("<b>Name:</b> Joe Blogs\n"
"<b>b:</b> 1906\n"
"<b>d:</b> 1937\n")
label.set_alignment(xalign=0.5,yalign=0.1)
# box
box = gtk.VBox()
@ -56,6 +66,49 @@ class PersonPreviewFrame(gtk.Frame):
self.add(align)
self._label = label
def _get_text_preview(self,person):
global escape
birth_str = ""
birth_ref = person.get_birth_ref()
if birth_ref:
birth = self._dbstate.db.get_event_from_handle(birth_ref.ref)
date_str = DateHandler.get_date(birth)
if date_str != "":
birth_str = escape(date_str)
death_str = ""
death_ref = person.get_death_ref()
if death_ref:
death = self._dbstate.db.get_event_from_handle(death_ref.ref)
date_str = DateHandler.get_date(death)
if date_str != "":
death_str = escape(date_str)
s = "<span weight=\"bold\">%s</span>\n"\
" <span weight=\"normal\">%s:</span> %s\n"\
" <span weight=\"normal\">%s:</span> %s\n"\
" <span weight=\"normal\">%s:</span> %s\n"% (
_("Person"),
_("Name"),escape(person.get_primary_name().get_name()),
_("Birth"),birth_str,
_("Death"),death_str)
if len(person.get_source_references()) > 0:
psrc_ref = person.get_source_references()[0]
psrc_id = psrc_ref.get_base_handle()
psrc = self._dbstate.db.get_source_from_handle(psrc_id)
s += "\n<span weight=\"bold\">%s</span>\n"\
" <span weight=\"normal\">%s:</span> %s\n" % (
_("Primary source"),
_("Name"),
escape(short(psrc.get_title())))
return s
def set_object_from_id(self,id):
try:
person = self._dbstate.db.get_person_from_gramps_id(id)
@ -69,14 +122,18 @@ class PersonPreviewFrame(gtk.Frame):
pixbuf = ImgManip.get_thumbnail_image(mobj.get_path())
self._image.set_from_pixbuf(pixbuf)
else:
self._image.set_from_file("../person.svg")
self._image.set_from_file(os.path.join(const.rootDir,"person.svg"))
self._label.set_markup(self._get_text_preview(person))
except:
log.warn("Failed to generate preview for person", exc_info=True)
self._clear_object()
self.clear_object()
def clear_object(self):
self._image.set_from_file("../person.svg")
self._image.set_from_file(os.path.join(const.rootDir,"person.svg"))
self._label.set_markup("")
if gtk.pygtk_version < (2,8,0):
gobject.type_register(PersonPreviewFrame)

View File

@ -0,0 +1,15 @@
from gettext import gettext as _
from _ObjectSelectorWindow import ObjectSelectorWindow
from _Constants import ObjectTypes
from _PersonFilterSpec import PersonFilterSpec
class PersonSelector(ObjectSelectorWindow):
"""Provides an ObjectSelectorWindow configured for selecting a person object."""
def __init__(self,dbstate,uistate,track,filter_spec=None,title=_("Select Person")):
ObjectSelectorWindow.__init__(self,dbstate,uistate,track,
title=title,
filter_spec=filter_spec,
default_object_type = ObjectTypes.PERSON,
object_list = [ObjectTypes.PERSON])

View File

@ -28,6 +28,7 @@ import os
import locale
import signal
import gettext
import exceptions
import logging
log = logging.getLogger(".")
@ -124,6 +125,9 @@ def setup_logging():
# put a hook on to catch any completly unhandled exceptions.
def exc_hook(type, value, tb):
if type == KeyboardInterrupt:
# Ctrl-C is not a bug.
return
import traceback
log.error("Unhandled exception\n" +
"".join(traceback.format_exception(type, value, tb)))