275
295
self._figure.move(child_align_x - self.inner_width/2, self._figure.y)
298
def draw_varrow(self, cr, x, y1, y2):
299
cr.set_line_width(self.get_line_width())
301
cr.line_to(x, y2 + 6 + self.get_line_width())
303
draw_varrow(cr, (x, y2), 10+self.get_line_width(), 6+self.get_line_width())
307
def draw_harrow(self, cr, x1, x2, y, w=10, h=6):
308
cr.set_line_width(self.get_line_width())
310
cr.line_to(x2 - (w + self.get_line_width()), y)
312
draw_harrow(cr, (x2, y), w+self.get_line_width(), h+self.get_line_width())
278
316
# hover tip handling
279
317
def handle_hover_out(self, fig, x, y):
280
318
if self._context.tooltip:
295
333
if self._context.tooltip:
296
334
self._context.tooltip.close()
297
335
self._context.tooltip = None
337
if self._context.overview_mode:
299
340
text = self.get_hint_text()
301
342
if type(text) is unicode:
302
343
text = text.encode("utf8")
303
344
self._context.tooltip = mforms.newPopover(mforms.PopoverStyleTooltip)
305
xx, yy = self._context.client_to_screen(fig._figure.root_x + fig.inner_width, fig._figure.root_y + fig.inner_height/2)
347
xx, yy = self._context.client_to_screen(x + 10, y)
349
xx, yy = self._context.client_to_screen(fig._figure.root_x + fig.inner_width, fig._figure.root_y + fig.inner_height/2)
306
350
box = mforms.newBox(False)
307
351
box.set_spacing(0)
394
444
cr.set_source_rgba(0, 0, 0, 1)
395
cr.set_line_width(self.get_line_width())
396
445
if isinstance(self.parent, NestedLoopNode):
397
446
if not is_inside_a_box:
398
cr.move_to(self.harrow_source[0], self.parent.harrow_target[1])
399
cr.line_to(*self.parent.harrow_target)
401
draw_harrow(cr, self.parent.harrow_target, 10, 6)
447
self.draw_harrow(cr, self.harrow_source[0], self.parent.harrow_target[0], self.parent.harrow_target[1])
403
448
self.render_row_count(cr, self.harrow_source[0] + 4, self.harrow_source[1] - 8)
405
450
if not is_inside_a_box:
406
cr.move_to(*self.varrow_source)
407
cr.line_to(self.varrow_source[0], self.parent.varrow_target[1])
409
draw_varrow(cr, (self.varrow_source[0], self.parent.varrow_target[1]), 10, 6)
451
self.draw_varrow(cr, self.varrow_source[0], self.varrow_source[1], self.parent.varrow_target[1])
411
452
self.render_row_count(cr, self.varrow_source[0] + 4, self.varrow_source[1])
453
self.render_cost(cr, self._figure.root_x, self.varrow_source[1] - 5)
607
665
if self.parent and not isinstance(self.parent, MaterializedTableNode):
609
667
cr.set_source_rgba(0, 0, 0, 1)
610
cr.set_line_width(self.get_line_width())
611
668
if isinstance(self.parent, NestedLoopNode) and self.parent.child_aside == self:
669
# special case (line in L)
613
670
cr.move_to(*self.varrow_source)
614
671
cr.line_to(self.varrow_source[0], self.parent.harrow_target[1])
615
cr.line_to(self.parent.harrow_target[0], self.parent.harrow_target[1])
617
draw_harrow(cr, self.parent.harrow_target, 10, 6)
672
self.draw_harrow(cr, self.varrow_source[0], self.parent.harrow_target[0], self.parent.harrow_target[1])
619
cr.move_to(*self.varrow_source)
620
cr.line_to(self.varrow_source[0], self.parent.varrow_target[1])
622
draw_varrow(cr, (self.varrow_source[0], self.parent.varrow_target[1]), 10, 6)
674
self.draw_varrow(cr, self.varrow_source[0], self.varrow_source[1], self.parent.varrow_target[1])
624
675
self.render_cost(cr, self._figure.root_x, self.varrow_source[1] - 5)
625
676
self.render_row_count(cr, self.varrow_source[0] + 4, self.varrow_source[1] - 5)
671
723
key_name = key_name, info = info,
672
724
cost_info = cost_info, rows_examined = rows_examined, rows_produced = rows_produced)
674
# we don't expect attached_subqueries to ever have a value for this type of node,
675
# if that's confirmed we can remove the arg
676
assert not attached_subqueries
678
self._figure.set_layout_flags(HFill)
679
726
self.set_spacing(0)
681
728
fill = self._figure._fill_color
682
729
self._figure.set_color(fill[0], fill[1], fill[2], 0.8)
683
730
self._figure.set_fill_color(fill[0], fill[1], fill[2], 0.8)
685
self._figure_name.set_layout_flags(HFill)
686
732
self._figure_name.set_color(0.9, 0.9, 0.9, 0.9)
687
self._figure_name.set_text("%s (materialized)" % name)
733
if name.startswith("<") and name.endswith(">"):
734
self._figure_name.set_text(name)
736
self._figure_name.set_text("%s (materialized)" % name)
688
737
self._figure_name.set_fill_color(0.9, 0.9, 0.9, 0.9)
689
738
self._figure_name.set_padding(4, 4, 4, 4)
706
755
def do_relayout(self, ctx):
707
756
self.child_materialized_from.do_relayout(ctx)
708
TableNode.do_relayout(self, ctx)
757
width = self.child_materialized_from.width + self._context.frame_padding*2
758
# skip Table specific layouting and do everything from scratch
759
VBoxFigure.do_relayout(self, ctx)
760
for f in self._items:
710
if self._width < self.child_materialized_from.width + self._context.frame_padding * 2:
711
self._width = int(self.child_materialized_from.width + self._context.frame_padding*2)
712
VBoxFigure.do_relayout(self, ctx)
714
self._height = int(self.inner_height + self.child_materialized_from.height + self._context.frame_padding*2)
765
self._height = max(self._height, int(self.inner_height + self.child_materialized_from.height + self._context.frame_padding*2))
716
767
if self.width <= self.child_materialized_from.width:
717
768
self.child_materialized_from.move(self._context.frame_padding,
718
769
self.inner_height + self._context.frame_padding)
720
self.child_materialized_from.move((self.width - self.child_materialized_from.width)/2,
771
self.child_materialized_from.move((width - self.child_materialized_from.width)/2,
721
772
self.inner_height + self._context.frame_padding)
774
# for subselects in the WHERE list (attached subqs), attach them from the right side
775
# select * from ... where (select ...) = ...
776
child = self.child_attached_subqueries
778
child.do_relayout(ctx)
779
child.move(width + self._context.hspacing, self._figure.y)
780
self._width = width + self._context.hspacing + child.width
781
self._height = max(self._height, child.height)
724
784
def do_render(self, ctx):
725
785
TableNode.do_render(self, ctx)
727
787
ctx.translate(self.x, self.y)
728
ctx.rectangle(0.5, 0.5, int(self._width), int(self._height)-1)
788
ctx.rectangle(0.5, 0.5,
789
int(self.child_materialized_from.width + self.child_materialized_from.x + self._context.frame_padding),
790
int(self.child_materialized_from.height + self.child_materialized_from.y + self._context.frame_padding))
729
791
ctx.set_line_width(1)
730
792
ctx.set_dash([4.0, 2.0], 0)
731
793
ctx.set_source_rgba(0.5, 0.5, 0.5, 0.9)
834
895
if self.parent and not isinstance(self.parent, MaterializedTableNode) and not isinstance(self.parent, MaterializedJoinNode):
836
897
cr.set_source_rgba(0, 0, 0, 1)
837
cr.set_line_width(self.get_line_width())
838
898
if isinstance(self.parent, NestedLoopNode) and self.parent.child_aside == self:
840
cr.move_to(*self.varrow_source)
841
cr.line_to(self.varrow_source[0], self.parent.harrow_target[1])
842
cr.line_to(self.parent.harrow_target[0], self.parent.harrow_target[1])
844
draw_harrow(cr, self.parent.harrow_target, 10, 6)
900
self.draw_harrow(cr, self.varrow_source[0], self.parent.harrow_target[0], self.parent.harrow_target[1])
845
901
elif isinstance(self.parent, OperationNode):
846
cr.move_to(*self.harrow_source)
847
cr.line_to(self.parent.harrow_target[0], self.harrow_source[1])
849
draw_harrow(cr, self.parent.harrow_target, 10, 6)
902
self.draw_harrow(cr, self.harrow_source[0], self.parent.harrow_target[0], self.harrow_source[1])
851
cr.move_to(*self.varrow_source)
852
cr.line_to(self.varrow_source[0], self.parent.varrow_target[1])
854
draw_varrow(cr, (self.varrow_source[0], self.parent.varrow_target[1]), 10, 6)
904
self.draw_varrow(cr, self.varrow_source[0], self.varrow_source[1], self.parent.varrow_target[1])
1029
1080
def do_render_extras(self, cr):
1082
#is_root_node = self.parent is None
1084
# draw rectangle around the whole query block
1086
cr.set_source_rgba(0, 0, 0, .3)
1087
cr.set_line_width(2)
1088
cr.rectangle(self.root_x - self._context.frame_padding, self._figure.root_y, self.width, self.height - self._figure.y)
1031
1092
cr.set_source_rgba(0, 0, 0, 1)
1032
if self.parent is None:
1033
cr.set_line_width(1)
1034
cr.rectangle(self._figure.root_x+2.5, self._figure.root_y+2.5, self._figure.width-4, self._figure.height-4)
1037
1093
self.render_cost(cr, self._figure.root_x, self._figure.root_y - 5)
1039
1095
if self.parent and not isinstance(self.parent, MaterializedTableNode):
1040
cr.set_line_width(self.get_line_width())
1041
cr.move_to(*self.varrow_source)
1042
cr.line_to(self.varrow_source[0], self.parent.varrow_target[1])
1044
draw_varrow(cr, (self.varrow_source[0], self.parent.varrow_target[1]), 10, 6)
1096
self.draw_varrow(cr, self.varrow_source[0], self.varrow_source[1], self.parent.varrow_target[1])
1197
1249
if self.parent and not isinstance(self.parent, MaterializedTableNode):
1199
1251
cr.set_source_rgba(0, 0, 0, 1)
1200
cr.set_line_width(self.get_line_width())
1201
1252
if isinstance(self.parent, NestedLoopNode):
1202
cr.move_to(self.harrow_source[0], self.parent.harrow_target[1])
1203
cr.line_to(*self.parent.harrow_target)
1205
draw_harrow(cr, self.parent.harrow_target, 10, 6)
1253
self.draw_harrow(cr, self.harrow_source[0], self.parent.harrow_target[0], self.parent.harrow_target[1])
1207
1254
self.render_row_count(cr, self.harrow_source[0] + 4, self.harrow_source[1] - 8)
1209
cr.move_to(*self.varrow_source)
1210
cr.line_to(self.varrow_source[0], self.parent.varrow_target[1])
1212
draw_varrow(cr, (self.varrow_source[0], self.parent.varrow_target[1]), 10, 6)
1256
self.draw_varrow(cr, self.varrow_source[0], self.varrow_source[1], self.parent.varrow_target[1])
1214
1257
self.render_row_count(cr, self.varrow_source[0] + 4, self.varrow_source[1])
1257
1300
cr.set_source_rgba(0, 0, 0, 1)
1258
1301
self.render_cost(cr, self._figure.root_x, self._figure.root_y - 5)
1260
cr.set_line_width(self.get_line_width())
1261
1303
if self.what in ("select_list_subqueries", "attached_subqueries"):
1262
1304
# parent is a QueryBlockNode or TableNode
1263
cr.move_to(self.root_x, self.harrow_source[1])
1264
1305
target_x = self.parent._figure.root_x + self.parent._figure.width
1265
cr.line_to(target_x, self.harrow_source[1])
1267
draw_harrow(cr, (target_x, self.harrow_source[1]), -10, 6)
1306
self.draw_harrow(cr, self.root_x, target_x, self.harrow_source[1], -10, 6)
1269
1307
elif self.parent and not isinstance(self.parent, MaterializedTableNode):
1270
cr.move_to(*self.varrow_source)
1271
cr.line_to(self.varrow_source[0], self.parent.varrow_target[1])
1273
draw_varrow(cr, (self.varrow_source[0], self.parent.varrow_target[1]), 10, 6)
1308
self.draw_varrow(cr, self.varrow_source[0], self.varrow_source[1], self.parent.varrow_target[1])
1645
1688
w, h = self._root.size
1646
1689
self.size = w + self.global_padding * 2, h + self.global_padding * 2
1648
1690
return self.size
1651
1693
def repaint(self, cr):
1652
cr.translate(self._offset[0], self._offset[1])
1694
cr.translate(self._offset[0] + self._extra_offset[0], self._offset[1] + self._extra_offset[1])
1695
cr.scale(self._scale, self._scale)
1654
#cr.set_source_rgba(0, 0, 0, 1)
1655
#cr.rectangle(0, 0, self.size[0], self.size[1])
1657
1697
self._canvas.repaint(cr, 0, 0, self.size[0], self.size[1])
1699
if self.overview_mode:
1700
x, y, w, h = self._overview_visible_rect
1702
total_w, total_h = self.size
1703
total_w = max(total_w, w)
1704
total_h = max(total_h, h)
1706
cr.set_line_width(1)
1707
cr.rectangle(0, 0, total_w, total_h)
1709
cr.rectangle(x, y, w, h)
1710
cr.set_fill_rule(cairo.CAIRO_FILL_RULE_EVEN_ODD)
1711
cr.set_source_rgba(0, 0, 0, 0.3)
1713
cr.rectangle(x, y, w, h)
1714
cr.set_source_rgba(0, 0, 0, 0.5)
1658
1716
except Exception:
1659
1717
import traceback
1660
1718
log_error("Exception repainting explain: %s\n" % traceback.format_exc())
1721
def enter_overview_mode(self):
1722
self._old_offset = self._offset
1723
self._old_scale = self._scale
1725
r = self._scroll.get_content_rect()
1726
_, _, view_width, view_height = r
1727
# calculate how much we have to scale to fit the whole diagram on screen
1728
total_width, total_height = self.size
1730
# check if whole thing already fits on screen
1731
if total_width <= view_width and total_height <= view_height:
1734
dw = float(total_width) / view_width
1735
dh = float(total_height) / view_height
1741
self.overview_mode = True
1742
self._overview_visible_rect = r
1744
# extra offset needed to center contents
1745
self._extra_offset = (view_width - total_width * self._scale)/2, (view_height - total_height * self._scale)/2
1747
self._view.set_back_color("#b3b3b3")
1748
self._view.set_size(int(view_width), int(view_height))
1749
self._view.set_needs_repaint()
1752
def leave_overview_mode(self):
1753
x, y, w, h = self._overview_visible_rect
1754
self.overview_mode = False
1755
self._scale = self._old_scale
1756
self._extra_offset = (0, 0)
1757
self._view.set_back_color("#ffffff")
1758
self._view.set_size(max(self._scroll.get_width(), self.size[0]), max(self._scroll.get_height(), self.size[1]))
1759
self._scroll.scroll_to(x, y)
1760
self._view.set_needs_repaint()
1763
def mouse_moved(self, x, y):
1764
x -= (self._offset[0] + self._extra_offset[0])
1765
y -= (self._offset[1] + self._extra_offset[1])
1768
total_width, total_height = self.size
1769
xx, yy, ww, hh = self._overview_visible_rect
1772
ww = min(ww, total_width)
1773
hh = min(hh, total_height)
1774
x = min(max(x, 0), total_width - ww)
1775
y = min(max(y, 0), total_height - hh)
1776
self._overview_visible_rect = int(x), int(y), ww, hh
1777
self._view.set_needs_repaint()
1780
def mouse_down(self, x, y):
1781
self.leave_overview_mode()
1663
1784
def fmt_cost(self, value):
1664
1785
if self.cost_value_is_amount:
1665
1786
return fmt_number(value)