* src/GenericFilter.py: Support for categories and descriptions

for filters
* src/Plugins.py: removed unused variable
* src/gramps.desktop: compatible with desktop-file-validate
* src/rule.glade: new add/edit filter dialog
* src/plugins/FilterEditor.py: support for spin buttons and new
dialog box


svn: r1975
This commit is contained in:
Don Allingham 2003-08-10 05:28:11 +00:00
parent 6ba9a5fb5b
commit 8ca2c2af54
5 changed files with 525 additions and 98 deletions

View File

@ -95,6 +95,12 @@ class Rule:
def name(self):
return 'None'
def category(self):
return _('Miscellaneous filters')
def description(self):
return _('No description')
def check(self):
return len(self.list) == len(self.labels)
@ -121,6 +127,12 @@ class Everyone(Rule):
def name(self):
return 'Everyone'
def category(self):
return _('General filters')
def description(self):
return _('Matches everyone in the database')
def apply(self,db,p):
return 1
@ -130,13 +142,19 @@ class Everyone(Rule):
#
#-------------------------------------------------------------------------
class HasIdOf(Rule):
"""Rule that checks for a person with a specific GID"""
"""Rule that checks for a person with a specific GRAMPS ID"""
labels = [ _('ID:') ]
def name(self):
return 'Has the Id'
def description(self):
return _("Matches the person with a specified GRAMPS ID")
def category(self):
return _('General filters')
def apply(self,db,p):
return p.getId() == self.list[0]
@ -153,6 +171,12 @@ class IsFemale(Rule):
def name(self):
return 'Is a female'
def category(self):
return _('General filters')
def description(self):
return _('Matches all females')
def apply(self,db,p):
return p.getGender() == RelLib.Person.female
@ -175,6 +199,12 @@ class IsDescendantOf(Rule):
def name(self):
return 'Is a descendant of'
def category(self):
return _('Descendant filters')
def description(self):
return _('Matches all descendants for the specified person')
def apply(self,db,p):
self.orig = p
@ -185,8 +215,6 @@ class IsDescendantOf(Rule):
return self.map.has_key(p.getId())
def init_list(self,p,first):
# if self.map.has_key(p.getId()) == 1:
# loop_error(self.orig,p)
if not first:
self.map[p.getId()] = 1
@ -211,6 +239,12 @@ class IsDescendantOfFilterMatch(IsDescendantOf):
def name(self):
return 'Is a descendant of filter match'
def category(self):
return _('Descendant filters')
def description(self):
return _("Matches people that are descendants of someone matched by a filter")
def apply(self,db,p):
self.orig = p
@ -241,6 +275,13 @@ class IsLessThanNthGenerationDescendantOf(Rule):
def name(self):
return 'Is a descendant of person not more than N generations away'
def category(self):
return _('Descendant filters')
def description(self):
return _("Matches people that are descendants of a specified person "
"not more than N generations away")
def apply(self,db,p):
self.orig = p
@ -251,8 +292,6 @@ class IsLessThanNthGenerationDescendantOf(Rule):
return self.map.has_key(p.getId())
def init_list(self,p,gen):
# if self.map.has_key(p.getId()) == 1:
# loop_error(self.orig,p)
if gen:
self.map[p.getId()] = 1
if gen >= int(self.list[1]):
@ -281,6 +320,13 @@ class IsMoreThanNthGenerationDescendantOf(Rule):
def name(self):
return 'Is a descendant of person at least N generations away'
def description(self):
return _("Matches people that are descendants of a specified "
"person at least N generations away")
def category(self):
return _("Descendant filters")
def apply(self,db,p):
self.orig = p
@ -291,8 +337,6 @@ class IsMoreThanNthGenerationDescendantOf(Rule):
return self.map.has_key(p.getId())
def init_list(self,p,gen):
# if self.map.has_key(p.getId()) == 1:
# loop_error(self.orig,p)
if gen >= int(self.list[1]):
self.map[p.getId()] = 1
@ -331,8 +375,6 @@ class IsChildOfFilterMatch(Rule):
return self.map.has_key(p.getId())
def init_list(self,p):
# if self.map.has_key(p.getId()) == 1:
# loop_error(self.orig,p)
for fam in p.getFamilyList():
for child in fam.getChildList():
self.map[child.getId()] = 1
@ -351,14 +393,19 @@ class IsDescendantFamilyOf(Rule):
def name(self):
return "Is a descendant family member of"
def category(self):
return _('Descendant filters')
def description(self):
return _("Matches people that are descendants or the spouse "
"of a descendant of a specified person")
def apply(self,db,p):
self.map = {}
self.orig = p
return self.search(p,1)
def search(self,p,val):
# if self.map.has_key(p.getId()):
# loop_error(self.orig,p)
if p.getId() == self.list[0]:
self.map[p.getId()] = 1
return 1
@ -982,26 +1029,6 @@ class IsSpouseOfFilterMatch(Rule):
return 1
return 0
#-------------------------------------------------------------------------
#
# loop_error
#
#-------------------------------------------------------------------------
# def loop_error(p1,p2):
# p1name = p1.getPrimaryName().getName()
# p2name = p2.getPrimaryName().getName()
# p1id = p1.getId()
# p2id = p2.getId()
# raise Errors.FilterError(_("Loop detected while applying filter"),
# _("A relationship loop was detected between %(person1_name)s "
# "[%(person1_id)s] and %(person2_name)s [%(person2_id)s]. "
# "This is probably due to an error in the "
# "database.") % {
# "person1_name" : p1name,
# "person1_id" : p1id,
# "person2_name" : p2name,
# "person2_id" : p2id})
#-------------------------------------------------------------------------
#
# GenericFilter

