Source code for SimpleGUICS2Pygame.simpleguics2pygame.canvas

# -*- coding: latin-1 -*-

"""
simpleguics2pygame module: simpleguics2pygame/canvas.

Class Canvas.

Piece of SimpleGUICS2Pygame.
https://bitbucket.org/OPiMedia/simpleguics2pygame

:license: GPLv3 --- Copyright (C) 2015-2016, 2020-2021 Olivier Pirson
:author: Olivier Pirson --- http://www.opimedia.be/
:version: May 4, 2021
"""

from __future__ import division
from __future__ import print_function

# print('IMPORT', __name__)


import math
import os.path
import re
import sys


__all__ = ('Canvas',
           'create_invisible_canvas')


try:
    from typing import Any, Callable, List, Optional, Sequence, Tuple, Union  # noqa
except ImportError:
    pass

import pygame

from SimpleGUICS2Pygame.simpleguics2pygame._colors import _SIMPLEGUICOLOR_TO_PYGAMECOLOR, _simpleguicolor_to_pygamecolor  # pylint: disable=wrong-import-position,no-name-in-module,ungrouped-imports  # noqa
from SimpleGUICS2Pygame.simpleguics2pygame._fonts import _SIMPLEGUIFONTFACE_TO_PYGAMEFONTNAME, _simpleguifontface_to_pygamefont  # pylint: disable=wrong-import-position,no-name-in-module,ungrouped-imports  # noqa
from SimpleGUICS2Pygame.simpleguics2pygame.image import Image  # pylint: disable=wrong-import-position,no-name-in-module,ungrouped-imports  # noqa


#
# Private global constants
##########################
_RADIAN_TO_DEGREE = 180 / math.pi
"""
Multiplicative constant to convert radian to degree.
"""


_RE_UNPRINTABLE_WHITESPACE_CHAR = re.compile('[\t\n\r\f\v]')
"""
Regular expression pattern to unprintable whitespace character.
"""


#
# "Private" function
####################
def _pos_round(position):
    # type: (Sequence[Union[int, float]]) -> Tuple[int, int]
    """
    Return the rounded `position`.

    **Don't require Pygame.**

    **(Not available in SimpleGUI of CodeSkulptor.)**

    :param position: (int or float, int or float)
                     or [int or float, int or float]

    :return: (int, int)
    """
    assert isinstance(position, (tuple, list)), type(position)
    assert len(position) == 2, len(position)
    assert isinstance(position[0], (int, float)), type(position[0])
    assert isinstance(position[1], (int, float)), type(position[1])

    return (int(round(position[0])), int(round(position[1])))


