mirror of
https://git.lynn.is/Gwen/python-layout.git
synced 2024-01-13 01:31:55 +01:00
clean up
This commit is contained in:
parent
cd4c111c78
commit
4d4dc38fe9
74
box.py
74
box.py
|
@ -19,82 +19,82 @@ class Box(Layout):
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self._title = title
|
self.title = title
|
||||||
self._title_font = title_font
|
self.title_font = title_font
|
||||||
self._title_color = title_color
|
self.title_color = title_color
|
||||||
self._title_anchor = title_anchor
|
self.title_anchor = title_anchor
|
||||||
self._title_position = title_position
|
self.title_position = title_position
|
||||||
self._title_padding = title_padding
|
self.title_padding = title_padding
|
||||||
self._content = content
|
self.content = content
|
||||||
|
|
||||||
def _children(self):
|
def children(self):
|
||||||
if self._content is not None:
|
if self.content is not None:
|
||||||
return [self._content]
|
return [self.content]
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _get_title_font(self):
|
def get_title_font(self):
|
||||||
if self._title_font is not None:
|
if self.title_font is not None:
|
||||||
return self._title_font
|
return self.title_font
|
||||||
else:
|
else:
|
||||||
return self.get_font()
|
return self.get_font()
|
||||||
|
|
||||||
def _get_title_color(self):
|
def get_title_color(self):
|
||||||
if self._title_color is not None:
|
if self.title_color is not None:
|
||||||
return self._title_color
|
return self.title_color
|
||||||
else:
|
else:
|
||||||
return self.get_fg_color()
|
return self.get_fg_color()
|
||||||
|
|
||||||
def get_min_inner_width(self, max_height=None):
|
def get_min_inner_width(self, max_height=None):
|
||||||
if self._content is None:
|
if self.content is None:
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
return self._content.get_min_outer_width(max_height)
|
return self.content.get_min_outer_width(max_height)
|
||||||
|
|
||||||
def get_min_inner_height(self, max_width=None):
|
def get_min_inner_height(self, max_width=None):
|
||||||
if self._content is None:
|
if self.content is None:
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
return self._content.get_min_outer_height(max_width)
|
return self.content.get_min_outer_height(max_width)
|
||||||
|
|
||||||
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
||||||
if self._content is None:
|
if self.content is None:
|
||||||
return 0, 0
|
return 0, 0
|
||||||
else:
|
else:
|
||||||
return self._content.get_ideal_outer_dimensions(min_width, min_height, available_width, available_height)
|
return self.content.get_ideal_outer_dimensions(min_width, min_height, available_width, available_height)
|
||||||
|
|
||||||
def render_content(self, rect):
|
def render_content(self, rect):
|
||||||
if self._content is None:
|
if self.content is None:
|
||||||
return self.make_canvas()
|
return self.make_canvas()
|
||||||
else:
|
else:
|
||||||
return self._content.render(rect)
|
return self.content.render(rect)
|
||||||
|
|
||||||
def _title_pos(self, rect):
|
def title_pos(self, rect):
|
||||||
left, top, right, bottom = self._get_title_font().getbbox(self._title)
|
left, top, right, bottom = self.get_title_font().getbbox(self.title)
|
||||||
height = bottom - top
|
height = bottom - top
|
||||||
width = right - left
|
width = right - left
|
||||||
border_radius = self.border_radius()
|
border_radius = self.get_border_radius()
|
||||||
available_width = rect[2] - rect[0] + 1 - border_radius[1] - border_radius[0]
|
available_width = rect[2] - rect[0] + 1 - border_radius[1] - border_radius[0]
|
||||||
pos_x = rect[0] + border_radius[0] + self._title_position
|
pos_x = rect[0] + border_radius[0] + self.title_position
|
||||||
if self._title_anchor == BoxTitleAnchor.LEFT:
|
if self.title_anchor == BoxTitleAnchor.LEFT:
|
||||||
pos_x += 0
|
pos_x += 0
|
||||||
elif self._title_anchor == BoxTitleAnchor.CENTER:
|
elif self.title_anchor == BoxTitleAnchor.CENTER:
|
||||||
pos_x += (available_width - width) / 2
|
pos_x += (available_width - width) / 2
|
||||||
elif self._title_anchor == BoxTitleAnchor.RIGHT:
|
elif self.title_anchor == BoxTitleAnchor.RIGHT:
|
||||||
pos_x += available_width - width
|
pos_x += available_width - width
|
||||||
pos_y = rect[1] - height / 2
|
pos_y = rect[1] - height / 2
|
||||||
return (pos_x, pos_y), (left - self._title_padding, top, right + self._title_padding, bottom)
|
return (pos_x, pos_y), (left - self.title_padding, top, right + self.title_padding, bottom)
|
||||||
|
|
||||||
def modify_border_mask(self, border_mask, rect):
|
def modify_border_mask(self, border_mask, rect):
|
||||||
if self._title is None:
|
if self.title is None:
|
||||||
return
|
return
|
||||||
(x, y), (left, top, right, bottom) = self._title_pos(rect)
|
(x, y), (left, top, right, bottom) = self.title_pos(rect)
|
||||||
d = ImageDraw.Draw(border_mask)
|
d = ImageDraw.Draw(border_mask)
|
||||||
d.rectangle((x + left, y + top, x + right, y + bottom), fill=0)
|
d.rectangle((x + left, y + top, x + right, y + bottom), fill=0)
|
||||||
|
|
||||||
def render_after_border(self, image, rect):
|
def render_after_border(self, image, rect):
|
||||||
if self._title is None:
|
if self.title is None:
|
||||||
return
|
return
|
||||||
(x, y), _ = self._title_pos(rect)
|
(x, y), _ = self.title_pos(rect)
|
||||||
d = ImageDraw.Draw(image)
|
d = ImageDraw.Draw(image)
|
||||||
d.text((x, y), self._title, font=self._get_title_font(), fill=self._get_title_color())
|
d.text((x, y), self.title, font=self.get_title_font(), fill=self.get_title_color())
|
||||||
|
|
106
container.py
106
container.py
|
@ -17,20 +17,20 @@ class Container(Layout):
|
||||||
contents = []
|
contents = []
|
||||||
self._contents = contents
|
self._contents = contents
|
||||||
|
|
||||||
def _children(self):
|
def children(self):
|
||||||
return self._contents
|
return self._contents
|
||||||
|
|
||||||
def get_min_inner_width(self, max_height=None):
|
def get_min_inner_width(self, max_height=None):
|
||||||
min_width = 0
|
min_width = 0
|
||||||
for c in self._contents:
|
for c in self._contents:
|
||||||
width = c.get_min_outer_width(max_height)
|
width = c.get_min_outer_width(max_height)
|
||||||
if c._left is not None:
|
if c.left is not None:
|
||||||
width += c._left
|
width += c.left
|
||||||
if c._right is not None:
|
if c.right is not None:
|
||||||
width += c._right
|
width += c.right
|
||||||
min_width = max(min_width, width)
|
min_width = max(min_width, width)
|
||||||
if self._min_width is not None:
|
if self.min_width is not None:
|
||||||
min_width = max(min_width, self._min_width)
|
min_width = max(min_width, self.min_width)
|
||||||
return min_width
|
return min_width
|
||||||
|
|
||||||
def get_min_inner_height(self, max_width=None):
|
def get_min_inner_height(self, max_width=None):
|
||||||
|
@ -38,17 +38,17 @@ class Container(Layout):
|
||||||
min_height_absolute = 0
|
min_height_absolute = 0
|
||||||
for c in self._contents:
|
for c in self._contents:
|
||||||
height = c.get_min_outer_height(max_width)
|
height = c.get_min_outer_height(max_width)
|
||||||
if c._top is None and c._bottom is None and c._left is None and c._right is None:
|
if c.top is None and c.bottom is None and c.left is None and c.right is None:
|
||||||
min_height_automatic += height
|
min_height_automatic += height
|
||||||
else:
|
else:
|
||||||
if c._top is not None:
|
if c.top is not None:
|
||||||
height += c._top
|
height += c.top
|
||||||
if c._bottom is not None:
|
if c.bottom is not None:
|
||||||
height += c._bottom
|
height += c.bottom
|
||||||
min_height_absolute = max(min_height_absolute, height)
|
min_height_absolute = max(min_height_absolute, height)
|
||||||
min_height = max(min_height_automatic, min_height_absolute)
|
min_height = max(min_height_automatic, min_height_absolute)
|
||||||
if self._min_height is not None:
|
if self.min_height is not None:
|
||||||
min_height = max(min_height, self._min_height)
|
min_height = max(min_height, self.min_height)
|
||||||
return min_height
|
return min_height
|
||||||
|
|
||||||
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
||||||
|
@ -56,24 +56,24 @@ class Container(Layout):
|
||||||
|
|
||||||
for c in self._contents:
|
for c in self._contents:
|
||||||
w, h = c.get_ideal_outer_dimensions(min_width, min_height, available_width, available_height)
|
w, h = c.get_ideal_outer_dimensions(min_width, min_height, available_width, available_height)
|
||||||
if c._left is not None:
|
if c.left is not None:
|
||||||
w += c._left
|
w += c.left
|
||||||
if c._right is not None:
|
if c.right is not None:
|
||||||
w += c._right
|
w += c.right
|
||||||
if c._top is not None:
|
if c.top is not None:
|
||||||
h += c._top
|
h += c.top
|
||||||
if c._bottom is not None:
|
if c.bottom is not None:
|
||||||
h += c._bottom
|
h += c.bottom
|
||||||
width = max(width, w)
|
width = max(width, w)
|
||||||
if c._top is None and c._bottom is None and c._left is None and c._right is None:
|
if c.top is None and c.bottom is None and c.left is None and c.right is None:
|
||||||
height_automatic += h
|
height_automatic += h
|
||||||
else:
|
else:
|
||||||
height_absolute = max(height_absolute, h)
|
height_absolute = max(height_absolute, h)
|
||||||
height = max(height_automatic, height_absolute)
|
height = max(height_automatic, height_absolute)
|
||||||
if self._width is not None:
|
if self.width is not None:
|
||||||
width = self._width
|
width = self.width
|
||||||
if self._height is not None:
|
if self.height is not None:
|
||||||
height = self._height
|
height = self.height
|
||||||
return width, height
|
return width, height
|
||||||
|
|
||||||
def render_content(self, rect):
|
def render_content(self, rect):
|
||||||
|
@ -83,29 +83,29 @@ class Container(Layout):
|
||||||
for c in self._contents:
|
for c in self._contents:
|
||||||
soft_max_width, soft_max_height = x2 - x1 + 1, y2 - y1 + 1
|
soft_max_width, soft_max_height = x2 - x1 + 1, y2 - y1 + 1
|
||||||
|
|
||||||
is_absolute = not (c._top is None and c._bottom is None and c._left is None and c._right is None)
|
is_absolute = not (c.top is None and c.bottom is None and c.left is None and c.right is None)
|
||||||
|
|
||||||
if c._left is not None:
|
if c.left is not None:
|
||||||
soft_max_width -= c._left
|
soft_max_width -= c.left
|
||||||
if c._right is not None:
|
if c.right is not None:
|
||||||
soft_max_width -= c._right
|
soft_max_width -= c.right
|
||||||
if c._top is not None:
|
if c.top is not None:
|
||||||
soft_max_height -= c._top
|
soft_max_height -= c.top
|
||||||
if c._bottom is not None:
|
if c.bottom is not None:
|
||||||
soft_max_height -= c._bottom
|
soft_max_height -= c.bottom
|
||||||
|
|
||||||
hard_max_width, hard_max_height = c._max_width, c._max_height
|
hard_max_width, hard_max_height = c.max_width, c.max_height
|
||||||
if c._left is not None and c._right is not None:
|
if c.left is not None and c.right is not None:
|
||||||
hard_max_width = min_with_none(hard_max_width, x2 - x1 + 1 - c._left - c._right)
|
hard_max_width = min_with_none(hard_max_width, x2 - x1 + 1 - c.left - c.right)
|
||||||
if c._top is not None and c._bottom is not None:
|
if c.top is not None and c.bottom is not None:
|
||||||
hard_max_height = min_with_none(hard_max_height, y2 - y1 + 1 - c._top - c._bottom)
|
hard_max_height = min_with_none(hard_max_height, y2 - y1 + 1 - c.top - c.bottom)
|
||||||
|
|
||||||
width, height = c.get_ideal_outer_dimensions(available_width=soft_max_width, available_height=soft_max_height)
|
width, height = c.get_ideal_outer_dimensions(available_width=soft_max_width, available_height=soft_max_height)
|
||||||
|
|
||||||
if c._left is not None and c._right is not None:
|
if c.left is not None and c.right is not None:
|
||||||
width = (x2 - c._right) - (x1 + c._left) + 1
|
width = (x2 - c.right) - (x1 + c.left) + 1
|
||||||
if c._top is not None and c._bottom is not None:
|
if c.top is not None and c.bottom is not None:
|
||||||
height = (y2 - c._bottom) - (y1 + c._top) + 1
|
height = (y2 - c.bottom) - (y1 + c.top) + 1
|
||||||
|
|
||||||
min_width, min_height = c.get_min_outer_width(hard_max_height), c.get_min_outer_height(hard_max_width)
|
min_width, min_height = c.get_min_outer_width(hard_max_height), c.get_min_outer_height(hard_max_width)
|
||||||
width = max_with_none(width, min_width)
|
width = max_with_none(width, min_width)
|
||||||
|
@ -115,26 +115,26 @@ class Container(Layout):
|
||||||
height = min_with_none(height, hard_max_height)
|
height = min_with_none(height, hard_max_height)
|
||||||
|
|
||||||
if is_absolute:
|
if is_absolute:
|
||||||
if c._left is None:
|
if c.left is None:
|
||||||
if c._right is None:
|
if c.right is None:
|
||||||
cx1 = x1
|
cx1 = x1
|
||||||
cx2 = cx1 + width - 1
|
cx2 = cx1 + width - 1
|
||||||
else:
|
else:
|
||||||
cx2 = x2 - c._right
|
cx2 = x2 - c.right
|
||||||
cx1 = cx2 - width + 1
|
cx1 = cx2 - width + 1
|
||||||
else:
|
else:
|
||||||
cx1 = x1 + c._left
|
cx1 = x1 + c.left
|
||||||
cx2 = cx1 + width - 1
|
cx2 = cx1 + width - 1
|
||||||
|
|
||||||
if c._top is None:
|
if c.top is None:
|
||||||
if c._bottom is None:
|
if c.bottom is None:
|
||||||
cy1 = y1
|
cy1 = y1
|
||||||
cy2 = cy1 + height - 1
|
cy2 = cy1 + height - 1
|
||||||
else:
|
else:
|
||||||
cy2 = y2 - c._bottom
|
cy2 = y2 - c.bottom
|
||||||
cy1 = cy2 - height + 1
|
cy1 = cy2 - height + 1
|
||||||
else:
|
else:
|
||||||
cy1 = y1 + c._top
|
cy1 = y1 + c.top
|
||||||
cy2 = cy1 + height - 1
|
cy2 = cy1 + height - 1
|
||||||
else:
|
else:
|
||||||
cx1 = x1
|
cx1 = x1
|
||||||
|
|
58
document.py
58
document.py
|
@ -14,67 +14,67 @@ class Document(Layout):
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
if self._overflow is None:
|
if self.overflow is None:
|
||||||
self._overflow = True
|
self.overflow = True
|
||||||
if self._font is None:
|
if self.font is None:
|
||||||
self._font = ImageFont.load_default()
|
self.font = ImageFont.load_default()
|
||||||
self._content = content
|
self.content = content
|
||||||
self._actual_size = None
|
self.actual_size = None
|
||||||
self.complete_init(None)
|
self.complete_init(None)
|
||||||
|
|
||||||
def _children(self):
|
def children(self):
|
||||||
if self._content is not None:
|
if self.content is not None:
|
||||||
return [self._content]
|
return [self.content]
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
||||||
if self._content is None:
|
if self.content is None:
|
||||||
return 0,0
|
return 0,0
|
||||||
else:
|
else:
|
||||||
return self._content.get_ideal_outer_dimensions(min_width, min_height, available_width, available_height)
|
return self.content.get_ideal_outer_dimensions(min_width, min_height, available_width, available_height)
|
||||||
|
|
||||||
def get_min_inner_height(self, max_width=None):
|
def get_min_inner_height(self, max_width=None):
|
||||||
if self._content is None:
|
if self.content is None:
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
return self._content.get_min_outer_height(max_width)
|
return self.content.get_min_outer_height(max_width)
|
||||||
|
|
||||||
def get_min_inner_width(self, max_height=None):
|
def get_min_inner_width(self, max_height=None):
|
||||||
if self._content is None:
|
if self.content is None:
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
return self._content.get_min_outer_width(max_height)
|
return self.content.get_min_outer_width(max_height)
|
||||||
|
|
||||||
def render_content(self, rect):
|
def render_content(self, rect):
|
||||||
image = self.make_canvas()
|
image = self.make_canvas()
|
||||||
if self._content is not None:
|
if self.content is not None:
|
||||||
content_image = self._content.render(rect)
|
content_image = self.content.render(rect)
|
||||||
image.alpha_composite(content_image)
|
image.alpha_composite(content_image)
|
||||||
return image
|
return image
|
||||||
|
|
||||||
def get_image(self):
|
def get_image(self):
|
||||||
min_width = max_with_none(self._width, self._min_width)
|
min_width = max_with_none(self.width, self.min_width)
|
||||||
min_height = max_with_none(self._height, self._min_height)
|
min_height = max_with_none(self.height, self.min_height)
|
||||||
max_width = min_with_none(self._width, self._max_width)
|
max_width = min_with_none(self.width, self.max_width)
|
||||||
max_height = min_with_none(self._height, self._max_height)
|
max_height = min_with_none(self.height, self.max_height)
|
||||||
|
|
||||||
width, height = self.get_ideal_outer_dimensions(min_width=min_width,
|
width, height = self.get_ideal_outer_dimensions(min_width=min_width,
|
||||||
min_height=min_height,
|
min_height=min_height,
|
||||||
available_width=max_width,
|
available_width=max_width,
|
||||||
available_height=max_height)
|
available_height=max_height)
|
||||||
|
|
||||||
if self._width is not None:
|
if self.width is not None:
|
||||||
width = self._width
|
width = self.width
|
||||||
else:
|
else:
|
||||||
width = min_with_none(max_with_none(width, self._min_width), self._max_width)
|
width = min_with_none(max_with_none(width, self.min_width), self.max_width)
|
||||||
if self._height is not None:
|
if self.height is not None:
|
||||||
height = self._height
|
height = self.height
|
||||||
else:
|
else:
|
||||||
height = min_with_none(max_with_none(height, self._min_height), self._max_height)
|
height = min_with_none(max_with_none(height, self.min_height), self.max_height)
|
||||||
|
|
||||||
self._actual_size = (width, height)
|
self.actual_size = (width, height)
|
||||||
|
|
||||||
background = Image.new('RGBA', (width, height), self._bg_color)
|
background = Image.new('RGBA', (width, height), self.bg_color)
|
||||||
content = self.render((0, 0, width - 1, height - 1))
|
content = self.render((0, 0, width - 1, height - 1))
|
||||||
return Image.alpha_composite(background, content)
|
return Image.alpha_composite(background, content)
|
||||||
|
|
44
flex.py
44
flex.py
|
@ -21,43 +21,43 @@ class Flex(Layout):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
if contents is None:
|
if contents is None:
|
||||||
contents = []
|
contents = []
|
||||||
self._direction = direction
|
self.direction = direction
|
||||||
self._wrap = wrap
|
self.wrap = wrap
|
||||||
self._justify = justify
|
self.justify = justify
|
||||||
self._align_items = align_items
|
self.align_items = align_items
|
||||||
self._align_content = align_content
|
self.align_content = align_content
|
||||||
self._gap = gap
|
self.gap = gap
|
||||||
self._contents = contents
|
self.contents = contents
|
||||||
self._flex_layouter = FlexLayouter(contents, gap, direction, wrap, justify, align_content, align_items)
|
self.flex_layouter = FlexLayouter(contents, gap, direction, wrap, justify, align_content, align_items)
|
||||||
|
|
||||||
def _children(self):
|
def children(self):
|
||||||
return self._contents
|
return self.contents
|
||||||
|
|
||||||
def get_min_inner_width(self, max_height=None):
|
def get_min_inner_width(self, max_height=None):
|
||||||
max_width = min_with_none(self._max_width, 0)
|
max_width = min_with_none(self.max_width, 0)
|
||||||
width, _ = self._flex_layouter.get_dimensions(self._min_width, self._min_height, max_width,
|
width, _ = self.flex_layouter.get_dimensions(self.min_width, self.min_height, max_width,
|
||||||
min_with_none(self._max_height, max_height))
|
min_with_none(self.max_height, max_height))
|
||||||
return width
|
return width
|
||||||
|
|
||||||
def get_min_inner_height(self, max_width=None):
|
def get_min_inner_height(self, max_width=None):
|
||||||
max_height = min_with_none(self._min_height, 0)
|
max_height = min_with_none(self.min_height, 0)
|
||||||
_, height = self._flex_layouter.get_dimensions(self._min_width, self._min_height,
|
_, height = self.flex_layouter.get_dimensions(self.min_width, self.min_height,
|
||||||
min_with_none(self._max_width, max_width), max_height)
|
min_with_none(self.max_width, max_width), max_height)
|
||||||
return height
|
return height
|
||||||
|
|
||||||
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
||||||
min_width = max_with_none(min_width, self._min_width)
|
min_width = max_with_none(min_width, self.min_width)
|
||||||
min_height = max_with_none(min_height, self._min_height)
|
min_height = max_with_none(min_height, self.min_height)
|
||||||
available_width = min_with_none(available_width, self._max_width, self._width)
|
available_width = min_with_none(available_width, self.max_width, self.width)
|
||||||
available_height = min_with_none(available_height, self._max_height, self._height)
|
available_height = min_with_none(available_height, self.max_height, self.height)
|
||||||
return self._flex_layouter.get_dimensions(min_width, min_height, available_width, available_height)
|
return self.flex_layouter.get_dimensions(min_width, min_height, available_width, available_height)
|
||||||
|
|
||||||
def render_content(self, rect):
|
def render_content(self, rect):
|
||||||
image = self.make_canvas()
|
image = self.make_canvas()
|
||||||
x1, y1, x2, y2 = rect
|
x1, y1, x2, y2 = rect
|
||||||
width = x2 - x1 + 1
|
width = x2 - x1 + 1
|
||||||
height = y2 - y1 + 1
|
height = y2 - y1 + 1
|
||||||
contents = self._flex_layouter.arrange(self._min_width, self._min_height, width, height)
|
contents = self.flex_layouter.arrange(self.min_width, self.min_height, width, height)
|
||||||
for (cx1, cy1, cx2, cy2), content in contents:
|
for (cx1, cy1, cx2, cy2), content in contents:
|
||||||
content_image = content.render((cx1 + x1, cy1 + y1, cx2 + x1, cy2 + y1))
|
content_image = content.render((cx1 + x1, cy1 + y1, cx2 + x1, cy2 + y1))
|
||||||
image.alpha_composite(content_image)
|
image.alpha_composite(content_image)
|
||||||
|
|
202
image.py
202
image.py
|
@ -19,69 +19,69 @@ class Image(Layout):
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self._image = image
|
self.image = image
|
||||||
self._mode = mode
|
self.mode = mode
|
||||||
self._scale = scale
|
self.scale = scale
|
||||||
self._position = position
|
self.position = position
|
||||||
self._offset_x = offset_x
|
self.offset_x = offset_x
|
||||||
self._offset_y = offset_y
|
self.offset_y = offset_y
|
||||||
|
|
||||||
def scale(self):
|
def get_scale(self):
|
||||||
if isinstance(self._scale, (tuple, list)):
|
if isinstance(self.scale, (tuple, list)):
|
||||||
if len(self._scale) == 0:
|
if len(self.scale) == 0:
|
||||||
return 1, 1
|
return 1, 1
|
||||||
elif len(self._scale) == 1:
|
elif len(self.scale) == 1:
|
||||||
return self._scale[0], self._scale[0]
|
return self.scale[0], self.scale[0]
|
||||||
else:
|
else:
|
||||||
return self._scale[0], self._scale[1]
|
return self.scale[0], self.scale[1]
|
||||||
elif isinstance(self._scale, (int, float)):
|
elif isinstance(self.scale, (int, float)):
|
||||||
return self._scale, self._scale
|
return self.scale, self.scale
|
||||||
else:
|
else:
|
||||||
return 1, 1
|
return 1, 1
|
||||||
|
|
||||||
def _get_image_width(self):
|
def get_image_width(self):
|
||||||
if self._image is None:
|
if self.image is None:
|
||||||
return 0
|
return 0
|
||||||
return self._image.size[0]
|
return self.image.size[0]
|
||||||
|
|
||||||
def _get_image_height(self):
|
def get_image_height(self):
|
||||||
if self._image is None:
|
if self.image is None:
|
||||||
return 0
|
return 0
|
||||||
return self._image.size[1]
|
return self.image.size[1]
|
||||||
|
|
||||||
def get_min_inner_width(self, max_height=None):
|
def get_min_inner_width(self, max_height=None):
|
||||||
if self._mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_Y, ImageMode.STRETCH_Y):
|
if self.mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_Y, ImageMode.STRETCH_Y):
|
||||||
width = self._get_image_width()
|
width = self.get_image_width()
|
||||||
else:
|
else:
|
||||||
width = 0
|
width = 0
|
||||||
return max_with_none(width, self._min_width)
|
return max_with_none(width, self.min_width)
|
||||||
|
|
||||||
def get_min_inner_height(self, max_width=None):
|
def get_min_inner_height(self, max_width=None):
|
||||||
if self._mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_X, ImageMode.STRETCH_X):
|
if self.mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_X, ImageMode.STRETCH_X):
|
||||||
height = self._get_image_height()
|
height = self.get_image_height()
|
||||||
else:
|
else:
|
||||||
height = 0
|
height = 0
|
||||||
return max_with_none(height, self._min_height)
|
return max_with_none(height, self.min_height)
|
||||||
|
|
||||||
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
||||||
width, height = 0, 0
|
width, height = 0, 0
|
||||||
if self._mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_Y, ImageMode.STRETCH_Y):
|
if self.mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_Y, ImageMode.STRETCH_Y):
|
||||||
width = self._get_image_width()
|
width = self.get_image_width()
|
||||||
if self._mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_X, ImageMode.STRETCH_X):
|
if self.mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_X, ImageMode.STRETCH_X):
|
||||||
height = self._get_image_height()
|
height = self.get_image_height()
|
||||||
if self._width is not None:
|
if self.width is not None:
|
||||||
width = self._width
|
width = self.width
|
||||||
if self._height is not None:
|
if self.height is not None:
|
||||||
height = self._height
|
height = self.height
|
||||||
width = min_with_none(width, available_width, self._max_width)
|
width = min_with_none(width, available_width, self.max_width)
|
||||||
height = min_with_none(height, available_height, self._max_height)
|
height = min_with_none(height, available_height, self.max_height)
|
||||||
width = max_with_none(width, min_width, self._min_width)
|
width = max_with_none(width, min_width, self.min_width)
|
||||||
height = max_with_none(height, min_height, self._min_height)
|
height = max_with_none(height, min_height, self.min_height)
|
||||||
return width, height
|
return width, height
|
||||||
|
|
||||||
def render_content(self, rect):
|
def render_content(self, rect):
|
||||||
image = self.make_canvas()
|
image = self.make_canvas()
|
||||||
if self._image is None:
|
if self.image is None:
|
||||||
return image
|
return image
|
||||||
|
|
||||||
x1, y1, x2, y2 = rect
|
x1, y1, x2, y2 = rect
|
||||||
|
@ -91,24 +91,24 @@ class Image(Layout):
|
||||||
if width == 0 or height == 0:
|
if width == 0 or height == 0:
|
||||||
return image
|
return image
|
||||||
|
|
||||||
image_width = int(self._image.size[0] * self.scale()[0])
|
image_width = int(self.image.size[0] * self.get_scale()[0])
|
||||||
image_height = int(self._image.size[1] * self.scale()[1])
|
image_height = int(self.image.size[1] * self.get_scale()[1])
|
||||||
|
|
||||||
offset_x = self._offset_x
|
offset_x = self.offset_x
|
||||||
offset_y = self._offset_y
|
offset_y = self.offset_y
|
||||||
|
|
||||||
if self._mode == ImageMode.ORIGINAL:
|
if self.mode == ImageMode.ORIGINAL:
|
||||||
if image_width != self._image.size[0] or image_height != self._image.size[1]:
|
if image_width != self.image.size[0] or image_height != self.image.size[1]:
|
||||||
img = self._image.resize((image_width, image_height), reducing_gap=3.0)
|
img = self.image.resize((image_width, image_height), reducing_gap=3.0)
|
||||||
else:
|
else:
|
||||||
img = self._image
|
img = self.image
|
||||||
elif self._mode == ImageMode.STRETCH:
|
elif self.mode == ImageMode.STRETCH:
|
||||||
img = self._image.resize((width, height), reducing_gap=3.0)
|
img = self.image.resize((width, height), reducing_gap=3.0)
|
||||||
elif self._mode == ImageMode.STRETCH_X:
|
elif self.mode == ImageMode.STRETCH_X:
|
||||||
img = self._image.resize((width, image_height), reducing_gap=3.0)
|
img = self.image.resize((width, image_height), reducing_gap=3.0)
|
||||||
elif self._mode == ImageMode.STRETCH_Y:
|
elif self.mode == ImageMode.STRETCH_Y:
|
||||||
img = self._image.resize((image_width, height), reducing_gap=3.0)
|
img = self.image.resize((image_width, height), reducing_gap=3.0)
|
||||||
elif self._mode == ImageMode.CONTAIN:
|
elif self.mode == ImageMode.CONTAIN:
|
||||||
ratio_width = width / image_width
|
ratio_width = width / image_width
|
||||||
ratio_height = height / image_height
|
ratio_height = height / image_height
|
||||||
if ratio_width < ratio_height:
|
if ratio_width < ratio_height:
|
||||||
|
@ -120,8 +120,8 @@ class Image(Layout):
|
||||||
else:
|
else:
|
||||||
w = width
|
w = width
|
||||||
h = height
|
h = height
|
||||||
img = self._image.resize((w, h), reducing_gap=3.0)
|
img = self.image.resize((w, h), reducing_gap=3.0)
|
||||||
elif self._mode == ImageMode.COVER:
|
elif self.mode == ImageMode.COVER:
|
||||||
ratio_width = width / image_width
|
ratio_width = width / image_width
|
||||||
ratio_height = height / image_height
|
ratio_height = height / image_height
|
||||||
if ratio_width > ratio_height:
|
if ratio_width > ratio_height:
|
||||||
|
@ -133,42 +133,42 @@ class Image(Layout):
|
||||||
else:
|
else:
|
||||||
w = width
|
w = width
|
||||||
h = height
|
h = height
|
||||||
img = self._image.resize((w, h), reducing_gap=3.0)
|
img = self.image.resize((w, h), reducing_gap=3.0)
|
||||||
|
|
||||||
if self._mode == ImageMode.REPEAT_X_FILL:
|
if self.mode == ImageMode.REPEAT_X_FILL:
|
||||||
repeat_width = int(image_width * height / image_height)
|
repeat_width = int(image_width * height / image_height)
|
||||||
repeat_height = height
|
repeat_height = height
|
||||||
elif self._mode == ImageMode.REPEAT_Y_FILL:
|
elif self.mode == ImageMode.REPEAT_Y_FILL:
|
||||||
repeat_width = width
|
repeat_width = width
|
||||||
repeat_height = int(image_height * width / image_width)
|
repeat_height = int(image_height * width / image_width)
|
||||||
else:
|
else:
|
||||||
repeat_width = image_width
|
repeat_width = image_width
|
||||||
repeat_height = image_height
|
repeat_height = image_height
|
||||||
|
|
||||||
if self._mode in (ImageMode.REPEAT, ImageMode.REPEAT_X, ImageMode.REPEAT_X_FILL, ImageMode.REPEAT_X_STRETCH):
|
if self.mode in (ImageMode.REPEAT, ImageMode.REPEAT_X, ImageMode.REPEAT_X_FILL, ImageMode.REPEAT_X_STRETCH):
|
||||||
if self._position in (ImageAnchor.TOP_LEFT, ImageAnchor.MIDDLE_LEFT, ImageAnchor.BOTTOM_LEFT):
|
if self.position in (ImageAnchor.TOP_LEFT, ImageAnchor.MIDDLE_LEFT, ImageAnchor.BOTTOM_LEFT):
|
||||||
repeat_offset_x = offset_x
|
repeat_offset_x = offset_x
|
||||||
elif self._position in (ImageAnchor.TOP_CENTER, ImageAnchor.MIDDLE_CENTER, ImageAnchor.BOTTOM_CENTER):
|
elif self.position in (ImageAnchor.TOP_CENTER, ImageAnchor.MIDDLE_CENTER, ImageAnchor.BOTTOM_CENTER):
|
||||||
repeat_offset_x = (width // 2) - repeat_width // 2 + offset_x
|
repeat_offset_x = (width // 2) - repeat_width // 2 + offset_x
|
||||||
elif self._position in (ImageAnchor.TOP_RIGHT, ImageAnchor.MIDDLE_RIGHT, ImageAnchor.BOTTOM_RIGHT):
|
elif self.position in (ImageAnchor.TOP_RIGHT, ImageAnchor.MIDDLE_RIGHT, ImageAnchor.BOTTOM_RIGHT):
|
||||||
repeat_offset_x = width - repeat_width + offset_x
|
repeat_offset_x = width - repeat_width + offset_x
|
||||||
else:
|
else:
|
||||||
raise Exception('invalid image position')
|
raise Exception('invalid image position')
|
||||||
if self._mode in (ImageMode.REPEAT, ImageMode.REPEAT_Y, ImageMode.REPEAT_Y_FILL, ImageMode.REPEAT_Y_STRETCH):
|
if self.mode in (ImageMode.REPEAT, ImageMode.REPEAT_Y, ImageMode.REPEAT_Y_FILL, ImageMode.REPEAT_Y_STRETCH):
|
||||||
if self._position in (ImageAnchor.TOP_LEFT, ImageAnchor.TOP_CENTER, ImageAnchor.TOP_RIGHT):
|
if self.position in (ImageAnchor.TOP_LEFT, ImageAnchor.TOP_CENTER, ImageAnchor.TOP_RIGHT):
|
||||||
repeat_offset_y = offset_y
|
repeat_offset_y = offset_y
|
||||||
elif self._position in (ImageAnchor.MIDDLE_LEFT, ImageAnchor.MIDDLE_CENTER, ImageAnchor.MIDDLE_RIGHT):
|
elif self.position in (ImageAnchor.MIDDLE_LEFT, ImageAnchor.MIDDLE_CENTER, ImageAnchor.MIDDLE_RIGHT):
|
||||||
repeat_offset_y = (height // 2) - repeat_height // 2 + offset_y
|
repeat_offset_y = (height // 2) - repeat_height // 2 + offset_y
|
||||||
elif self._position in (ImageAnchor.BOTTOM_LEFT, ImageAnchor.BOTTOM_CENTER, ImageAnchor.BOTTOM_RIGHT):
|
elif self.position in (ImageAnchor.BOTTOM_LEFT, ImageAnchor.BOTTOM_CENTER, ImageAnchor.BOTTOM_RIGHT):
|
||||||
repeat_offset_y = height - repeat_height + offset_y
|
repeat_offset_y = height - repeat_height + offset_y
|
||||||
else:
|
else:
|
||||||
raise Exception('invalid image position')
|
raise Exception('invalid image position')
|
||||||
|
|
||||||
if self._mode == ImageMode.REPEAT:
|
if self.mode == ImageMode.REPEAT:
|
||||||
if repeat_width != self._image.size[0] or repeat_height != self._image.size[1]:
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
||||||
img_part = self._image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
||||||
else:
|
else:
|
||||||
img_part = self._image
|
img_part = self.image
|
||||||
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
||||||
num_x = width // repeat_width + 1
|
num_x = width // repeat_width + 1
|
||||||
num_y = height // repeat_height + 1
|
num_y = height // repeat_height + 1
|
||||||
|
@ -179,66 +179,66 @@ class Image(Layout):
|
||||||
img.paste(img_part, (i * repeat_width + ox, j * repeat_height + oy))
|
img.paste(img_part, (i * repeat_width + ox, j * repeat_height + oy))
|
||||||
offset_x = 0
|
offset_x = 0
|
||||||
offset_y = 0
|
offset_y = 0
|
||||||
elif self._mode == ImageMode.REPEAT_X:
|
elif self.mode == ImageMode.REPEAT_X:
|
||||||
if repeat_width != self._image.size[0] or repeat_height != self._image.size[1]:
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
||||||
img_part = self._image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
||||||
else:
|
else:
|
||||||
img_part = self._image
|
img_part = self.image
|
||||||
img = PilImage.new('RGBA', (width, repeat_height), (0,0,0,0))
|
img = PilImage.new('RGBA', (width, repeat_height), (0,0,0,0))
|
||||||
num_x = width // repeat_width + 1
|
num_x = width // repeat_width + 1
|
||||||
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -repeat_width)
|
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -repeat_width)
|
||||||
for i in range(-1, num_x):
|
for i in range(-1, num_x):
|
||||||
img.paste(img_part, (i * repeat_width + ox, 0))
|
img.paste(img_part, (i * repeat_width + ox, 0))
|
||||||
offset_x = 0
|
offset_x = 0
|
||||||
elif self._mode == ImageMode.REPEAT_Y:
|
elif self.mode == ImageMode.REPEAT_Y:
|
||||||
if repeat_width != self._image.size[0] or repeat_height != self._image.size[1]:
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
||||||
img_part = self._image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
||||||
else:
|
else:
|
||||||
img_part = self._image
|
img_part = self.image
|
||||||
img = PilImage.new('RGBA', (repeat_width, height), (0,0,0,0))
|
img = PilImage.new('RGBA', (repeat_width, height), (0,0,0,0))
|
||||||
num_y = height // repeat_height + 1
|
num_y = height // repeat_height + 1
|
||||||
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
||||||
for i in range(-1, num_y):
|
for i in range(-1, num_y):
|
||||||
img.paste(img_part, (0, i * repeat_height + oy))
|
img.paste(img_part, (0, i * repeat_height + oy))
|
||||||
offset_y = 0
|
offset_y = 0
|
||||||
elif self._mode == ImageMode.REPEAT_X_STRETCH:
|
elif self.mode == ImageMode.REPEAT_X_STRETCH:
|
||||||
if repeat_width != self._image.size[0] or height != self._image.size[1]:
|
if repeat_width != self.image.size[0] or height != self.image.size[1]:
|
||||||
img_part = self._image.resize((repeat_width, height), reducing_gap=3.0)
|
img_part = self.image.resize((repeat_width, height), reducing_gap=3.0)
|
||||||
else:
|
else:
|
||||||
img_part = self._image
|
img_part = self.image
|
||||||
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
||||||
num_x = width // repeat_width + 1
|
num_x = width // repeat_width + 1
|
||||||
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -repeat_width)
|
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -repeat_width)
|
||||||
for i in range(-1, num_x):
|
for i in range(-1, num_x):
|
||||||
img.paste(img_part, (i * repeat_width + ox, 0))
|
img.paste(img_part, (i * repeat_width + ox, 0))
|
||||||
offset_x = 0
|
offset_x = 0
|
||||||
elif self._mode == ImageMode.REPEAT_Y_STRETCH:
|
elif self.mode == ImageMode.REPEAT_Y_STRETCH:
|
||||||
if width != self._image.size[0] or repeat_height != self._image.size[1]:
|
if width != self.image.size[0] or repeat_height != self.image.size[1]:
|
||||||
img_part = self._image.resize((width, repeat_height), reducing_gap=3.0)
|
img_part = self.image.resize((width, repeat_height), reducing_gap=3.0)
|
||||||
else:
|
else:
|
||||||
img_part = self._image
|
img_part = self.image
|
||||||
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
||||||
num_y = height // repeat_height + 1
|
num_y = height // repeat_height + 1
|
||||||
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
||||||
for i in range(-1, num_y):
|
for i in range(-1, num_y):
|
||||||
img.paste(img_part, (0, i * repeat_height + oy))
|
img.paste(img_part, (0, i * repeat_height + oy))
|
||||||
offset_y = 0
|
offset_y = 0
|
||||||
elif self._mode == ImageMode.REPEAT_X_FILL:
|
elif self.mode == ImageMode.REPEAT_X_FILL:
|
||||||
if repeat_width != self._image.size[0] or repeat_height != self._image.size[1]:
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
||||||
img_part = self._image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
||||||
else:
|
else:
|
||||||
img_part = self._image
|
img_part = self.image
|
||||||
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
||||||
num_x = width // repeat_width + 1
|
num_x = width // repeat_width + 1
|
||||||
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -w)
|
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -w)
|
||||||
for i in range(-1, num_x):
|
for i in range(-1, num_x):
|
||||||
img.paste(img_part, (i * repeat_width + ox, 0))
|
img.paste(img_part, (i * repeat_width + ox, 0))
|
||||||
offset_x = 0
|
offset_x = 0
|
||||||
elif self._mode == ImageMode.REPEAT_Y_FILL:
|
elif self.mode == ImageMode.REPEAT_Y_FILL:
|
||||||
if repeat_width != self._image.size[0] or repeat_height != self._image.size[1]:
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
||||||
img_part = self._image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
||||||
else:
|
else:
|
||||||
img_part = self._image
|
img_part = self.image
|
||||||
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
||||||
num_y = height // repeat_height + 1
|
num_y = height // repeat_height + 1
|
||||||
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
||||||
|
@ -246,19 +246,19 @@ class Image(Layout):
|
||||||
img.paste(img_part, (0, i * repeat_height + oy))
|
img.paste(img_part, (0, i * repeat_height + oy))
|
||||||
offset_y = 0
|
offset_y = 0
|
||||||
|
|
||||||
if self._position in (ImageAnchor.TOP_LEFT, ImageAnchor.MIDDLE_LEFT, ImageAnchor.BOTTOM_LEFT):
|
if self.position in (ImageAnchor.TOP_LEFT, ImageAnchor.MIDDLE_LEFT, ImageAnchor.BOTTOM_LEFT):
|
||||||
x = x1 + offset_x
|
x = x1 + offset_x
|
||||||
elif self._position in (ImageAnchor.TOP_CENTER, ImageAnchor.MIDDLE_CENTER, ImageAnchor.BOTTOM_CENTER):
|
elif self.position in (ImageAnchor.TOP_CENTER, ImageAnchor.MIDDLE_CENTER, ImageAnchor.BOTTOM_CENTER):
|
||||||
x = x1 + (width // 2) - img.size[0] // 2 + offset_x
|
x = x1 + (width // 2) - img.size[0] // 2 + offset_x
|
||||||
elif self._position in (ImageAnchor.TOP_RIGHT, ImageAnchor.MIDDLE_RIGHT, ImageAnchor.BOTTOM_RIGHT):
|
elif self.position in (ImageAnchor.TOP_RIGHT, ImageAnchor.MIDDLE_RIGHT, ImageAnchor.BOTTOM_RIGHT):
|
||||||
x = x1 + width - img.size[0] + offset_x
|
x = x1 + width - img.size[0] + offset_x
|
||||||
else:
|
else:
|
||||||
raise Exception('invalid image position')
|
raise Exception('invalid image position')
|
||||||
if self._position in (ImageAnchor.TOP_LEFT, ImageAnchor.TOP_CENTER, ImageAnchor.TOP_RIGHT):
|
if self.position in (ImageAnchor.TOP_LEFT, ImageAnchor.TOP_CENTER, ImageAnchor.TOP_RIGHT):
|
||||||
y = y1 + offset_y
|
y = y1 + offset_y
|
||||||
elif self._position in (ImageAnchor.MIDDLE_LEFT, ImageAnchor.MIDDLE_CENTER, ImageAnchor.MIDDLE_RIGHT):
|
elif self.position in (ImageAnchor.MIDDLE_LEFT, ImageAnchor.MIDDLE_CENTER, ImageAnchor.MIDDLE_RIGHT):
|
||||||
y = y1 + (height // 2) - img.size[1] // 2 + offset_y
|
y = y1 + (height // 2) - img.size[1] // 2 + offset_y
|
||||||
elif self._position in (ImageAnchor.BOTTOM_LEFT, ImageAnchor.BOTTOM_CENTER, ImageAnchor.BOTTOM_RIGHT):
|
elif self.position in (ImageAnchor.BOTTOM_LEFT, ImageAnchor.BOTTOM_CENTER, ImageAnchor.BOTTOM_RIGHT):
|
||||||
y = y1 + height - img.size[1] + offset_y
|
y = y1 + height - img.size[1] + offset_y
|
||||||
else:
|
else:
|
||||||
raise Exception('invalid image position')
|
raise Exception('invalid image position')
|
||||||
|
|
|
@ -2,7 +2,7 @@ from ..enums import FlexDirection, FlexWrap, FlexJustify, FlexAlignItems, FlexAl
|
||||||
from .helpers import min_with_none, max_with_none
|
from .helpers import min_with_none, max_with_none
|
||||||
|
|
||||||
|
|
||||||
class FlexLineItem():
|
class FlexLineItem:
|
||||||
def __init__(self, content, max_width, max_height, direction):
|
def __init__(self, content, max_width, max_height, direction):
|
||||||
self.__content = content
|
self.__content = content
|
||||||
self.__max_width = max_width
|
self.__max_width = max_width
|
||||||
|
@ -20,14 +20,14 @@ class FlexLineItem():
|
||||||
|
|
||||||
def base_size(self):
|
def base_size(self):
|
||||||
if self.__direction in (FlexDirection.ROW, FlexDirection.ROW_REVERSE):
|
if self.__direction in (FlexDirection.ROW, FlexDirection.ROW_REVERSE):
|
||||||
if self.__content._width is not None:
|
if self.__content.width is not None:
|
||||||
return self.__content._width
|
return self.__content.width
|
||||||
else:
|
else:
|
||||||
return self.__content.get_ideal_outer_dimensions(available_width=self.__max_width,
|
return self.__content.get_ideal_outer_dimensions(available_width=self.__max_width,
|
||||||
available_height=self.__max_height)[0]
|
available_height=self.__max_height)[0]
|
||||||
else:
|
else:
|
||||||
if self.__content._height is not None:
|
if self.__content.height is not None:
|
||||||
return self.__content._height
|
return self.__content.height
|
||||||
else:
|
else:
|
||||||
return self.__content.get_ideal_outer_dimensions(available_width=self.__max_width,
|
return self.__content.get_ideal_outer_dimensions(available_width=self.__max_width,
|
||||||
available_height=self.__max_height)[1]
|
available_height=self.__max_height)[1]
|
||||||
|
@ -46,15 +46,15 @@ class FlexLineItem():
|
||||||
|
|
||||||
def max_main_size(self):
|
def max_main_size(self):
|
||||||
if self.__direction in (FlexDirection.ROW, FlexDirection.ROW_REVERSE):
|
if self.__direction in (FlexDirection.ROW, FlexDirection.ROW_REVERSE):
|
||||||
return self.__content._max_width
|
return self.__content.max_width
|
||||||
else:
|
else:
|
||||||
return self.__content._max_height
|
return self.__content.max_height
|
||||||
|
|
||||||
def max_cross_size(self):
|
def max_cross_size(self):
|
||||||
if self.__direction in (FlexDirection.ROW, FlexDirection.ROW_REVERSE):
|
if self.__direction in (FlexDirection.ROW, FlexDirection.ROW_REVERSE):
|
||||||
return self.__content._max_height
|
return self.__content.max_height
|
||||||
else:
|
else:
|
||||||
return self.__content._max_width
|
return self.__content.max_width
|
||||||
|
|
||||||
def hypothetical_main_size(self):
|
def hypothetical_main_size(self):
|
||||||
return min_with_none(self.max_main_size(), max_with_none(self.min_main_size(), self.base_size()))
|
return min_with_none(self.max_main_size(), max_with_none(self.min_main_size(), self.base_size()))
|
||||||
|
@ -81,10 +81,10 @@ class FlexLineItem():
|
||||||
return self.__cross_pos
|
return self.__cross_pos
|
||||||
|
|
||||||
def grow_factor(self):
|
def grow_factor(self):
|
||||||
return self.__content._flex_grow
|
return self.__content.flex_grow
|
||||||
|
|
||||||
def shrink_factor(self):
|
def shrink_factor(self):
|
||||||
return self.__content._flex_shrink
|
return self.__content.flex_shrink
|
||||||
|
|
||||||
def scaled_shrink_factor(self):
|
def scaled_shrink_factor(self):
|
||||||
return self.shrink_factor() * self.base_size()
|
return self.shrink_factor() * self.base_size()
|
||||||
|
|
124
layout.py
124
layout.py
|
@ -32,68 +32,68 @@ class Layout:
|
||||||
flex_shrink=0,
|
flex_shrink=0,
|
||||||
debug_layout=False,
|
debug_layout=False,
|
||||||
):
|
):
|
||||||
self._width = width
|
self.width = width
|
||||||
self._height = height
|
self.height = height
|
||||||
self._min_width = min_width
|
self.min_width = min_width
|
||||||
self._min_height = min_height
|
self.min_height = min_height
|
||||||
self._max_width = max_width
|
self.max_width = max_width
|
||||||
self._max_height = max_height
|
self.max_height = max_height
|
||||||
self._fg_color = fg_color
|
self.fg_color = fg_color
|
||||||
self._bg_color = bg_color
|
self.bg_color = bg_color
|
||||||
self._border_color = border_color
|
self.border_color = border_color
|
||||||
self._border_width = border_width
|
self.border_width = border_width
|
||||||
self._border_radius = border_radius
|
self.border_radius = border_radius
|
||||||
self._padding = padding
|
self.padding = padding
|
||||||
self._font = font
|
self.font = font
|
||||||
self._overflow = overflow
|
self.overflow = overflow
|
||||||
self._left = left
|
self.left = left
|
||||||
self._top = top
|
self.top = top
|
||||||
self._right = right
|
self.right = right
|
||||||
self._bottom = bottom
|
self.bottom = bottom
|
||||||
self._flex_grow = flex_grow
|
self.flex_grow = flex_grow
|
||||||
self._flex_shrink = flex_shrink
|
self.flex_shrink = flex_shrink
|
||||||
self._debug_layout = debug_layout
|
self.debug_layout = debug_layout
|
||||||
self._container = None
|
self.container = None
|
||||||
|
|
||||||
def complete_init(self, container):
|
def complete_init(self, container):
|
||||||
self._container = container
|
self.container = container
|
||||||
for c in self._children():
|
for c in self.children():
|
||||||
c.complete_init(self)
|
c.complete_init(self)
|
||||||
|
|
||||||
def _children(self):
|
def children(self):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_document_size(self):
|
def get_document_size(self):
|
||||||
if isinstance(self, layout.Document):
|
if isinstance(self, layout.Document):
|
||||||
return self._actual_size
|
return self.actual_size
|
||||||
else:
|
else:
|
||||||
return self._container.get_document_size()
|
return self.container.get_document_size()
|
||||||
|
|
||||||
def get_fg_color(self):
|
def get_fg_color(self):
|
||||||
if self._fg_color is not None:
|
if self.fg_color is not None:
|
||||||
return self._fg_color
|
return self.fg_color
|
||||||
elif self._container is not None:
|
elif self.container is not None:
|
||||||
return self._container.get_fg_color()
|
return self.container.get_fg_color()
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_font(self):
|
def get_font(self):
|
||||||
if self._font is not None:
|
if self.font is not None:
|
||||||
return self._font
|
return self.font
|
||||||
elif self._container is not None:
|
elif self.container is not None:
|
||||||
return self._container.get_font()
|
return self.container.get_font()
|
||||||
else:
|
else:
|
||||||
raise Exception('no font defined in {0}'.format(self.__class__))
|
raise Exception('no font defined in {0}'.format(self.__class__))
|
||||||
|
|
||||||
def get_overflow(self):
|
def get_overflow(self):
|
||||||
if self._overflow is not None:
|
if self.overflow is not None:
|
||||||
return self._overflow
|
return self.overflow
|
||||||
elif self._container is not None:
|
elif self.container is not None:
|
||||||
return self._container.get_overflow()
|
return self.container.get_overflow()
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_tuple_property(self, name, default, allow_tuple=True):
|
def get_tuple_property(self, name, default, allow_tuple=True):
|
||||||
value = getattr(self, name)
|
value = getattr(self, name)
|
||||||
if isinstance(value, list) or (allow_tuple and isinstance(value, tuple)):
|
if isinstance(value, list) or (allow_tuple and isinstance(value, tuple)):
|
||||||
if len(value) == 0:
|
if len(value) == 0:
|
||||||
|
@ -111,33 +111,33 @@ class Layout:
|
||||||
else:
|
else:
|
||||||
return value, value, value, value
|
return value, value, value, value
|
||||||
|
|
||||||
def padding(self):
|
def get_padding(self):
|
||||||
return self._get_tuple_property('_padding', 0)
|
return self.get_tuple_property('padding', 0)
|
||||||
|
|
||||||
def border_width(self):
|
def get_border_width(self):
|
||||||
return self._get_tuple_property('_border_width', 0)
|
return self.get_tuple_property('border_width', 0)
|
||||||
|
|
||||||
def border_color(self):
|
def get_border_color(self):
|
||||||
return self._get_tuple_property('_border_color', None, False)
|
return self.get_tuple_property('border_color', None, False)
|
||||||
|
|
||||||
def border_radius(self):
|
def get_border_radius(self):
|
||||||
return self._get_tuple_property('_border_radius', None)
|
return self.get_tuple_property('border_radius', None)
|
||||||
|
|
||||||
def get_min_inner_width(self, max_height=None):
|
def get_min_inner_width(self, max_height=None):
|
||||||
return self._min_width
|
return self.min_width
|
||||||
|
|
||||||
def get_min_inner_height(self, max_width=None):
|
def get_min_inner_height(self, max_width=None):
|
||||||
return self._min_height
|
return self.min_height
|
||||||
|
|
||||||
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
||||||
return self._width, self._height
|
return self.width, self.height
|
||||||
|
|
||||||
def get_x_padding_border_size(self):
|
def get_x_padding_border_size(self):
|
||||||
border, padding = self.border_width(), self.padding()
|
border, padding = self.get_border_width(), self.get_padding()
|
||||||
return border[0] + border[2] + padding[0] + padding[2]
|
return border[0] + border[2] + padding[0] + padding[2]
|
||||||
|
|
||||||
def get_y_padding_border_size(self):
|
def get_y_padding_border_size(self):
|
||||||
border, padding = self.border_width(), self.padding()
|
border, padding = self.get_border_width(), self.get_padding()
|
||||||
return border[1] + border[3] + padding[1] + padding[3]
|
return border[1] + border[3] + padding[1] + padding[3]
|
||||||
|
|
||||||
def get_min_outer_width(self, max_outer_height=None):
|
def get_min_outer_width(self, max_outer_height=None):
|
||||||
|
@ -208,7 +208,7 @@ class Layout:
|
||||||
def make_border_inside_mask(self, rect, border_radii):
|
def make_border_inside_mask(self, rect, border_radii):
|
||||||
mask = Image.new('1', self.get_document_size(), 0)
|
mask = Image.new('1', self.get_document_size(), 0)
|
||||||
x1, y1, x2, y2 = rect
|
x1, y1, x2, y2 = rect
|
||||||
width_left, width_top, width_right, width_bottom = self.border_width()
|
width_left, width_top, width_right, width_bottom = self.get_border_width()
|
||||||
radius_top_left, radius_top_right, radius_bottom_right, radius_bottom_left = border_radii
|
radius_top_left, radius_top_right, radius_bottom_right, radius_bottom_left = border_radii
|
||||||
|
|
||||||
inner_rect = (x1 + width_left, y1 + width_top, x2 - width_right, y2 - width_bottom)
|
inner_rect = (x1 + width_left, y1 + width_top, x2 - width_right, y2 - width_bottom)
|
||||||
|
@ -236,8 +236,8 @@ class Layout:
|
||||||
|
|
||||||
def make_border_color_image(self, rect, border_radii):
|
def make_border_color_image(self, rect, border_radii):
|
||||||
x1, y1, x2, y2 = rect
|
x1, y1, x2, y2 = rect
|
||||||
color_left, color_top, color_right, color_bottom = self.border_color()
|
color_left, color_top, color_right, color_bottom = self.get_border_color()
|
||||||
width_left, width_top, width_right, width_bottom = self.border_width()
|
width_left, width_top, width_right, width_bottom = self.get_border_width()
|
||||||
radius_top_left, radius_top_right, radius_bottom_right, radius_bottom_left = border_radii
|
radius_top_left, radius_top_right, radius_bottom_right, radius_bottom_left = border_radii
|
||||||
|
|
||||||
image = self.make_canvas()
|
image = self.make_canvas()
|
||||||
|
@ -297,7 +297,7 @@ class Layout:
|
||||||
x1, y1, x2, y2 = rect
|
x1, y1, x2, y2 = rect
|
||||||
width, height = x2 - x1 + 1, y2 - y1 + 1
|
width, height = x2 - x1 + 1, y2 - y1 + 1
|
||||||
|
|
||||||
border_radii = self.border_radius()
|
border_radii = self.get_border_radius()
|
||||||
radius_sum_top = border_radii[0] + border_radii[1]
|
radius_sum_top = border_radii[0] + border_radii[1]
|
||||||
radius_sum_right = border_radii[1] + border_radii[2]
|
radius_sum_right = border_radii[1] + border_radii[2]
|
||||||
radius_sum_bottom = border_radii[2] + border_radii[3]
|
radius_sum_bottom = border_radii[2] + border_radii[3]
|
||||||
|
@ -354,8 +354,8 @@ class Layout:
|
||||||
self.modify_inside_mask(inside_mask, rect)
|
self.modify_inside_mask(inside_mask, rect)
|
||||||
self.modify_border_mask(border_mask, rect)
|
self.modify_border_mask(border_mask, rect)
|
||||||
|
|
||||||
if self._bg_color is not None:
|
if self.bg_color is not None:
|
||||||
image.paste(self._bg_color, mask=outside_mask)
|
image.paste(self.bg_color, mask=outside_mask)
|
||||||
self.render_after_background(image, rect)
|
self.render_after_background(image, rect)
|
||||||
|
|
||||||
border_color = self.make_border_color_image(rect, border_radii)
|
border_color = self.make_border_color_image(rect, border_radii)
|
||||||
|
@ -365,8 +365,8 @@ class Layout:
|
||||||
image.alpha_composite(border_image)
|
image.alpha_composite(border_image)
|
||||||
self.render_after_border(image, rect)
|
self.render_after_border(image, rect)
|
||||||
|
|
||||||
border_width = self.border_width()
|
border_width = self.get_border_width()
|
||||||
padding = self.padding()
|
padding = self.get_padding()
|
||||||
content_x1 = rect[0] + border_width[0] + padding[0]
|
content_x1 = rect[0] + border_width[0] + padding[0]
|
||||||
content_y1 = rect[1] + border_width[1] + padding[1]
|
content_y1 = rect[1] + border_width[1] + padding[1]
|
||||||
content_x2 = rect[2] - border_width[2] - padding[2]
|
content_x2 = rect[2] - border_width[2] - padding[2]
|
||||||
|
@ -380,7 +380,7 @@ class Layout:
|
||||||
image.alpha_composite(content_image)
|
image.alpha_composite(content_image)
|
||||||
self.render_after_content(image, rect)
|
self.render_after_content(image, rect)
|
||||||
|
|
||||||
if self._debug_layout:
|
if self.debug_layout:
|
||||||
dc = Image.new('RGBA', image.size, (0, 0, 0, 0))
|
dc = Image.new('RGBA', image.size, (0, 0, 0, 0))
|
||||||
d = ImageDraw.Draw(dc)
|
d = ImageDraw.Draw(dc)
|
||||||
|
|
||||||
|
|
|
@ -485,7 +485,11 @@ Where the image is anchored for positioning with `offset_x` and `offset_y`.
|
||||||
|
|
||||||
#### ImageAnchor.BOTTOM_RIGHT
|
#### ImageAnchor.BOTTOM_RIGHT
|
||||||
|
|
||||||
####
|
# custom layouts
|
||||||
|
|
||||||
|
A layout should extend the `Layout` class and implement the following methods:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# possible future features
|
# possible future features
|
||||||
|
|
||||||
|
|
98
text.py
98
text.py
|
@ -18,65 +18,65 @@ class Text(Layout):
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self._text_layouter = None
|
self.text_layouter = None
|
||||||
self._text = text
|
self.text = text
|
||||||
self._text_align = text_align
|
self.text_align = text_align
|
||||||
self._vertical_text_align = vertical_text_align
|
self.vertical_text_align = vertical_text_align
|
||||||
self._text_wrap = text_wrap
|
self.text_wrap = text_wrap
|
||||||
self._line_spacing = line_spacing
|
self.line_spacing = line_spacing
|
||||||
|
|
||||||
def complete_init(self, container):
|
def complete_init(self, container):
|
||||||
super().complete_init(container)
|
super().complete_init(container)
|
||||||
if self._is_empty():
|
if self.is_empty():
|
||||||
lines = []
|
lines = []
|
||||||
elif isinstance(self._text, list):
|
elif isinstance(self.text, list):
|
||||||
lines = self._text
|
lines = self.text
|
||||||
else:
|
else:
|
||||||
lines = self._text.splitlines()
|
lines = self.text.splitlines()
|
||||||
self._text_layouter = TextLayouter(lines=lines,
|
self.text_layouter = TextLayouter(lines=lines,
|
||||||
align=self._text_align,
|
align=self.text_align,
|
||||||
vertical_align=self._vertical_text_align,
|
vertical_align=self.vertical_text_align,
|
||||||
wrap=self._text_wrap,
|
wrap=self.text_wrap,
|
||||||
font=self.get_font(),
|
font=self.get_font(),
|
||||||
line_height=self._get_line_height_with_spacing())
|
line_height=self.get_line_height_with_spacing())
|
||||||
|
|
||||||
def _is_empty(self):
|
def is_empty(self):
|
||||||
return self._text is None or self._text == '' or (isinstance(self._text, list)
|
return self.text is None or self.text == '' or (isinstance(self.text, list)
|
||||||
and len(self._text) == 0)
|
and len(self.text) == 0)
|
||||||
|
|
||||||
def get_min_inner_width(self, max_height=None):
|
def get_min_inner_width(self, max_height=None):
|
||||||
if self._is_empty():
|
if self.is_empty():
|
||||||
width = 0
|
width = 0
|
||||||
else:
|
else:
|
||||||
if max_height is not None:
|
if max_height is not None:
|
||||||
max_height += self._line_spacing
|
max_height += self.line_spacing
|
||||||
width = self._text_layouter.get_dimensions(0, max_height)[0]
|
width = self.text_layouter.get_dimensions(0, max_height)[0]
|
||||||
return max_with_none(width, self._min_width)
|
return max_with_none(width, self.min_width)
|
||||||
|
|
||||||
def get_min_inner_height(self, max_width=None):
|
def get_min_inner_height(self, max_width=None):
|
||||||
if self._is_empty():
|
if self.is_empty():
|
||||||
height = 0
|
height = 0
|
||||||
else:
|
else:
|
||||||
# no spacing after the last line
|
# no spacing after the last line
|
||||||
height = self._text_layouter.get_dimensions(max_width, 0)[1] - self._line_spacing
|
height = self.text_layouter.get_dimensions(max_width, 0)[1] - self.line_spacing
|
||||||
return max_with_none(height, self._min_height)
|
return max_with_none(height, self.min_height)
|
||||||
|
|
||||||
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
||||||
available_width = min_with_none(available_width, self._width)
|
available_width = min_with_none(available_width, self.width)
|
||||||
available_height = min_with_none(available_height, self._height)
|
available_height = min_with_none(available_height, self.height)
|
||||||
|
|
||||||
width, height = self._text_layouter.get_dimensions(available_width, available_height)
|
width, height = self.text_layouter.get_dimensions(available_width, available_height)
|
||||||
height -= self._line_spacing # no spacing after the last line
|
height -= self.line_spacing # no spacing after the last line
|
||||||
|
|
||||||
if self._width is not None:
|
if self.width is not None:
|
||||||
width = self._width
|
width = self.width
|
||||||
if self._height is not None:
|
if self.height is not None:
|
||||||
height = self._height
|
height = self.height
|
||||||
|
|
||||||
width = max_with_none(width, self._min_width)
|
width = max_with_none(width, self.min_width)
|
||||||
height = max_with_none(height, self._min_height)
|
height = max_with_none(height, self.min_height)
|
||||||
width = min_with_none(width, self._max_width)
|
width = min_with_none(width, self.max_width)
|
||||||
height = min_with_none(height, self._max_height)
|
height = min_with_none(height, self.max_height)
|
||||||
|
|
||||||
return width, height
|
return width, height
|
||||||
|
|
||||||
|
@ -86,21 +86,21 @@ class Text(Layout):
|
||||||
|
|
||||||
x1, y1, x2, y2 = rect
|
x1, y1, x2, y2 = rect
|
||||||
width, height = x2 - x1 + 1, y2 - y1 + 1
|
width, height = x2 - x1 + 1, y2 - y1 + 1
|
||||||
if self._max_width is not None:
|
if self.max_width is not None:
|
||||||
width = min(width, self._max_width)
|
width = min(width, self.max_width)
|
||||||
if self._min_width is not None:
|
if self.min_width is not None:
|
||||||
width = max(width, self._min_width)
|
width = max(width, self.min_width)
|
||||||
if self._max_height is not None:
|
if self.max_height is not None:
|
||||||
height = min(height, self._max_height)
|
height = min(height, self.max_height)
|
||||||
if self._min_height is not None:
|
if self.min_height is not None:
|
||||||
height = max(height, self._min_height)
|
height = max(height, self.min_height)
|
||||||
|
|
||||||
lines = self._text_layouter.get_lines(width, height)
|
lines = self.text_layouter.get_lines(width, height)
|
||||||
color = self.get_fg_color()
|
color = self.get_fg_color()
|
||||||
font = self.get_font()
|
font = self.get_font()
|
||||||
for posx, posy, text in lines:
|
for posx, posy, text in lines:
|
||||||
d.text((x1 + posx, y1 + posy), text, font=font, fill=color)
|
d.text((x1 + posx, y1 + posy), text, font=font, fill=color)
|
||||||
return image
|
return image
|
||||||
|
|
||||||
def _get_line_height_with_spacing(self):
|
def get_line_height_with_spacing(self):
|
||||||
return get_line_height(self.get_font()) + self._line_spacing
|
return get_line_height(self.get_font()) + self.line_spacing
|
||||||
|
|
Loading…
Reference in a new issue