New Descendant Graph report, along with various report enhancements

svn: r679
This commit is contained in:
Don Allingham 2002-01-05 19:55:03 +00:00
parent de6c6c15c4
commit 49be38d769
19 changed files with 786 additions and 135 deletions

View File

@ -6,7 +6,10 @@ Python 1.5.2 or greater
Gnome 1.2 or greater
PyGnome 1.0.53 or greater
PyXML 0.6.2 or greater (http://sourceforge.net/project/showfiles.php?group_id=6473)
If you are using python 1.5.2, you may also need PyXML 0.6.2 or
greater. Many distributions already provide this, but if your
installation does not have it, you can get it from
http://sourceforge.net/project/showfiles.php?group_id=6473
Building on non-Linux systems: i18n support and GNU make
--------------------------------------------------------

View File

@ -136,6 +136,9 @@ class DrawDoc:
def draw_box(self,style,text,x,y):
pass
def write_at(self,style,text,x,y):
pass
def draw_line(self,style,x1,y1,x2,y2):
pass

View File

@ -40,7 +40,7 @@ try:
except:
no_pil = 1
t_header_line_re = re.compile(r"(.*)<TITLE>.*</TITLE>(.*)", re.DOTALL)
t_header_line_re = re.compile(r"(.*)<TITLE>(.*)</TITLE>(.*)", re.DOTALL|re.IGNORECASE|re.MULTILINE)
#------------------------------------------------------------------------
#
@ -48,10 +48,10 @@ t_header_line_re = re.compile(r"(.*)<TITLE>.*</TITLE>(.*)", re.DOTALL)
#
#------------------------------------------------------------------------
_top = [
'<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n',
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">\n',
'<HTML>\n',
'<HEAD>\n',
' <META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n',
' <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">\n',
' <TITLE>\n',
' </TITLE>\n',
' <STYLE type="text/css">\n',
@ -155,7 +155,7 @@ class HtmlDoc(TextDoc):
self.base = os.path.dirname(self.filename)
self.f = open(self.filename,"w")
self.f.write(self.file_header % self.title)
self.f.write(self.file_header)
self.f.write(self.style_declaration)
def build_header(self):
@ -163,9 +163,9 @@ class HtmlDoc(TextDoc):
match = t_header_line_re.match(top)
if match:
m = match.groups()
self.file_header = '%s<TITLE>%%s</TITLE>%s\n' % (m[0],m[1])
self.file_header = '%s<TITLE>%s</TITLE>%s\n' % (m[0],m[1],m[2])
else:
self.file_header = None
self.file_header = top
def build_style_declaration(self):
fl2txt = utils.fl2txt

View File

@ -96,6 +96,14 @@ class PdfDrawDoc(DrawDoc):
lines = string.split(text,'\n')
self.center_print(lines,font,x*cm,y*cm,w,h)
def write_at(self,style,text,x,y):
p = self.style_list[style]
font = p.get_font()
self.f.setStrokeColor(make_color(font.get_color()))
self.left_print(text,font,x*cm,y*cm)
def center_print(self,lines,font,x,y,w,h):
l = len(lines)
size = font.get_size()
@ -118,9 +126,31 @@ class PdfDrawDoc(DrawDoc):
for text in lines:
self.f.drawCentredString(start_x,start_y,text)
start_y = start_y + size*1.2
start_y = start_y + size*1.2
self.f.restoreState()
def left_print(self,text,font,x,y):
size = font.get_size()
start_y = y
start_x = x
self.f.saveState()
self.f.setFillColor(make_color(font.get_color()))
if font.get_type_face() == FONT_SANS_SERIF:
if font.get_bold():
self.f.setFont("Helvetica-Bold",font.get_size())
else:
self.f.setFont("Helvetica",font.get_size())
else:
if font.get_bold():
self.f.setFont("Times-Bold",font.get_size())
else:
self.f.setFont("Times-Roman",font.get_size())
self.f.drawString(start_x,start_y,text)
self.f.restoreState()
if __name__ == "__main__":

View File

@ -69,6 +69,7 @@ DOCSTRING = "d"
IMAGE = "i"
TASK = "f"
TITLE = "t"
STATUS = "s"
pymod = compile(r"^(.*)\.py$")
@ -102,10 +103,13 @@ class ReportPlugins:
def on_report_node_selected(self,obj):
doc = obj.get_data(DOCSTRING)
xpm = obj.get_data(IMAGE)
status = ": %s" % obj.get_data(STATUS)
title = obj.get_data(TITLE)
img = self.dialog.get_widget("image")
self.dialog.get_widget("description").set_text(doc)
self.dialog.get_widget("report_status").set_text(status)
self.dialog.get_widget("report_label").show()
i,m = gtk.create_pixmap_from_xpm_d(gtk.GtkWindow(),None,xpm)
img.set(i,m)
@ -162,7 +166,8 @@ def build_tree(tree,list,task):
item.set_data(TITLE,report[2])
item.set_data(DOCSTRING,report[3])
item.set_data(IMAGE,report[4])
item.set_data(STATUS,report[5])
if item_hash.has_key(report[1]):
item_hash[report[1]].append(item)
else:
@ -259,7 +264,11 @@ def register_export(task, name):
def register_import(task, name):
_imports.append((task, name))
def register_report(task, name, category=None, description=None, xpm=None):
def register_report(task, name,
category=_("Uncategorized"),
description=_("No description was provided"),
xpm=None,
status=_("Unknown")):
if xpm == None:
xpm_data = no_image()
elif type(xpm) == type([]):
@ -267,14 +276,13 @@ def register_report(task, name, category=None, description=None, xpm=None):
else:
xpm_data = xpm
if category == None:
category = _("Uncategorized")
if description == None:
description = _("No description was provided")
_reports.append((task, category, name, description, xpm_data))
_reports.append((task, category, name, description, xpm_data, status))
def register_tool(task, name, category=None, description=None, xpm=None):
def register_tool(task, name,
category=_("Uncategorized"),
description=_("No description was provided"),
xpm=None,
status=_("Unknown")):
if xpm == None:
xpm_data = no_image()
elif type(xpm) == type([]):
@ -282,12 +290,7 @@ def register_tool(task, name, category=None, description=None, xpm=None):
else:
xpm_data = xpm
if category == None:
category = _("Uncategorized")
if description == None:
description = _("No description was provided")
_tools.append((task, category, name, description, xpm_data))
_tools.append((task, category, name, description, xpm_data, status))
#-------------------------------------------------------------------------
#

View File

@ -38,8 +38,8 @@ import Config
import FindDoc
import PaperMenu
import gtk
import gnome.ui
from gtk import *
from gnome.ui import *
import libglade
#------------------------------------------------------------------------
@ -136,6 +136,10 @@ class Report:
#
#------------------------------------------------------------------------
class ReportDialog:
frame_pad = 5
border_pad = 5
def __init__(self,database,person,filename="basicreport.glade"):
"""Initialize a dialog to request that the user select options
for a basic report. The glade filename is optional. If
@ -146,27 +150,35 @@ class ReportDialog:
# Save info about who the report is about.
self.db = database
self.person = person
# Load the glade file
base = os.path.dirname(__file__)
self.glade_file = os.path.join(base, "plugins", filename)
self.topDialog = libglade.GladeXML(self.glade_file,"report_dialog")
self.output_notebook = None
self.notebook_page = 1
self.pagecount_menu = None
self.filter_combo = None
self.extra_menu = None
self.extra_textbox = None
self.pagebreak_checkbox = None
self.generations_spinbox = None
self.window = GnomeDialog('My Window',STOCK_BUTTON_OK,STOCK_BUTTON_CANCEL)
self.window.set_default(0)
self.window.button_connect(0,self.on_ok_clicked)
self.window.button_connect(1,self.on_cancel)
self.window.set_resize_mode(0)
# Set up and run the dialog. These calls are not in top down
# order when looking at the dialog box as there is some
# interaction between the various frames.
self.setup_title()
self.setup_header()
self.setup_output_notebook()
self.setup_target_frame()
self.setup_style_frame()
self.setup_format_frame()
self.setup_style_frame()
self.setup_output_notebook()
self.setup_paper_frame()
self.setup_html_frame()
self.setup_report_options_frame()
self.setup_other_frames()
self.connect_signals()
self.window.show_all()
#------------------------------------------------------------------------
#
# Customization hooks for subclasses
@ -323,10 +335,16 @@ class ReportDialog:
# Is this to be a printed report or an electronic report
# (i.e. a set of web pages)
if obj.get_data("paper") == 1:
self.output_notebook.set_page(0)
self.notebook_page = 0
else:
self.output_notebook.set_page(1)
self.notebook_page = 1
if self.output_notebook == None:
return
self.output_notebook.set_page(self.notebook_page)
# Does this report format use styles?
self.style_frame.set_sensitive(obj.get_data("styles"))
@ -340,7 +358,8 @@ class ReportDialog:
"""Set up the title bar of the dialog. This function relies
on the get_title() customization function for what the title
should be."""
self.window = self.topDialog.get_widget("report_dialog")
title = self.get_title()
self.name = self.person.getPrimaryName().getRegularName()
self.window.set_title(self.get_title())
@ -350,9 +369,12 @@ class ReportDialog:
header line should read. If no customization function is
supplied by the subclass, the default is to use the full name
of the currently selected person."""
name = self.person.getPrimaryName().getRegularName()
self.header = self.topDialog.get_widget("header_label")
self.header.set_text(self.get_header(name))
title = self.get_header(self.name)
label = GtkLabel(title)
label.set_usize(450,10)
self.window.vbox.pack_start(label,TRUE,TRUE,ReportDialog.border_pad)
self.window.vbox.add(GtkHSeparator())
def setup_target_frame(self):
"""Set up the target frame of the dialog. This function
@ -360,15 +382,37 @@ class ReportDialog:
determine whether the target is a directory or file, what the
title of any browser window should be, and what default
directory should be used."""
self.target_fileentry = self.topDialog.get_widget("fileentry1")
self.target_fileentry.set_title(self.get_target_browser_title())
# Save Frame
frame = GtkFrame(_("Save As"))
frame.set_border_width(ReportDialog.frame_pad)
hbox = GtkHBox()
hbox.set_border_width(ReportDialog.border_pad)
if (self.get_target_is_directory()):
import _gnomeui
_gnomeui.gnome_file_entry_set_directory(self.target_fileentry._o, 1)
label = GtkLabel(_("Directory"))
else:
label = GtkLabel(_("Filename"))
hbox.pack_start(label,0,0,5)
hid = self.get_stylesheet_savefile()
if hid[-4:]==".xml":
hid = hid[0:-4]
self.target_fileentry = GnomeFileEntry(hid,_("Save As"))
hbox.add(self.target_fileentry)
frame.add(hbox)
self.window.vbox.add(frame)
self.target_fileentry.set_default_path(self.get_default_directory())
if (self.get_target_is_directory()):
import _gnomeui
_gnomeui.gnome_file_entry_set_directory(self.target_fileentry._o, 1)
self.topDialog.get_widget("saveas").set_text(_("Directory"))
self.target_filename = self.topDialog.get_widget("filename")
self.target_filename.set_text(self.get_default_directory())
target_filename = self.target_fileentry.children()[0].entry
target_filename.set_text(self.get_default_directory())
# Faugh! The following line of code would allow the 'Enter'
# key in the file name box to close the dialog. However there
@ -385,9 +429,13 @@ class ReportDialog:
"""Set up the format frame of the dialog. This function
relies on the make_doc_menu() function to do all the hard
work."""
self.topDialog.get_widget("format_frame").show()
self.format_menu = self.topDialog.get_widget("format")
self.format_menu = GtkOptionMenu()
self.make_doc_menu()
frame = GtkFrame(_("Output Format"))
frame.add(self.format_menu)
frame.set_border_width(ReportDialog.frame_pad)
self.window.vbox.add(frame)
def setup_style_frame(self):
"""Set up the style frame of the dialog. This function relies
@ -395,10 +443,20 @@ class ReportDialog:
and to read in any user defined styles for this report. It
the builds a menu of all the available styles for the user to
choose from."""
self.style_frame = self.topDialog.get_widget("style_frame")
self.style_frame.show()
self.style_menu = self.topDialog.get_widget("style_menu")
# Styles Frame
self.style_frame = GtkFrame('Styles')
hbox = GtkHBox()
hbox.set_border_width(ReportDialog.border_pad)
self.style_menu = GtkOptionMenu()
hbox.pack_start(self.style_menu,TRUE,TRUE,2)
style_button = GtkButton('Style Editor')
style_button.connect('clicked',self.on_style_edit_clicked)
hbox.pack_end(style_button,0,0,2)
self.style_frame.add(hbox)
self.style_frame.set_border_width(ReportDialog.frame_pad)
self.window.vbox.add(self.style_frame)
# Build the default style set for this report.
self.default_style = StyleSheet()
self.make_default_style()
@ -416,34 +474,63 @@ class ReportDialog:
"""Set up the output notebook of the dialog. This sole
purpose of this function is to grab a pointer for later use in
the callback from when the file format is changed."""
self.output_notebook = self.topDialog.get_widget("output_notebook")
self.output_notebook = GtkNotebook()
self.paper_frame = GtkFrame('Paper Options')
self.paper_frame.set_border_width(ReportDialog.frame_pad)
self.output_notebook.append_page(self.paper_frame,GtkLabel('Paper Options'))
self.html_frame = GtkFrame('HTML Options')
self.html_frame.set_border_width(ReportDialog.frame_pad)
self.output_notebook.append_page(self.html_frame,GtkLabel('HTML Options'))
self.output_notebook.set_show_tabs(0)
self.output_notebook.set_show_border(0)
self.output_notebook.set_page(self.notebook_page)
self.window.vbox.add(self.output_notebook)
def setup_paper_frame(self):
"""Set up the paper selection frame of the dialog. This
function relies on a paper_xxx() customization functions to
determine whether the pagecount menu should appear and what
its strings should be."""
# Paper size and orientation. Always present in this frame.
self.papersize_menu = self.topDialog.get_widget("papersize")
(pagecount_map, start_text) = self.get_print_pagecount_map()
if pagecount_map:
table = GtkTable(2,4)
else:
table = GtkTable(1,4)
self.paper_frame.add(table)
self.papersize_menu = GtkOptionMenu()
self.orientation_menu = GtkOptionMenu()
l = GtkLabel(_("Size"))
pad = ReportDialog.border_pad
l.set_alignment(1.0,0.5)
table.attach(l,0,1,0,1,FILL,FILL,pad,pad)
table.attach(self.papersize_menu,1,2,0,1,xpadding=pad,ypadding=pad)
l = GtkLabel(_("Orientation"))
l.set_alignment(1.0,0.5)
table.attach(l,2,3,0,1,FILL,FILL,pad,pad)
table.attach(self.orientation_menu,3,4,0,1,xpadding=pad,ypadding=pad)
PaperMenu.make_paper_menu(self.papersize_menu)
self.orientation_menu = self.topDialog.get_widget("orientation")
PaperMenu.make_orientation_menu(self.orientation_menu)
# The optional pagecount stuff.
self.pagecount_menu = self.topDialog.get_widget("pagecount_menu")
self.pagecount_label = self.topDialog.get_widget("pagecount_label")
(pagecount_map, start_text) = self.get_print_pagecount_map()
if pagecount_map:
self.pagecount_menu = GtkOptionMenu()
myMenu = utils.build_string_optmenu(pagecount_map, start_text)
self.pagecount_menu.set_menu(myMenu)
self.pagecount_menu.show()
self.pagecount_label.show()
table.attach(GtkLabel(_("Count")),0,1,1,2,FILL,FILL,pad,pad)
table.attach(self.pagecount_menu,1,2,1,2,xpadding=pad,ypadding=pad)
def setup_html_frame(self):
"""Set up the html frame of the dialog. This sole purpose of
this function is to grab a pointer for later use in the parse
html frame function."""
self.html_fileentry = self.topDialog.get_widget("htmltemplate")
hbox = GtkHBox()
hbox.pack_start(GtkLabel("Template"))
self.html_fileentry = GnomeFileEntry(_("HTML Template"),_("Choose File"))
hbox.add(self.html_fileentry)
self.html_frame.add(hbox)
def setup_report_options_frame(self):
"""Set up the report options frame of the dialog. This
@ -452,53 +539,88 @@ class ReportDialog:
this box. *All* of these items are optional, although the
generations fields and the filter combo box are used in most
(but not all) dialog boxes."""
self.topDialog.get_widget("options_frame").show()
# Set up the generations spin and page break checkbox
(use_gen, use_break) = self.get_report_generations()
self.generations_spinbox = self.topDialog.get_widget("generations")
self.pagebreak_checkbox = self.topDialog.get_widget("pagebreak")
if use_gen:
self.topDialog.get_widget("gen_label").show()
self.generations_spinbox.set_value(use_gen)
self.generations_spinbox.show()
else:
use_break = 0
if use_break:
self.pagebreak_checkbox.show()
# Now the filter combo
self.filter_combo = self.topDialog.get_widget("filter_combo")
filter_strings = self.get_report_filter_strings()
(em_label, extra_map, preset, em_tip) = self.get_report_extra_menu_info()
(et_label, string, et_tip) = self.get_report_extra_textbox_info()
row = 0
max_rows = 0
if use_gen:
max_rows = max_rows + 1
if use_break:
max_rows = max_rows + 1
if filter_strings:
self.topDialog.get_widget("filter_label").show()
max_rows = max_rows + 1
if extra_map:
max_rows = max_rows + 1
if string:
max_rows = max_rows + 1
if max_rows == 0:
return
frame = GtkFrame(_("Report Options"))
frame.set_border_width(ReportDialog.frame_pad)
self.window.vbox.add(frame)
table = GtkTable(2,max_rows)
frame.add(table)
pad = ReportDialog.border_pad
if filter_strings:
self.filter_combo = GtkCombo()
l = GtkLabel("Filter")
l.set_alignment(1.0,0.5)
table.attach(l,0,1,row,row+1,FILL,FILL,pad,pad)
table.attach(self.filter_combo,1,2,row,row+1,xpadding=pad,ypadding=pad)
filter_strings.sort()
self.filter_combo.set_popdown_strings(filter_strings)
self.filter_combo.show()
row = row + 1
# Set up the generations spin and page break checkbox
if use_gen:
self.generations_spinbox = GtkSpinButton(digits=0)
self.generations_spinbox.set_numeric(1)
adjustment = GtkAdjustment(use_gen,1,31,1,0)
self.generations_spinbox.set_adjustment(adjustment)
adjustment.value_changed()
l = GtkLabel(_("Generations"))
l.set_alignment(1.0,0.5)
table.attach(l,0,1,row,row+1,FILL,FILL,pad,pad)
table.attach(self.generations_spinbox,1,2,row,row+1,xpadding=pad,ypadding=pad)
row = row + 1
if use_break:
self.pagebreak_checkbox = GtkCheckButton(_("Page break between generations"))
table.attach(self.pagebreak_checkbox,1,2,row,row+1,xpadding=pad,ypadding=pad)
row = row + 1
# Now the "extra" option menu
self.extra_menu_label = self.topDialog.get_widget("extra_menu_label")
self.extra_menu = self.topDialog.get_widget("extra_menu")
(label, extra_map, preset, tip) = self.get_report_extra_menu_info()
if extra_map:
self.extra_menu_label.set_text(label)
self.extra_menu_label.show()
self.extra_menu_label = GtkLabel(em_label)
self.extra_menu_label.set_alignment(1.0,0.5)
self.extra_menu = GtkOptionMenu()
myMenu = utils.build_string_optmenu(extra_map, preset)
self.extra_menu.set_menu(myMenu)
self.extra_menu.set_sensitive(len(extra_map) > 1)
self.add_tooltip(self.extra_menu,tip)
self.extra_menu.show()
self.add_tooltip(self.extra_menu,em_tip)
table.attach(self.extra_menu_label,0,1,row,row+1,FILL,FILL,pad,pad)
table.attach(self.extra_menu,1,2,row,row+1,xpadding=pad,ypadding=pad)
row = row + 1
# Now the "extra" text box
self.extra_textbox_label = self.topDialog.get_widget("extra_textbox_label")
self.extra_textbox = self.topDialog.get_widget("extra_textbox")
(label, string, tip) = self.get_report_extra_textbox_info()
if string:
self.extra_textbox_label.set_text(label)
self.extra_textbox_label.show()
self.extra_textbox_label = GtkLabel(et_label)
self.extra_textbox_label.set_alignment(1.0,0)
self.extra_textbox = GtkText()
self.extra_textbox.insert_defaults(string)
self.add_tooltip(self.extra_textbox,tip)
self.topDialog.get_widget("extra_scrolledwindow").show()
self.extra_textbox.set_editable(1)
self.add_tooltip(self.extra_textbox,et_tip)
table.attach(self.extra_textbox_label,0,1,row,row+1,FILL,FILL,pad,pad)
table.attach(self.extra_textbox,1,2,row,row+1,xpadding=pad,ypadding=pad)
row = row + 1
# self.topDialog.get_widget("extra_scrolledwindow").show()
def setup_other_frames(self):
"""Do nothing. This sole purpose of this function is to give
@ -506,19 +628,6 @@ class ReportDialog:
that are unique to that specific report."""
pass
def connect_signals(self):
"""Connect the signal handlers for this dialog. The signal
handlers in this default function will handle all of the items
in the basic report dialog. Most items do not interact with
each other, and their vales will be read back when the user
clicks the OK button. This dialog only need be sub-classed if
there is interaction between/within any additional frames
added in a subclass."""
self.topDialog.signal_autoconnect({
"destroy_passed_object" : utils.destroy_passed_object,
"on_style_edit_clicked" : self.on_style_edit_clicked,
"on_ok_clicked" : self.on_ok_clicked
})
#------------------------------------------------------------------------
#
@ -558,7 +667,10 @@ class ReportDialog:
it has enabled. This is for simplicity of programming."""
self.paper = self.papersize_menu.get_menu().get_active().get_data("i")
self.orien = self.orientation_menu.get_menu().get_active().get_data("i")
self.pagecount = self.pagecount_menu.get_menu().get_active().get_data("d")
if self.pagecount_menu == None:
self.pagecount = 0
else:
self.pagecount = self.pagecount_menu.get_menu().get_active().get_data("d")
def parse_html_frame(self):
"""Parse the html frame of the dialog. Save the user selected
@ -575,11 +687,31 @@ class ReportDialog:
whether or not they are displayed on the screen. The subclass
will know which ones it has enabled. This is for simplicity
of programming."""
self.max_gen = self.generations_spinbox.get_value_as_int()
self.pg_brk = self.pagebreak_checkbox.get_active()
self.filter = self.filter_combo.entry.get_text()
self.report_menu = self.extra_menu.get_menu().get_active().get_data("d")
self.report_text = string.split(self.extra_textbox.get_chars(0,-1),'\n')
if self.generations_spinbox:
self.max_gen = self.generations_spinbox.get_value_as_int()
else:
self.max_gen = 0
if self.pagebreak_checkbox:
self.pg_brk = self.pagebreak_checkbox.get_active()
else:
self.pg_brk = 0
if self.filter_combo:
self.filter = self.filter_combo.entry.get_text()
else:
self.filter = ""
if self.extra_menu:
self.report_menu = self.extra_menu.get_menu().get_active().get_data("d")
else:
self.report_menu = None
if self.extra_textbox:
self.report_text = string.split(self.extra_textbox.get_chars(0,-1),'\n')
else:
self.report_text = ""
def parse_other_frames(self):
"""Do nothing. This sole purpose of this function is to give
@ -599,6 +731,9 @@ class ReportDialog:
menu for selecting a style."""
StyleListDisplay(self.style_sheet_list,self.build_style_menu,None)
def on_cancel(self, obj):
self.window.destroy()
def on_ok_clicked(self, obj):
"""The user is satisfied with the dialog choices. Validate
the output file name before doing anything else. If there is
@ -623,7 +758,7 @@ class ReportDialog:
self.make_report()
# Clean up the dialog object
utils.destroy_passed_object(obj)
self.window.destroy()
#------------------------------------------------------------------------
#

View File

@ -233,9 +233,9 @@ class StyleEditor:
else:
p.set_alignment(PARA_ALIGN_JUSTIFY)
p.set_right_margin(utils.txt2fl(self.top.get_widget("rmargin").get_text()))
p.set_left_margin(utils.txt2fl(self.top.get_widget("lmargin").get_text()))
p.set_padding(utils.txt2fl(self.top.get_widget("pad").get_text()))
p.set_right_margin(float(self.top.get_widget("rmargin").get_text()))
p.set_left_margin(float(self.top.get_widget("lmargin").get_text()))
p.set_padding(float(self.top.get_widget("pad").get_text()))
p.set_top_border(self.top.get_widget("tborder").get_active())
p.set_left_border(self.top.get_widget("lborder").get_active())
p.set_right_border(self.top.get_widget("rborder").get_active())

View File

@ -455,10 +455,10 @@ class StyleSheetList:
rm = float(p.get_right_margin())
lm = float(p.get_left_margin())
fi = float(p.get_first_indent())
f.write('rmargin="%s" ' % utils.fl2txt("%.3f",rm))
f.write('lmargin="%s" ' % utils.fl2txt("%.3f",lm))
f.write('first="%s" ' % utils.fl2txt("%.3f",fi))
f.write('pad="%s" ' % utils.fl2txt("%.3f",p.get_padding()))
f.write('rmargin="%.3f" ' % rm)
f.write('lmargin="%.3f" ' % lm)
f.write('first="%.3f" ' % fi)
f.write('pad="%.3f" ' % p.get_padding())
f.write('bgcolor="#%02x%02x%02x" ' % p.get_background_color())
f.write('level="%d" ' % p.get_header_level())
f.write('align="%d" ' % p.get_alignment())
@ -479,8 +479,8 @@ class StyleSheetList:
parser.parse(self.file)
except IOError:
pass
except:
print "could not parse file"
# except:
# print "could not parse file"
#------------------------------------------------------------------------
#
@ -541,10 +541,10 @@ class SheetParser(handler.ContentHandler):
self.f.set_underline(int(attrs['underline']))
self.f.set_color(cnv2color(attrs['color']))
elif tag == "para":
self.p.set_right_margin(utils.txt2fl(attrs['rmargin']))
self.p.set_left_margin(utils.txt2fl(attrs['lmargin']))
self.p.set_first_indent(utils.txt2fl(attrs['first']))
self.p.set_padding(utils.txt2fl(attrs['pad']))
self.p.set_right_margin(float(attrs['rmargin']))
self.p.set_left_margin(float(attrs['lmargin']))
self.p.set_first_indent(float(attrs['first']))
self.p.set_padding(float(attrs['pad']))
self.p.set_alignment(int(attrs['align']))
self.p.set_right_border(int(attrs['rborder']))
self.p.set_header_level(int(attrs['level']))

View File

@ -439,6 +439,53 @@
<fill>True</fill>
</child>
</widget>
<widget>
<class>GtkHBox</class>
<name>hbox3</name>
<homogeneous>True</homogeneous>
<spacing>0</spacing>
<child>
<padding>0</padding>
<expand>False</expand>
<fill>True</fill>
</child>
<widget>
<class>GtkLabel</class>
<name>report_label</name>
<visible>False</visible>
<label>Report Status</label>
<justify>GTK_JUSTIFY_CENTER</justify>
<wrap>False</wrap>
<xalign>1</xalign>
<yalign>0.5</yalign>
<xpad>3</xpad>
<ypad>3</ypad>
<child>
<padding>0</padding>
<expand>True</expand>
<fill>True</fill>
</child>
</widget>
<widget>
<class>GtkLabel</class>
<name>report_status</name>
<label></label>
<justify>GTK_JUSTIFY_CENTER</justify>
<wrap>False</wrap>
<xalign>0</xalign>
<yalign>0.5</yalign>
<xpad>0</xpad>
<ypad>0</ypad>
<child>
<padding>0</padding>
<expand>True</expand>
<fill>True</fill>
</child>
</widget>
</widget>
</widget>
</widget>
</widget>

View File

@ -388,6 +388,7 @@ register_report(
report,
_("Ancestor Chart"),
category=_("Graphical Reports"),
status=(_("Beta")),
description=_("Produces a graphical ancestral tree graph"),
xpm=get_xpm_image()
)

View File

@ -372,6 +372,7 @@ register_report(
report,
_("Ahnentafel Report"),
category=_("Text Reports"),
status=(_("Beta")),
description= _("Produces a textual ancestral report"),
xpm=get_xpm_image()
)

View File

@ -0,0 +1,434 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000 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
#
"Generate files/Descendant Report"
import GraphLayout
from FontScale import string_width
from DrawDoc import *
from Report import *
from TextDoc import *
import Config
import libglade
import gtk
import string
import intl
_ = intl.gettext
_sep = 0.5
#------------------------------------------------------------------------
#
# pt2cm - convert points to centimeters
#
#------------------------------------------------------------------------
def pt2cm(pt):
return (float(pt)/72.0)*2.54
class DescendantReport:
#--------------------------------------------------------------------
#
#
#
#--------------------------------------------------------------------
def __init__(self,database,display,person,output,doc):
self.doc = doc
self.doc.creator(database.getResearcher().getName())
self.map = {}
self.text = {}
self.start = person
self.output = output
self.box_width = 0
self.height = 0
self.lines = 0
self.display = display
plist = database.getPersonMap().values()
self.layout = GraphLayout.DescendLine(plist,person)
(self.v,self.e) = self.layout.layout()
self.text = {}
for (p,x,y) in self.v:
self.text[p] = []
n = p.getPrimaryName().getRegularName()
N = p.getPrimaryName().getName()
b = p.getBirth().getDate()
d = p.getDeath().getDate()
B = p.getBirth().getPlaceName()
D = p.getDeath().getPlaceName()
i = "%s" % p.getId()
for line in self.display:
line = string.replace(line,"$n",n)
line = string.replace(line,"$N",N)
line = string.replace(line,"$b",b)
line = string.replace(line,"$B",B)
line = string.replace(line,"$d",d)
line = string.replace(line,"$D",D)
line = string.replace(line,"$i",i)
line = string.replace(line,"$$",'$')
self.text[p].append(line)
self.font = self.doc.style_list["Normal"].get_font()
for line in self.text[p]:
new_width = string_width(self.font,line)
self.box_width = max(self.box_width,new_width)
self.lines = max(self.lines,len(self.text[p]))
def write_report(self):
self.calc()
maxx,maxy = self.layout.max_size()
maxx = int(maxx)
maxy = int(maxy)
cols = ((maxx-1)/self.maxx)
rows = ((maxy-1)/self.maxy)
self.pg = []
self.ln = []
for i in range(rows+1):
self.pg.append([None]*(cols+1))
self.ln.append([None]*(cols+1))
for (p,x,y) in self.v:
r = int((y-1)/self.maxy)
c = int((x-1)/self.maxx)
nx = x - (self.maxx)*c
ny = y - (self.maxy)*r
l = self.pg[r]
if l[c] == None:
l[c] = [(p,nx,ny)]
else:
l[c].append((p,nx,ny))
for (x1,y1,x2,y2) in self.e:
r1 = int((y1-1)/self.maxy)
c1 = int((x1-1)/self.maxx)
r2 = int((y2-1)/self.maxy)
c2 = int((x2-1)/self.maxx)
nx1 = x1 - (self.maxx)*c1
nx2 = x2 - (self.maxx)*c2
ny1 = y1 - (self.maxy)*r1
ny2 = y2 - (self.maxy)*r2
if r1 == r2:
if c1 == c2:
l = self.ln[r1][c1]
if l == None:
self.ln[r1][c1] = [(nx1,ny1,nx2,ny2)]
else:
l.append((nx1,ny1,nx2,ny2))
else:
l1 = self.ln[r1][c1]
l2 = self.ln[r2][c2]
if l1 == None:
self.ln[r1][c1] = [(nx1,ny1,-nx2,ny2)]
else:
l1.append((nx1,ny1,-nx2,ny2))
if l2 == None:
self.ln[r2][c2] = [(-nx2,ny2,nx2,ny2)]
else:
l2.append((-nx2,ny2,nx2,ny2))
for c in range(c1+1,c2):
if self.ln[r1][c]:
self.ln[r1][c].append((nx1,-ny1,nx2,-ny2))
else:
self.ln[r1][c] = [(nx1,-ny1,nx2,-ny2)]
else:
if c1 == c2:
l1 = self.ln[r1][c1]
l2 = self.ln[r2][c2]
if l1 == None:
self.ln[r1][c1] = [(nx1,ny1,nx2,-ny2)]
else:
l1.append((nx1,ny1,nx2,-ny2))
if l2 == None:
self.ln[r2][c2] = [(nx1,-ny2,nx2,ny2)]
else:
l2.append((nx1,-ny2,nx2,ny2))
for r in range(r1+1,r2):
if self.ln[r][c1]:
self.ln[r][c1].append((nx1,-ny1,nx2,-ny2))
else:
self.ln[r][c1] = [(nx1,-ny1,nx2,-ny2)]
else:
l1 = self.ln[r1][c1]
l2 = self.ln[r2][c2]
l3 = self.ln[r2][c1]
if l1 == None:
self.ln[r1][c1] = [(nx1,ny1,-nx2,-ny2)]
else:
l1.append((nx1,ny1,-nx2,-ny2))
if l2 == None:
self.ln[r2][c2] = [(-nx1,ny2,nx2,ny2)]
else:
l2.append((-nx1,ny2,nx2,ny2))
if l3 == None:
self.ln[r2][c1] = [(nx1,-ny2,-nx2,ny2)]
else:
l3.append((nx1,-ny2,-nx2,ny2))
try:
self.doc.open(self.output)
except:
print "Document open failure"
for r in range(len(self.pg)):
for c in range(len(self.pg[r])):
self.print_page(self.pg[r][c],self.ln[r][c],r,c)
try:
self.doc.close()
except:
print "Document close failure"
#--------------------------------------------------------------------
#
# calc - calculate the maximum width that a box needs to be. From
# that and the page dimensions, calculate the proper place to put
# the elements on a page.
#
#--------------------------------------------------------------------
def calc(self):
width = 0
self.height = self.lines*pt2cm(1.25*self.font.get_size())
self.box_width = pt2cm(self.box_width+20)
start = self.doc.get_right_margin()
self.maxx = int(self.doc.get_usable_width()/(self.box_width+_sep))
self.maxy = int(self.doc.get_usable_height()/(self.height+_sep))
g = GraphicsStyle()
g.set_height(self.height)
g.set_width(self.box_width)
g.set_paragraph_style("Normal")
g.set_shadow(1)
self.doc.add_draw_style("box",g)
g = GraphicsStyle()
self.doc.add_draw_style("line",g)
#--------------------------------------------------------------------
#
#
#
#--------------------------------------------------------------------
def print_page(self, plist,elist,r,c):
self.doc.start_page()
delta = self.doc.get_usable_width()/(self.maxx)
top = self.doc.get_top_margin()
bottom = self.doc.get_top_margin() + self.doc.get_usable_height()
left = self.doc.get_left_margin()
right = self.doc.get_right_margin() + self.doc.get_usable_width() - (2*_sep)
if plist:
for (p,x,y) in plist:
name = string.join(self.text[p],"\n")
x = (x-1)*delta + left + _sep
y = (y-1)*(self.height+_sep)+top
self.doc.draw_box("box",name,x,y)
if elist:
for (x1,y1,x2,y2) in elist:
if x1 < 0:
nx1 = left
else:
nx1 = (x1-1) * delta + left + self.box_width + _sep
if x2 < 0:
nx2 = right + _sep
else:
nx2 = (x2-1) * delta + left + _sep
if y1 < 0:
ny1 = top
else:
ny1 = (y1-1)*(self.height+_sep)+ top + self.height/2.0
if y2 < 0:
ny2 = bottom
else:
ny2 = (y2-1)*(self.height+_sep) + top + self.height/2.0
if y1 < 0 and y2 < 0:
half = (nx1+nx2)/2.0
print x1,x2,y1,y2,nx1,nx2,half
self.doc.draw_line("line",half,ny1,half,ny2)
elif ny1 != ny2:
if x1 == -x2:
self.doc.draw_line("line",nx1,ny1,nx2,ny2)
else:
half = (nx1+nx2)/2.0
if y1 > 0:
self.doc.draw_line("line",nx1,ny1,half,ny1)
self.doc.draw_line("line",half,ny1,half,ny2)
if y2 > 0:
self.doc.draw_line("line",half,ny2,nx2,ny2)
else:
self.doc.draw_line("line",nx1,ny1,nx2,ny2)
y = bottom + (self.doc.get_bottom_margin()/2.0)
if r or c:
self.doc.write_at("Normal","(%d,%d)" % (r,c), right, y)
self.doc.end_page()
class DescendantReportDialog(DrawReportDialog):
def __init__(self,database,person):
DrawReportDialog.__init__(self,database,person)
def get_title(self):
return _("Gramps - Descendant Graph")
def get_header(self,name):
return _("Descendant Graph for %s") % name
def get_target_browser_title(self):
return _("Save Descendant Graph")
def get_stylesheet_savefile(self):
return "descendant_graph.xml"
def get_report_generations(self):
"""Default to 10 generations, no page breaks."""
return (0, 0)
def get_report_extra_textbox_info(self):
"""Label the textbox and provide the default contents."""
return (_("Display Format"), "$n\nb. $b\nd. $d",
_("Allows you to customize the data in the boxes in the report"))
def make_default_style(self):
"""Make the default output style for the Ancestor Chart report."""
f = FontStyle()
f.set_size(9)
f.set_type_face(FONT_SANS_SERIF)
p = ParagraphStyle()
p.set_font(f)
self.default_style.add_style("Normal",p)
def make_report(self):
"""Create the object that will produce the Descendant Graph.
All user dialog has already been handled and the output file
opened."""
MyReport = DescendantReport(self.db,self.report_text,
self.person, self.target_path, self.doc)
MyReport.write_report()
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
def report(database,person):
DescendantReportDialog(database,person)
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
def get_xpm_image():
return [
"48 48 4 1",
" c None",
". c #FFFFFF",
"+ c #C0C0C0",
"@ c #000000",
" ",
" ",
" ",
" +++++++++++++++++++++++++++++++++++++ ",
" +...................................+ ",
" +..@@@@@@.......@@@@@@......@@@@@@..+ ",
" +..@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@..+ ",
" +..@@@@@@...@...@@@@@@...@..@@@@@@..+ ",
" +...........@............@..........+ ",
" +...........@............@..........+ ",
" +...........@............@..@@@@@@..+ ",
" +...........@............@@@@@@@@@..+ ",
" +...........@...............@@@@@@..+ ",
" +...........@.......................+ ",
" +...........@.......................+ ",
" +...........@...@@@@@@......@@@@@@..+ ",
" +...........@@@@@@@@@@@@@@@@@@@@@@..+ ",
" +...........@...@@@@@@...@..@@@@@@..+ ",
" +...........@............@..........+ ",
" +...........@............@..........+ ",
" +...........@............@..@@@@@@..+ ",
" +...........@............@@@@@@@@@..+ ",
" +...........@...............@@@@@@..+ ",
" +...........@.......................+ ",
" +...........@.......................+ ",
" +...........@...@@@@@@..............+ ",
" +...........@@@@@@@@@@..............+ ",
" +...........@...@@@@@@..............+ ",
" +...........@.......... ............+ ",
" +...........@.......................+ ",
" +...........@...@@@@@@..............+ ",
" +...........@@@@@@@@@@..............+ ",
" +...........@...@@@@@@..............+ ",
" +...........@.......................+ ",
" +...........@.......................+ ",
" +...........@...@@@@@@.....@@@@@@...+ ",
" +...........@@@@@@@@@@@@@@@@@@@@@...+ ",
" +...............@@@@@@..@..@@@@@@...+ ",
" +.......................@...........+ ",
" +.......................@...........+ ",
" +.......................@..@@@@@@...+ ",
" +.......................@@@@@@@@@...+ ",
" +..........................@@@@@@...+ ",
" +...................................+ ",
" +++++++++++++++++++++++++++++++++++++ ",
" ",
" ",
" "]
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
from Plugins import register_report
register_report(
report,
_("Descendant Graph"),
category=_("Graphical Reports"),
description=_("Generates a list of descendants of the active person"),
status=(_("Alpha")),
xpm=get_xpm_image()
)

View File

@ -273,6 +273,7 @@ register_report(
report,
_("Descendant Report"),
category=_("Text Reports"),
status=(_("Beta")),
description=_("Generates a list of descendants of the active person"),
xpm=get_xpm_image()
)

View File

@ -457,6 +457,7 @@ register_report(
report,
_("Family Group Report"),
category=_("Text Reports"),
status=(_("Beta")),
description=_("Creates a family group report, showing information on a set of parents and their children.")
)

View File

@ -284,6 +284,7 @@ from Plugins import register_report
register_report(
report,
_("Relationship graph"),
status=(_("Beta")),
category=_("Graphical Reports"),
description=get_description()
)

View File

@ -426,6 +426,7 @@ from Plugins import register_report
register_report(
report,
_("Individual Summary"),
status=(_("Beta")),
category=_("Text Reports"),
description=_("Produces a detailed report on the selected person.")
)

View File

@ -130,6 +130,7 @@ from Plugins import register_report
register_report(
report,
_("Summary of the database"),
status=(_("Beta")),
category=_("View"),
description=_("Provides a summary of the current database")
)

View File

@ -1031,6 +1031,7 @@ register_report(
report,
_("Generate Web Site"),
category=_("Web Page"),
status=(_("Beta")),
description=_("Generates web (HTML) pages for individuals, or a set of individuals.")
)

View File

@ -161,18 +161,6 @@ def destroy_passed_object(obj):
while gtk.events_pending():
gtk.mainiteration()
#-------------------------------------------------------------------------
#
# Get around python's interpretation of commas/periods in floating
# point numbers
#
#-------------------------------------------------------------------------
def txt2fl_(st):
return string.atof(string.replace(st,',','.'))
def fl2txt(fmt,val):
return string.replace(fmt % val, ',', '.')
#-------------------------------------------------------------------------
#
#