2007-12-24 Douglas S. Blank <dblank@cs.brynmawr.edu>
* src/DataViews/_MyGrampsView.py: new DataView for Gadgets * src/DataViews/__init__.py: import things from _MyGrampsView * src/Config/_GrampsConfigKeys.py: added data-views * src/glade/gramps.glade: added My Gramps Gadget gui * src/plugins/DefaultGadgets.py: sample gadgets svn: r9573
This commit is contained in:
parent
677a0e280f
commit
50492711e8
@ -1,3 +1,10 @@
|
||||
2007-12-24 Douglas S. Blank <dblank@cs.brynmawr.edu>
|
||||
* src/DataViews/_MyGrampsView.py: new DataView for Gadgets
|
||||
* src/DataViews/__init__.py: import things from _MyGrampsView
|
||||
* src/Config/_GrampsConfigKeys.py: added data-views
|
||||
* src/glade/gramps.glade: added My Gramps Gadget gui
|
||||
* src/plugins/DefaultGadgets.py: sample gadgets
|
||||
|
||||
2007-12-24 Brian Matherly <brian@gramps-project.org>
|
||||
* src/ReportBase/Makefile.am: Add _DocReportDialog.py to the Makefile
|
||||
|
||||
|
@ -160,6 +160,7 @@ MAX_SIB_AGE_DIFF = ('behavior', 'max-sib-age-diff', 1)
|
||||
MIN_GENERATION_YEARS = ('behavior', 'min-generation-years', 1)
|
||||
AVG_GENERATION_GAP = ('behavior', 'avg-generation-gap', 1)
|
||||
GENERATION_DEPTH = ('behavior', 'generation-depth', 1)
|
||||
DATA_VIEWS = ('interface','data-views', 2)
|
||||
|
||||
default_value = {
|
||||
DEFAULT_SOURCE : False,
|
||||
@ -276,4 +277,7 @@ default_value = {
|
||||
MIN_GENERATION_YEARS : 13,
|
||||
AVG_GENERATION_GAP : 20,
|
||||
GENERATION_DEPTH : 15,
|
||||
DATA_VIEWS: ('PersonView,RelationshipView,FamilyListView,PedigreeView,'
|
||||
'EventView,SourceView,PlaceView,MediaView,RepositoryView,'
|
||||
'NoteView'),
|
||||
}
|
||||
|
381
src/DataViews/_MyGrampsView.py
Normal file
381
src/DataViews/_MyGrampsView.py
Normal file
@ -0,0 +1,381 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
# $Id: _MyGrampsView.py $
|
||||
|
||||
"""
|
||||
MyGrampsView interface
|
||||
"""
|
||||
|
||||
__author__ = "Doug Blank"
|
||||
__revision__ = "$Revision: $"
|
||||
|
||||
import gtk
|
||||
import PageView
|
||||
import const
|
||||
import gobject
|
||||
import traceback
|
||||
import time
|
||||
|
||||
AVAILABLE_GADGETS = []
|
||||
|
||||
debug = False
|
||||
|
||||
def register_gadget(data_dict):
|
||||
global AVAILABLE_GADGETS
|
||||
AVAILABLE_GADGETS.append(data_dict)
|
||||
|
||||
def register(**data):
|
||||
if "type" in data:
|
||||
if data["type"].lower() == "gadget":
|
||||
register_gadget(data)
|
||||
|
||||
def get_gadget_opts(name, opts):
|
||||
for data in AVAILABLE_GADGETS:
|
||||
if data.get("name", None) == name:
|
||||
my_data = data.copy()
|
||||
my_data.update(opts)
|
||||
return my_data
|
||||
return {}
|
||||
|
||||
def make_requested_gadget(viewpage, name, opts, dbstate):
|
||||
for data in AVAILABLE_GADGETS:
|
||||
if data.get("name", None) == name:
|
||||
gui = GuiGadget(viewpage, dbstate, **opts)
|
||||
if opts.get("content", None):
|
||||
opts["content"](gui)
|
||||
return gui
|
||||
return None
|
||||
|
||||
class Gadget(object):
|
||||
def __init__(self, gui):
|
||||
self._idle_id = 0
|
||||
self._generator = None
|
||||
self._need_to_update = False
|
||||
self.gui = gui
|
||||
self.dbstate = gui.dbstate
|
||||
self.init()
|
||||
self.dbstate.connect('database-changed', self._db_changed)
|
||||
self.dbstate.connect('active-changed', self.active_changed)
|
||||
|
||||
def active_changed(self, handle):
|
||||
pass
|
||||
|
||||
def _db_changed(self, dbstate):
|
||||
if debug: print "%s is _connecting" % self.gui.title
|
||||
self.dbstate = dbstate
|
||||
self.gui.dbstate = dbstate
|
||||
self.db_changed()
|
||||
self.update()
|
||||
|
||||
def db_changed(self):
|
||||
if debug: print "%s is connecting" % self.gui.title
|
||||
pass
|
||||
|
||||
def init(self): # once, constructor
|
||||
pass
|
||||
|
||||
def main(self): # once per db open
|
||||
pass
|
||||
|
||||
def update(self, *handles):
|
||||
self.main()
|
||||
if self._idle_id != 0:
|
||||
if debug: print "%s interrupt!" % self.gui.title
|
||||
self.interrupt()
|
||||
if debug: print "%s creating generator" % self.gui.title
|
||||
self._generator = self.background()
|
||||
if debug: print "%s adding to gobject" % self.gui.title
|
||||
self._idle_id = gobject.idle_add(self._updater,
|
||||
priority=gobject.PRIORITY_LOW)
|
||||
|
||||
def background(self): # return false finishes
|
||||
if debug: print "%s dummy" % self.gui.title
|
||||
yield False
|
||||
|
||||
def interrupt(self):
|
||||
"""
|
||||
Force the generator to stop running.
|
||||
"""
|
||||
if self._idle_id == 0:
|
||||
if debug: print "%s removing from gobject" % self.gui.title
|
||||
gobject.source_remove(self._idle_id)
|
||||
self._idle_id = 0
|
||||
|
||||
def _updater(self):
|
||||
if debug: print "%s _updater" % self.gui.title
|
||||
try:
|
||||
retval = self._generator.next()
|
||||
if retval == False:
|
||||
self._idle_id = 0
|
||||
return retval
|
||||
except StopIteration:
|
||||
self._idle_id = 0
|
||||
return False
|
||||
except Exception, e:
|
||||
#self._error = e
|
||||
traceback.print_exc()
|
||||
self._idle_id = 0
|
||||
return False
|
||||
except:
|
||||
self._idle_id = 0
|
||||
return False
|
||||
|
||||
def append_text(self, text):
|
||||
self.gui.buffer.insert_at_cursor(text)
|
||||
|
||||
def clear_text(self):
|
||||
self.gui.buffer.set_text('')
|
||||
|
||||
def set_text(self, text):
|
||||
self.gui.buffer.set_text(text)
|
||||
|
||||
|
||||
class GuiGadget:
|
||||
"""
|
||||
Class that handles the plugin interfaces for the MyGrampsView.
|
||||
"""
|
||||
TARGET_TYPE_FRAME = 80
|
||||
LOCAL_DRAG_TYPE = 'GADGET'
|
||||
LOCAL_DRAG_TARGET = (LOCAL_DRAG_TYPE, 0, TARGET_TYPE_FRAME)
|
||||
def __init__(self, viewpage, dbstate, title, **kwargs):
|
||||
self.viewpage = viewpage
|
||||
self.dbstate = dbstate
|
||||
self.title = title
|
||||
########## Set defaults
|
||||
self.expand = kwargs.get("expand", False)
|
||||
self.height = kwargs.get("height", 200)
|
||||
self.column = kwargs.get("column", -1)
|
||||
self.row = kwargs.get("row", -1)
|
||||
self.state = kwargs.get("state", "maximized")
|
||||
##########
|
||||
self.xml = gtk.glade.XML(const.GLADE_FILE, 'gvgadget', "gramps")
|
||||
self.mainframe = self.xml.get_widget('gvgadget')
|
||||
self.textview = self.xml.get_widget('gvtextview')
|
||||
self.buffer = self.textview.get_buffer()
|
||||
self.scrolledwindow = self.xml.get_widget('gvscrolledwindow')
|
||||
self.titlelabel = self.xml.get_widget('gvtitle')
|
||||
self.titlelabel.set_text("<b><i>%s</i></b>" % self.title)
|
||||
self.titlelabel.set_use_markup(True)
|
||||
self.xml.get_widget('gvclose').connect('clicked', self.close)
|
||||
self.xml.get_widget('gvstate').connect('clicked', self.change_state)
|
||||
self.xml.get_widget('gvproperties').connect('clicked',
|
||||
self.set_properties)
|
||||
self.xml.get_widget('gvcloseimage').set_from_stock(gtk.STOCK_CLOSE,
|
||||
gtk.ICON_SIZE_MENU)
|
||||
self.xml.get_widget('gvstateimage').set_from_stock(gtk.STOCK_REMOVE,
|
||||
gtk.ICON_SIZE_MENU)
|
||||
self.xml.get_widget('gvpropertiesimage').set_from_stock(gtk.STOCK_PROPERTIES,
|
||||
gtk.ICON_SIZE_MENU)
|
||||
|
||||
# source:
|
||||
drag = self.xml.get_widget('gvproperties')
|
||||
drag.drag_source_set(gtk.gdk.BUTTON1_MASK,
|
||||
[GuiGadget.LOCAL_DRAG_TARGET],
|
||||
gtk.gdk.ACTION_COPY)
|
||||
|
||||
def close(self, obj):
|
||||
del self.viewpage.gadget_map[self.title]
|
||||
del self.viewpage.frame_map[str(self.mainframe)]
|
||||
self.mainframe.destroy()
|
||||
|
||||
def change_state(self, obj):
|
||||
if self.state == "maximized":
|
||||
self.scrolledwindow.hide()
|
||||
self.xml.get_widget('gvstateimage').set_from_stock(gtk.STOCK_ADD,
|
||||
gtk.ICON_SIZE_MENU)
|
||||
self.state = "minimized"
|
||||
else:
|
||||
self.scrolledwindow.show()
|
||||
self.xml.get_widget('gvstateimage').set_from_stock(gtk.STOCK_REMOVE,
|
||||
gtk.ICON_SIZE_MENU)
|
||||
self.state = "maximized"
|
||||
if self.expand:
|
||||
column = self.mainframe.get_parent() # column
|
||||
expand,fill,padding,pack = column.query_child_packing(self.mainframe)
|
||||
column.set_child_packing(self.mainframe,(not expand),fill,padding,pack)
|
||||
|
||||
def set_properties(self, obj):
|
||||
self.expand = not self.expand
|
||||
if self.state == "maximized":
|
||||
column = self.mainframe.get_parent() # column
|
||||
expand,fill,padding,pack = column.query_child_packing(self.mainframe)
|
||||
column.set_child_packing(self.mainframe,self.expand,fill,padding,pack)
|
||||
|
||||
def append_text(self, text):
|
||||
self.buffer.insert_at_cursor(text)
|
||||
|
||||
def clear_text(self):
|
||||
self.buffer.set_text('')
|
||||
|
||||
def set_text(self, text):
|
||||
self.buffer.set_text(text)
|
||||
|
||||
|
||||
|
||||
class MyGrampsView(PageView.PageView):
|
||||
"""
|
||||
MyGrampsView interface
|
||||
"""
|
||||
|
||||
def __init__(self, dbstate, uistate):
|
||||
"""
|
||||
Creates a MyGrampsView, with the current dbstate and uistate
|
||||
"""
|
||||
PageView.PageView.__init__(self, _('My Gramps'), dbstate, uistate)
|
||||
self.column_count = 3
|
||||
|
||||
def change_db(self, event):
|
||||
"""
|
||||
"""
|
||||
# FIXME: remove/add widgets from new db ini file
|
||||
pass
|
||||
|
||||
def build_widget(self):
|
||||
"""
|
||||
Builds the container widget for the interface. Must be overridden by the
|
||||
the base class. Returns a gtk container widget.
|
||||
"""
|
||||
frame = gtk.ScrolledWindow()
|
||||
frame.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
hbox = gtk.HBox(homogeneous=True)
|
||||
# Set up drag and drop
|
||||
frame.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
|
||||
gtk.DEST_DEFAULT_HIGHLIGHT |
|
||||
gtk.DEST_DEFAULT_DROP,
|
||||
[('GADGET', 0, 80)],
|
||||
gtk.gdk.ACTION_COPY)
|
||||
frame.connect('drag_drop', self.drop_widget)
|
||||
|
||||
frame.add_with_viewport(hbox)
|
||||
# Create the columns:
|
||||
self.columns = []
|
||||
for i in range(self.column_count):
|
||||
self.columns.append(gtk.VBox())
|
||||
hbox.pack_start(self.columns[-1],expand=True)
|
||||
# Load the gadgets
|
||||
self.gadget_map = {} # title->gadget
|
||||
self.frame_map = {} # frame->gadget
|
||||
# FIXME
|
||||
# get the user's gadgets from .gramps
|
||||
# and/or from open database
|
||||
# Load the user's gadgets:
|
||||
for (name, opts) in [('Stats Gadget', {}),
|
||||
('Top Surnames Gadget', {}),
|
||||
#('Families Gadget', {}),
|
||||
#('Families Gadget', {"title": "My Peeps"}),
|
||||
('Hello World Gadget', {}),
|
||||
('Log Gadget', {}),
|
||||
#('Events Gadget', {}),
|
||||
]:
|
||||
all_opts = get_gadget_opts(name, opts)
|
||||
if "title" not in all_opts:
|
||||
all_opts["title"] = "Untitled Gadget"
|
||||
# uniqify titles:
|
||||
unique = all_opts["title"]
|
||||
cnt = 1
|
||||
while unique in self.gadget_map:
|
||||
unique = all_opts["title"] + ("-%d" % cnt)
|
||||
cnt += 1
|
||||
all_opts["title"] = unique
|
||||
if all_opts["title"] not in self.gadget_map:
|
||||
g = make_requested_gadget(self, name, all_opts, self.dbstate)
|
||||
if g:
|
||||
self.gadget_map[all_opts["title"]] = g
|
||||
self.frame_map[str(g.mainframe)] = g
|
||||
else:
|
||||
print "Can't make gadget of type '%s'." % name
|
||||
else:
|
||||
print "Ignoring duplicate named gadget '%s'." % all_opts["title"]
|
||||
|
||||
# put the gadgets where they go:
|
||||
cnt = 0
|
||||
for gadget in self.gadget_map.values():
|
||||
# see if the user wants this in a particular location:
|
||||
# and if there are that many columns
|
||||
if gadget.column >= 0 and gadget.column < len(self.columns):
|
||||
pos = gadget.column
|
||||
else:
|
||||
# else, spread them out:
|
||||
pos = cnt % len(self.columns)
|
||||
if gadget.state == "minimized": # starts max, change to min it
|
||||
gadget.state = "maximized"
|
||||
gadget.change_state(gadget) # minimize it
|
||||
# to make as big as possible, set to True:
|
||||
self.columns[pos].pack_start(gadget.mainframe, expand=gadget.expand)
|
||||
# set height on gadget.scrolledwindow here:
|
||||
gadget.scrolledwindow.set_size_request(-1, gadget.height)
|
||||
cnt += 1
|
||||
return frame
|
||||
|
||||
def drop_widget(self, source, context, x, y, timedata):
|
||||
button = context.get_source_widget()
|
||||
hbox = button.get_parent()
|
||||
mainframe = hbox.get_parent()
|
||||
rect = source.get_allocation()
|
||||
sx, sy = rect.width, rect.height
|
||||
# first, find column:
|
||||
col = 0
|
||||
for i in range(len(self.columns)):
|
||||
if x < (sx/len(self.columns) * (i + 1)):
|
||||
col = i
|
||||
break
|
||||
fromcol = mainframe.get_parent()
|
||||
fromcol.remove(mainframe)
|
||||
# now find where to insert in column:
|
||||
stack = []
|
||||
for gframe in self.columns[col]:
|
||||
rect = gframe.get_allocation()
|
||||
if y < (rect.y + 15): # starts at 0, this allows insert before
|
||||
self.columns[col].remove(gframe)
|
||||
stack.append(gframe)
|
||||
maingadget = self.frame_map[str(mainframe)]
|
||||
if maingadget.state == "maximized":
|
||||
expand = maingadget.expand
|
||||
else:
|
||||
expand = False
|
||||
self.columns[col].pack_start(mainframe, expand=expand)
|
||||
for gframe in stack:
|
||||
gadget = self.frame_map[str(gframe)]
|
||||
if gadget.state == "maximized":
|
||||
expand = gadget.expand
|
||||
else:
|
||||
expand = False
|
||||
self.columns[col].pack_start(gframe, expand=expand)
|
||||
return True
|
||||
|
||||
def define_actions(self):
|
||||
"""
|
||||
Defines the UIManager actions. Called by the ViewManager to set up the
|
||||
View. The user typically defines self.action_list and
|
||||
self.action_toggle_list in this function.
|
||||
"""
|
||||
return ''
|
||||
|
||||
def get_stock(self):
|
||||
"""
|
||||
Returns image associated with the view, which is used for the
|
||||
icon for the button.
|
||||
"""
|
||||
return 'gtk-home'
|
||||
|
||||
def build_tree(self):
|
||||
return
|
||||
|
@ -26,6 +26,7 @@ Package init for the DataView package
|
||||
__author__ = "Don Allingham"
|
||||
__revision__ = "$Revision: $"
|
||||
|
||||
from _MyGrampsView import MyGrampsView, register, Gadget
|
||||
from _PersonView import PersonView
|
||||
from _RelationView import RelationshipView
|
||||
from _FamilyList import FamilyListView
|
||||
@ -37,11 +38,14 @@ from _MediaView import MediaView
|
||||
from _RepositoryView import RepositoryView
|
||||
from _NoteView import NoteView
|
||||
|
||||
def get_views():
|
||||
"""
|
||||
Returns a list of PageView instances
|
||||
"""
|
||||
return [
|
||||
try:
|
||||
import Config
|
||||
DATA_VIEWS = eval("["+Config.get(Config.DATA_VIEWS)+"]")
|
||||
except:
|
||||
# Fallback if bad config line, or if no Config system
|
||||
print "Ignoring malformed 'data-views' entry"
|
||||
DATA_VIEWS = [
|
||||
#MyGrampsView,
|
||||
PersonView,
|
||||
RelationshipView,
|
||||
FamilyListView,
|
||||
@ -54,3 +58,9 @@ def get_views():
|
||||
RepositoryView,
|
||||
NoteView,
|
||||
]
|
||||
|
||||
def get_views():
|
||||
"""
|
||||
Returns a list of PageView instances, in order
|
||||
"""
|
||||
return DATA_VIEWS
|
||||
|
@ -16202,4 +16202,175 @@ Very High</property>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
<widget class="GtkWindow" id="gvwin">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">window2</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">False</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<property name="focus_on_map">True</property>
|
||||
<property name="urgency_hint">False</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkFrame" id="gvgadget">
|
||||
<property name="border_width">10</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="label_yalign">0.5</property>
|
||||
<property name="shadow_type">GTK_SHADOW_IN</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="gvscrolledwindow">
|
||||
<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_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTextView" id="gvtextview">
|
||||
<property name="visible">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="overwrite">False</property>
|
||||
<property name="accepts_tab">True</property>
|
||||
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap_mode">GTK_WRAP_WORD</property>
|
||||
<property name="cursor_visible">True</property>
|
||||
<property name="pixels_above_lines">0</property>
|
||||
<property name="pixels_below_lines">0</property>
|
||||
<property name="pixels_inside_wrap">0</property>
|
||||
<property name="left_margin">0</property>
|
||||
<property name="right_margin">0</property>
|
||||
<property name="indent">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox144">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="gvproperties">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="gvpropertiesimage">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-properties</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="gvstate">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="gvstateimage">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-remove</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="gvclose">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="gvcloseimage">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-close</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="gvtitle">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><b><i>Gramplet</i></b></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</property>
|
||||
<property name="xpad">7</property>
|
||||
<property name="ypad">9</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="type">label_item</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
|
262
src/plugins/DefaultGadgets.py
Normal file
262
src/plugins/DefaultGadgets.py
Normal file
@ -0,0 +1,262 @@
|
||||
from DataViews import register, Gadget
|
||||
from BasicUtils import name_displayer
|
||||
import DateHandler
|
||||
import gen.lib
|
||||
|
||||
#
|
||||
# Hello World, in Gramps Gadgets
|
||||
#
|
||||
# First, you need a function or class that takes a single argument
|
||||
# a GuiGadget:
|
||||
|
||||
def init(gui):
|
||||
gui.set_text("Hello world!")
|
||||
|
||||
# In this function, you can do some things to update the gadget,
|
||||
# like set text of the main scroll window.
|
||||
|
||||
# Then, you need to register the gadget:
|
||||
|
||||
register(type="gadget", # case in-senstitive keyword "gadget"
|
||||
name="Hello World Gadget", # gadget name, unique among gadgets
|
||||
height = 20,
|
||||
content = init, # function/class; takes state and container
|
||||
title="Sample Gadget", # default title, user changeable, unique
|
||||
)
|
||||
|
||||
# There are a number of arguments that you can provide, including:
|
||||
# name, height, content, title, expand, minimized
|
||||
|
||||
# Hereare a couple of other examples, with their register lines at the
|
||||
# bottom:
|
||||
|
||||
def make_family_content(gui):
|
||||
gui.set_text("Families:")
|
||||
|
||||
def make_event_content(gui):
|
||||
gui.set_text("Events:")
|
||||
|
||||
# Here is a Gadget object. It has a number of methods possibilities:
|
||||
# init- run once, on construction
|
||||
# db_changed- run when db-changed is triggered
|
||||
# main- run once per db, main process (for fast code)
|
||||
# background- run once per db, main process (for slow code)
|
||||
# active_changed- run when active-changed is triggered
|
||||
|
||||
# You can also call update to run main and background
|
||||
|
||||
class LogGadget(Gadget):
|
||||
def db_changed(self):
|
||||
self.dbstate.connect('person-add', self.log_person_add)
|
||||
self.dbstate.connect('person-delete', self.log_person_delete)
|
||||
self.dbstate.connect('person-update', self.log_person_update)
|
||||
self.dbstate.connect('family-add', self.log_family_add)
|
||||
self.dbstate.connect('family-delete', self.log_family_delete)
|
||||
self.dbstate.connect('family-update', self.log_family_update)
|
||||
|
||||
def active_changed(self, handle):
|
||||
self.log_active_changed(handle)
|
||||
|
||||
def init(self):
|
||||
self.set_text("Log for this Session\n--------------------\n")
|
||||
|
||||
def log_person_add(self, handles):
|
||||
self.append_text("person-add: ")
|
||||
self.get_person(handles)
|
||||
def log_person_delete(self, handles):
|
||||
self.append_text("person-delete: ")
|
||||
self.get_person(handles)
|
||||
def log_person_update(self, handles):
|
||||
self.append_text("person-update: ")
|
||||
self.get_person(handles)
|
||||
def log_family_add(self, handles):
|
||||
self.append_text("family-add: %s" % handles)
|
||||
def log_family_delete(self, handles):
|
||||
self.append_text("family-delete: %s" % handles)
|
||||
def log_family_update(self, handles):
|
||||
self.append_text("family-update: %s" % handles)
|
||||
def log_active_changed(self, handles):
|
||||
self.append_text("active-changed: ")
|
||||
self.get_person([handles])
|
||||
|
||||
def get_person(self, handles):
|
||||
for person_handle in handles:
|
||||
person = self.dbstate.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
self.append_text(name_displayer.display(person))
|
||||
else:
|
||||
self.append_text(person_handle)
|
||||
self.append_text("\n")
|
||||
|
||||
class TopSurnamesGadget(Gadget):
|
||||
def main(self):
|
||||
self.set_text("Processing...\n")
|
||||
|
||||
def background(self):
|
||||
people = self.dbstate.get_person_handles(sort_handles=False)
|
||||
surnames = {}
|
||||
cnt = 0
|
||||
for person_handle in people:
|
||||
person = self.dbstate.get_person_from_handle(person_handle)
|
||||
if person:
|
||||
surname = person.get_primary_name().get_surname().strip()
|
||||
surnames[surname] = surnames.get(surname, 0) + 1
|
||||
if cnt % 500 == 0:
|
||||
yield True
|
||||
cnt += 1
|
||||
total_people = cnt
|
||||
surname_sort = []
|
||||
total = 0
|
||||
cnt = 0
|
||||
for surname in surnames:
|
||||
surname_sort.append( (surnames[surname], surname) )
|
||||
total += surnames[surname]
|
||||
if cnt % 500 == 0:
|
||||
yield True
|
||||
cnt += 1
|
||||
total_surnames = cnt
|
||||
surname_sort.sort(lambda a,b: -cmp(a,b))
|
||||
line = 0
|
||||
### All done!
|
||||
self.set_text("")
|
||||
for (count, surname) in surname_sort:
|
||||
self.append_text(" %s, %d%%\n" %
|
||||
(surname, int((float(count)/total) * 100)))
|
||||
line += 1
|
||||
if line >= 10:
|
||||
break
|
||||
self.append_text("\nTotal unique surnames: %d\n" % total_surnames)
|
||||
self.append_text("Total people: %d" % total_people)
|
||||
|
||||
class StatsGadget(Gadget):
|
||||
def db_changed(self):
|
||||
self.dbstate.connect('person-add', self.update)
|
||||
self.dbstate.connect('person-delete', self.update)
|
||||
self.dbstate.connect('family-add', self.update)
|
||||
self.dbstate.connect('family-delete', self.update)
|
||||
|
||||
def background(self):
|
||||
self.set_text("Processing...")
|
||||
database = self.dbstate
|
||||
personList = database.get_person_handles(sort_handles=False)
|
||||
familyList = database.get_family_handles()
|
||||
|
||||
with_photos = 0
|
||||
total_photos = 0
|
||||
incomp_names = 0
|
||||
disconnected = 0
|
||||
missing_bday = 0
|
||||
males = 0
|
||||
females = 0
|
||||
unknowns = 0
|
||||
bytes = 0
|
||||
namelist = []
|
||||
notfound = []
|
||||
|
||||
pobjects = len(database.get_media_object_handles())
|
||||
for photo_id in database.get_media_object_handles():
|
||||
photo = database.get_object_from_handle(photo_id)
|
||||
try:
|
||||
bytes = bytes + posixpath.getsize(photo.get_path())
|
||||
except:
|
||||
notfound.append(photo.get_path())
|
||||
|
||||
cnt = 0
|
||||
for person_handle in personList:
|
||||
person = database.get_person_from_handle(person_handle)
|
||||
if not person:
|
||||
continue
|
||||
length = len(person.get_media_list())
|
||||
if length > 0:
|
||||
with_photos = with_photos + 1
|
||||
total_photos = total_photos + length
|
||||
|
||||
person = database.get_person_from_handle(person_handle)
|
||||
name = person.get_primary_name()
|
||||
if name.get_first_name() == "" or name.get_surname() == "":
|
||||
incomp_names = incomp_names + 1
|
||||
if (not person.get_main_parents_family_handle()) and (not len(person.get_family_handle_list())):
|
||||
disconnected = disconnected + 1
|
||||
birth_ref = person.get_birth_ref()
|
||||
if birth_ref:
|
||||
birth = database.get_event_from_handle(birth_ref.ref)
|
||||
if not DateHandler.get_date(birth):
|
||||
missing_bday = missing_bday + 1
|
||||
else:
|
||||
missing_bday = missing_bday + 1
|
||||
if person.get_gender() == gen.lib.Person.FEMALE:
|
||||
females = females + 1
|
||||
elif person.get_gender() == gen.lib.Person.MALE:
|
||||
males = males + 1
|
||||
else:
|
||||
unknowns += 1
|
||||
if name.get_surname() not in namelist:
|
||||
namelist.append(name.get_surname())
|
||||
if cnt % 500 == 0:
|
||||
yield True
|
||||
cnt += 1
|
||||
|
||||
text = _("Individuals") + "\n"
|
||||
text = text + "----------------------------\n"
|
||||
text = text + "%s: %d\n" % (_("Number of individuals"),len(personList))
|
||||
text = text + "%s: %d\n" % (_("Males"),males)
|
||||
text = text + "%s: %d\n" % (_("Females"),females)
|
||||
text = text + "%s: %d\n" % (_("Individuals with unknown gender"),unknowns)
|
||||
text = text + "%s: %d\n" % (_("Individuals with incomplete names"),incomp_names)
|
||||
text = text + "%s: %d\n" % (_("Individuals missing birth dates"),missing_bday)
|
||||
text = text + "%s: %d\n" % (_("Disconnected individuals"),disconnected)
|
||||
text = text + "\n%s\n" % _("Family Information")
|
||||
text = text + "----------------------------\n"
|
||||
text = text + "%s: %d\n" % (_("Number of families"),len(familyList))
|
||||
text = text + "%s: %d\n" % (_("Unique surnames"),len(namelist))
|
||||
text = text + "\n%s\n" % _("Media Objects")
|
||||
text = text + "----------------------------\n"
|
||||
text = text + "%s: %d\n" % (_("Individuals with media objects"),with_photos)
|
||||
text = text + "%s: %d\n" % (_("Total number of media object references"),total_photos)
|
||||
text = text + "%s: %d\n" % (_("Number of unique media objects"),pobjects)
|
||||
text = text + "%s: %d %s\n" % (_("Total size of media objects"),bytes,\
|
||||
_("bytes"))
|
||||
|
||||
if len(notfound) > 0:
|
||||
text = text + "\n%s\n" % _("Missing Media Objects")
|
||||
text = text + "----------------------------\n"
|
||||
for p in notfound:
|
||||
text = text + "%s\n" % p
|
||||
self.set_text(text)
|
||||
|
||||
|
||||
register(type="gadget",
|
||||
name="Families Gadget",
|
||||
height=300,
|
||||
content = make_family_content,
|
||||
title="Favorite Families",
|
||||
)
|
||||
|
||||
register(type="gadget",
|
||||
name="Events Gadget",
|
||||
height=100,
|
||||
content = make_event_content,
|
||||
title="Favorite Events",
|
||||
)
|
||||
|
||||
register(type="gadget",
|
||||
name="Top Surnames Gadget",
|
||||
height=230,
|
||||
content = TopSurnamesGadget,
|
||||
title="Top 10 Surnames",
|
||||
)
|
||||
|
||||
register(type="gadget",
|
||||
name="Stats Gadget",
|
||||
height=230,
|
||||
content = StatsGadget,
|
||||
title="Stats",
|
||||
)
|
||||
|
||||
register(type="gadget",
|
||||
name="Log Gadget",
|
||||
height=230,
|
||||
content = LogGadget,
|
||||
title="Session Log",
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user