def is_point_in(self, p: Point, category=MouseEvent.UNKNOWN): v = self._vertexes start = Point(min(x.x for x in v), min(x.y for x in v)) width = max(x.x for x in v) - start.x height = max(x.y for x in v) - start.y rectangle = Rectangle(start, width, height) return rectangle.is_point_in(p)
def is_point_in(self,p: Point,category=MouseEvent.UNKNOWN): v = self._vertexes start = Point(min(x.x for x in v), min(x.y for x in v)) width = max(x.x for x in v) - start.x height = max(x.y for x in v) - start.y rectangle = Rectangle(start, width, height) return rectangle.is_point_in(p)
class _GuideLine(Widget): def __init__(self, elements: List[int], orientation: Orientation, line_thickness: int, font_size: int, max_size: int = None, element_distance: int = 10, line_distance: int = 1.8, **kwargs): super().__init__(**kwargs) if not elements: elements = [0] self._elements = [_GuideElement(str(e)) for e in elements] self.orientation = orientation self.line_thickness = line_thickness self.font_size = font_size self.shape = None self.max_size = max_size self.element_distance = element_distance self.line_distance = line_distance self.mouse_was_down = False self.line_extension = 0 self._selected_cell = None def set_done(self): for e in self._elements: e.is_done = True e.is_wrong = False def set_wrong(self): for e in self._elements: e.is_wrong = True e.is_done = False def set_default(self): for e in self._elements: e.is_wrong = False e.is_done = False def set_cancelled(self, is_it: bool, index: int): self._elements[index].is_cancelled = is_it def toggle_cancelled(self, index: int): e = self._elements[index] e.is_cancelled = not e.is_cancelled def set_shape_from_context(self, context: cairo.Context): widths_heights = [] done = False while not done: max_width = 0 max_height = 0 context.set_font_size(self.font_size) for e in reversed(self._elements): xb, yb, w, h, xa, ya = context.text_extents(e.label) widths_heights.append((xa, h)) e.width = xa e.height = h max_width = max(max_width, xa) max_height = max(max_height, h) # adjust font size in case it's too big if self.max_size is None: done = True else: if self.orientation == Orientation.HORIZONTAL: reference = max_height else: reference = max_width if reference + 2 * self.line_distance <= self.max_size: done = True else: self.font_size -= 1 positions = [] width = self.element_distance height = self.element_distance def get_padding(actual_size): if self.max_size is not None: return (self.max_size - actual_size) / 2 else: return self.line_distance if self.orientation == Orientation.HORIZONTAL: def handle_extents(e: _GuideElement): nonlocal width, height, positions, max_height width += e.width e.position = (-width, max_height + get_padding(max_height)) width += self.element_distance else: def handle_extents(e: _GuideElement): nonlocal width, height, positions, max_width e.position = (get_padding(e.width), -height) height += e.height + self.element_distance # for w, h in widths_heights: # handle_extents(w, h) for element in reversed(self._elements): handle_extents(element) if self.orientation == Orientation.HORIZONTAL: height = max_height + get_padding(max_height) * 2 width = width - self.element_distance + self.line_distance base_point = Point(-width, 0) else: width = max_width + get_padding(max_width) * 2 height = height - self.element_distance + self.line_distance base_point = Point(0, -height) self.shape = Rectangle(base_point, width, height) # for e, p in zip(self._elements, reversed(positions)): # e.position = p def on_draw(self, widget: Widget, context: cairo.Context): self.set_shape_from_context(context) shape = self.shape context.set_line_width(self.line_thickness) if self.orientation == Orientation.HORIZONTAL: context.move_to(shape.start.x - self.line_extension, shape.start.y) context.line_to(shape.start.x + shape.width, shape.start.y) else: context.move_to(shape.start.x, shape.start.y - self.line_extension) context.line_to(shape.start.x, shape.start.y + shape.height) context.stroke() for element in self._elements: context.move_to(*element.position) context.set_source_rgb(*element.color) context.show_text(element.label) def get_cell(self, p: Point): delta = self.line_distance for index, element in enumerate(self._elements): if Rectangle( Point(*element.position) + Point(-delta, delta), element.width + 2 * delta, -element.height - 2 * delta).is_point_in(p): return index return None def on_mouse_down(self, widget: Widget, event: MouseEvent): self._selected_cell = self.get_cell(event) def on_mouse_up(self, widget: Widget, event: MouseEvent): if self._selected_cell is not None and \ self._selected_cell == self.get_cell(event): self.toggle_cancelled(self._selected_cell) self.invalidate() self._selected_cell = None def is_point_in(self, p: Point, category=MouseEvent.UNKNOWN): if self.shape is None: return False elif (category == MouseEvent.MOUSE_UP and self.mouse_was_down) \ or self.shape.is_point_in(p): return True else: return False
class _GuideLine(Widget): def __init__(self, elements: List[int], orientation: Orientation, line_thickness: int, font_size: int, max_size: int = None, element_distance: int=10, line_distance: int=1.8, **kwargs): super().__init__(**kwargs) if not elements: elements = [0] self._elements = [_GuideElement(str(e)) for e in elements] self.orientation = orientation self.line_thickness = line_thickness self.font_size = font_size self.shape = None self.max_size = max_size self.element_distance = element_distance self.line_distance = line_distance self.mouse_was_down = False self.line_extension = 0 self._selected_cell = None def set_done(self): for e in self._elements: e.is_done = True e.is_wrong = False def set_wrong(self): for e in self._elements: e.is_wrong = True e.is_done = False def set_default(self): for e in self._elements: e.is_wrong = False e.is_done = False def set_cancelled(self, is_it: bool, index: int): self._elements[index].is_cancelled = is_it def toggle_cancelled(self, index: int): e = self._elements[index] e.is_cancelled = not e.is_cancelled def set_shape_from_context(self, context: cairo.Context): widths_heights = [] done = False while not done: max_width = 0 max_height = 0 context.set_font_size(self.font_size) for e in reversed(self._elements): xb, yb, w, h, xa, ya = context.text_extents(e.label) widths_heights.append((xa, h)) e.width = xa e.height = h max_width = max(max_width, xa) max_height = max(max_height, h) # adjust font size in case it's too big if self.max_size is None: done = True else: if self.orientation == Orientation.HORIZONTAL: reference = max_height else: reference = max_width if reference + 2 * self.line_distance <= self.max_size: done = True else: self.font_size -= 1 positions = [] width = self.element_distance height = self.element_distance def get_padding(actual_size): if self.max_size is not None: return (self.max_size - actual_size) / 2 else: return self.line_distance if self.orientation == Orientation.HORIZONTAL: def handle_extents(e: _GuideElement): nonlocal width, height, positions, max_height width += e.width e.position = (-width, max_height + get_padding(max_height)) width += self.element_distance else: def handle_extents(e: _GuideElement): nonlocal width, height, positions, max_width e.position = (get_padding(e.width), -height) height += e.height + self.element_distance # for w, h in widths_heights: # handle_extents(w, h) for element in reversed(self._elements): handle_extents(element) if self.orientation == Orientation.HORIZONTAL: height = max_height + get_padding(max_height) * 2 width = width - self.element_distance + self.line_distance base_point = Point(-width, 0) else: width = max_width + get_padding(max_width) * 2 height = height - self.element_distance + self.line_distance base_point = Point(0, -height) self.shape = Rectangle(base_point, width, height) # for e, p in zip(self._elements, reversed(positions)): # e.position = p def on_draw(self, widget: Widget, context: cairo.Context): self.set_shape_from_context(context) shape = self.shape context.set_line_width(self.line_thickness) if self.orientation == Orientation.HORIZONTAL: context.move_to(shape.start.x - self.line_extension, shape.start.y) context.line_to(shape.start.x + shape.width, shape.start.y) else: context.move_to(shape.start.x, shape.start.y - self.line_extension) context.line_to(shape.start.x, shape.start.y + shape.height) context.stroke() for element in self._elements: context.move_to(*element.position) context.set_source_rgb(*element.color) context.show_text(element.label) def get_cell(self, p: Point): delta = self.line_distance for index, element in enumerate(self._elements): if Rectangle(Point(*element.position) + Point(-delta, delta), element.width + 2 * delta, -element.height - 2 * delta).is_point_in(p): return index return None def on_mouse_down(self, widget: Widget, event: MouseEvent): self._selected_cell = self.get_cell(event) def on_mouse_up(self, widget: Widget, event: MouseEvent): if self._selected_cell is not None and \ self._selected_cell == self.get_cell(event): self.toggle_cancelled(self._selected_cell) self.invalidate() self._selected_cell = None def is_point_in(self, p: Point, category=MouseEvent.UNKNOWN): if self.shape is None: return False elif (category == MouseEvent.MOUSE_UP and self.mouse_was_down) \ or self.shape.is_point_in(p): return True else: return False
class Navigator(UncheckedContainer): def __init__(self, side=40, protrusion=30): super().__init__() base_size = side height = protrusion colour = global_constants.start_selected arrow_up = Arrow(base_size, height, Arrow.UP, colour) arrow_down = Arrow(base_size, height, Arrow.DOWN, colour) arrow_left = Arrow(base_size, height, Arrow.LEFT, colour) arrow_right = Arrow(base_size, height, Arrow.RIGHT, colour) factor=6 center = height + base_size/2 + (base_size/factor) distance = (base_size + height)/2 + (base_size/factor) arrow_up.translate(center, center - distance) arrow_left.translate(center - distance, center) arrow_right.translate(center + distance, center) arrow_down.translate(center, center + distance) self.background_rectangle = Rectangle(Point(0,0), center * 2, center * 2) self.add(arrow_up) self.add(arrow_down) self.add(arrow_left) self.add(arrow_right) self._should_pass_move = False def on_mouse_up(self, widget, event): self._should_pass_move = False super().on_mouse_up(widget, event) return False def on_mouse_enter(self) -> bool: if self.mouse_is_down: self._should_pass_move = True return super().on_mouse_enter() def on_mouse_exit(self) -> bool: self._should_pass_move = False return super().on_mouse_exit() def on_mouse_move(self, widget, event): super().on_mouse_move(widget, event) return False def on_mouse_down(self, widget, event): super().on_mouse_down(widget, event) return True @property def fromWidgetCoords(self): width = self.background_rectangle.width height = self.background_rectangle.height total_width, total_height = self.container_size transl_width, transl_height = self._fromScale.transform_point(width, height) self.set_translate(total_width - transl_width, total_height - transl_height) return super().fromWidgetCoords @property def width(self): return self.background_rectangle.width def on_draw(self,widget,context): start = self.background_rectangle.start width = self.background_rectangle.width height = self.background_rectangle.height context.save() context.rectangle(start.x,start.y,width,height) context.set_source_rgb(0,0,0) context.stroke_preserve() context.set_source_rgb(*global_constants.background) context.fill() context.restore() super().on_draw(widget,context) def is_point_in(self, p:"Point", category=MouseEvent.UNKNOWN): return self.background_rectangle.is_point_in(p)