Addition of a print action on the fanchart
svn: r20305
This commit is contained in:
parent
86c4e407e9
commit
c233adcd64
@ -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.)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user