Esempio n. 1
0
    def canvas_to_view(self, in_coords, clamp_to_canvas=False):
        """
        Return canvas coordinates in viewport coordinates as a Loc.

        :param in_coords: Canvas coordinates as a Loc.

        :param clamp_to_canvas: Whether to clamp coordinates to valid
        viewport coordinates.

        :return: Viewport coordinates converted from in_coords.
        """
        canvas_dim = self.get_canvas_dim()
        tr, bl = self.get_view_coords()

        if clamp_to_canvas:
            coords = Loc(
            MathStat.map_range_clamped(in_coords.x, 0, canvas_dim.x, bl.x, tr.x),
            MathStat.map_range_clamped(in_coords.y, canvas_dim.y, 0, bl.y, tr.y))

        else:
            coords = Loc(
            MathStat.map_range(in_coords.x, 0, canvas_dim.x, bl.x, tr.x),
            MathStat.map_range(in_coords.y, canvas_dim.y, 0, bl.y, tr.y))

        return coords
Esempio n. 2
0
 def inp_press(self, event, func=None):
     """Bind this to a widget on a ButtonPress-X event"""
     self._isheld = True
     ##self._held_buttons[event.num] = True
     self._last_loc = Loc(event.x, event.y)
     self._orig_press_loc = self._last_loc.copy()
     # call function if specified with event
     if func:
         func(event)
Esempio n. 3
0
    def on_any_mov(self, event):
        # Cancel old canvas clear timer.
        if self.atimer is not None:
            self.after_cancel(self.atimer)

        # Delete old visualisation preview.
        self.acanvas.delete("delta")

        # Create a new line of delta preview.
        center = Loc(int(self.acanvas.cget("width")),
                     int(self.acanvas.cget("height"))) // 2

        offset = event.delta * 50
        target = center + offset
        coords2 = MathStat.lerp(self.last_a_pos, target, self.interp_speed)
        self.last_a_pos = coords2

        self.acanvas.create_line(center,
                                 coords2,
                                 fill=self.acol,
                                 width=5,
                                 tags=("delta", "line"))

        self.acanvas.create_text(center + (0, 80),
                                 text=str(round(event.delta, 1)),
                                 tags=("delta"))

        self.acanvas.create_oval(coords2 + 5,
                                 coords2 - 5,
                                 fill=self.acol,
                                 outline=self.acol,
                                 tags=("delta"))

        # Set timer to remove after 50ms.
        self.atimer = self.after(50, lambda: self.acanvas.delete("line"))
Esempio n. 4
0
    def deferred_spawn_actor(self, actor_class, loc):
        """
        Begin to initialise a new actor in this world, then allow
        attributes to be set before finishing the spawning process.

        Warning: It is not safe to call any gameplay functions (eg tick)
        or anything involving the world because the actor is not officially
        in the world yet.

        * actor_class: class of actor to spawn
        * loc: location to spawn actor at

        * return: initialised actor object if successful, otherwise None
        """

        # validate actor_class to check it is valid
        if actor_class is None:
            return None

        # initialise new actor object
        actor_object = actor_class()
        actor_object.__spawn__(self, Loc(loc))

        # return the newly created actor_object for further modification and
        # to pass in to finish_deferred_spawn_actor
        return actor_object
Esempio n. 5
0
    def generate_reg_poly(num_sides, **kw):
        """
        Generate a set of vertices for a regular polygon.

        :param num_sides: (int) Number of sides of the polygon.

        :keyword center: (Loc) Center point of the polygon, in relative
        coordinates.
        Defaults to origin.

        :keyword radius: (float) Radius of the circle bounds by the polygon's
        vertices, in world units.
        Defaults to 1.0.

        :keyword radial_offset: (float) Angle to rotate the polygon, in radians.
        Defaults to 0.0.

        :return: A generator that yields vertices as Loc objects.
        """
        radius = kw.get("radius", 1.0)

        center = kw.get("center", Loc(0, 0))

        # Rotate the polygon so that the bottom has a flat side.
        radial_offset = GeomHelper.get_poly_start_angle(num_sides)
        to_add = kw.get("radial_offset")
        if to_add is not None:
            radial_offset += to_add

        for i in range(num_sides):
            yield GeomHelper.get_nth_vertex_offset(i, num_sides, radius, radial_offset) \
                + center
