Addition of a print action on the fanchart

svn: r20305
This commit is contained in:
Benny Malengier 2012-09-01 10:11:07 +00:00
parent 86c4e407e9
commit c233adcd64
3 changed files with 200 additions and 23 deletions

View File

@ -77,7 +77,7 @@ class FanChartWidget(Gtk.DrawingArea):
""" """
Interactive Fan Chart Widget. Interactive Fan Chart Widget.
""" """
BORDER_WIDTH = 10 BORDER_EDGE_WIDTH = 10
GENCOLOR = ((229,191,252), GENCOLOR = ((229,191,252),
(191,191,252), (191,191,252),
(191,222,252), (191,222,252),
@ -131,9 +131,7 @@ class FanChartWidget(Gtk.DrawingArea):
self.set_generations(self.generations) self.set_generations(self.generations)
self.center = 50 # pixel radius of center self.center = 50 # pixel radius of center
self.gen_color = True self.gen_color = True
self.layout = self.create_pango_layout('cairo') self.set_size_request(120, 120)
self.layout.set_font_description(Pango.FontDescription("sans 8"))
self.set_size_request(120,120)
def reset_generations(self): def reset_generations(self):
""" """
@ -165,9 +163,8 @@ class FanChartWidget(Gtk.DrawingArea):
""" """
Overridden method to handle size request events. Overridden method to handle size request events.
""" """
width, height = self.layout.get_size() requisition.width = 2 * self.halfdist()
requisition.width = (width // Pango.SCALE + self.BORDER_WIDTH*4)* 1.45 requisition.height = requisition.width
requisition.height = (3 * height // Pango.SCALE + self.BORDER_WIDTH*4) * 1.2
def do_get_preferred_width(self): def do_get_preferred_width(self):
""" GTK3 uses width for height sizing model. This method will """ GTK3 uses width for height sizing model. This method will
@ -185,11 +182,8 @@ class FanChartWidget(Gtk.DrawingArea):
self.do_size_request(req) self.do_size_request(req)
return req.height, req.height return req.height, req.height
def on_draw(self, widget, cr): def nrgen(self):
""" #compute the number of generations present
The main method to do the drawing.
"""
# first do size request of what we will need
nrgen = None nrgen = None
for generation in range(self.generations - 1, 0, -1): for generation in range(self.generations - 1, 0, -1):
for p in range(len(self.data[generation])): for p in range(len(self.data[generation])):
@ -201,13 +195,35 @@ class FanChartWidget(Gtk.DrawingArea):
break break
if nrgen is None: if nrgen is None:
nrgen = 1 nrgen = 1
return nrgen
def halfdist(self):
"""
Compute the half radius of the circle
"""
nrgen = self.nrgen()
return self.pixels_per_generation * nrgen + self.center \
+ self.BORDER_EDGE_WIDTH
def on_draw(self, widget, cr, scale=1.):
"""
The main method to do the drawing.
If widget is given, we assume we draw in GTK3 and use the allocation.
To draw raw on the cairo context cr, set widget=None.
"""
# first do size request of what we will need
nrgen = self.nrgen()
halfdist = self.pixels_per_generation * nrgen + self.center halfdist = self.pixels_per_generation * nrgen + self.center
self.set_size_request(2 * halfdist, 2 * halfdist) self.set_size_request(2 * halfdist, 2 * halfdist)
#obtain the allocation #obtain the allocation
alloc = self.get_allocation() alloc = self.get_allocation()
x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height x, y, w, h = alloc.x, alloc.y, alloc.width, alloc.height
cr.translate(w/2. - self.center_xy[0], h/2. - self.center_xy[1]) cr.scale(scale, scale)
if widget:
cr.translate(w/2. - self.center_xy[0], h/2. - self.center_xy[1])
else:
cr.translate(halfdist - self.center_xy[0], halfdist - self.center_xy[1])
cr.save() cr.save()
cr.rotate(self.rotate_value * math.pi/180) cr.rotate(self.rotate_value * math.pi/180)
for generation in range(self.generations - 1, 0, -1): for generation in range(self.generations - 1, 0, -1):
@ -241,10 +257,6 @@ class FanChartWidget(Gtk.DrawingArea):
cr.arc(0, 0, self.TRANSLATE_PX, 0, 2 * math.pi) cr.arc(0, 0, self.TRANSLATE_PX, 0, 2 * math.pi)
cr.move_to(0,0) cr.move_to(0,0)
cr.fill() cr.fill()
fontw, fonth = self.layout.get_pixel_size()
cr.move_to((w - fontw - 4), (h - fonth ))
self.layout.context_changed()
PangoCairo.show_layout(cr, self.layout)
def draw_person(self, cr, gender, name, start, stop, generation, def draw_person(self, cr, gender, name, start, stop, generation,
state, parents, child, person): state, parents, child, person):
@ -276,11 +288,11 @@ class FanChartWidget(Gtk.DrawingArea):
# draw an indicator # draw an indicator
cr.move_to(0, 0) cr.move_to(0, 0)
cr.set_source_rgb(255, 255, 255) # white cr.set_source_rgb(255, 255, 255) # white
cr.arc(0, 0, radius + 10, start_rad, stop_rad) cr.arc(0, 0, radius + self.BORDER_EDGE_WIDTH, start_rad, stop_rad)
cr.fill() cr.fill()
cr.move_to(0, 0) cr.move_to(0, 0)
cr.set_source_rgb(0, 0, 0) # black cr.set_source_rgb(0, 0, 0) # black
cr.arc(0, 0, radius + 10, start_rad, stop_rad) cr.arc(0, 0, radius + self.BORDER_EDGE_WIDTH, start_rad, stop_rad)
cr.line_to(0, 0) cr.line_to(0, 0)
cr.stroke() cr.stroke()
cr.set_source_rgb(r/255., g/255., b/255.) cr.set_source_rgb(r/255., g/255., b/255.)

View File

@ -1699,5 +1699,3 @@ links (like ODF) and write PDF from that format.
cr.stroke() cr.stroke()
self._pages[page_nr].draw(cr, layout, width, dpi_x, dpi_y) self._pages[page_nr].draw(cr, layout, width, dpi_x, dpi_y)

View File

@ -34,6 +34,7 @@
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from gi.repository import Gdk from gi.repository import Gdk
from gi.repository import Gtk from gi.repository import Gtk
import cairo
from cgi import escape from cgi import escape
from gen.ggettext import gettext as _ from gen.ggettext import gettext as _
@ -52,6 +53,10 @@ from gen.errors import WindowActiveError
from gui.views.bookmarks import PersonBookmarks from gui.views.bookmarks import PersonBookmarks
from gui.editors import EditPerson from gui.editors import EditPerson
# the print settings to remember between print sessions
PRINT_SETTINGS = None
class FanChartView(NavigationView): class FanChartView(NavigationView):
""" """
The Gramplet code that realizes the FanChartWidget. The Gramplet code that realizes the FanChartWidget.
@ -110,6 +115,11 @@ class FanChartView(NavigationView):
<separator/> <separator/>
</placeholder> </placeholder>
</menu> </menu>
<menu action="EditMenu">
<placeholder name="CommonEdit">
<menuitem action="PrintView"/>
</placeholder>
</menu>
</menubar> </menubar>
<toolbar name="ToolBar"> <toolbar name="ToolBar">
<placeholder name="CommonNavigation"> <placeholder name="CommonNavigation">
@ -117,10 +127,24 @@ class FanChartView(NavigationView):
<toolitem action="Forward"/> <toolitem action="Forward"/>
<toolitem action="HomePerson"/> <toolitem action="HomePerson"/>
</placeholder> </placeholder>
<placeholder name="CommonEdit">
<toolitem action="PrintView"/>
</placeholder>
</toolbar> </toolbar>
</ui> </ui>
''' '''
def define_actions(self):
"""
Required define_actions function for PageView. Builds the action
group information required.
"""
NavigationView.define_actions(self)
self._add_action('PrintView', Gtk.STOCK_PRINT, _("_Print/Save View..."),
accel="<PRIMARY>P",
tip=_("Print or save the Fan Chart View"),
callback=self.printview)
def build_tree(self): def build_tree(self):
pass # will build when active_changes pass # will build when active_changes
@ -473,9 +497,9 @@ class FanChartView(NavigationView):
per_item.set_image(go_image) per_item.set_image(go_image)
label.set_use_markup(True) label.set_use_markup(True)
label.show() label.show()
label.set_alignment(0,0) label.set_alignment(0, 0)
per_item.add(label) per_item.add(label)
per_item.connect("activate",self.on_childmenu_changed,p_id) per_item.connect("activate", self.on_childmenu_changed, p_id)
per_item.show() per_item.show()
per_menu.append(per_item) per_menu.append(per_item)
@ -485,3 +509,146 @@ class FanChartView(NavigationView):
menu.append(item) menu.append(item)
menu.popup(None, None, None, None, event.button, event.time) menu.popup(None, None, None, None, event.button, event.time)
return 1 return 1
def printview(self, obj):
"""
Print or save the view that is currently shown
"""
widthpx = 2*(self.fan.pixels_per_generation * self.fan.nrgen()
+ self.fan.center)
prt = CairoPrintSave(widthpx, self.fan.on_draw, self.uistate.window)
prt.run()
#------------------------------------------------------------------------
#
# CairoPrintSave class
#
#------------------------------------------------------------------------
class CairoPrintSave():
"""Act as an abstract document that can render onto a cairo context.
It can render the model onto cairo context pages, according to the received
page style.
"""
def __init__(self, widthpx, drawfunc, parent):
"""
This class provides the things needed so as to dump a cairo drawing on
a context to output
"""
self.widthpx = widthpx
self.drawfunc = drawfunc
self.parent = parent
def run(self):
"""Create the physical output from the meta document.
"""
global PRINT_SETTINGS
# set up a print operation
operation = Gtk.PrintOperation()
operation.connect("draw_page", self.on_draw_page)
operation.connect("preview", self.on_preview)
operation.connect("paginate", self.on_paginate)
operation.set_n_pages(1)
#paper_size = Gtk.PaperSize.new(name="iso_a4")
## WHY no Gtk.Unit.PIXEL ?? Is there a better way to convert
## Pixels to MM ??
paper_size = Gtk.PaperSize.new_custom("custom",
"Custom Size",
round(self.widthpx * 0.2646),
round(self.widthpx * 0.2646),
Gtk.Unit.MM)
page_setup = Gtk.PageSetup()
page_setup.set_paper_size(paper_size)
#page_setup.set_orientation(Gtk.PageOrientation.PORTRAIT)
operation.set_default_page_setup(page_setup)
#operation.set_use_full_page(True)
if PRINT_SETTINGS is not None:
operation.set_print_settings(PRINT_SETTINGS)
# run print dialog
while True:
self.preview = None
res = operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, self.parent)
if self.preview is None: # cancel or print
break
# set up printing again; can't reuse PrintOperation?
operation = Gtk.PrintOperation()
operation.set_default_page_setup(page_setup)
operation.connect("draw_page", self.on_draw_page)
operation.connect("preview", self.on_preview)
operation.connect("paginate", self.on_paginate)
# set print settings if it was stored previously
if PRINT_SETTINGS is not None:
operation.set_print_settings(PRINT_SETTINGS)
# store print settings if printing was successful
if res == Gtk.PrintOperationResult.APPLY:
PRINT_SETTINGS = operation.get_print_settings()
def on_draw_page(self, operation, context, page_nr):
"""Draw a page on a Cairo context.
"""
cr = context.get_cairo_context()
pxwidth = round(context.get_width())
pxheight = round(context.get_height())
dpi_x = context.get_dpi_x()
dpi_y = context.get_dpi_y()
self.drawfunc(None, cr, scale=pxwidth/self.widthpx)
def on_paginate(self, operation, context):
"""Paginate the whole document in chunks.
We don't need this as there is only one page, however,
we provide a dummy holder here, because on_preview crashes if no
default application is set with gir 3.3.2 (typically evince not installed)!
It will provide the start of the preview dialog, which cannot be
started in on_preview
"""
finished = True
# update page number
operation.set_n_pages(1)
# start preview if needed
if self.preview:
self.preview.run()
return finished
def on_preview(self, operation, preview, context, parent):
"""Implement custom print preview functionality.
We provide a dummy holder here, because on_preview crashes if no
default application is set with gir 3.3.2 (typically evince not installed)!
"""
dlg = Gtk.MessageDialog(parent,
flags=Gtk.DialogFlags.MODAL,
type=Gtk.MessageType.WARNING,
buttons=Gtk.ButtonsType.CLOSE,
message_format=_('No preview available'))
self.preview = dlg
self.previewopr = operation
#dlg.format_secondary_markup(msg2)
dlg.set_title("Fan Chart Preview - Gramps")
dlg.connect('response', self.previewdestroy)
# give a dummy cairo context to Gtk.PrintContext,
try:
width = int(round(context.get_width()))
except ValueError:
width = 0
try:
height = int(round(context.get_height()))
except ValueError:
height = 0
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
cr = cairo.Context(surface)
context.set_cairo_context(cr, 72.0, 72.0)
return True
def previewdestroy(self, dlg, res):
self.preview.destroy()
self.previewopr.end_preview()