#
# Class
#######
[docs]class Canvas: """Canvas similar to SimpleGUI `Canvas` of CodeSkulptor.""" _background_pygame_color = _SIMPLEGUICOLOR_TO_PYGAMECOLOR['black'] """Default `pygame.Color` of the background of the canvas.""" _bg_pygame_surface_image = None # type: Optional[pygame.surface.Surface] """ `pygame.surface.Surface` default background image replaces `_background_pygame_color`. """
[docs] def __init__(self, frame, canvas_width, canvas_height): # type: (Optional[pygame.Frame], int, int) -> None # noqa """ Set the canvas. **Don't use directly**, a canvas is created by `Frame()` and reachable by handler defined by `Frame.set_draw_handler()`. :param frame: Frame (or None) :param canvas_width: int >= 0 :param canvas_height: int >= 0 """ assert isinstance(canvas_width, int), type(canvas_width) assert canvas_width >= 0, canvas_width assert isinstance(canvas_height, int), type(canvas_height) assert canvas_height >= 0, canvas_height self._frame_parent = frame self._width = canvas_width self._height = canvas_height self._background_pygame_color = Canvas._background_pygame_color self._draw_handler = None # type: Optional[Callable[[Canvas], Any]] self._pygame_surface = pygame.surface.Surface((canvas_width, canvas_height)) # pylint: disable=too-many-function-args # noqa
[docs] def __repr__(self): # type: () -> str """ Return `'<Canvas object>'`. :return: str """ return '<Canvas object>'
[docs] def _draw(self): # type: () -> None """ If `self._draw_handler` != `None` then call it and update display of the canvas. **(Not available in SimpleGUI of CodeSkulptor.)** """ if ((self._draw_handler is not None) and (self._frame_parent is not None)): if self._bg_pygame_surface_image is None: if self._background_pygame_color.a == 255: # Without alpha self._pygame_surface.fill(self._background_pygame_color) elif self._background_pygame_color.a > 0: # With alpha (not null) s_alpha = pygame.surface.Surface((self._width, self._height), # pylint: disable=too-many-function-args # noqa pygame.SRCALPHA) # pylint: disable=no-member # noqa s_alpha.fill(self._background_pygame_color) self._pygame_surface.blit(s_alpha, (0, 0)) else: self._pygame_surface.blit( self._bg_pygame_surface_image, (0, 0)) self._draw_handler(self) if self._frame_parent._display_fps_average: # pylint: disable=protected-access # noqa self._pygame_surface.blit( _simpleguifontface_to_pygamefont(None, 40) .render(str(int(round(self._frame_parent._fps_average))), # pylint: disable=protected-access # noqa True, _SIMPLEGUICOLOR_TO_PYGAMECOLOR['red']), (10, self._height - 40)) self._frame_parent._pygame_surface.blit( # pylint: disable=protected-access # noqa self._pygame_surface, (self._frame_parent._canvas_x_offset, # pylint: disable=protected-access # noqa self._frame_parent._canvas_y_offset)) # pylint: disable=protected-access # noqa pygame.display.update((self._frame_parent._canvas_x_offset, # pylint: disable=protected-access # noqa self._frame_parent._canvas_y_offset, # pylint: disable=protected-access # noqa self._width, # pylint: disable=protected-access # noqa self._height)) # pylint: disable=protected-access # noqa
[docs] def _save(self, filename): # type: (str) -> None """ Save the canvas in `filename`. Supported formats are supported formats by Pygame to save: TGA, PNG, JPEG or BMP (see https://www.pygame.org/docs/ref/image.html#pygame.image.save ). If `filename` extension is not recognized then TGA format is used. **(Not available in SimpleGUI of CodeSkulptor.)** :param filename: str """ assert isinstance(filename, str), type(filename) filename = os.path.abspath(os.path.expanduser(filename)) pygame.image.save(self._pygame_surface, filename)
[docs] def draw_arc(self, # pylint: disable=too-many-arguments center_point, radius, start_angle, end_angle, line_width, line_color): # type: (Sequence[Union[int, float]], Union[int, float], Union[int, float], Union[int, float], Union[int, float], str) -> None # noqa """ Draw an arc of circle, from `start_angle` to `end_angle`. Angles given in radians are clockwise and start from 0 at the 3 o'clock position. (Available in CodeSkulptor3 but *not in CodeSkulptor2*!) :param center_point: (int or float, int or float) or [int or float, int or float] :param radius: (int or float) > 0 :param start_angle: int or float :param end_angle: int or float :param line_width: (int or float) > 0 :param line_color: str """ assert isinstance(center_point, (tuple, list)), type(center_point) assert len(center_point) == 2, len(center_point) assert isinstance(center_point[0], (int, float)), type(center_point[0]) assert isinstance(center_point[1], (int, float)), type(center_point[1]) assert isinstance(radius, (int, float)), type(radius) assert radius > 0, radius assert isinstance(start_angle, (int, float)), (start_angle) assert isinstance(end_angle, (int, float)), type(end_angle) assert isinstance(line_width, (int, float)), type(line_width) assert line_width > 0, line_width assert isinstance(line_color, str), type(line_color) line_width = (1 if line_width <= 1 else int(round(line_width))) radius = int(round(radius)) + int(round(line_width // 2)) # Adapt Codeskulptor angles to Pygame if start_angle == end_angle: return start_angle = -start_angle end_angle = -end_angle start_angle, end_angle = end_angle, start_angle double_pi = math.pi * 2 start_angle %= double_pi end_angle %= double_pi if start_angle == end_angle: return # Draw if radius > 1: pygamecolor = _simpleguicolor_to_pygamecolor(line_color) if pygamecolor.a > 0: diameter = radius * 2 s_tmp = pygame.surface.Surface((diameter, diameter), # pylint: disable=too-many-function-args # noqa pygame.SRCALPHA) # pylint: disable=no-member # noqa pygame.draw.arc(s_tmp, pygamecolor, s_tmp.get_rect(), start_angle, end_angle, min(line_width, radius)) self._pygame_surface.blit(s_tmp, (center_point[0] - radius, center_point[1] - radius)) elif radius > 0: # == 1 self.draw_point(center_point, line_color)
[docs] def draw_circle(self, # pylint: disable=too-many-arguments center_point, radius, line_width, line_color, fill_color=None): # type: (Sequence[Union[int, float]], Union[int, float], Union[int, float], str, Optional[str]) -> None # noqa """ Draw a circle. If `fill_color` != `None` then fill with this color. :param center_point: (int or float, int or float) or [int or float, int or float] :param radius: (int or float) > 0 :param line_width: (int or float) > 0 :param line_color: str :param fill_color: None or str """ assert isinstance(center_point, (tuple, list)), type(center_point) assert len(center_point) == 2, len(center_point) assert isinstance(center_point[0], (int, float)), type(center_point[0]) assert isinstance(center_point[1], (int, float)), type(center_point[1]) assert isinstance(radius, (int, float)), type(radius) assert radius > 0, radius assert isinstance(line_width, (int, float)), type(line_width) assert line_width > 0, line_width assert isinstance(line_color, str), type(line_color) assert (fill_color is None) or isinstance(fill_color, str), \ type(fill_color) line_width = (1 if line_width <= 1 else int(round(line_width))) radius = int(round(radius)) + int(round(line_width // 2)) if radius > 1: pygamecolor = _simpleguicolor_to_pygamecolor(line_color) pygamefillcolor = (None if fill_color is None else _simpleguicolor_to_pygamecolor(fill_color)) center_point_rounded = _pos_round(center_point) if ((pygamecolor.a == 255) and ((pygamefillcolor is None) or (pygamefillcolor.a == 255))): # Without alpha if pygamefillcolor is not None: pygame.draw.circle(self._pygame_surface, pygamefillcolor, center_point_rounded, radius, 0) if pygamecolor != pygamefillcolor: pygame.draw.circle(self._pygame_surface, pygamecolor, center_point_rounded, radius, min(line_width, radius)) elif ((pygamecolor.a > 0) or ((pygamefillcolor is not None) and (pygamefillcolor.a > 0))): # With one or two alpha (not null) diameter = radius * 2 s_alpha = pygame.surface.Surface((diameter, diameter), # pylint: disable=too-many-function-args # noqa pygame.SRCALPHA) # pylint: disable=no-member # noqa if (pygamefillcolor is not None) and (pygamefillcolor.a > 0): pygame.draw.circle(s_alpha, pygamefillcolor, (radius, radius), radius, 0) if (pygamecolor != pygamefillcolor) and (pygamecolor.a > 0): pygame.draw.circle(s_alpha, pygamecolor, (radius, radius), radius, min(line_width, radius)) self._pygame_surface.blit(s_alpha, (center_point_rounded[0] - radius, center_point_rounded[1] - radius)) elif radius > 0: # == 1 self.draw_point(center_point, line_color)
[docs] def draw_image(self, # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements # noqa image, center_source, width_height_source, center_dest, width_height_dest, rotation=0): # type: (Image, Sequence[Union[int, float]], Sequence[Union[int, float]], Sequence[Union[int, float]], Sequence[Union[int, float]], Union[int, float]) -> None # noqa """ Draw `image` on the canvas. Specify center position and size of the source (`image`) and center position and size of the destination (the canvas). Size of the source allow get a piece of `image`. If `width_height_source` is bigger than `image` then draw nothing. Size of the destination allow rescale the drawed image. `rotation` specify a clockwise rotation in radians. Each new Pygame surface used is added to `image._pygamesurfaces_cached`. See `Image._pygamesurfaces_cached_clear()`_ . .. _`Image._pygamesurfaces_cached_clear()`: image.html#SimpleGUICS2Pygame.simpleguics2pygame.image.Image._pygamesurfaces_cached_clear If number of surfaces in this caches is greater than `image._pygamesurfaces_cache_max_size` then remove the oldest surface. :param image: Image :param center_source: (int or float, int or float) or [int or float, int or float] :param width_height_source: ((int or float) >= 0, (int or float) >= 0) or [(int or float) >= 0, (int or float) >= 0] :param center_dest: (int or float, int or float) or [int or float, int or float] :param width_height_dest: ((int or float) >= 0, (int or float) >= 0) or [(int or float) >= 0, (int or float) >= 0] :param rotation: int or float """ # noqa assert isinstance(image, Image), type(image) assert isinstance(center_source, (tuple, list)), \ type(center_source) assert len(center_source) == 2, len(center_source) assert isinstance(center_source[0], (int, float)), \ type(center_source[0]) assert isinstance(center_source[1], (int, float)), \ type(center_source[1]) assert isinstance(width_height_source, (tuple, list)), \ type(width_height_source) assert len(width_height_source) == 2, len(width_height_source) assert isinstance(width_height_source[0], (int, float)), \ type(width_height_source[0]) assert width_height_source[0] >= 0, width_height_source[0] assert isinstance(width_height_source[1], (int, float)), \ type(width_height_source[1]) assert width_height_source[1] >= 0, width_height_source[1] assert isinstance(center_dest, (tuple, list)), type(center_dest) assert len(center_dest) == 2, len(center_dest) assert isinstance(center_dest[0], (int, float)), type(center_dest[0]) assert isinstance(center_dest[1], (int, float)), type(center_dest[1]) assert isinstance(width_height_dest, (tuple, list)), \ type(width_height_dest) assert len(width_height_dest) == 2, len(width_height_dest) assert isinstance(width_height_dest[0], (int, float)), \ type(width_height_dest[0]) assert width_height_dest[0] >= 0, width_height_dest[0] assert isinstance(width_height_dest[1], (int, float)), \ type(width_height_dest[1]) assert width_height_dest[1] >= 0, width_height_dest[1] assert isinstance(rotation, (int, float)), type(rotation) if image._pygame_surface is None: # pylint: disable=protected-access return # Calculate parameters width_source, height_source = width_height_source x0_source = center_source[0] - width_source / 2 y0_source = center_source[1] - height_source / 2 if x0_source >= 0: x0_source = int(round(x0_source)) elif -1 < x0_source: # rounding error correcting width_source -= x0_source x0_source = 0 else: # outside of source image return if y0_source >= 0: y0_source = int(round(y0_source)) elif -1 < y0_source: # rounding error correcting height_source -= y0_source y0_source = 0 else: # outside of source image return width_source = int(round(width_source)) height_source = int(round(height_source)) if ((x0_source + width_source > image.get_width() + 1) or (y0_source + height_source > image.get_height() + 1)): # Bigger than source image return if x0_source + width_source > image.get_width(): # Keep this image (seem too big, maybe rounding error) width_source -= 1 if y0_source + height_source > image.get_height(): # Keep this image (seem too big, maybe rounding error) height_source -= 1 width_height_dest = _pos_round(width_height_dest) rotation = int(round(-rotation * _RADIAN_TO_DEGREE)) % 360 # Get in cache or build Pygame surface if sys.version_info[:2] >= (3, 2): move_to_end = image._pygamesurfaces_cached.move_to_end # pylint: disable=protected-access # noqa else: def move_to_end(key): # type: (int) -> None """ Move the `key` item to the newest place of the surfaces cache. :param key: tuple of 7 (int >= 0) """ del image._pygamesurfaces_cached[key] # pylint: disable=protected-access # noqa image._pygamesurfaces_cached[key] = pygame_surface_image # pylint: disable=protected-access # noqa key = (x0_source, y0_source, width_source, height_source, width_height_dest[0], width_height_dest[1], rotation) pygame_surface_image = image._pygamesurfaces_cached.get(key) # type: Optional[pygame.surface.Surface] # pylint: disable=protected-access # noqa if pygame_surface_image is not None: # Result available move_to_end(key) if __debug__: image._pygamesurfaces_cached_counts[0] += 1 # pylint: disable=protected-access # noqa else: # Build result key_0 = key[:-1] + (0, ) if rotation != 0: # Get not rotated surface in cache pygame_surface_image = image._pygamesurfaces_cached.get(key_0) # pylint: disable=protected-access # noqa if pygame_surface_image is not None: # Not rotated available move_to_end(key_0) if __debug__: image._pygamesurfaces_cached_counts[1] += 1 # pylint: disable=protected-access # noqa else: # Build piece and/or resize if ((x0_source == 0) and (y0_source == 0) and (width_source == image.get_width()) and (height_source == image.get_height())): pygame_surface_image = image._pygame_surface # pylint: disable=protected-access # noqa else: # Get a piece in source pygame_surface_image = image._pygame_surface.subsurface( # pylint: disable=protected-access # noqa (x0_source, y0_source, width_source, height_source)) if ((width_height_dest[0] != width_source) or (width_height_dest[1] != height_source)): # Resize to destination dimensions pygame_surface_image = pygame.transform.scale( pygame_surface_image, width_height_dest) image._pygamesurfaces_cached[key_0] = pygame_surface_image # pylint: disable=protected-access # noqa if (self._frame_parent and # pylint: disable=protected-access self._frame_parent._print_stats_cache and # pylint: disable=protected-access # noqa (len(image._pygamesurfaces_cached) == image._pygamesurfaces_cache_max_size)): # pylint: disable=protected-access # noqa image._print_stats_cache( # pylint: disable=protected-access # noqa 'Surfaces full cache ') elif len(image._pygamesurfaces_cached) > image._pygamesurfaces_cache_max_size: # pylint: disable=protected-access # noqa image._pygamesurfaces_cached.popitem(False) # pylint: disable=protected-access # noqa if rotation != 0: # Rotate pygame_surface_image = pygame.transform.rotate( pygame_surface_image, rotation) image._pygamesurfaces_cached[key] = pygame_surface_image # pylint: disable=protected-access # noqa if (self._frame_parent and # pylint: disable=protected-access self._frame_parent._print_stats_cache and # pylint: disable=protected-access # noqa (len(image._pygamesurfaces_cached) == image._pygamesurfaces_cache_max_size)): # pylint: disable=protected-access # noqa image._print_stats_cache( # pylint: disable=protected-access # noqa 'Surfaces full cache with rotated ') elif len(image._pygamesurfaces_cached) > image._pygamesurfaces_cache_max_size: # pylint: disable=protected-access # noqa image._pygamesurfaces_cached.popitem(False) # pylint: disable=protected-access # noqa # Draw the result self._pygame_surface.blit( pygame_surface_image, (int(round(center_dest[0] - pygame_surface_image.get_width() / 2)), int(round(center_dest[1] - pygame_surface_image.get_height() / 2)))) if __debug__: image._draw_count += 1
[docs] def draw_line(self, point1, point2, line_width, line_color): # type: (Sequence[Union[int, float]], Sequence[Union[int, float]], Union[int, float], str) -> None # noqa """ Draw a line segment from point1 to point2. :param point1: (int or float, int or float) or [int or float, int or float] :param point2: (int or float, int or float) or [int or float, int or float] :param line_width: (int or float) > 0 :param line_color: str """ assert isinstance(point1, (tuple, list)), type(point1) assert len(point1) == 2, len(point1) assert isinstance(point1[0], (int, float)), type(point1[0]) assert isinstance(point1[1], (int, float)), type(point1[1]) assert isinstance(point2, (tuple, list)), type(point2) assert len(point2) == 2, len(point2) assert isinstance(point2[0], (int, float)), type(point2[0]) assert isinstance(point2[1], (int, float)), type(point2[1]) assert isinstance(line_width, (int, float)), type(line_width) assert line_width > 0, line_width assert isinstance(line_color, str), type(line_color) pygamecolor = _simpleguicolor_to_pygamecolor(line_color) if pygamecolor.a == 255: # without alpha pygame.draw.line(self._pygame_surface, pygamecolor, _pos_round(point1), _pos_round(point2), int(round(line_width))) elif pygamecolor.a > 0: # with alpha (not null) x1, y1 = _pos_round(point1) x2, y2 = _pos_round(point2) # pylint: disable=invalid-name # noqa width = abs(x2 - x1) + line_width * 2 height = abs(y2 - y1) + line_width * 2 x_min = min(x1, x2) y_min = min(y1, y2) s_alpha = pygame.surface.Surface((width, height), pygame.SRCALPHA) # pylint: disable=too-many-function-args,no-member # noqa pygame.draw.line(s_alpha, pygamecolor, (x1 - x_min + line_width, y1 - y_min + line_width), (x2 - x_min + line_width, y2 - y_min + line_width), int(round(line_width))) self._pygame_surface.blit(s_alpha, (x_min - line_width, y_min - line_width))
[docs] def draw_point(self, position, color): # type: (Sequence[Union[int, float]], str) -> None """ Draw a point. :param position: (int or float, int or float) or [int or float, int or float] :param color: str """ assert isinstance(position, (tuple, list)), type(position) assert len(position) == 2, len(position) assert isinstance(position[0], (int, float)), type(position[0]) assert isinstance(position[1], (int, float)), type(position[1]) assert isinstance(color, str), type(color) pygamecolor = _simpleguicolor_to_pygamecolor(color) if pygamecolor.a == 255: # without alpha self._pygame_surface.set_at(_pos_round(position), pygamecolor) elif pygamecolor.a > 0: # with alpha (not null) s_alpha = pygame.surface.Surface((1, 1), pygame.SRCALPHA) # pylint: disable=too-many-function-args,no-member # noqa s_alpha.set_at((0, 0), pygamecolor) self._pygame_surface.blit(s_alpha, _pos_round(position))
[docs] def draw_polygon(self, point_list, line_width, line_color, fill_color=None): # type: (Sequence[Sequence[Union[int, float]]], Union[int, float], str, Optional[str]) -> None # noqa """ Draw a polygon from a list of points. A segment is automatically drawed between the last point and the first point. If `fill color` is not None then fill with this color. If `line_width` > 1, ends are poorly made! :param point_list: not empty (tuple or list) of ((int or float, int or float) or [int or float, int or float]) :param line_width: (int or float) > 0 :param line_color: str :param fill_color: None or str """ assert isinstance(point_list, (tuple, list)), type(point_list) assert len(point_list) > 0, len(point_list) if __debug__: for point in point_list: assert isinstance(point, (tuple, list)), type(point) assert len(point) == 2, len(point) assert isinstance(point[0], (int, float)), type(point[0]) assert isinstance(point[1], (int, float)), type(point[1]) assert isinstance(line_width, (int, float)), type(line_width) assert line_width >= 0, line_width assert isinstance(line_color, str), type(line_color) assert (fill_color is None) or isinstance(fill_color, str), \ type(fill_color) if len(point_list) == 1: return pygamecolor = _simpleguicolor_to_pygamecolor(line_color) pygamefillcolor = (None if fill_color is None else _simpleguicolor_to_pygamecolor(fill_color)) point_list_rounded = [_pos_round(point) for point in point_list] del point_list line_width = int(round(line_width)) if ((pygamecolor.a == 255) and ((pygamefillcolor is None) or (pygamefillcolor.a == 255))): # Without alpha if pygamefillcolor is not None: pygame.draw.polygon(self._pygame_surface, pygamefillcolor, point_list_rounded, 0) if pygamecolor != pygamefillcolor: pygame.draw.lines(self._pygame_surface, pygamecolor, True, point_list_rounded, line_width) elif ((pygamecolor.a > 0) or ((pygamefillcolor is not None) and (pygamefillcolor.a > 0))): # With one or two alpha (not null) s_alpha = pygame.surface.Surface((self._width, self._height), # pylint: disable=too-many-function-args # noqa pygame.SRCALPHA) # pylint: disable=no-member # noqa if (pygamefillcolor is not None) and (pygamefillcolor.a > 0): pygame.draw.polygon(s_alpha, pygamefillcolor, point_list_rounded, 0) if (pygamecolor != pygamefillcolor) and (pygamecolor.a > 0): pygame.draw.lines(s_alpha, pygamecolor, True, point_list_rounded, line_width) self._pygame_surface.blit(s_alpha, (0, 0))
[docs] def draw_polyline(self, point_list, line_width, line_color): # type: (Sequence[Sequence[Union[int, float]]], Union[int, float], str) -> None # noqa """ Draw line segments between a list of points. If `line_width` > 1, ends are poorly made! :param point_list: not empty (tuple or list) of ((int or float, int or float) or [int or float, int or float]) :param line_width: (int or float) > 0 :param line_color: str """ assert isinstance(point_list, (tuple, list)), type(point_list) assert len(point_list) > 0, len(point_list) if __debug__: for point in point_list: assert isinstance(point, (tuple, list)), type(point) assert len(point) == 2, len(point) assert isinstance(point[0], (int, float)), type(point[0]) assert isinstance(point[1], (int, float)), type(point[1]) assert isinstance(line_width, (int, float)), type(line_width) assert line_width > 0, line_width assert isinstance(line_color, str), type(line_color) if len(point_list) == 1: return pygamecolor = _simpleguicolor_to_pygamecolor(line_color) point_list_rounded = [_pos_round(point) for point in point_list] del point_list line_width = int(round(line_width)) if pygamecolor.a == 255: # without alpha pygame.draw.lines(self._pygame_surface, pygamecolor, False, point_list_rounded, line_width) elif pygamecolor.a > 0: # with alpha (not null) s_alpha = pygame.surface.Surface((self._width, self._height), # pylint: disable=too-many-function-args # noqa pygame.SRCALPHA) # pylint: disable=no-member # noqa pygame.draw.lines(s_alpha, pygamecolor, False, point_list_rounded, line_width) self._pygame_surface.blit(s_alpha, (0, 0))
[docs] def draw_text(self, # pylint: disable=too-many-arguments text, point, font_size, font_color, font_face='serif', _font_size_coef=3 / 4): # type: (str, Sequence[Union[int, float]], Union[int, float], str, str, Union[int, float]) -> None # noqa """ Draw the `text` string at the position `point`. (`point[0]` is the left of the text, `point[1]` is the bottom of the text.) If correponding font in Pygame is not founded, then use the default `pygame.font.Font`. `_font_size_coef` is used to adjust the vertical positioning. **(This paramater is not available in SimpleGUI of CodeSkulptor.)** :warning: This method can't draw multiline text. To draw multiline text, see `simplegui_lib_draw.draw_text_multi()`_ . .. _`simplegui_lib_draw.draw_text_multi()`: ../simplegui_lib_draw.html#SimpleGUICS2Pygame.simplegui_lib_draw.draw_text_multi :param text: str :param point: (int or float, int or float) or [int or float, int or float] :param font_size: (int or float) >= 0 :param font_color: str :param font_face: str == 'monospace', 'sans-serif', 'serif' :param _font_size_coef: int or float :raise: ValueError if text contains unprintable whitespace character **(Alpha color channel don't work!!!)** """ # noqa assert isinstance(text, str), type(text) assert isinstance(point, (tuple, list)), type(point) assert len(point) == 2, len(point) assert isinstance(point[0], (int, float)), type(point[0]) assert isinstance(point[1], (int, float)), type(point[1]) assert isinstance(font_size, (int, float)), type(font_size) assert font_size >= 0, font_size assert isinstance(font_color, str), type(font_color) assert isinstance(font_face, str), type(font_face) assert font_face in _SIMPLEGUIFONTFACE_TO_PYGAMEFONTNAME, font_face assert isinstance(_font_size_coef, (int, float)), type(_font_size_coef) if text == '': return if _RE_UNPRINTABLE_WHITESPACE_CHAR.search(text): raise ValueError('text may not contain non-printing characters') pygamecolor = _simpleguicolor_to_pygamecolor(font_color) font_size = int(round(font_size)) if (pygamecolor.a > 0) and (font_size > 0): pygame_surface_text = _simpleguifontface_to_pygamefont( font_face, font_size).render(text, True, pygamecolor) # if pygamecolor.a == 255: # without alpha self._pygame_surface.blit( pygame_surface_text, (point[0], (point[1] - pygame_surface_text.get_height() * _font_size_coef)))
# else: # with alpha (not null) # # Don't work!!! # s_alpha = pygame.surface.Surface((pygame_surface_text.get_width(), # noqa # pygame_surface_text.get_height()), # noqa # pygame.SRCALPHA) # s_alpha.blit(pygame_surface_text, (0, 0)) # self._pygame_surface.blit( # s_alpha, # (point[0], # (point[1] - # pygame_surface_text.get_height() * _font_size_coef))) # # SimpleGUI function ####################
[docs]def create_invisible_canvas(width, height): # type: (int, int) -> Canvas """ NOT IMPLEMENTED! (Return a "weak" `Canvas`.) (Available in SimpleGUI of CodeSkulptor but *not in CodeSkulptor documentation*!) :param width: int >= 0 :param height: int >= 0 :return: Canvas """ assert isinstance(width, int), type(width) assert width >= 0, width assert isinstance(height, int), type(height) assert height >= 0, height return Canvas(None, width, height)