Esempio n. 6
0
    def get_unit_vector(angle):
        """
        Find the unit vector in the direction of the angle.

        :param angle: (float) Angle of vector, in radians.

        :return: (Loc) The unit vector.
        """
        return Loc(math.sin(angle), math.cos(angle))
Esempio n. 7
0
 def on_graph_button_release_input(self, event):
     """Call input events on nodes that are released."""
     # Find the node we released.
     center = Loc(event.x, event.y)
     found_nodes = []
     self.multi_box_trace_for_objects(center, 2, found_nodes)
     for node in found_nodes:
         if not node.generate_click_events:
             continue
         node.on_release(event)
Esempio n. 8
0
    def get_mouse_screen_position(self):
        """Returns mouse position in absolute screen coordinates.

        :rtype: Loc
        """
        if (GameplayStatics.is_game_valid()
            and self.winfo_exists()
            and GameplayStatics.game_engine._window.winfo_exists()
        ):
            return Loc(self.winfo_pointerx(), self.winfo_pointery())
        return None
Esempio n. 9
0
    def get_mouse_viewport_position(self):
        """Returns mouse position in viewport screen coordinates.

        :rtype: Loc
        """

        screen_pos = self.get_mouse_screen_position()
        if screen_pos is None:
            return

        return screen_pos - Loc(self.winfo_rootx(), self.winfo_rooty())
Esempio n. 10
0
    def __init__(self):
        """Set default values."""
        super().__init__()

        ## Amount of viewport padding to give when deciding whether to draw
        self.drawable_padding = Loc(300, 300)

        ## Whether to generate cursor over events.
        self.generate_cursor_over_events = True

        ## Whether to generate click events (default LMB).
        self.generate_click_events = True
Esempio n. 11
0
    def __init__(self, name=None, cnf={}, master=None, **kw):
        """Create an image with NAME.

        Valid resource names: data, format, file, gamma, height, palette,
        width.
        """

        # Currently displayed image proportion as an integer fraction.
        # Store in a list to avoid instant simplification where possible.
        self.current_frac = Loc(Loc(1, 1), Loc(1, 1))

        # Current input values received from using fast mode (continuous). When
        # scale_continuous_end is called the accurate value will be computed from
        # these values.
        self.current_fast_input = (1, 1)

        # Sensitivity to unprecise easy to compute figures
        # Greater remainder tolerance means less precision is retained.
        self.remainder_tolerance = 1

        # Cached image data.
        # Use preloaded base64 data so we don't have to read
        # from HDD every time (slower)
        self._imgdata = None

        # Reference to timer for automatically calling scale_continuous_end
        # after scaling is finished.
        self._end_scale_timer = None

        # Function to be called when scaling is finished and the reference to the
        # new image must be used somewhere.
        # :param: Reference to new image
        self.on_assign_image = None

        # Attempt to load image data.
        kw.update(cnf)
        cnf = {}
        self._load_image_data(kw)

        super().__init__(name=name, cnf=cnf, master=master, **kw)
Esempio n. 12
0
    def __draw_grid_origin_lines(self):
        """Create grid lines for origin lines of x and y."""

        graph = self.world
        dim = graph.get_canvas_dim()

        line_color = self.origin_line_color.to_hex()

        # Vertical
        c1 = graph.view_to_canvas(Loc(0, 0))
        if c1.x > 0 and c1.x < dim.x:
            c1.y = 0
            c2 = c1 + (0, dim.y)
            graph.create_line(c1, c2, fill=line_color, width=3,
                tags=(self.unique_id, "origin_line_vertical"))

        # Horizontal
        c1 = graph.view_to_canvas(Loc(0, 0))
        if c1.y > 0 and c1.y < dim.y:
            c1.x = 0
            c2 = c1 + (dim.x, 0)
            graph.create_line(c1, c2, fill=line_color, width=3,
            tags=(self.unique_id, "origin_line_horizontal"))
