Skip to content

Commit

Permalink
Canvas: moving "invert_rect_colors" into it, wrapping .text() and .re…
Browse files Browse the repository at this point in the history
…ctangle(), adding .clear() and .check_coordinates()
  • Loading branch information
CRImier committed Mar 10, 2018
1 parent 0a94d57 commit e65cfe0
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 34 deletions.
27 changes: 13 additions & 14 deletions ui/base_list_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

from canvas import Canvas
from helpers import setup_logger
from ui.utils import invert_rect_colors
from utils import to_be_foreground, clamp_list_index


Expand Down Expand Up @@ -522,8 +521,8 @@ def get_fow_height_in_chars(self):
def refresh(self):
logger.debug("{}: refreshed data on display".format(self.el.name))
self.fix_pointers_on_refresh()
canvas = self.get_displayed_canvas(cursor_y=self.get_active_line_num())
self.o.display_image(canvas)
image = self.get_displayed_image(cursor_y=self.get_active_line_num())
self.o.display_image(image)

def get_scrollbar_top_bottom(self):
scrollbar_max_length = self.o.height - (self.scrollbar_y_offset * 2)
Expand All @@ -542,7 +541,7 @@ def get_scrollbar_top_bottom(self):
return top, bottom

@to_be_foreground
def get_displayed_canvas(self, cursor_x=0, cursor_y=None):
def get_displayed_image(self, cursor_x=0, cursor_y=None):
"""Generates the displayed data for a canvas-based output device. The output of this function can be fed to the o.display_image function.
|Doesn't support partly-rendering entries yet."""
menu_text = self.get_displayed_text()
Expand All @@ -555,11 +554,11 @@ def get_displayed_canvas(self, cursor_x=0, cursor_y=None):
else:
left_offset = self.x_scrollbar_offset
y1, y2 = scrollbar_coordinates
c.rectangle((1, y1, 2, y2), outline="white")
c.rectangle((1, y1, 2, y2))
#Drawing the text itself
for i, line in enumerate(menu_text):
y = (i * self.charheight - 1) if i != 0 else 0
c.text((left_offset, y), line, fill="white")
c.text(line, (left_offset, y))
if cursor_y is not None:
c_x = cursor_x * self.charwidth + 1
c_y = cursor_y * self.charheight + 1
Expand All @@ -569,7 +568,7 @@ def get_displayed_canvas(self, cursor_x=0, cursor_y=None):
c_x + self.charwidth * len(menu_text[cursor_y]) + left_offset,
c_y + self.charheight
)
invert_rect_colors(cursor_dims, c)
c.invert_rect_colors(cursor_dims)
return c.get_image()


Expand All @@ -578,7 +577,7 @@ class SixteenPtView(EightPtView):
charheight = 16

@to_be_foreground
def get_displayed_canvas(self, cursor_x=0, cursor_y=None):
def get_displayed_image(self, cursor_x=0, cursor_y=None):
"""Generates the displayed data for a canvas-based output device. The output of this function can be fed to the o.display_image function.
|Doesn't support partly-rendering entries yet."""
menu_text = self.get_displayed_text()
Expand All @@ -591,13 +590,13 @@ def get_displayed_canvas(self, cursor_x=0, cursor_y=None):
else:
left_offset = self.x_scrollbar_offset
y1, y2 = scrollbar_coordinates
c.rectangle((1, y1, 2, y2), outline="white")
c.rectangle((1, y1, 2, y2))
#Drawing the text itself
#http://pillow.readthedocs.io/en/3.1.x/reference/ImageFont.html
font = ImageFont.truetype("ui/fonts/Fixedsys62.ttf", 16)
for i, line in enumerate(menu_text):
y = (i * self.charheight - 1) if i != 0 else 0
c.text((left_offset, y), line, fill="white", font=font)
c.text(line, (left_offset, y), font=font)
# Drawing cursor, if enabled
if cursor_y is not None:
c_x = cursor_x * self.charwidth
Expand All @@ -608,7 +607,7 @@ def get_displayed_canvas(self, cursor_x=0, cursor_y=None):
c_x + self.charwidth * len(menu_text[cursor_y]) + left_offset,
c_y + self.charheight
)
invert_rect_colors(cursor_dims, c)
c.invert_rect_colors(cursor_dims)
return c.get_image()


Expand All @@ -629,12 +628,12 @@ def get_displayed_canvas(self, cursor_x=0, cursor_y=None):
central_position = (10, 16)
font = ImageFont.truetype("ui/fonts/Fixedsys62.ttf", 32)
current_entry = self.el.contents[self.el.pointer]
c.text(central_position, current_entry[0], fill="white", font=font)
c.text(current_entry[0], central_position, font=font)
font = ImageFont.truetype("ui/fonts/Fixedsys62.ttf", 16)
if self.el.pointer != 0:
line = self.el.contents[self.el.pointer - 1][0]
c.text((2, 0), line, fill="white", font=font)
c.text(line, (2, 0), font=font)
if self.el.pointer < len(self.el.contents) - 1:
line = self.el.contents[self.el.pointer + 1][0]
c.text((2, 48), line, fill="white", font=font)
c.text(line, (2, 48), font=font)
return c.get_image()
75 changes: 74 additions & 1 deletion ui/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class Canvas(object):
width = 0 #: width of canvas in pixels.
image = None #: ``PIL.Image`` object the ``Canvas`` is currently operating on.
size = (0, 0) #: a tuple of (width, height).
background_color = "black" #: default background color to use for drawing
default_color = "white" #: default color to use for drawing

