class ColourPicker: pick_colour_callback: Callable[[Tuple[float, float, float, float]], None] __base: ShowBase __palette_img: PNMImage __palette_size: Tuple[int, int] __palette_frame: DirectFrame __marker: DirectFrame __marker_center: DirectFrame enabled: bool def __init__(self, base: ShowBase, pick_colour_callback: Callable[ [Tuple[float, float, float, float]], None], **kwargs) -> None: self.__base = base self.pick_colour_callback = pick_colour_callback self.enabled = True # PALETTE # palette_filename = os.path.join(GUI_DATA_PATH, "colour_palette.png") self.__palette_img = PNMImage( Filename.fromOsSpecific(palette_filename)) self.__palette_size = (self.__palette_img.getReadXSize(), self.__palette_img.getReadYSize()) self.__palette_frame = DirectFrame(image=palette_filename, **kwargs) self.__palette_frame['state'] = DGG.NORMAL self.__palette_frame.bind(DGG.B1PRESS, command=self.__pick) # MARKER # self.__marker = DirectFrame(parent=self.__palette_frame, frameColor=(0.0, 0.0, 0.0, 1.0), frameSize=(-0.08, .08, -.08, .08), pos=(0.0, 0.0, 0.0)) self.__marker_center = DirectFrame(parent=self.__marker, frameSize=(-0.03, 0.03, -0.03, 0.03)) self.__marker.hide() def __colour_at( self, x: float, y: float) -> Union[Tuple[float, float, float, float], None]: w, h = self.__palette_size screen = self.__base.pixel2d img_scale = self.__palette_frame['image_scale'] sx = self.__palette_frame.getSx(screen) * img_scale[0] sy = self.__palette_frame.getSz(screen) * img_scale[2] x -= self.__palette_frame.getX(screen) y -= self.__palette_frame.getZ(screen) x = (0.5 + x / (2.0 * sx)) * w y = (0.5 - y / (2.0 * sy)) * h if 0 <= x < w and 0 <= y < h: return (*self.__palette_img.getXel(int(x), int(y)), 1.0) else: return None def __update_marker_colour(self) -> Tuple[float, float, float, float]: c = self.colour_under_marker() if c is None: c = self.__marker_center['frameColor'] else: self.__marker_center['frameColor'] = c return c def __update_marker_pos(self) -> None: if not self.__base.mouseWatcherNode.hasMouse(): return None pointer = self.__base.win.get_pointer(0) x, y = pointer.getX(), -pointer.getY() w, h = self.__palette_size screen = self.__base.pixel2d img_scale = self.__palette_frame['image_scale'] sx = self.__palette_frame.getSx(screen) * img_scale[0] sy = self.__palette_frame.getSz(screen) * img_scale[2] x -= self.__palette_frame.getX(screen) y -= self.__palette_frame.getZ(screen) x /= sx y /= sy x = max(-0.92, min(0.92, x)) y = max(-0.92, min(0.92, y)) self.__marker.set_pos(x, 0.0, y) self.__marker.show() def colour_under_marker( self) -> Union[Tuple[float, float, float, float], None]: x, _, y = self.__marker.get_pos() w, h = self.__palette_size screen = self.__base.pixel2d img_scale = self.__palette_frame['image_scale'] sx = self.__palette_frame.getSx(screen) * img_scale[0] sy = self.__palette_frame.getSz(screen) * img_scale[2] x *= sx y *= sy x += self.__palette_frame.getX(screen) y += self.__palette_frame.getZ(screen) return self.__colour_at(x, y) def colour_under_mouse( self) -> Union[Tuple[float, float, float, float], None]: if not self.__base.mouseWatcherNode.hasMouse(): return None pointer = self.__base.win.get_pointer(0) return self.__colour_at(pointer.getX(), -pointer.getY()) def __pick(self, *args): if self.enabled: self.__update_marker_pos() self.pick_colour_callback(self.__update_marker_colour()) @property def frame(self) -> DirectFrame: return self.__palette_frame @property def marker(self) -> DirectFrame: return self.__marker