Esempio n. 13
0
    def on_vert_mov(self, event):
        # Cancel old canvas clear timer.
        if self.vtimer is not None:
            self.after_cancel(self.vtimer)

        # Delete old visualisation preview.
        self.vcanvas.delete("delta")

        # Create a new line of delta preview.
        center = Loc(int(self.vcanvas.cget("width")),
                     int(self.vcanvas.cget("height"))) // 2

        # WARNING: The delta will contain x and y components, even if
        # this function is only called on changes to one axis!
        offset = Loc(0, event.delta.y * 50)
        target = center + offset
        coords2 = MathStat.lerp(self.last_v_pos, target, self.interp_speed)
        self.last_v_pos = coords2

        self.vcanvas.create_line(center,
                                 coords2,
                                 fill=self.vcol,
                                 width=5,
                                 tags=("delta", "line"))

        self.vcanvas.create_text(center + (0, 80),
                                 text=str(round(event.delta, 1)),
                                 tags=("delta"))

        self.vcanvas.create_oval(coords2 + 5,
                                 coords2 - 5,
                                 fill=self.vcol,
                                 outline=self.vcol,
                                 tags=("delta"))

        # Set timer to remove after 50ms.
        self.vtimer = self.after(50, lambda: self.vcanvas.delete("line"))
Esempio n. 14
0
    def __init__(self, master=None, cnf={}, **kw):
        """Initialiase blueprint graph in widget MASTER."""

        # Set default values.

        ## Offset of viewport from center of the graph in pixels.
        self._view_offset   = Loc(0, 0)

        ## Relative zoom factor of the viewport.
        self._zoom_amt      = 0.0

        ## Set zoom ratio. Default is 3.
        self._zoom_ratio    = 3

        ## Inversion scale for graph motion, for x and y independently.
        ## Should only be 1 or -1 per axis.
        self.axis_inversion = Loc(1, 1)


        # Initialise canvas parent.
        Canvas.__init__(self, master, cnf, **kw)
        self.config(bg=FColor(240).to_hex())

        self.__setup_input_bindings()
Esempio n. 15
0
    def on_graph_motion_input(self, event):
        """Called when a motion event occurs on the graph."""

        # Calculate world displacement between 2 canvas pixels,
        # but not necessarily touching pixels.

        canvas_a = Loc(0, 0)  # Start from top left corner
        canvas_b = canvas_a + event.delta

        # MotionInput's delta in capped to 5 pixels of movement per event.
        canvas_b *= 5

        world_displacement = \
            self.canvas_to_view(canvas_a) - self.canvas_to_view(canvas_b)

        self._view_offset += world_displacement
Esempio n. 16
0
    def inp_motion(self, event, func=None):
        """Bind this to a widget on a ButtonX-Motion event"""
        def near_to_zero(val, offset=0.05):
            return val < offset and val > -offset

        if self._isheld:
            # get and set delta
            new_loc = Loc(event.x, event.y)
            self._delta = d = new_loc - self._last_loc
            if self._use_normalisation:
                self._normalise_delta(d)
            else:
                d *= 0.2
            # set delta in event object to return in callbacks
            event.delta = d
            # ensure the last location is updated for next motion
            self._last_loc = new_loc

            # call function if specified with event and delta (in event)
            if func:
                func(event)

        # fire bound events for button invariant bindings

        if not near_to_zero(d.x):
            be = self._get_bound_events("X", "Motion")
            if be:
                for func in be:
                    func(event)

        if not near_to_zero(d.y):
            be = self._get_bound_events("Y", "Motion")
            if be:
                for func in be:
                    func(event)

        if not near_to_zero(d.x) or not near_to_zero(d.y):
            be = self._get_bound_events("XY", "Motion")
            if be:
                for func in be:
                    func(event)
Esempio n. 17
0
    def _normalise_delta(self, in_delta, set_in_place=True):
        """Normalises in_delta to range (-1, 1).
        Optionally don't set in place"""

        # set attributes of passed in list object if necessary
        if set_in_place:
            in_delta.x = MathStat.map_range(in_delta.x,
                                            -self._normalised_delta_max,
                                            self._normalised_delta_max, -1, 1)
            in_delta.y = MathStat.map_range(in_delta.y,
                                            -self._normalised_delta_max,
                                            self._normalised_delta_max, -1, 1)
            return in_delta

        # otherwise initialise a new Loc object
        else:
            return Loc(
                MathStat.map_range(in_delta.x, -self._normalised_delta_max,
                                   self._normalised_delta_max, -1, 1),
                MathStat.map_range(in_delta.y, -self._normalised_delta_max,
                                   self._normalised_delta_max, -1, 1))
Esempio n. 18
0
    def on_graph_pointer_movement_input(self, event):
        """Call input events on nodes that that are hovered."""
        center = Loc(event.x, event.y)
        found_nodes = []
        self.multi_box_trace_for_objects(center, 2, found_nodes)

        # Use sets for easy intersection and overlaps.
        found_nodes = set(found_nodes)
        hovered_nodes = self.render_manager.hovered_nodes

        # Call mouse leave events on nodes that are no longer hovered.
        for node in hovered_nodes.difference(found_nodes):
            if not node.generate_cursor_over_events:
                continue
            node.on_end_cursor_over(event)

        # Call mouse over events on nodes that are newly hovered.
        for node in found_nodes.difference(hovered_nodes):
            if not node.generate_cursor_over_events:
                continue
            node.on_begin_cursor_over(event)

        # Save state for next call.
        self.render_manager.hovered_nodes = found_nodes
Esempio n. 19
0
 def begin_play(self):
     # Spawn the world render manager first for tick priority.
     self._render_manager = self.spawn_actor(RenderManager, Loc(0, 0))