View File

@ -232,7 +232,6 @@ class PluginDialog:
key_list.reverse()
prev = None
self.ilist = []
for key in key_list:
data = item_hash[key]
node = self.store.insert_after(None,prev)

View File

@ -1,10 +1,11 @@
[Desktop Entry]
Encoding=Legacy-Mixed
Name=GRAMPS Genealogy System
Name[sv]=GRAMPS
Comment=Manage genealogical information, perform genealogical research and analysis
Comment[sv]=Ett släktforskningsprogram
Exec=gramps
Icon=gramps.png
Terminal=0
Terminal=false
Type=Application
StartupNotify=true

View File

@ -26,6 +26,7 @@ import gtk
import gtk.glade
import string
import os
import gobject
import const
import GenericFilter
@ -42,6 +43,112 @@ _name2list = {
_('Relationship type:') : const.familyRelations,
}
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class MyInteger(gtk.SpinButton):
def __init__(self,min,max):
gtk.SpinButton.__init__(self)
self.set_adjustment(gtk.Adjustment(min,min,max,1))
self.show()
def get_text(self):
return str(self.get_value())
def set_text(self,val):
self.set_value(val)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class MyFilters(gtk.Combo):
def __init__(self,filters):
gtk.Combo.__init__(self)
flist = []
for f in filters:
flist.append(f.get_name())
flist.sort()
if len(flist) == 0:
self.ok = 0
self.set_sensitive(0)
else:
self.ok = 1
AutoComp.AutoCombo(self,flist)
self.show()
def get_text(self):
if self.ok:
return self.entry.get_text()
else:
return ""
def set_text(self,val):
self.entry.set_text(val)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class MyPlaces(gtk.Combo):
def __init__(self,places):
gtk.Combo.__init__(self)
AutoComp.AutoCombo(self,places)
self.show()
def get_text(self):
return self.entry.get_text()
def set_text(self,val):
self.entry.set_text(val)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class MySelect(gtk.Combo):
def __init__(self,list):
gtk.Combo.__init__(self)
list.sort()
self.set_popdown_strings(list)
self.set_value_in_list(1,0)
self.entry.set_editable(0)
self.show()
def get_text(self):
return self.entry.get_text()
def set_text(self,val):
self.entry.set_text(val)
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class MyEntry(gtk.Entry):
def __init__(self):
gtk.Entry.__init__(self)
self.show()
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class FilterEditor:
def __init__(self,filterdb,db):
self.db = db
@ -167,7 +274,7 @@ class FilterEditor:
self.ok.set_sensitive(len(name) != 0)
def select_row(self,obj):
store,iter = self.clist.get_selected()
store,iter = self.rlist.get_selected()
if iter:
self.edit_btn.set_sensitive(1)
self.del_btn.set_sensitive(1)
@ -206,15 +313,15 @@ class FilterEditor:
self.top.destroy()
def on_add_clicked(self,obj):
self.edit_rule(None)
self.edit_rule2(None,_('Add Rule'))
def on_edit_clicked(self,obj):
store,iter = self.rlist.get_selected()
if iter:
d = self.rlist.get_object(iter)
self.edit_rule(d)
self.edit_rule2(d,_('Edit Rule'))
def edit_rule(self,val):
def edit_rule2(self,val,label):
self.pmap = {}
self.add_places = []
@ -222,19 +329,19 @@ class FilterEditor:
self.pmap[p.get_title()] = p
self.active_rule = val
self.rule = gtk.glade.XML(const.filterFile,'add_rule')
self.rule_top = self.rule.get_widget('add_rule')
self.frame = self.rule.get_widget('values')
self.rname = self.rule.get_widget('rule_name')
self.rule = gtk.glade.XML(const.filterFile,'rule_editor')
self.rule_top = self.rule.get_widget('rule_editor')
self.valuebox = self.rule.get_widget('valuebox')
self.rname = self.rule.get_widget('ruletree')
self.rule_name = self.rule.get_widget('rulename')
Utils.set_titles(self.rule_top, self.rule.get_widget('title'),
_('Add rule'))
Utils.set_titles(self.rule_top, self.rule.get_widget('title'),label)
self.notebook = gtk.Notebook()
self.notebook.set_show_tabs(0)
self.notebook.set_show_border(0)
self.notebook.show()
self.frame.add(self.notebook)
self.valuebox.add(self.notebook)
self.page_num = 0
self.page = []
self.name2page = {}
@ -248,7 +355,10 @@ class FilterEditor:
vallist = []
tlist = []
self.page.append((name,cname,vallist,tlist))
table = gtk.Table(2,len(arglist))
table = gtk.Table(3,len(arglist))
table.set_border_width(6)
table.set_col_spacings(6)
table.set_row_spacings(6)
table.show()
pos = 0
l2 = gtk.Label(name)
@ -266,54 +376,98 @@ class FilterEditor:
l.set_alignment(1,0.5)
l.show()
if v == 'Place:':
t = gtk.Combo()
AutoComp.AutoCombo(t,self.pmap.keys())
tlist.append(t.entry)
t = MyPlaces(self.pmap.keys())
elif v == 'Number of generations:':
t = MyInteger(1,32)
elif v == 'Filter name:':
t = gtk.Combo()
flist = []
for f in self.filterdb.get_filters():
flist.append(f.get_name())
flist.sort()
AutoComp.AutoCombo(t,flist)
tlist.append(t.entry)
t = MyFilters(self.filterdb.get_filters())
elif _name2list.has_key(v1):
t = gtk.Combo()
_name2list[v1].sort()
t.set_popdown_strings(_name2list[v1])
t.set_value_in_list(1,0)
t.entry.set_editable(0)
tlist.append(t.entry)
t = MySelect(_name2list[v1])
else:
t = gtk.Entry()
tlist.append(t)
t.show()
table.attach(l,0,1,pos,pos+1,gtk.FILL,0,5,5)
table.attach(t,1,2,pos,pos+1,gtk.EXPAND|gtk.FILL,0,5,5)
t = MyEntry()
tlist.append(t)
table.attach(l,1,2,pos,pos+1,gtk.FILL,0,5,5)
table.attach(t,2,3,pos,pos+1,gtk.EXPAND|gtk.FILL,0,5,5)
pos = pos + 1
self.notebook.append_page(table,gtk.Label(name))
self.name2page[name] = self.page_num
self.page_num = self.page_num + 1
self.page_num = 0
self.rname.disable_activate()
self.rname.list.clear_items(0,-1)
self.rname.list.append_items(list)
for v in map.keys():
self.rname.set_item_string(map[_(v)],_(v))
self.store = gtk.TreeStore(gobject.TYPE_STRING)
self.selection = self.rname.get_selection()
col = gtk.TreeViewColumn(_('Rule Name'),gtk.CellRendererText(),text=0)
self.rname.append_column(col)
self.rname.set_model(self.store)
prev = None
last_top = None
top_level = {}
top_node = {}
#
# If editing a rule, get the name so that we can select it later
#
sel_node = None
if self.active_rule:
page = self.name2page[self.active_rule.name()]
self.rname.entry.set_text(self.active_rule.name())
sel_name = self.active_rule.name()
else:
sel_name = ""
for v in map.keys():
filter = GenericFilter.tasks[v]([None])
category = filter.category()
if top_level.has_key(category):
top_level[category].append(v)
else:
top_level[category] = [v]
top_node[category] = self.store.insert_after(None,last_top)
last_top = top_node[category]
self.store.set(last_top,0,category)
node = self.store.insert_after(top_node[category],prev)
self.store.set(node,0,v)
#
# if this is an edit rule, save the node
if v == sel_name:
sel_node = node
if sel_node:
self.selection.select_iter(sel_node)
page = self.name2page[sel_name]
self.notebook.set_current_page(page)
self.display_values(sel_name)
(n,c,v,t) = self.page[page]
r = self.active_rule.values()
for i in range(0,len(t)):
t[i].set_text(r[i])
self.rname.entry.connect('changed',self.rule_changed)
self.selection.connect('changed', self.on_node_selected)
self.rule.get_widget('ok').connect('clicked',self.rule_ok)
self.rule.get_widget('cancel').connect('clicked',self.rule_cancel)
def on_node_selected(self,obj):
"""Updates the informational display on the right hand side of
the dialog box with the description of the selected report"""
store,iter = self.selection.get_selected()
if iter:
try:
key = store.get_value(iter,0)
self.display_values(key)
except:
self.valuebox.set_sensitive(0)
self.rule_name.set_text(_('No rule selected'))
def display_values(self,key):
page = self.name2page[key]
self.notebook.set_current_page(page)
self.valuebox.set_sensitive(1)
self.rule_name.set_text(key)
filter = GenericFilter.tasks[key]([None])
self.rule.get_widget('description').set_text(filter.description())
def on_delete_clicked(self,obj):
store,iter = self.rlist.get_selected()
if iter:
@ -321,29 +475,25 @@ class FilterEditor:
self.filter.delete_rule(filter)
self.draw_rules()
def rule_changed(self,obj):
def rule_ok(self,obj):
name = self.rule_name.get_text()
try:
page = self.name2page[obj.get_text()]
self.notebook.set_current_page(page)
page = self.name2page[name]
(n,c,v,t) = self.page[page]
value_list = []
for x in t:
value_list.append(x.get_text())
new_rule = c(value_list)
store,iter = self.rlist.get_selected()
if iter:
rule = self.rlist.get_object(iter)
self.filter.delete_rule(rule)
self.filter.add_rule(new_rule)
self.draw_rules()
self.rule_top.destroy()
except:
pass
def rule_ok(self,obj):
name = self.rname.entry.get_text()
page = self.name2page[name]
(n,c,v,t) = self.page[page]
value_list = []
for x in t:
value_list.append(x.get_text())
new_rule = c(value_list)
store,iter = self.rlist.get_selected()
if iter:
rule = self.rlist.get_object(iter)
self.filter.delete_rule(rule)
self.filter.add_rule(new_rule)
self.draw_rules()
self.rule_top.destroy()
def rule_cancel(self,obj):
self.rule_top.destroy()
@ -356,9 +506,7 @@ class ShowResults:
Utils.set_titles(self.top, self.glade.get_widget('title'),
_('Filter Test'))
self.glade.signal_autoconnect({
'on_close_clicked' : self.close,
})
self.glade.signal_autoconnect({'on_close_clicked' : self.close})
n = ""
for p in plist:

View File

@ -2,6 +2,7 @@
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<requires lib="gnome"/>
<widget class="GtkDialog" id="define_filter">
<property name="visible">True</property>
@ -1086,4 +1087,255 @@
</child>
</widget>
<widget class="GtkDialog" id="rule_editor">
<property name="visible">True</property>
<property name="title" translatable="yes"></property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">640</property>
<property name="default_height">500</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox5">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area5">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="response_id">-5</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox9">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="title">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">6</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHPaned" id="hpaned1">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">0</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="ruletree">
<property name="width_request">250</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">False</property>
</widget>
</child>
</widget>
<packing>
<property name="shrink">False</property>
<property name="resize">False</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="valuebox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label27">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Selected Rule&lt;/b&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="rulename">
<property name="visible">True</property>
<property name="label" translatable="yes">No rule selected</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">12</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">6</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label25">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Description&lt;/b&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="description">
<property name="visible">True</property>
<property name="label" translatable="yes">No description</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="xpad">12</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">6</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label24">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Values&lt;/b&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>