def __init__(self, o, base_image=None):
"""
Expand All @@ -32,6 +34,30 @@ def __init__(self, o, base_image=None):
self.image = Image.new(o.device_mode, self.size)
self.draw = ImageDraw.Draw(self.image)

def text(self, text, coords, **kwargs):
"""
Draw text on the canvas. Coordinates are expected in (x, y)
format, where ``x``&``y`` are coordinates of the top left corner.
Do notice that order of first two arguments is reversed compared
to the corresponding ``PIL.ImageDraw`` method.
"""
assert(isinstance(text, basestring))
fill = kwargs.pop("fill", self.default_color)
coords = self.check_coordinates(coords)
self.draw.text(coords, text, fill=fill, **kwargs)

def rectangle(self, coords, **kwargs):
"""
Draw a rectangle on the canvas. Coordinates are expected in
``(x1, y1, x2, y2)`` format, where ``x1``&``y1`` are coordinates
of the top left corner, and ``x2``&``y2`` are coordinates
of the bottom right corner.
"""
coords = self.check_coordinates(coords)
outline = kwargs.pop("outline", self.default_color)
self.draw.rectangle(coords, outline=outline, **kwargs)

def get_image(self):
"""
Get the current ``PIL.Image`` object.
Expand All @@ -54,10 +80,57 @@ def invert(self):

def display(self):
"""
Display the current image on the ``o`` object that was supplied to ``Canvas``.
Display the current image on the ``o`` object that was supplied to
``Canvas``.
"""
self.o.display_image(self.image)

def clear(self, coords):
# type: tuple -> None
"""
Fill an area of the image with default background color.
"""
coords = self.check_coordinates(coords)
self.rectangle(coords, fill=self.background_color) # paint the background black first

def check_coordinates(self, coords):
# type: tuple -> tuple
"""
A helper function to check (later, also reformat) coordinates supplied to
functions. Currently, only accepts integer coordinates.
"""
for i in coords:
assert isinstance(i, int), "{} not an integer!".format(i)
if len(coords) == 2:
return coords
elif len(coords) == 4:
x1, y1, x2, y2 = coords
assert (x2 >= x1), "x2 ({}) is smaller than x1 ({}), rearrange?".format(x2, x1)
assert (y2 >= y1), "y2 ({}) is smaller than y1 ({}), rearrange?".format(y2, y1)
return coords
else:
raise ValueError("Invalid number of coordinates!")

def invert_rect_colors(self, coords):
# type: tuple -> tuple
"""
Inverts colors of the image in the given rectangle. Is useful for
highlighting a part of the image, for example.
"""

coords = self.check_coordinates(coords)
#self.rectangle(coords)
image_subset = self.image.crop(coords)

if image_subset.mode != "L": # PIL can only invert "L" and "RGBA" images
# We only support "L" for now
image_subset = image_subset.convert("L")
image_subset = ImageOps.invert(image_subset)
image_subset = image_subset.convert(self.o.device_mode)

self.clear(coords)
self.draw.bitmap((coords[0], coords[1]), image_subset, fill=self.default_color)

def __getattr__(self, name):
if hasattr(self.draw, name):
return getattr(self.draw, name)
Expand Down
4 changes: 2 additions & 2 deletions ui/char_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import string

from utils import to_be_foreground, invert_rect_colors
from utils import to_be_foreground
from canvas import Canvas


Expand Down Expand Up @@ -285,7 +285,7 @@ def get_image(self):
cursor_dims = ( c_x1, c_y1, c_x2 + 2, c_y2 + 1 )

#Drawing the cursor
invert_rect_colors(cursor_dims, c)
c.invert_rect_colors(cursor_dims)

return c.get_image()

Expand Down
3 changes: 1 addition & 2 deletions ui/dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from helpers import setup_logger

from canvas import Canvas
from utils import invert_rect_colors

logger = setup_logger(__name__, "info")

Expand Down Expand Up @@ -178,7 +177,7 @@ def get_image(self):
cursor_dims = ( c_x1, c_y1, c_x2 + 2, c_y2 + 2 )

#Drawing the cursor
invert_rect_colors(cursor_dims, c)
c.invert_rect_colors(cursor_dims)

return c.get_image()

Expand Down
15 changes: 0 additions & 15 deletions ui/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,19 +202,4 @@ def tick(self):
return elapsed


def invert_rect_colors(coordinates, canvas):
# type: (tuple, ui.Canvas) -> None
# inverts colors of the image in the given rectangle
canvas.rectangle(coordinates, outline="white")
image_subset = canvas.image.crop(coordinates)

if image_subset.mode == "1": # PIL doesn't support invert on mode "1"
image_subset = image_subset.convert("L")
image_subset = ImageOps.invert(image_subset)
image_subset = image_subset.convert("1")

canvas.rectangle(coordinates, fill="black") # paint the background black first
canvas.bitmap((coordinates[0], coordinates[1]), image_subset, fill="white")


Rect = namedtuple('Rect', ['left', 'top', 'right', 'bottom'])

0 comments on commit e65cfe0

Please sign in to comment.