diff --git a/gramps/gui/widgets/grabbers.py b/gramps/gui/widgets/grabbers.py index 999845eee..0f5163a62 100644 --- a/gramps/gui/widgets/grabbers.py +++ b/gramps/gui/widgets/grabbers.py @@ -189,7 +189,7 @@ GRABBERS_SWITCH = [ [INSIDE, INSIDE, INSIDE], [GRABBER_UPPER_RIGHT, GRABBER_LOWER_RIGHT, GRABBER_LOWER_LEFT], [GRABBER_UPPER, GRABBER_LOWER, GRABBER_LOWER], - [GRABBER_UPPER_LEFT, GRABBER_LOWER_LEFT, GRABBER_UPPER_RIGHT], + [GRABBER_UPPER_LEFT, GRABBER_LOWER_LEFT, GRABBER_LOWER_RIGHT], [GRABBER_LEFT, GRABBER_LEFT, GRABBER_RIGHT], [GRABBER_LOWER_LEFT, GRABBER_UPPER_LEFT, GRABBER_UPPER_RIGHT], [GRABBER_LOWER, GRABBER_UPPER, GRABBER_UPPER], diff --git a/gramps/gui/widgets/selectionwidget.py b/gramps/gui/widgets/selectionwidget.py index b91d5aeb6..ebb7eb025 100644 --- a/gramps/gui/widgets/selectionwidget.py +++ b/gramps/gui/widgets/selectionwidget.py @@ -68,6 +68,10 @@ SHADING_OPACITY = 0.7 MIN_SELECTION_SIZE = 10 def scale_to_fit(orig_x, orig_y, target_x, target_y): + """ + Calculates the scale factor to fit the rectangle + orig_x * orig_y by scaling keeping the aspect ratio. + """ orig_aspect = orig_x / orig_y target_aspect = target_x / target_y if orig_aspect > target_aspect: @@ -76,6 +80,11 @@ def scale_to_fit(orig_x, orig_y, target_x, target_y): return target_y / orig_y def resize_keep_aspect(orig_x, orig_y, target_x, target_y): + """ + Calculates the dimensions of the rectangle obtained from + the rectangle orig_x * orig_y by scaling to fit + target_x * target_y keeping the aspect ratio. + """ orig_aspect = orig_x / orig_y target_aspect = target_x / target_y if orig_aspect > target_aspect: @@ -94,6 +103,14 @@ def order_coordinates(point1, point2): y2 = max(point1[1], point2[1]) return (x1, y1, x2, y2) +def minimum_region(point1, point2): + """ + Returns whether the rectangle defined by the corner points point1 + and point2 exceeds the minimum dimensions. + """ + return (abs(point1[0] - point2[0]) >= MIN_SELECTION_SIZE and + abs(point1[1] - point2[1]) >= MIN_SELECTION_SIZE) + class Region(object): """ @@ -102,35 +119,65 @@ class Region(object): """ def __init__(self, x1, y1, x2, y2): - self.set_coords(x1, y1, x2, y2) + """ + Creates a new region with the specified coordinates. + """ + self.x1 = x1 + self.y1 = y1 + self.x2 = x2 + self.y2 = y2 self.person = None self.mediaref = None def coords(self): + """ + Returns the coordinates of the region as a 4-tuple in the + format (x1, y1, x2, y2). + """ return (self.x1, self.y1, self.x2, self.y2) def set_coords(self, x1, y1, x2, y2): + """ + Sets the coordinates of this region. + """ self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 def contains(self, x, y): + """ + Returns whether the point with coordinates (x, y) lies insided + this region. + """ return self.x1 <= x <= self.x2 and self.y1 <= y <= self.y2 def contains_rect(self, other): + """ + Returns whether this region fully contains the region other. + """ return (self.contains(other.x1, other.y1) and self.contains(other.x2, other.y2)) def area(self): + """ + Returns the area of this region. + """ return abs(self.x1 - self.x2) * abs(self.y1 - self.y2) def intersects(self, other): + """ + Returns whether the current region intersects other. + """ # assumes that x1 <= x2 and y1 <= y2 return not (self.x2 < other.x1 or self.x1 > other.x2 or self.y2 < other.y1 or self.y1 > other.y2) class SelectionWidget(Gtk.ScrolledWindow): + """ + A widget that displays an image and permits GIMP-like selection of regions + within the image. The widget derives from gtk.ScrolledWindow. + """ __gsignals__ = { "region-modified": (GObject.SIGNAL_RUN_FIRST, None, ()), @@ -143,6 +190,9 @@ class SelectionWidget(Gtk.ScrolledWindow): } def __init__(self): + """ + Creates a new selection widget. + """ self.multiple_selection = True self.loaded = False @@ -158,24 +208,27 @@ class SelectionWidget(Gtk.ScrolledWindow): self.scale = 1.0 Gtk.ScrolledWindow.__init__(self) - self.add(self.build_gui()) + self.add(self._build_gui()) self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) - def build_gui(self): + def _build_gui(self): + """ + Builds and lays out the GUI of the widget. + """ self.image = Gtk.Image() self.image.set_has_tooltip(True) - self.image.connect_after("draw", self.expose_handler) - self.image.connect("query-tooltip", self.show_tooltip) + self.image.connect_after("draw", self._expose_handler) + self.image.connect("query-tooltip", self._show_tooltip) self.event_box = Gtk.EventBox() self.event_box.connect('button-press-event', - self.button_press_event) + self._button_press_event) self.event_box.connect('button-release-event', - self.button_release_event) + self._button_release_event) self.event_box.connect('motion-notify-event', - self.motion_notify_event) + self._motion_notify_event) self.event_box.connect('scroll-event', - self.motion_scroll_event) + self._motion_scroll_event) self.event_box.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self.event_box.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self.event_box.add_events(Gdk.EventMask.POINTER_MOTION_MASK) @@ -187,7 +240,7 @@ class SelectionWidget(Gtk.ScrolledWindow): return self.viewport # ====================================================== - # field accessors + # public field accessors # ====================================================== def get_multiple_selection(self): @@ -202,16 +255,34 @@ class SelectionWidget(Gtk.ScrolledWindow): """ self.multiple_selection = enable + def is_image_loaded(self): + """ + Returns whether an image has been loaded into this selection widget. + """ + return self.loaded + def set_regions(self, regions): + """ + Sets the list of regions to be displayed in the widget. + """ self.regions = regions def get_current(self): + """ + Returns the currently active region. + """ return self.current def set_current(self, region): + """ + Activates the given region in the widget. + """ self.current = region def get_selection(self): + """ + Returns the coordinates of the current selection. + """ return self.selection # ====================================================== @@ -219,6 +290,9 @@ class SelectionWidget(Gtk.ScrolledWindow): # ====================================================== def load_image(self, image_path): + """ + Loads an image from a given path into this selection widget. + """ self.start_point_screen = None self.selection = None self.in_region = None @@ -235,73 +309,160 @@ class SelectionWidget(Gtk.ScrolledWindow): self.pixbuf.get_height(), viewport_size.width, viewport_size.height) - self.rescale() + self._rescale() self.loaded = True except (GObject.GError, OSError): self.show_missing() def show_missing(self): + """ + Displays a 'missing image' icon in the widget. + """ self.pixbuf = None self.image.set_from_stock(Gtk.STOCK_MISSING_IMAGE, Gtk.IconSize.DIALOG) self.image.queue_draw() + # ====================================================== + # coordinate transformations (public methods) + # ====================================================== + + def proportional_to_real_rect(self, rect): + """ + Translates proportional (ranging from 0 to 100) coordinates to image + coordinates (in pixels). + """ + x1, y1, x2, y2 = rect + return (self._proportional_to_real((x1, y1)) + + self._proportional_to_real((x2, y2))) + + def real_to_proportional_rect(self, rect): + """ + Translates image coordinates (in pixels) to proportional (ranging + from 0 to 100). + """ + x1, y1, x2, y2 = rect + return (self._real_to_proportional((x1, y1)) + + self._real_to_proportional((x2, y2))) + + # ====================================================== + # widget manipulation + # ====================================================== + + def refresh(self): + """ + Schedules a redraw of the image. + """ + self.image.queue_draw() + + def can_zoom_in(self): + """ + Returns whether it is possible to zoom in the image. + """ + scaled_size = (self.original_image_size[0] * self.scale * RESIZE_RATIO, + self.original_image_size[1] * self.scale * RESIZE_RATIO) + return scaled_size[0] < MAX_SIZE and scaled_size[1] < MAX_SIZE + + def can_zoom_out(self): + """ + Returns whether it is possible to zoom out the image. + """ + scaled_size = (self.original_image_size[0] * self.scale * RESIZE_RATIO, + self.original_image_size[1] * self.scale * RESIZE_RATIO) + return scaled_size[0] >= MIN_SIZE and scaled_size[1] >= MIN_SIZE + + def zoom_in(self): + """ + Zooms in the image. The zoom factor is defined by RESIZE_RATIO. + """ + if self.can_zoom_in(): + self.scale *= RESIZE_RATIO + self._rescale() + self.emit("zoomed-in") + + def zoom_out(self): + """ + Zooms out the image. The zoom factor is defined by RESIZE_RATIO. + """ + if self.can_zoom_out(): + self.scale /= RESIZE_RATIO + self._rescale() + self.emit("zoomed-out") + + def select(self, region): + """ + Highlights the given region in the image. + """ + self.current = region + if self.current is not None: + self.selection = self.current.coords() + self.image.queue_draw() + + def clear_selection(self): + """ + Clears the selection. + """ + self.current = None + self.selection = None + self.image.queue_draw() + + # ====================================================== + # thumbnails + # ====================================================== + + def get_thumbnail(self, region, thumbnail_size): + """ + Returns the thumbnail of the given region. + """ + w = region.x2 - region.x1 + h = region.y2 - region.y1 + if w >= 1 and h >= 1: + subpixbuf = self.pixbuf.new_subpixbuf(region.x1, region.y1, w, h) + size = resize_keep_aspect(w, h, *thumbnail_size) + return subpixbuf.scale_simple(size[0], size[1], + GdkPixbuf.InterpType.BILINEAR) + else: + return None + # ====================================================== # utility functions for retrieving properties # ====================================================== - def is_image_loaded(self): - return self.loaded - - def get_original_image_size(self): + def _get_original_image_size(self): + """ + Returns the size of the image before scaling. + """ return self.original_image_size - def get_scaled_image_size(self): - unscaled_size = self.get_original_image_size() + def _get_scaled_image_size(self): + """ + Returns the size of images scaled with the current scaled. + """ + unscaled_size = self._get_original_image_size() return (unscaled_size[0] * self.scale, unscaled_size[1] * self.scale) - def get_viewport_size(self): - rect = self.viewport.get_allocation() - return (rect.width, rect.height) - - def get_used_screen_size(self): - scaled_image_size = self.get_scaled_image_size() - viewport_size = self.get_viewport_size() - return (min(scaled_image_size[0], viewport_size[0]), - min(scaled_image_size[1], viewport_size[1])) - # ====================================================== # coordinate transformations # ====================================================== - def proportional_to_real(self, coord): + def _proportional_to_real(self, coord): """ - Translate proportional (ranging from 0 to 100) coordinates to image + Translates proportional (ranging from 0 to 100) coordinates to image coordinates (in pixels). """ w, h = self.original_image_size return (int(round(coord[0] * w / 100)), int(round(coord[1] * h / 100))) - def real_to_proportional(self, coord): + def _real_to_proportional(self, coord): """ - Translate image coordinates (in pixels) to proportional (ranging + Translates image coordinates (in pixels) to proportional (ranging from 0 to 100). """ w, h = self.original_image_size return (int(round(coord[0] * 100 / w)), int(round(coord[1] * 100 / h))) - def proportional_to_real_rect(self, rect): - x1, y1, x2, y2 = rect - return (self.proportional_to_real((x1, y1)) + - self.proportional_to_real((x2, y2))) - - def real_to_proportional_rect(self, rect): - x1, y1, x2, y2 = rect - return (self.real_to_proportional((x1, y1)) + - self.real_to_proportional((x2, y2))) - - def image_to_screen(self, coords): + def _image_to_screen(self, coords): """ - Translate image coordinates to viewport coordinates using the current + Translates image coordinates to viewport coordinates using the current scale and viewport size. """ viewport_rect = self.viewport.get_allocation() @@ -317,9 +478,9 @@ class SelectionWidget(Gtk.ScrolledWindow): return (int(coords[0] * self.scale - offset_x), int(coords[1] * self.scale - offset_y)) - def screen_to_image(self, coords): + def _screen_to_image(self, coords): """ - Translate viewport coordinates to original (unscaled) image coordinates + Translates viewport coordinates to original (unscaled) image coordinates using the current scale and viewport size. """ viewport_rect = self.viewport.get_allocation() @@ -335,59 +496,85 @@ class SelectionWidget(Gtk.ScrolledWindow): return (int((coords[0] + offset_x) / self.scale), int((coords[1] + offset_y) / self.scale)) - def truncate_to_image_size(self, coords): + def _truncate_to_image_size(self, coords): + """ + Modifies the coordinates of the given point to ensure that it lies + within the image. Negative values are replaced with 0, positive values + exceeding the image dimensions - with those corresponding dimensions. + """ x, y = coords - (image_width, image_height) = self.get_original_image_size() + (image_width, image_height) = self._get_original_image_size() x = max(x, 0) x = min(x, image_width) y = max(y, 0) y = min(y, image_height) - return self.proportional_to_real(self.real_to_proportional((x, y))) + return self._proportional_to_real(self._real_to_proportional((x, y))) - def screen_to_truncated(self, coords): - return self.truncate_to_image_size(self.screen_to_image(coords)) + def _screen_to_truncated(self, coords): + """ + Transforms the screen coordinates to image coordinates and truncate to + the image size. + """ + return self._truncate_to_image_size(self._screen_to_image(coords)) - def rect_image_to_screen(self, rect): + def _rect_image_to_screen(self, rect): + """ + Translates the coordinates of the rectangle from image to screen. + """ x1, y1, x2, y2 = rect - x1, y1 = self.image_to_screen((x1, y1)) - x2, y2 = self.image_to_screen((x2, y2)) + x1, y1 = self._image_to_screen((x1, y1)) + x2, y2 = self._image_to_screen((x2, y2)) return (x1, y1, x2, y2) # ====================================================== - # drawing, refreshing and zooming the image + # drawing and scaling the image # ====================================================== - def draw_selection(self): + def _expose_handler(self, widget, event): + """ + Handles the expose-event signal of the underlying widget. + """ + if self.pixbuf: + self._draw_selection() + + def _draw_selection(self): + """ + Draws the image, the selection boxes and does the necessary + shading. + """ if not self.scaled_size: return w, h = self.scaled_size - offset_x, offset_y = self.image_to_screen((0, 0)) + offset_x, offset_y = self._image_to_screen((0, 0)) offset_x -= 1 offset_y -= 1 cr = self.image.get_window().cairo_create() if self.selection: - x1, y1, x2, y2 = self.rect_image_to_screen(self.selection) + x1, y1, x2, y2 = self._rect_image_to_screen(self.selection) # transparent shading - self.draw_transparent_shading(cr, x1, y1, x2, y2, w, h, + self._draw_transparent_shading(cr, x1, y1, x2, y2, w, h, offset_x, offset_y) # selection frame - self.draw_selection_frame(cr, x1, y1, x2, y2) + self._draw_selection_frame(cr, x1, y1, x2, y2) # draw grabber - self.draw_grabber(cr) + self._draw_grabber(cr) else: # selection frame for region in self.regions: - x1, y1, x2, y2 = self.rect_image_to_screen(region.coords()) - self.draw_region_frame(cr, x1, y1, x2, y2) + x1, y1, x2, y2 = self._rect_image_to_screen(region.coords()) + self._draw_region_frame(cr, x1, y1, x2, y2) - def draw_transparent_shading(self, cr, x1, y1, x2, y2, w, h, + def _draw_transparent_shading(self, cr, x1, y1, x2, y2, w, h, offset_x, offset_y): + """ + Draws the shading for a selection box. + """ cr.set_source_rgba(1.0, 1.0, 1.0, SHADING_OPACITY) cr.rectangle(offset_x, offset_y, x1 - offset_x, y1 - offset_y) cr.rectangle(offset_x, y1, x1 - offset_x, y2 - y1) @@ -399,10 +586,16 @@ class SelectionWidget(Gtk.ScrolledWindow): cr.rectangle(x1, offset_y, x2 - x1 + 1, y1 - offset_y) cr.fill() - def draw_selection_frame(self, cr, x1, y1, x2, y2): - self.draw_region_frame(cr, x1, y1, x2, y2) + def _draw_selection_frame(self, cr, x1, y1, x2, y2): + """ + Draws the frame during selection. + """ + self._draw_region_frame(cr, x1, y1, x2, y2) - def draw_region_frame(self, cr, x1, y1, x2, y2): + def _draw_region_frame(self, cr, x1, y1, x2, y2): + """ + Draws a region frame. + """ cr.set_source_rgb(1.0, 1.0, 1.0) # white cr.rectangle(x1, y1, x2 - x1, y2 - y1) cr.stroke() @@ -410,9 +603,12 @@ class SelectionWidget(Gtk.ScrolledWindow): cr.rectangle(x1 - 2, y1 - 2, x2 - x1 + 4, y2 - y1 + 4) cr.stroke() - def draw_grabber(self, cr): + def _draw_grabber(self, cr): + """ + Draws a grabber. + """ if self.selection is not None and self.grabber is not None: - selection_rect = self.rect_image_to_screen(self.selection) + selection_rect = self._rect_image_to_screen(self.selection) cr.set_source_rgb(1.0, 0, 0) if self.grabber_position is None: generators = grabber_generators(selection_rect) @@ -429,10 +625,11 @@ class SelectionWidget(Gtk.ScrolledWindow): cr.rectangle(x1, y1, x2 - x1, y2 - y1) cr.stroke() - def refresh(self): - self.image.queue_draw() - - def rescale(self): + def _rescale(self): + """ + Recalculates the sizes using the current scale and updates + the buffers. + """ self.scaled_size = (int(self.original_image_size[0] * self.scale), int(self.original_image_size[1] * self.scale)) self.scaled_image = self.pixbuf.scale_simple(self.scaled_size[0], @@ -442,48 +639,14 @@ class SelectionWidget(Gtk.ScrolledWindow): self.image.set_size_request(*self.scaled_size) self.event_box.set_size_request(*self.scaled_size) - def can_zoom_in(self): - scaled_size = (self.original_image_size[0] * self.scale * RESIZE_RATIO, - self.original_image_size[1] * self.scale * RESIZE_RATIO) - return scaled_size[0] < MAX_SIZE and scaled_size[1] < MAX_SIZE - - def can_zoom_out(self): - scaled_size = (self.original_image_size[0] * self.scale * RESIZE_RATIO, - self.original_image_size[1] * self.scale * RESIZE_RATIO) - return scaled_size[0] >= MIN_SIZE and scaled_size[1] >= MIN_SIZE - - def zoom_in(self): - if self.can_zoom_in(): - self.scale *= RESIZE_RATIO - self.rescale() - self.emit("zoomed-in") - - def zoom_out(self): - if self.can_zoom_out(): - self.scale /= RESIZE_RATIO - self.rescale() - self.emit("zoomed-out") - - def expose_handler(self, widget, event): - if self.pixbuf: - self.draw_selection() - - def select(self, region): - self.current = region - if self.current is not None: - self.selection = self.current.coords() - self.image.queue_draw() - - def clear_selection(self): - self.current = None - self.selection = None - self.image.queue_draw() - # ====================================================== # managing regions # ====================================================== - def find_region(self, x, y): + def _find_region(self, x, y): + """ + Finds the smallest region containing point (x, y). + """ result = None for region in self.regions: if region.contains(x, y): @@ -491,26 +654,14 @@ class SelectionWidget(Gtk.ScrolledWindow): result = region return result - # ====================================================== - # thumbnails - # ====================================================== - - def get_thumbnail(self, region, thumbnail_size): - w = region.x2 - region.x1 - h = region.y2 - region.y1 - if w >= 1 and h >= 1: - subpixbuf = self.pixbuf.new_subpixbuf(region.x1, region.y1, w, h) - size = resize_keep_aspect(w, h, *thumbnail_size) - return subpixbuf.scale_simple(size[0], size[1], - GdkPixbuf.InterpType.BILINEAR) - else: - return None - # ====================================================== # mouse event handlers # ====================================================== - def button_press_event(self, obj, event): + def _button_press_event(self, obj, event): + """ + Handles the button-press-event signal. + """ if not self.is_image_loaded(): return if event.button == 1: # left button @@ -522,8 +673,8 @@ class SelectionWidget(Gtk.ScrolledWindow): self.emit("selection-cleared") elif event.button == 3: # right button # select a region, if clicked inside one - click_point = self.screen_to_image((event.x, event.y)) - self.current = self.find_region(*click_point) + click_point = self._screen_to_image((event.x, event.y)) + self.current = self._find_region(*click_point) self.selection = \ self.current.coords() if self.current is not None else None self.start_point_screen = None @@ -535,7 +686,10 @@ class SelectionWidget(Gtk.ScrolledWindow): self.emit("selection-cleared") return True # don't propagate the event further - def button_release_event(self, obj, event): + def _button_release_event(self, obj, event): + """ + Handles the button-release-event signal. + """ if not self.is_image_loaded(): return if event.button == 1: @@ -551,14 +705,14 @@ class SelectionWidget(Gtk.ScrolledWindow): # clicked on one of the grabbers dx, dy = (event.x - self.start_point_screen[0], event.y - self.start_point_screen[1]) - self.grabber_to_draw = self.modify_selection(dx, dy) + self.grabber_to_draw = self._modify_selection(dx, dy) self.current.set_coords(*self.selection) self.emit("region-modified") else: # nothing is currently selected - if (self.minimum_region(self.start_point_screen, - (event.x, event.y)) and - self.can_select()): + if (minimum_region(self.start_point_screen, + (event.x, event.y)) and + self._can_select()): # region selection region = Region(*self.selection) self.regions.append(region) @@ -567,8 +721,8 @@ class SelectionWidget(Gtk.ScrolledWindow): else: # nothing selected, just a click click_point = \ - self.screen_to_image(self.start_point_screen) - self.current = self.find_region(*click_point) + self._screen_to_image(self.start_point_screen) + self.current = self._find_region(*click_point) self.selection = \ self.current.coords() if self.current is not None \ else None @@ -577,28 +731,31 @@ class SelectionWidget(Gtk.ScrolledWindow): self.start_point_screen = None self.refresh() - def motion_notify_event(self, widget, event): + def _motion_notify_event(self, widget, event): + """ + Handles the motion-notify-event signal. + """ if not self.is_image_loaded(): return - end_point_orig = self.screen_to_image((event.x, event.y)) - end_point = self.truncate_to_image_size(end_point_orig) + end_point_orig = self._screen_to_image((event.x, event.y)) + end_point = self._truncate_to_image_size(end_point_orig) if self.start_point_screen: # selection or dragging (mouse button pressed) if self.grabber is not None and self.grabber != INSIDE: # dragging the grabber dx, dy = (event.x - self.start_point_screen[0], event.y - self.start_point_screen[1]) - self.grabber_to_draw = self.modify_selection(dx, dy) - elif self.can_select(): + self.grabber_to_draw = self._modify_selection(dx, dy) + elif self._can_select(): # making new selection - start_point = self.screen_to_truncated(self.start_point_screen) + start_point = self._screen_to_truncated(self.start_point_screen) self.selection = order_coordinates(start_point, end_point) else: # motion (mouse button is not pressed) - self.in_region = self.find_region(*end_point_orig) + self.in_region = self._find_region(*end_point_orig) if self.current is not None: # a box is active, so check if the pointer is inside a grabber - rect = self.rect_image_to_screen(self.current.coords()) + rect = self._rect_image_to_screen(self.current.coords()) self.grabber = can_grab(rect, event.x, event.y) if self.grabber is not None: self.grabber_to_draw = self.grabber @@ -616,7 +773,10 @@ class SelectionWidget(Gtk.ScrolledWindow): self.event_box.get_window().set_cursor(None) self.image.queue_draw() - def motion_scroll_event(self, widget, event): + def _motion_scroll_event(self, widget, event): + """ + Handles the motion-scroll-event signal. + """ if not self.is_image_loaded(): return if event.direction == Gdk.ScrollDirection.UP: @@ -628,18 +788,24 @@ class SelectionWidget(Gtk.ScrolledWindow): # helpers for mouse event handlers # ====================================================== - def minimum_region(self, point1, point2): - return (abs(point1[0] - point2[0]) >= MIN_SELECTION_SIZE and - abs(point1[1] - point2[1]) >= MIN_SELECTION_SIZE) - - def can_select(self): + def _can_select(self): + """ + Returns whether selection is currently possible, which is when + multiple selection is enabled or otherwise when no region is + currently selected. + """ return self.multiple_selection or len(self.regions) < 1 - def modify_selection(self, dx, dy): - x1, y1, x2, y2 = self.rect_image_to_screen(self.current.coords()) + def _modify_selection(self, dx, dy): + """ + Changes the selection when a grabber is dragged, returns the new + grabber if a grabber switch has happened, and the current grabber + otherwise. + """ + x1, y1, x2, y2 = self._rect_image_to_screen(self.current.coords()) x1, y1, x2, y2 = MOTION_FUNCTIONS[self.grabber](x1, y1, x2, y2, dx, dy) - (x1, y1) = self.screen_to_truncated((x1, y1)) - (x2, y2) = self.screen_to_truncated((x2, y2)) + (x1, y1) = self._screen_to_truncated((x1, y1)) + (x2, y2) = self._screen_to_truncated((x2, y2)) grabber = switch_grabber(self.grabber, x1, y1, x2, y2) self.selection = order_coordinates((x1, y1), (x2, y2)) return grabber @@ -648,7 +814,10 @@ class SelectionWidget(Gtk.ScrolledWindow): # tooltips # ====================================================== - def show_tooltip(self, widget, x, y, keyboard_mode, tooltip): + def _show_tooltip(self, widget, x, y, keyboard_mode, tooltip): + """ + Handles the query-tooltip signal. + """ if self.in_region: person = self.in_region.person if person: