Luke-zhang-04/CanvasPlus

View on GitHub
CanvasPlus/pythonBelow35.py

Summary

Maintainability
A
0 mins
Test Coverage
"""The CanvasPlus package, version 1.3.2 for Python 3.0-3.4."""
"""Luke-zhang-04
CanvasPlus (https://github.com/Luke-zhang-04/CanvasPlus)
Copyright (C) 2020 Luke Zhang
Licensed under the MIT License
"""

# This file is meant for those who intend on using this package for Python versions 3.0 - 3.4
# For Python 3.5 and above, use the main CanvasPlus folder

from tkinter import Canvas

if __name__ != "__main__":
    from CanvasPlus._errors import *
    from CanvasPlus.templates import Template
    from CanvasPlus.pythonBelow35Assets import (
        WidgetWindows,
        AsyncTransformations,
        Transformations,
    )
else:
    from _errors import *
    from templates import Template
    from pythonBelow35Assets import WidgetWindows, AsyncTransformations, Transformations

_canvasPlusVersion = "v1.4.1"

print("This is CanvasPlus %s for Python versions below 3.5" % _canvasPlusVersion)


class CanvasPlus(Canvas, WidgetWindows, Transformations, AsyncTransformations):
    """Improved Canvas widget with more functionality to display graphical elements like lines or text."""

    def clone(self, tagOrId, *args):
        """clones tagOrId and places is at optional coordinates, or places is on top of the first object."""
        if len(args) == 0:
            args = self.coords(tagOrId)

        output = self.get_attributes(tagOrId)

        return self._create(self.tk.call(self._w, "type", tagOrId), args, output)

    def create_arrow(
        self, x1, y1, headLength, headWidth, bodyLength, bodyWidth, **kwargs
    ):
        """Create arrow with x1, y1 as the tip; headWith, headLengh as the length and width of the arrowhead; and bodyLength, bodyWidth as the length and width of the arrow body, as well as direction = val (0 by default)."""

        points = [
            x1,
            y1,
            x1 - headWidth // 2,
            y1 + headLength,
            x1 - bodyWidth // 2,
            y1 + headLength,
            x1 - bodyWidth // 2,
            y1 + bodyLength,
            x1 + bodyWidth // 2,
            y1 + bodyLength,
            x1 + bodyWidth // 2,
            y1 + headLength,
            x1 + headWidth // 2,
            y1 + headLength,
        ]

        return self._create("polygon", points, kwargs)

    def create_circle(self, x, y, radius, **kwargs):
        """Create circle with coordinates x, y, radius."""
        return self._create(
            "oval", [x + radius, y + radius, x - radius, y - radius], kwargs
        )

    def create_round_rectangle(self, x1, y1, x2, y2, radius=25, **kwargs):
        """Create circle with coordinates x1, y1, x2, y2, radius = val (default 25)."""
        points = [
            x1 + radius,
            y1,
            x1 + radius,
            y1,
            x2 - radius,
            y1,
            x2 - radius,
            y1,
            x2,
            y1,
            x2,
            y1 + radius,
            x2,
            y1 + radius,
            x2,
            y2 - radius,
            x2,
            y2 - radius,
            x2,
            y2,
            x2 - radius,
            y2,
            x2 - radius,
            y2,
            x1 + radius,
            y2,
            x1 + radius,
            y2,
            x1,
            y2,
            x1,
            y2 - radius,
            x1,
            y2 - radius,
            x1,
            y1 + radius,
            x1,
            y1 + radius,
            x1,
            y1,
        ]

        kwargs["smooth"] = True
        return self._create("polygon", points, kwargs)

    def get_attributes(self, tagOrId):
        """Returns all properties of tagOrId."""
        properties = self.itemconfig(tagOrId)
        return {key: properties[key][-1] for key in properties}

    get_attr = get_attributes

    def __iter__(self):
        """Creates iterator of everything on the canvas."""
        return iter(self.find_all())

    def to_polygon(self, tagOrId):
        """converts rectangle to polygon"""
        output = self.get_attributes(tagOrId)

        cords = [
            self.tk.getdouble(x)
            for x in self.tk.splitlist(
                self.tk.call((self._w, "coords") + tuple([tagOrId]))
            )
        ]

        if output["width"] == "0.0":
            output["outline"] = ""

        if self.tk.call(self._w, "type", tagOrId) == "rectangle":
            newCords = [
                cords[0],
                cords[1],
                cords[2],
                cords[1],
                cords[2],
                cords[3],
                cords[0],
                cords[3],
            ]
        else:
            raise InvalidObjectType(
                'Invalid canvas element "'
                + self.tk.call(self._w, "type", tagOrId)
                + '"'
            )

        self.tk.call((self._w, "delete") + tuple([tagOrId]))

        return self._create("polygon", newCords, output)

    poly = to_polygon

    def tags_bind(self, tagsOrIds, sequences=None, funcs=None, add=False):
        """Binds either multiple tags to one function, or multiple tags to multiple functions with matching indicies in one function.
        
        i.e (tag1, tag2, tag3), func1 will bind tag1, tag2, tag3 into fun1, while (tag1, tag2, tag3), (func1, func2, func3) will bind tag1 to func1, tag2 to func2, tag3 to func3.
        """
        if type(tagsOrIds) == int and callable(funcs):  # normal tag_bind
            return self._bind((self._w, "bind", tagsOrIds), sequences, funcs, add)
        else:
            bindings = []
            if type(funcs) not in [list, tuple]:
                funcs = tuple([funcs])
            if type(sequences) not in [list, tuple]:
                sequences = tuple([sequences])

            for index, obj in enumerate(tagsOrIds):
                bindings.append(
                    self._bind(
                        (self._w, "bind", obj),
                        sequences[index % len(sequences)],
                        funcs[index % len(funcs)],
                        add,
                    )
                )
            return bindings

    def render_template(self, template: Template, *args, **kwargs):
        """Renders a template from the template class."""
        coords = args if len(template.coords) == 0 else template.coords

        properties = template.properties
        for i in kwargs:
            properties[i] = kwargs[i]

        shape = self._create(template.type, coords, properties)

        if template.onRender:
            template.onRender(shape)

        return shape