Esempio n. 20
0
class MotionInputTest(GuiTest):
    # Colors to use for vertical, horizontal and any direction visualisation.
    hcol = "dark green"
    vcol = "dark cyan"
    acol = "dark red"

    last_h_pos = Loc(0, 0)
    last_v_pos = Loc(0, 0)
    last_a_pos = Loc(0, 0)
    interp_speed = 0.4

    _test_name = "Motion Input Visualisation"

    def on_horiz_mov(self, event):
        # Cancel old canvas clear timer.
        if self.htimer is not None:
            self.after_cancel(self.htimer)

        # Delete old visualisation preview.
        self.hcanvas.delete("delta")

        # Create a new line of delta preview.
        center = Loc(int(self.hcanvas.cget("width")),
                     int(self.hcanvas.cget("height"))) // 2

        # WARNING: The delta will contain x and y components, even if
        # this function is only called on changes to one axis!
        offset = Loc(event.delta.x * 50, 0)
        target = center + offset
        coords2 = MathStat.lerp(self.last_h_pos, target, self.interp_speed)
        self.last_h_pos = coords2

        self.hcanvas.create_line(center,
                                 coords2,
                                 fill=self.hcol,
                                 width=5,
                                 tags=("delta", "line"))

        self.hcanvas.create_text(center + (0, 80),
                                 text=str(round(event.delta, 1)),
                                 tags=("delta"))

        self.hcanvas.create_oval(coords2 + 5,
                                 coords2 - 5,
                                 fill=self.hcol,
                                 outline=self.hcol,
                                 tags=("delta"))

        # Set timer to remove after 50ms.
        self.htimer = self.after(50, lambda: self.hcanvas.delete("line"))

    def on_vert_mov(self, event):
        # Cancel old canvas clear timer.
        if self.vtimer is not None:
            self.after_cancel(self.vtimer)

        # Delete old visualisation preview.
        self.vcanvas.delete("delta")

        # Create a new line of delta preview.
        center = Loc(int(self.vcanvas.cget("width")),
                     int(self.vcanvas.cget("height"))) // 2

        # WARNING: The delta will contain x and y components, even if
        # this function is only called on changes to one axis!
        offset = Loc(0, event.delta.y * 50)
        target = center + offset
        coords2 = MathStat.lerp(self.last_v_pos, target, self.interp_speed)
        self.last_v_pos = coords2

        self.vcanvas.create_line(center,
                                 coords2,
                                 fill=self.vcol,
                                 width=5,
                                 tags=("delta", "line"))

        self.vcanvas.create_text(center + (0, 80),
                                 text=str(round(event.delta, 1)),
                                 tags=("delta"))

        self.vcanvas.create_oval(coords2 + 5,
                                 coords2 - 5,
                                 fill=self.vcol,
                                 outline=self.vcol,
                                 tags=("delta"))

        # Set timer to remove after 50ms.
        self.vtimer = self.after(50, lambda: self.vcanvas.delete("line"))

    def on_any_mov(self, event):
        # Cancel old canvas clear timer.
        if self.atimer is not None:
            self.after_cancel(self.atimer)

        # Delete old visualisation preview.
        self.acanvas.delete("delta")

        # Create a new line of delta preview.
        center = Loc(int(self.acanvas.cget("width")),
                     int(self.acanvas.cget("height"))) // 2

        offset = event.delta * 50
        target = center + offset
        coords2 = MathStat.lerp(self.last_a_pos, target, self.interp_speed)
        self.last_a_pos = coords2

        self.acanvas.create_line(center,
                                 coords2,
                                 fill=self.acol,
                                 width=5,
                                 tags=("delta", "line"))

        self.acanvas.create_text(center + (0, 80),
                                 text=str(round(event.delta, 1)),
                                 tags=("delta"))

        self.acanvas.create_oval(coords2 + 5,
                                 coords2 - 5,
                                 fill=self.acol,
                                 outline=self.acol,
                                 tags=("delta"))

        # Set timer to remove after 50ms.
        self.atimer = self.after(50, lambda: self.acanvas.delete("line"))

    # Setup widgets and movement components.

    def start(self):
        """Called when initialised to create test widgets."""

        # Initialise timer variables to None. (no need to clear canvas yet!)
        self.vtimer = self.htimer = self.atimer = None

        Label(self,
              text="WARNING: The delta will contain x and y components, "
              "even if that function is only called on changes to one axis!\n"
              "WARNING 2: Smoothing not included").pack()

        # Horizontal movement

        horiz_frame = Labelframe(self, text="Horizontal")
        horiz_frame.pack(side="left", padx=10, pady=10)

        # Canvas for previewing delta movement.
        self.hcanvas = Canvas(horiz_frame, width=200, height=200)
        self.hcanvas.create_oval(110,
                                 110,
                                 90,
                                 90,
                                 fill=self.hcol,
                                 outline=self.hcol)
        self.hcanvas.pack()

        # Label for dragging from.
        l = Label(horiz_frame, text="DRAG ME", relief="ridge")
        l.pack(ipadx=10, ipady=10, padx=20, pady=20)

        # Create button to reset canvas.
        Button(horiz_frame,
               text="Reset",
               command=lambda: self.hcanvas.delete("delta")).pack(padx=3,
                                                                  pady=3)

        # Motion input (the actual thing being tested!)
        m = MotionInput(l)
        m.bind("<Motion-X>", self.on_horiz_mov)

        # Vertical movement

        vert_frame = Labelframe(self, text="Vertical")
        vert_frame.pack(side="left", padx=10, pady=10)

        # Canvas for previewing delta movement.
        self.vcanvas = Canvas(vert_frame, width=200, height=200)
        self.vcanvas.create_oval(110,
                                 110,
                                 90,
                                 90,
                                 fill=self.vcol,
                                 outline=self.vcol)
        self.vcanvas.pack()

        # Label for dragging from.
        l = Label(vert_frame, text="DRAG ME", relief="ridge")
        l.pack(ipadx=10, ipady=10, padx=20, pady=20)

        # Create button to reset canvas.
        Button(vert_frame,
               text="Reset",
               command=lambda: self.vcanvas.delete("delta")).pack(padx=3,
                                                                  pady=3)

        # Motion input (the actual thing being tested!)
        m = MotionInput(l)
        m.bind("<Motion-Y>", self.on_vert_mov)

        # Any movement

        any_frame = Labelframe(self, text="Any Direction")
        any_frame.pack(side="left", padx=10, pady=10)

        # Canvas for previewing delta movement.
        self.acanvas = Canvas(any_frame, width=200, height=200)
        self.acanvas.create_oval(110,
                                 110,
                                 90,
                                 90,
                                 fill=self.acol,
                                 outline=self.acol)
        self.acanvas.pack()

        # Label for dragging from.
        l = Label(any_frame, text="DRAG ME", relief="ridge")
        l.pack(ipadx=10, ipady=10, padx=20, pady=20)

        # Create button to reset canvas.
        Button(any_frame,
               text="Reset",
               command=lambda: self.acanvas.delete("delta")).pack(padx=3,
                                                                  pady=3)

        # Motion input (the actual thing being tested!)
        m = MotionInput(l)
        m.bind("<Motion-XY>", self.on_any_mov)
