Fix Fan charts for scrolling/resizing of window; bad rendering (#572)

Fixes #10381
This commit is contained in:
Paul Culley 2018-03-09 19:53:34 -06:00 committed by Sam Manzi
parent babe13f811
commit ae11eceef7
6 changed files with 96 additions and 40 deletions

View File

@ -120,6 +120,7 @@ class FanChartBaseWidget(Gtk.DrawingArea):
self.dbstate = dbstate self.dbstate = dbstate
self.uistate = uistate self.uistate = uistate
self.translating = False self.translating = False
self.surface = None
self.goto = None self.goto = None
self.on_popup = callback_popup self.on_popup = callback_popup
self.last_x, self.last_y = None, None self.last_x, self.last_y = None, None
@ -248,7 +249,15 @@ class FanChartBaseWidget(Gtk.DrawingArea):
""" """
callback to draw the fanchart callback to draw the fanchart
""" """
raise NotImplementedError if self.surface:
cr.set_source_surface(self.surface, 0, 0)
cr.paint()
def prt_draw(self, widget, cr, scale=1.0):
"""
method to allow direct drawing to cairo context for printing
"""
self.draw(cr=cr, scale=scale)
def people_generator(self): def people_generator(self):
""" """
@ -749,7 +758,7 @@ class FanChartBaseWidget(Gtk.DrawingArea):
return lambda x, y: \ return lambda x, y: \
(rho(y) * math.cos(phi(x)), rho(y) * math.sin(phi(x))) (rho(y) * math.cos(phi(x)), rho(y) * math.sin(phi(x)))
def draw_gradient_legend(self, cr, widget, halfdist): def draw_gradient_legend(self, cr, halfdist):
gradwidth = 10 gradwidth = 10
gradheight = 10 gradheight = 10
starth = 15 starth = 15
@ -924,6 +933,7 @@ class FanChartBaseWidget(Gtk.DrawingArea):
diff_angle = (end_angle - start_angle) % (math.pi * 2.0) diff_angle = (end_angle - start_angle) % (math.pi * 2.0)
self.rotate_value -= math.degrees(diff_angle) self.rotate_value -= math.degrees(diff_angle)
self.last_x, self.last_y = event.x, event.y self.last_x, self.last_y = event.x, event.y
self.draw()
self.queue_draw() self.queue_draw()
return True return True
@ -954,8 +964,12 @@ class FanChartBaseWidget(Gtk.DrawingArea):
return True return True
if self.translating: if self.translating:
self.translating = False self.translating = False
else:
self.center_delta_xy = -1, 0
self.center_xy = self.center_xy_from_delta()
self.last_x, self.last_y = None, None self.last_x, self.last_y = None, None
self.draw()
self.queue_draw() self.queue_draw()
return True return True
@ -1207,27 +1221,39 @@ class FanChartWidget(FanChartBaseWidget):
(person, parents, child, userdata) = childdata (person, parents, child, userdata) = childdata
yield (person, userdata) yield (person, userdata)
def on_draw(self, widget, cr, scale=1.): def draw(self, cr=None, scale=1.0):
""" """
The main method to do the drawing. The main method to do the drawing.
If widget is given, we assume we draw in GTK3 and use the allocation. If cr is given, we assume we draw draw raw on the cairo context cr
To draw raw on the cairo context cr, set widget=None. To draw in GTK3 and use the allocation, set cr=None.
Note: when drawing for display, to counter a Gtk issue with scrolling
or resizing the drawing window, we draw on a surface, then copy to the
drawing context when the Gtk 'draw' signal arrives.
""" """
# first do size request of what we will need # first do size request of what we will need
halfdist = self.halfdist() halfdist = self.halfdist()
if widget: if not cr: # Display
if self.form == FORM_CIRCLE: if self.form == FORM_CIRCLE:
self.set_size_request(2 * halfdist, 2 * halfdist) size_w = size_h = 2 * halfdist
elif self.form == FORM_HALFCIRCLE: elif self.form == FORM_HALFCIRCLE:
self.set_size_request(2 * halfdist, halfdist + self.CENTER + PAD_PX) size_w = 2 * halfdist
size_h = halfdist + self.CENTER + PAD_PX
elif self.form == FORM_QUADRANT: elif self.form == FORM_QUADRANT:
self.set_size_request(halfdist + self.CENTER + PAD_PX, halfdist + self.CENTER + PAD_PX) size_w = size_h = halfdist + self.CENTER + PAD_PX
cr.scale(scale, scale) size_w_a = self.get_allocated_width()
if widget: size_h_a = self.get_allocated_height()
self.set_size_request(max(size_w, size_w_a), max(size_h, size_h_a))
size_w = self.get_allocated_width()
size_h = self.get_allocated_height()
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
size_w, size_h)
cr = cairo.Context(self.surface)
self.center_xy = self.center_xy_from_delta() self.center_xy = self.center_xy_from_delta()
cr.translate(*self.center_xy) cr.translate(*self.center_xy)
else: else: # printing
self.center_xy = halfdist, halfdist
cr.scale(scale, scale)
cr.translate(halfdist, halfdist) cr.translate(halfdist, halfdist)
cr.save() cr.save()
@ -1262,7 +1288,7 @@ class FanChartWidget(FanChartBaseWidget):
if child and self.childring: if child and self.childring:
self.draw_childring(cr) self.draw_childring(cr)
if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]: if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]:
self.draw_gradient_legend(cr, widget, halfdist) self.draw_gradient_legend(cr, halfdist)
def draw_childring(self, cr): def draw_childring(self, cr):
cr.move_to(TRANSLATE_PX + CHILDRING_WIDTH, 0) cr.move_to(TRANSLATE_PX + CHILDRING_WIDTH, 0)
@ -1460,6 +1486,7 @@ class FanChartWidget(FanChartBaseWidget):
# no drag occured, expand or collapse the section # no drag occured, expand or collapse the section
self.toggle_cell_state(self._mouse_click_cell_address) self.toggle_cell_state(self._mouse_click_cell_address)
self._mouse_click = False self._mouse_click = False
self.draw()
self.queue_draw() self.queue_draw()
class FanChartGrampsGUI: class FanChartGrampsGUI:
@ -1494,6 +1521,7 @@ class FanChartGrampsGUI:
self.grad_start, self.grad_end, self.grad_start, self.grad_end,
self.generic_filter, self.alpha_filter, self.form) self.generic_filter, self.alpha_filter, self.form)
self.fan.reset() self.fan.reset()
self.fan.draw()
self.fan.queue_draw() self.fan.queue_draw()
def on_popup(self, obj, event, person_handle, family_handle=None): def on_popup(self, obj, event, person_handle, family_handle=None):

