# # Python GUI - Scrollable Views - Generic # from GUI.Geometry import rect_sized, add_pt, sub_pt from GUI.Properties import overridable_property from GUI.Geometry import sect_rect from GUI import DrawableContainer default_extent = (300, 300) default_line_scroll_amount = (16, 16) default_scrolling = 'hv' class ScrollableView(DrawableContainer): """A ScrollableView is a 2D drawing area having its own coordinate system and clipping area, with support for scrolling.""" scrolling = overridable_property('scrolling', "String containing 'h' for horizontal and 'v' for vertical scrolling.") hscrolling = overridable_property('hscrolling', "True if horizontal scrolling is enabled.") vscrolling = overridable_property('vscrolling', "True if vertical scrolling is enabled.") extent = overridable_property('extent', "Size of scrollable area in local coordinates.") scroll_offset = overridable_property('scroll_offset', "Current scrolling position.") line_scroll_amount = overridable_property('line_scroll_amount', "Tuple specifying horizontal and vertical line scrolling increments.") background_color = overridable_property('background_color', "Color with which to fill areas outside the extent, or None") #scroll_bars = overridable_property('scroll_bars', # "Attached ScrollBar instances.") # ## _scroll_bars [ScrollBar] def set(self, **kwds): if 'scrolling' in kwds: self.scrolling = kwds.pop('scrolling') DrawableContainer.set(self, **kwds) def get_scrolling(self): chars = [] if self.hscrolling: chars.append('h') if self.vscrolling: chars.append('v') return ''.join(chars) def set_scrolling(self, value): self.hscrolling = 'h' in value self.vscrolling = 'v' in value def viewed_rect(self): """Return the rectangle in local coordinates bounding the currently visible part of the extent.""" return rect_sized(self.scroll_offset, self.size) def get_print_extent(self): return self.extent def get_background_color(self): return self._background_color def set_background_color(self, x): self._background_color = x self.invalidate() # # Coordinate transformation # def local_to_container_offset(self): return sub_pt(self.position, self.scroll_offset) # # Scrolling # def h_line_scroll_amount(self): """Return the horizontal line scroll increment.""" return self.line_scroll_amount[0] def v_line_scroll_amount(self): """Return the vertical line scroll increment.""" return self.line_scroll_amount[1] def h_page_scroll_amount(self): """Return the horizontal page scroll increment.""" return self.width - self.h_line_scroll_amount() def v_page_scroll_amount(self): """Return the vertical page scroll increment.""" return self.height - self.v_line_scroll_amount() def scroll_by(self, dx, dy): """Scroll by the given amount horizontally and vertically.""" self.scroll_offset = add_pt(self.scroll_offset, (dx, dy)) def scroll_line_left(self): """Called by horizontal scroll bar to scroll left by one line.""" self.scroll_by(-self.h_line_scroll_amount(), 0) def scroll_line_right(self): """Called by horizontal scroll bar to scroll right by one line.""" self.scroll_by(self.h_line_scroll_amount(), 0) def scroll_line_up(self): """Called by vertical scroll bar to scroll up by one line.""" self.scroll_by(0, -self.v_line_scroll_amount()) def scroll_line_down(self): """Called by vertical scroll bar to scroll down by one line.""" self.scroll_by(0, self.v_line_scroll_amount()) def scroll_page_left(self): """Called by horizontal scroll bar to scroll left by one page.""" self.scroll_by(-self.h_page_scroll_amount(), 0) def scroll_page_right(self): """Called by horizontal scroll bar to scroll right by one page.""" self.scroll_by(self.h_page_scroll_amount(), 0) def scroll_page_up(self): """Called by vertical scroll bar to scroll up by one page.""" self.scroll_by(0, -self.v_page_scroll_amount()) def scroll_page_down(self): """Called by vertical scroll bar to scroll down by one page.""" self.scroll_by(0, self.v_page_scroll_amount()) # # Background drawing # def _draw_background(self, canvas, clip_rect): # If the view has a background color, uses it to fill the parts of the # clip_rect that are outside the view's extent and returns the remaining # rectangle. Otherwise, returns the clip_rect unchanged. color = self._background_color if color: vl, vt, vr, vb = clip_rect ew, eh = self.extent if vr > ew or vb > eh: #if getattr(self, "_debug_bg", False): ### # print "ScrollableView: old backcolor =", canvas.backcolor ### canvas.gsave() canvas.backcolor = color if ew < vr: #if getattr(self, "_debug_bg", False): ### # print "ScrollableView: erasing", (ew, vt, vr, vb) ### canvas.erase_rect((ew, vt, vr, vb)) if eh < vb: if getattr(self, "_debug_bg", False): ### print "ScrollableView: erasing", (vl, eh, ew, vb) ### canvas.erase_rect((vl, eh, ew, vb)) canvas.grestore() #if getattr(self, "_debug_bg", False): ### # print "ScrollableView: restored backcolor =", canvas.backcolor ### return sect_rect(clip_rect, (0, 0, ew, eh)) return clip_rect