Esempio n. 21
0
 def delta(self):
     return Loc(self._delta)
Esempio n. 22
0
class MotionInput(object):
    """Add motion input event to widgets."""
    def __init__(self, *args, **kw):
        """initialise attributes. optionally call bind_to_widget with
        specified args if args are not empty

        AVAILABLE KEYWORDS
            normalise (bool) Whether to use acceleration smoothing on motion.
            True by default
        """
        self._isheld = False

        self._delta = (0, 0)
        self._normalised_delta_max = 5
        self._use_normalisation = kw.get("normalise", True)

        self._bound_events = {}
        ##self._held_buttons = {}

        # bind to widget if extra args given
        if args:
            self.bind_to_widget(*args)

    @property
    def delta(self):
        return Loc(self._delta)

    def bind_to_widget(self, in_widget, button="1"):
        """binds relevant inputs to in_widget, optionally using the
        specified button (1=LMB, 2=MMB, 3=RMB)"""
        in_widget.bind("<ButtonPress-%s>" % button, self.inp_press, True)
        in_widget.bind("<ButtonRelease-%s>" % button, self.inp_release, True)
        in_widget.bind("<Button%s-Motion>" % button, self.inp_motion, True)
        # add to held buttons dict (defualt False not held)
        ##self._held_buttons[button] = False

    def bind(self, event_code=None, func=None, add=None):
        """Binds func to be called on event_code.
        Event codes is written in the format <MODIFIER-MODIFIER-IDENTIFIER>.
        Available MODIFIERS:
            Motion
        Available IDENTIFIERS:
            X
            Y
            XY"""
        event_code = event_code.replace("<", "").replace(">", "")
        keys = event_code.split("-")
        identifier = keys.pop()
        modifiers = keys
        # check if the event_code is valid
        if identifier not in ["X", "Y", "XY"]:
            return False  # fail
        for m in modifiers:
            if m not in ["Motion", "Button1", "Button2", "Button3"]:
                return False  # epic fail!

        # bind the function
        # create new list for event if not already bound
        if event_code not in self._bound_events:
            self._bound_events[event_code] = [func]
        else:
            # append to list if necessary
            if add:
                self._bound_events[event_code].append(func)
            # otherwise initialise new list
            else:
                self._bound_events[event_code] = [func]
        return True  # success

    def _get_bound_events(self, identifier=None, *modifiers):
        """Returns list of bound functions to call for the specified event"""
        ret_funcs = []
        modifiers = set(modifiers)  # ensure modifiers are unique

        # check every bound event code
        for event_code, func_list in self._bound_events.items():
            event_keys = event_code.split("-")
            event_id = event_keys.pop()
            event_mods = event_keys
            all_mods_work = True

            # check identifier
            id_works = (identifier is None
                        or identifier is not None and identifier == event_id)
            # only check modifiers if identifier is correct
            if id_works:
                for m in modifiers:
                    if m not in event_mods:
                        all_mods_work = False
                        break
            # add bound functions if id and modifiers are correct
            if id_works and all_mods_work:
                ret_funcs = ret_funcs + func_list

        # finally return found functions
        return ret_funcs if ret_funcs else None

    def _normalise_delta(self, in_delta, set_in_place=True):
        """Normalises in_delta to range (-1, 1).
        Optionally don't set in place"""

        # set attributes of passed in list object if necessary
        if set_in_place:
            in_delta.x = MathStat.map_range(in_delta.x,
                                            -self._normalised_delta_max,
                                            self._normalised_delta_max, -1, 1)
            in_delta.y = MathStat.map_range(in_delta.y,
                                            -self._normalised_delta_max,
                                            self._normalised_delta_max, -1, 1)
            return in_delta

        # otherwise initialise a new Loc object
        else:
            return Loc(
                MathStat.map_range(in_delta.x, -self._normalised_delta_max,
                                   self._normalised_delta_max, -1, 1),
                MathStat.map_range(in_delta.y, -self._normalised_delta_max,
                                   self._normalised_delta_max, -1, 1))

    # def _is_held(self, button):
    #     """returns whether the button is held"""
    #     return button in self._held_buttons and self._held_buttons[button]

    def inp_press(self, event, func=None):
        """Bind this to a widget on a ButtonPress-X event"""
        self._isheld = True
        ##self._held_buttons[event.num] = True
        self._last_loc = Loc(event.x, event.y)
        self._orig_press_loc = self._last_loc.copy()
        # call function if specified with event
        if func:
            func(event)

    def inp_release(self, event=None, func=None):
        """Bind this to a widget on a ButtonRelease-X event"""
        self._isheld = False
        ##self._held_buttons[event.num] = False
        # call function if specified with event
        if func:
            func(event)

    def inp_motion(self, event, func=None):
        """Bind this to a widget on a ButtonX-Motion event"""
        def near_to_zero(val, offset=0.05):
            return val < offset and val > -offset

        if self._isheld:
            # get and set delta
            new_loc = Loc(event.x, event.y)
            self._delta = d = new_loc - self._last_loc
            if self._use_normalisation:
                self._normalise_delta(d)
            else:
                d *= 0.2
            # set delta in event object to return in callbacks
            event.delta = d
            # ensure the last location is updated for next motion
            self._last_loc = new_loc

            # call function if specified with event and delta (in event)
            if func:
                func(event)

        # fire bound events for button invariant bindings

        if not near_to_zero(d.x):
            be = self._get_bound_events("X", "Motion")
            if be:
                for func in be:
                    func(event)

        if not near_to_zero(d.y):
            be = self._get_bound_events("Y", "Motion")
            if be:
                for func in be:
                    func(event)

        if not near_to_zero(d.x) or not near_to_zero(d.y):
            be = self._get_bound_events("XY", "Motion")
            if be:
                for func in be:
                    func(event)
Esempio n. 23
0
 def get_canvas_dim(self):
     """Return dimensions of canvas in pixels as a Loc."""
     return Loc(self.winfo_width(), self.winfo_height())
Esempio n. 24
0
 def get_screen_size_factor(self):
     """Return the viewport scale factor to ensure the same
     sized viewport is shown at all scales."""
     return MathStat.clamp(max(MathStat.getpercent(self.get_canvas_dim(), Loc(3840, 2160), Loc(640, 480))), 0.5, 8)
Esempio n. 25
0
 def __set_location(self, value):
     self._location = Loc(value)