View File

@ -359,23 +359,33 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
cr.fill() cr.fill()
cr.restore() cr.restore()
def draw(self, cr=None, scale=1.0):
def on_draw(self, widget, cr, scale=1.):
""" """
The main method to do the drawing. The main method to do the drawing.
If widget is given, we assume we draw in GTK3 and use the allocation. If cr is given, we assume we draw draw raw on the cairo context cr
To draw raw on the cairo context cr, set widget=None. To draw in GTK3 and use the allocation, set cr=None.
Note: when drawing for display, to counter a Gtk issue with scrolling
or resizing the drawing window, we draw on a surface, then copy to the
drawing context when the Gtk 'draw' signal arrives.
""" """
# first do size request of what we will need # first do size request of what we will need
halfdist = self.halfdist() halfdist = self.halfdist()
if widget: if not cr: # Display
self.set_size_request(2 * halfdist, 2 * halfdist) size_w = size_h = 2 * halfdist
cr.scale(scale, scale) size_w_a = self.get_allocated_width()
if widget: size_h_a = self.get_allocated_height()
self.set_size_request(max(size_w, size_w_a), max(size_h, size_h_a))
size_w = self.get_allocated_width()
size_h = self.get_allocated_height()
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
size_w, size_h)
cr = cairo.Context(self.surface)
self.center_xy = self.center_xy_from_delta() self.center_xy = self.center_xy_from_delta()
cr.translate(*self.center_xy) cr.translate(*self.center_xy)
else: else: # printing
self.center_xy = halfdist, halfdist
cr.scale(scale, scale)
cr.translate(halfdist, halfdist) cr.translate(halfdist, halfdist)
cr.save() cr.save()
@ -444,7 +454,7 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
cr.restore() cr.restore()
if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]: if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]:
self.draw_gradient_legend(cr, widget, halfdist) self.draw_gradient_legend(cr, halfdist)
def cell_address_under_cursor(self, curx, cury): def cell_address_under_cursor(self, curx, cury):
""" """
@ -542,6 +552,7 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
# no drag occured, expand or collapse the section # no drag occured, expand or collapse the section
self.toggle_cell_state(self._mouse_click_cell_address) self.toggle_cell_state(self._mouse_click_cell_address)
self._mouse_click = False self._mouse_click = False
self.draw()
self.queue_draw() self.queue_draw()
def expand_parents(self, generation, selected, current): def expand_parents(self, generation, selected, current):
@ -624,4 +635,5 @@ class FanChart2WayGrampsGUI(FanChartGrampsGUI):
self.generic_filter, self.alpha_filter, self.generic_filter, self.alpha_filter,
self.angle_algo, self.dupcolor) self.angle_algo, self.dupcolor)
self.fan.reset() self.fan.reset()
self.fan.draw()
self.fan.queue_draw() self.fan.queue_draw()

View File

@ -404,29 +404,40 @@ class FanChartDescWidget(FanChartBaseWidget):
parent, userdata = parentdata parent, userdata = parentdata
yield (parent, userdata) yield (parent, userdata)
def on_draw(self, widget, cr, scale=1.): def draw(self, cr=None, scale=1.0):
""" """
The main method to do the drawing. The main method to do the drawing.
If widget is given, we assume we draw in GTK3 and use the allocation. If cr is given, we assume we draw draw raw on the cairo context cr
To draw raw on the cairo context cr, set widget=None. To draw in GTK3 and use the allocation, set cr=None.
Note: when drawing for display, to counter a Gtk issue with scrolling
or resizing the drawing window, we draw on a surface, then copy to the
drawing context when the Gtk 'draw' signal arrives.
""" """
# first do size request of what we will need # first do size request of what we will need
halfdist = self.halfdist() halfdist = self.halfdist()
if widget: if not cr: # Display
if self.form == FORM_CIRCLE: if self.form == FORM_CIRCLE:
self.set_size_request(2 * halfdist, 2 * halfdist) size_w = size_h = 2 * halfdist
elif self.form == FORM_HALFCIRCLE: elif self.form == FORM_HALFCIRCLE:
self.set_size_request(2 * halfdist, halfdist + self.CENTER size_w = 2 * halfdist
+ PAD_PX) size_h = halfdist + self.CENTER + PAD_PX
elif self.form == FORM_QUADRANT: elif self.form == FORM_QUADRANT:
self.set_size_request(halfdist + self.CENTER + PAD_PX, size_w = size_h = halfdist + self.CENTER + PAD_PX
halfdist + self.CENTER + PAD_PX)
cr.scale(scale, scale) size_w_a = self.get_allocated_width()
# when printing, we need not recalculate size_h_a = self.get_allocated_height()
if widget: self.set_size_request(max(size_w, size_w_a), max(size_h, size_h_a))
size_w = self.get_allocated_width()
size_h = self.get_allocated_height()
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
size_w, size_h)
cr = cairo.Context(self.surface)
self.center_xy = self.center_xy_from_delta() self.center_xy = self.center_xy_from_delta()
cr.translate(*self.center_xy) cr.translate(*self.center_xy)
else: # printing
self.center_xy = halfdist, halfdist
cr.scale(scale, scale)
cr.translate(halfdist, halfdist)
cr.save() cr.save()
# Draw center person: # Draw center person:
@ -477,7 +488,7 @@ class FanChartDescWidget(FanChartBaseWidget):
cr.restore() cr.restore()
if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]: if self.background in [BACKGROUND_GRAD_AGE, BACKGROUND_GRAD_PERIOD]:
self.draw_gradient_legend(cr, widget, halfdist) self.draw_gradient_legend(cr, halfdist)
def cell_address_under_cursor(self, curx, cury): def cell_address_under_cursor(self, curx, cury):
""" """
@ -595,6 +606,7 @@ class FanChartDescWidget(FanChartBaseWidget):
self.toggle_cell_state(self._mouse_click_cell_address) self.toggle_cell_state(self._mouse_click_cell_address)
self._compute_angles(*self.rootangle_rad) self._compute_angles(*self.rootangle_rad)
self._mouse_click = False self._mouse_click = False
self.draw()
self.queue_draw() self.queue_draw()
def toggle_cell_state(self, cell_address): def toggle_cell_state(self, cell_address):
@ -647,4 +659,5 @@ class FanChartDescGrampsGUI(FanChartGrampsGUI):
self.generic_filter, self.alpha_filter, self.form, self.generic_filter, self.alpha_filter, self.form,
self.angle_algo, self.dupcolor) self.angle_algo, self.dupcolor)
self.fan.reset() self.fan.reset()
self.fan.draw()
self.fan.queue_draw() self.fan.queue_draw()

View File

@ -256,7 +256,8 @@ class FanChart2WayView(fanchart2way.FanChart2WayGrampsGUI, NavigationView):
widthpx = 2 * self.fan.halfdist() widthpx = 2 * self.fan.halfdist()
heightpx = widthpx heightpx = widthpx
prt = CairoPrintSave(widthpx, heightpx, self.fan.on_draw, self.uistate.window) prt = CairoPrintSave(widthpx, heightpx, self.fan.prt_draw,
self.uistate.window)
prt.run() prt.run()
def on_childmenu_changed(self, obj, person_handle): def on_childmenu_changed(self, obj, person_handle):

View File

@ -256,7 +256,8 @@ class FanChartDescView(fanchartdesc.FanChartDescGrampsGUI, NavigationView):
heightpx = heightpx / 2 + self.fan.CENTER + fanchart.PAD_PX heightpx = heightpx / 2 + self.fan.CENTER + fanchart.PAD_PX
widthpx = heightpx widthpx = heightpx
prt = CairoPrintSave(widthpx, heightpx, self.fan.on_draw, self.uistate.window) prt = CairoPrintSave(widthpx, heightpx, self.fan.prt_draw,
self.uistate.window)
prt.run() prt.run()
def on_childmenu_changed(self, obj, person_handle): def on_childmenu_changed(self, obj, person_handle):

View File

@ -252,7 +252,8 @@ class FanChartView(fanchart.FanChartGrampsGUI, NavigationView):
heightpx = heightpx / 2 + self.fan.CENTER + fanchart.PAD_PX heightpx = heightpx / 2 + self.fan.CENTER + fanchart.PAD_PX
widthpx = heightpx widthpx = heightpx
prt = CairoPrintSave(widthpx, heightpx, self.fan.on_draw, self.uistate.window) prt = CairoPrintSave(widthpx, heightpx, self.fan.prt_draw,
self.uistate.window)
prt.run() prt.run()
def on_childmenu_changed(self, obj, person_handle): def on_childmenu_changed(self, obj, person_handle):