drawlib 0.2.3.dev2__py3-none-any.whl → 0.2.3.dev4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of drawlib might be problematic. Click here for more details.
- drawlib/__init__.py +1 -1
- drawlib/v0_2/apis.py +5 -0
- drawlib/v0_2/private/core/model.py +19 -1
- drawlib/v0_2/private/core/util.py +6 -0
- drawlib/v0_2/private/core_canvas/base.py +53 -1
- drawlib/v0_2/private/core_canvas/canvas.py +8 -1
- drawlib/v0_2/private/core_canvas/line.py +192 -1
- drawlib/v0_2/private/core_canvas/original_arrow.py +477 -1
- drawlib/v0_2/private/core_canvas/patches.py +19 -69
- drawlib/v0_2/private/smartarts/boxlist.py +178 -0
- drawlib/v0_2/private/smartarts/bubblespeech.py +16 -16
- drawlib/v0_2/private/smartarts/bulletpoints.py +188 -0
- drawlib/v0_2/private/smartarts/dsart.py +7 -0
- drawlib/v0_2/private/smartarts/gridlayout.py +47 -26
- drawlib/v0_2/private/smartarts/pyramid.py +229 -17
- drawlib/v0_2/private/smartarts/table.py +683 -0
- drawlib/v0_2/private/smartarts/tree.py +10 -5
- drawlib/v0_2/private/util.py +116 -1
- drawlib/v0_2/private/validators/args.py +26 -11
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/METADATA +1 -1
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/RECORD +24 -21
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/LICENSE.txt +0 -0
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/WHEEL +0 -0
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Copyright (c) 2024 Yuichi Ito (yuichi@yuichi.com)
|
|
2
|
+
#
|
|
3
|
+
# This software is licensed under the Apache License, Version 2.0.
|
|
4
|
+
# For more information, please visit: https://github.com/yuichi110/drawlib
|
|
5
|
+
#
|
|
6
|
+
# This software is provided "as is", without warranty of any kind,
|
|
7
|
+
# express or implied, including but not limited to the warranties of
|
|
8
|
+
# merchantability, fitness for a particular purpose and noninfringement.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
"""BoxList implementation module."""
|
|
12
|
+
|
|
13
|
+
from typing import Any, List, Literal, Optional, Tuple, Union
|
|
14
|
+
|
|
15
|
+
from drawlib.v0_2.private.core.colors import Colors
|
|
16
|
+
from drawlib.v0_2.private.core.model import ShapeStyle, ShapeTextStyle
|
|
17
|
+
from drawlib.v0_2.private.core.theme import dtheme
|
|
18
|
+
from drawlib.v0_2.private.core_canvas.canvas import rectangle
|
|
19
|
+
from drawlib.v0_2.private.util import error_handler
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BoxList:
|
|
23
|
+
"""A class to draw a list of boxes with text, supporting highlighting of certain boxes.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
box_style (Union[str, ShapeStyle, None]): The style for the boxes.
|
|
27
|
+
text_style (Union[str, ShapeTextStyle, None]): The style for the text inside the boxes.
|
|
28
|
+
box_highlight_style (Union[str, ShapeStyle, None]): The style for the highlighted boxes.
|
|
29
|
+
text_highlight_style (Union[str, ShapeTextStyle, None]): The style for the text inside the highlighted boxes.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@error_handler
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
box_style: Union[str, ShapeStyle, None] = None,
|
|
36
|
+
text_style: Union[str, ShapeTextStyle, None] = None,
|
|
37
|
+
box_highlight_style: Union[str, ShapeStyle, None] = None,
|
|
38
|
+
text_highlight_style: Union[str, ShapeTextStyle, None] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Initialize BoxList.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
box_style (Union[str, ShapeStyle, None]): The style for the boxes.
|
|
44
|
+
text_style (Union[str, ShapeTextStyle, None]): The style for the text inside the boxes.
|
|
45
|
+
box_highlight_style (Union[str, ShapeStyle, None]): The style for the highlighted boxes.
|
|
46
|
+
text_highlight_style (Union[str, ShapeTextStyle, None]):
|
|
47
|
+
The style for the text inside the highlighted boxes.
|
|
48
|
+
"""
|
|
49
|
+
if isinstance(box_style, str):
|
|
50
|
+
box_style = dtheme.rectanglestyles.get(box_style)
|
|
51
|
+
if box_style is None:
|
|
52
|
+
box_style = dtheme.rectanglestyles.get()
|
|
53
|
+
box_style.halign = "center"
|
|
54
|
+
box_style.valign = "center"
|
|
55
|
+
self._box_style = box_style
|
|
56
|
+
|
|
57
|
+
if isinstance(text_style, str):
|
|
58
|
+
text_style = dtheme.rectangletextstyles.get(text_style)
|
|
59
|
+
if text_style is None:
|
|
60
|
+
text_style = dtheme.rectangletextstyles.get()
|
|
61
|
+
self._text_style = text_style
|
|
62
|
+
|
|
63
|
+
if isinstance(box_highlight_style, str):
|
|
64
|
+
box_highlight_style = dtheme.rectanglestyles.get(box_highlight_style)
|
|
65
|
+
if box_highlight_style is None:
|
|
66
|
+
box_highlight_style = box_style
|
|
67
|
+
box_highlight_style.halign = "center"
|
|
68
|
+
box_highlight_style.valign = "center"
|
|
69
|
+
self._box_highlight_style = box_highlight_style
|
|
70
|
+
|
|
71
|
+
if isinstance(text_highlight_style, str):
|
|
72
|
+
text_highlight_style = dtheme.rectangletextstyles.get(text_highlight_style)
|
|
73
|
+
if text_highlight_style is None:
|
|
74
|
+
text_highlight_style = text_style
|
|
75
|
+
self._text_highlight_style = text_highlight_style
|
|
76
|
+
|
|
77
|
+
@error_handler
|
|
78
|
+
def draw(
|
|
79
|
+
self,
|
|
80
|
+
xy: Tuple[float, float],
|
|
81
|
+
box_width: float,
|
|
82
|
+
box_height: float,
|
|
83
|
+
items: List[Any],
|
|
84
|
+
highlight_indexs: Optional[List[int]] = None,
|
|
85
|
+
length: Optional[int] = None,
|
|
86
|
+
align: Literal["left", "right", "bottom", "top"] = "left",
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Draws a list of boxes at the specified location.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
xy (Tuple[float, float]): The starting point (x, y) to draw the list of boxes.
|
|
92
|
+
box_width (float): The width of each box.
|
|
93
|
+
box_height (float): The height of each box.
|
|
94
|
+
items (List[Any]): The list of items to be displayed inside the boxes.
|
|
95
|
+
highlight_indexs (Optional[List[int]]):
|
|
96
|
+
The list of indices of the boxes to be highlighted.
|
|
97
|
+
length (Optional[int]): The total number of boxes to be drawn.
|
|
98
|
+
align (Literal["left", "right", "bottom", "top"]):
|
|
99
|
+
The alignment of the boxes relative to the starting point.
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
if highlight_indexs is None:
|
|
103
|
+
highlight_indexs = []
|
|
104
|
+
|
|
105
|
+
if length is not None:
|
|
106
|
+
indexs = range(length)
|
|
107
|
+
else:
|
|
108
|
+
indexs = range(len(items))
|
|
109
|
+
|
|
110
|
+
non_highlight_indexs = [item for item in indexs if item not in highlight_indexs]
|
|
111
|
+
highlight_indexs = [item for item in indexs if item in highlight_indexs]
|
|
112
|
+
|
|
113
|
+
for index in non_highlight_indexs:
|
|
114
|
+
if index <= len(items) - 1:
|
|
115
|
+
text = str(items[index])
|
|
116
|
+
else:
|
|
117
|
+
text = ""
|
|
118
|
+
|
|
119
|
+
self._draw_cell(
|
|
120
|
+
start_xy=xy,
|
|
121
|
+
index=index,
|
|
122
|
+
text=text,
|
|
123
|
+
box_width=box_width,
|
|
124
|
+
box_height=box_height,
|
|
125
|
+
box_style=self._box_style,
|
|
126
|
+
text_style=self._text_style,
|
|
127
|
+
align=align,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
for index in highlight_indexs:
|
|
131
|
+
if index <= len(items) - 1:
|
|
132
|
+
text = str(items[index])
|
|
133
|
+
else:
|
|
134
|
+
text = ""
|
|
135
|
+
|
|
136
|
+
self._draw_cell(
|
|
137
|
+
start_xy=xy,
|
|
138
|
+
index=index,
|
|
139
|
+
text=text,
|
|
140
|
+
box_width=box_width,
|
|
141
|
+
box_height=box_height,
|
|
142
|
+
box_style=self._box_highlight_style,
|
|
143
|
+
text_style=self._text_highlight_style,
|
|
144
|
+
align=align,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def _draw_cell(
|
|
149
|
+
start_xy: Tuple[float, float],
|
|
150
|
+
index: int,
|
|
151
|
+
text: str,
|
|
152
|
+
box_width: float,
|
|
153
|
+
box_height: float,
|
|
154
|
+
box_style: ShapeStyle,
|
|
155
|
+
text_style: ShapeTextStyle,
|
|
156
|
+
align: Literal["left", "right", "bottom", "top"],
|
|
157
|
+
) -> None:
|
|
158
|
+
if align == "left":
|
|
159
|
+
x = start_xy[0] + box_width * (index + 0.5)
|
|
160
|
+
y = start_xy[1] + box_height / 2
|
|
161
|
+
elif align == "right":
|
|
162
|
+
x = start_xy[0] - box_width * (index + 0.5)
|
|
163
|
+
y = start_xy[1] + box_height / 2
|
|
164
|
+
elif align == "bottom":
|
|
165
|
+
x = start_xy[0] + box_width / 2
|
|
166
|
+
y = start_xy[1] + box_height * (index + 0.5)
|
|
167
|
+
else:
|
|
168
|
+
x = start_xy[0] + box_width / 2
|
|
169
|
+
y = start_xy[1] - box_height * (index + 0.5)
|
|
170
|
+
|
|
171
|
+
rectangle(
|
|
172
|
+
xy=(x, y),
|
|
173
|
+
width=box_width,
|
|
174
|
+
height=box_height,
|
|
175
|
+
style=box_style,
|
|
176
|
+
text=text,
|
|
177
|
+
textstyle=text_style,
|
|
178
|
+
)
|
|
@@ -28,9 +28,9 @@ def bubblespeech(
|
|
|
28
28
|
width: float,
|
|
29
29
|
height: float,
|
|
30
30
|
tail_edge: Literal["left", "top", "right", "bottom"],
|
|
31
|
-
|
|
31
|
+
tail_start_ratio: float,
|
|
32
32
|
tail_vertex_xy: Tuple[float, float],
|
|
33
|
-
|
|
33
|
+
tail_end_ratio: float,
|
|
34
34
|
style: Union[ShapeStyle, str, None] = None,
|
|
35
35
|
text: str = "",
|
|
36
36
|
textsize: Optional[float] = None,
|
|
@@ -43,9 +43,9 @@ def bubblespeech(
|
|
|
43
43
|
width (float): The width of the bubble.
|
|
44
44
|
height (float): The height of the bubble.
|
|
45
45
|
tail_edge (Literal["left", "top", "right", "bottom"]): The edge on which the tail will be positioned.
|
|
46
|
-
|
|
46
|
+
tail_start_ratio (float): The ratio along the edge where the tail starts.
|
|
47
47
|
tail_vertex_xy (Tuple[float, float]): The (x, y) coordinates of the tail's vertex.
|
|
48
|
-
|
|
48
|
+
tail_end_ratio (float): The ratio along the edge where the tail ends.
|
|
49
49
|
style (Union[ShapeStyle, str, None], optional): The style of the bubble. Defaults to None.
|
|
50
50
|
text (str, optional): The text to display inside the bubble. Defaults to an empty string.
|
|
51
51
|
textsize (Optional[float], optional): The size of the text. Defaults to None.
|
|
@@ -66,33 +66,33 @@ def bubblespeech(
|
|
|
66
66
|
if textsize is not None:
|
|
67
67
|
textstyle.size = textsize
|
|
68
68
|
|
|
69
|
-
if
|
|
70
|
-
raise ValueError("
|
|
71
|
-
if
|
|
72
|
-
raise ValueError("
|
|
69
|
+
if tail_start_ratio > tail_end_ratio:
|
|
70
|
+
raise ValueError("tail_start_ratio must be smaller than tail_end_ratio.")
|
|
71
|
+
if tail_end_ratio > 1.0:
|
|
72
|
+
raise ValueError("tail_start_ratio and tail_end_ratio must be smaller than 1.0")
|
|
73
73
|
|
|
74
74
|
x, y = xy
|
|
75
75
|
xys = []
|
|
76
76
|
xys.append((x, y)) # left bottom
|
|
77
77
|
if tail_edge == "left":
|
|
78
|
-
xys.append((x, y + height *
|
|
78
|
+
xys.append((x, y + height * tail_start_ratio))
|
|
79
79
|
xys.append(tail_vertex_xy)
|
|
80
|
-
xys.append((x, y + height *
|
|
80
|
+
xys.append((x, y + height * tail_end_ratio))
|
|
81
81
|
xys.append((x, y + height)) # left top
|
|
82
82
|
if tail_edge == "top":
|
|
83
|
-
xys.append((x + width *
|
|
83
|
+
xys.append((x + width * tail_start_ratio, y + height))
|
|
84
84
|
xys.append(tail_vertex_xy)
|
|
85
|
-
xys.append((x + width *
|
|
85
|
+
xys.append((x + width * tail_end_ratio, y + height))
|
|
86
86
|
xys.append((x + width, y + height)) # right top
|
|
87
87
|
if tail_edge == "right":
|
|
88
|
-
xys.append((x + width, y + height *
|
|
88
|
+
xys.append((x + width, y + height * tail_end_ratio))
|
|
89
89
|
xys.append(tail_vertex_xy)
|
|
90
|
-
xys.append((x + width, y + height *
|
|
90
|
+
xys.append((x + width, y + height * tail_start_ratio))
|
|
91
91
|
xys.append((x + width, y)) # right bottom
|
|
92
92
|
if tail_edge == "bottom":
|
|
93
|
-
xys.append((x + width *
|
|
93
|
+
xys.append((x + width * tail_end_ratio, y))
|
|
94
94
|
xys.append(tail_vertex_xy)
|
|
95
|
-
xys.append((x + width *
|
|
95
|
+
xys.append((x + width * tail_start_ratio, y))
|
|
96
96
|
|
|
97
97
|
options = ShapeUtil.get_shape_options(style)
|
|
98
98
|
canvas._artists.append(Polygon(xy=xys, closed=True, **options))
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Copyright (c) 2024 Yuichi Ito (yuichi@yuichi.com)
|
|
2
|
+
#
|
|
3
|
+
# This software is licensed under the Apache License, Version 2.0.
|
|
4
|
+
# For more information, please visit: https://github.com/yuichi110/drawlib
|
|
5
|
+
#
|
|
6
|
+
# This software is provided "as is", without warranty of any kind,
|
|
7
|
+
# express or implied, including but not limited to the warranties of
|
|
8
|
+
# merchantability, fitness for a particular purpose and noninfringement.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
"""BulletPoints implementation module."""
|
|
12
|
+
|
|
13
|
+
import dataclasses
|
|
14
|
+
from typing import Callable, List, Optional, Tuple, Union
|
|
15
|
+
|
|
16
|
+
from drawlib.v0_2.private.core.colors import Colors
|
|
17
|
+
from drawlib.v0_2.private.core.model import ShapeStyle, TextStyle
|
|
18
|
+
from drawlib.v0_2.private.core.theme import dtheme
|
|
19
|
+
from drawlib.v0_2.private.core_canvas.canvas import circle, text
|
|
20
|
+
from drawlib.v0_2.private.util import error_handler
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BulletPoints:
|
|
24
|
+
"""A class to draw a list of bullet points with customizable styles and indentation.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
vertical_margin (float): The vertical space between bullet points.
|
|
28
|
+
indent_width (float): The width of the indentation for each level.
|
|
29
|
+
default_style (Union[str, TextStyle, None]): The default text style for the bullet points.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@error_handler
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
vertical_margin: float,
|
|
36
|
+
indent_width: float,
|
|
37
|
+
default_style: Union[str, TextStyle, None] = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Initialize BulletPoints.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
vertical_margin (float): The vertical space between bullet points.
|
|
43
|
+
indent_width (float): The width of the indentation for each level.
|
|
44
|
+
default_style (Union[str, TextStyle, None]): The default text style for the bullet points.
|
|
45
|
+
"""
|
|
46
|
+
self._vertical_margin = vertical_margin
|
|
47
|
+
self._indent_width = indent_width
|
|
48
|
+
if isinstance(default_style, str):
|
|
49
|
+
default_style = dtheme.textstyles.get(default_style)
|
|
50
|
+
if default_style is None:
|
|
51
|
+
default_style = dtheme.textstyles.get()
|
|
52
|
+
self._default_style = default_style
|
|
53
|
+
|
|
54
|
+
self._indent_level = 0
|
|
55
|
+
self._bullet_texts: List[_BulletPointsText] = []
|
|
56
|
+
self._bullet_shape_map: dict[int, _BulletPointsShape] = {}
|
|
57
|
+
|
|
58
|
+
# set default bullet styles
|
|
59
|
+
text_color = default_style.color
|
|
60
|
+
style1 = dtheme.shapestyles.get()
|
|
61
|
+
style1.lcolor = text_color
|
|
62
|
+
style1.fcolor = text_color
|
|
63
|
+
style2 = dtheme.shapestyles.get()
|
|
64
|
+
style2.lcolor = text_color
|
|
65
|
+
style2.fcolor = Colors.Transparent
|
|
66
|
+
self.set_bullet_style(1, circle, style1, args={"radius": 0.5})
|
|
67
|
+
self.set_bullet_style(2, circle, style2, args={"radius": 0.5})
|
|
68
|
+
|
|
69
|
+
@error_handler
|
|
70
|
+
def set_indent(self, level: int) -> None:
|
|
71
|
+
"""Sets the indentation level for the next bullet point.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
level (int): The indentation level.
|
|
75
|
+
"""
|
|
76
|
+
self._indent_level = level
|
|
77
|
+
|
|
78
|
+
@error_handler
|
|
79
|
+
def set_bullet_style(
|
|
80
|
+
self,
|
|
81
|
+
indent_level: int,
|
|
82
|
+
function: Callable,
|
|
83
|
+
style: Union[str, ShapeStyle],
|
|
84
|
+
args: dict,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Sets the style and function for drawing bullets at a specific indentation level.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
indent_level (int): The indentation level to apply the style to.
|
|
90
|
+
function (Callable): The function to draw the bullet shape.
|
|
91
|
+
style (Union[str, ShapeStyle]): The style to apply to the bullet shape.
|
|
92
|
+
args (dict): Additional arguments to pass to the drawing function.
|
|
93
|
+
"""
|
|
94
|
+
if isinstance(style, str):
|
|
95
|
+
style = dtheme.shapestyles.get(style)
|
|
96
|
+
style.halign = "center"
|
|
97
|
+
style.valign = "center"
|
|
98
|
+
|
|
99
|
+
item = _BulletPointsShape(
|
|
100
|
+
function=function,
|
|
101
|
+
style=style,
|
|
102
|
+
args=args,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
self._bullet_shape_map[indent_level] = item
|
|
106
|
+
|
|
107
|
+
@error_handler
|
|
108
|
+
def add(
|
|
109
|
+
self,
|
|
110
|
+
text: str,
|
|
111
|
+
style: Union[str, TextStyle, None] = None,
|
|
112
|
+
) -> None:
|
|
113
|
+
"""Adds a bullet point with the specified text and style.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
text (str): The text for the bullet point.
|
|
117
|
+
style (Union[str, TextStyle, None]): The text style for the bullet point.
|
|
118
|
+
"""
|
|
119
|
+
if isinstance(style, str):
|
|
120
|
+
style = dtheme.textstyles.get(style)
|
|
121
|
+
elif style is None:
|
|
122
|
+
style = self._default_style
|
|
123
|
+
if style is None:
|
|
124
|
+
style = dtheme.textstyles.get()
|
|
125
|
+
style.halign = "left"
|
|
126
|
+
style.valign = "center"
|
|
127
|
+
|
|
128
|
+
self._bullet_texts.append(
|
|
129
|
+
_BulletPointsText(
|
|
130
|
+
self._indent_level,
|
|
131
|
+
text,
|
|
132
|
+
style,
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
@error_handler
|
|
137
|
+
def draw(
|
|
138
|
+
self,
|
|
139
|
+
xy: Tuple[float, float],
|
|
140
|
+
) -> None:
|
|
141
|
+
"""Draws the list of bullet points starting from the specified location.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
xy (Tuple[float, float]): The starting point (x, y) to draw the bullet points.
|
|
145
|
+
"""
|
|
146
|
+
y = xy[1]
|
|
147
|
+
for bullet_text in self._bullet_texts:
|
|
148
|
+
indent = bullet_text.indent
|
|
149
|
+
text_ = bullet_text.text
|
|
150
|
+
textstyle = bullet_text.style
|
|
151
|
+
|
|
152
|
+
x = xy[0] + self._indent_width * indent
|
|
153
|
+
text(
|
|
154
|
+
xy=(x, y),
|
|
155
|
+
text=text_,
|
|
156
|
+
style=textstyle,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if indent == 0:
|
|
160
|
+
...
|
|
161
|
+
elif indent not in self._bullet_shape_map:
|
|
162
|
+
...
|
|
163
|
+
else:
|
|
164
|
+
bps = self._bullet_shape_map[indent]
|
|
165
|
+
function = bps.function
|
|
166
|
+
shapestyle = bps.style
|
|
167
|
+
args = bps.args
|
|
168
|
+
|
|
169
|
+
x = xy[0] + self._indent_width * (indent - 0.5)
|
|
170
|
+
args["xy"] = (x, y)
|
|
171
|
+
args["style"] = shapestyle
|
|
172
|
+
function(**args)
|
|
173
|
+
|
|
174
|
+
y -= self._vertical_margin
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@dataclasses.dataclass
|
|
178
|
+
class _BulletPointsShape:
|
|
179
|
+
function: Callable
|
|
180
|
+
style: ShapeStyle
|
|
181
|
+
args: dict
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@dataclasses.dataclass
|
|
185
|
+
class _BulletPointsText:
|
|
186
|
+
indent: int
|
|
187
|
+
text: str
|
|
188
|
+
style: TextStyle
|
|
@@ -11,8 +11,15 @@
|
|
|
11
11
|
|
|
12
12
|
"""Smart art module."""
|
|
13
13
|
|
|
14
|
+
# Using "bubblespeech as bubblespeech" for explisitly esposing contents.
|
|
15
|
+
# It is good for type checker and IDE.
|
|
16
|
+
# Warnings such as "reportPrivateImportUsage" might be appear if removing it.
|
|
17
|
+
|
|
18
|
+
from drawlib.v0_2.private.smartarts.boxlist import BoxList as BoxList
|
|
14
19
|
from drawlib.v0_2.private.smartarts.bubblespeech import bubblespeech as bubblespeech
|
|
20
|
+
from drawlib.v0_2.private.smartarts.bulletpoints import BulletPoints as BulletPoints
|
|
15
21
|
from drawlib.v0_2.private.smartarts.gridlayout import GridLayout as GridLayout
|
|
16
22
|
from drawlib.v0_2.private.smartarts.pyramid import Pyramid as Pyramid
|
|
17
23
|
from drawlib.v0_2.private.smartarts.sourcecode import SourceCode as SourceCode
|
|
24
|
+
from drawlib.v0_2.private.smartarts.table import Table as Table
|
|
18
25
|
from drawlib.v0_2.private.smartarts.tree import TreeNode as TreeNode
|
|
@@ -16,11 +16,13 @@ from typing import List, Optional, Tuple, Union
|
|
|
16
16
|
from drawlib.v0_2.private.core.model import ShapeStyle, ShapeTextStyle
|
|
17
17
|
from drawlib.v0_2.private.core.theme import dtheme
|
|
18
18
|
from drawlib.v0_2.private.core_canvas.canvas import rectangle
|
|
19
|
+
from drawlib.v0_2.private.util import error_handler
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class GridLayout:
|
|
22
23
|
"""Class for rendering multiple rectangles which fit to grid."""
|
|
23
24
|
|
|
25
|
+
@error_handler
|
|
24
26
|
def __init__(
|
|
25
27
|
self,
|
|
26
28
|
num_column: int,
|
|
@@ -28,6 +30,7 @@ class GridLayout:
|
|
|
28
30
|
default_r: int = 0,
|
|
29
31
|
default_style: Union[str, ShapeStyle, None] = None,
|
|
30
32
|
default_textstyle: Union[str, ShapeTextStyle, None] = None,
|
|
33
|
+
default_textangle: Optional[float] = None,
|
|
31
34
|
) -> None:
|
|
32
35
|
"""Initialize class."""
|
|
33
36
|
self._num_column = num_column
|
|
@@ -39,9 +42,11 @@ class GridLayout:
|
|
|
39
42
|
if isinstance(default_textstyle, str):
|
|
40
43
|
default_textstyle = dtheme.rectangletextstyles.get(default_textstyle)
|
|
41
44
|
self._default_textstyle = default_textstyle
|
|
45
|
+
self._default_textangle = default_textangle
|
|
42
46
|
|
|
43
47
|
self._items: List[_GridLayoutItem] = []
|
|
44
48
|
|
|
49
|
+
@error_handler
|
|
45
50
|
def add( # noqa: C901
|
|
46
51
|
self,
|
|
47
52
|
column_range: Tuple[int, int],
|
|
@@ -50,6 +55,8 @@ class GridLayout:
|
|
|
50
55
|
style: Union[str, ShapeStyle, None] = None,
|
|
51
56
|
text: str = "",
|
|
52
57
|
textstyle: Union[str, ShapeTextStyle, None] = None,
|
|
58
|
+
textangle: Optional[float] = None,
|
|
59
|
+
text_xy_shift: Optional[Tuple[float, float]] = None,
|
|
53
60
|
) -> None:
|
|
54
61
|
"""
|
|
55
62
|
Add an item to the grid layout.
|
|
@@ -66,6 +73,10 @@ class GridLayout:
|
|
|
66
73
|
textstyle (Union[str, ShapeTextStyle, None], optional):
|
|
67
74
|
The text style of the item. Can be a string key for a predefined text style,
|
|
68
75
|
a ShapeTextStyle object, or None to use the default text style.
|
|
76
|
+
textangle (Optional[float], optional):
|
|
77
|
+
The angle of the text. Default is None, which is same to 0.
|
|
78
|
+
text_xy_shift (Optional[Tuple[float, float]], optional):
|
|
79
|
+
The XY shift of the text. Default is None, which is same to (0, 0).
|
|
69
80
|
|
|
70
81
|
Raises:
|
|
71
82
|
ValueError: If the column or row ranges are invalid or out of bounds.
|
|
@@ -73,20 +84,22 @@ class GridLayout:
|
|
|
73
84
|
# validate column
|
|
74
85
|
c1, c2 = column_range
|
|
75
86
|
if c1 > c2:
|
|
76
|
-
|
|
87
|
+
column_range = (c2, c1)
|
|
88
|
+
c1, c2 = c2, c1
|
|
77
89
|
if c1 < 0:
|
|
78
|
-
raise ValueError()
|
|
90
|
+
raise ValueError("Column range must be between 0 ~ last-index.")
|
|
79
91
|
if c2 > self._num_column:
|
|
80
|
-
raise ValueError()
|
|
92
|
+
raise ValueError("Column range must be between 0 ~ last-index.")
|
|
81
93
|
|
|
82
94
|
# validate row
|
|
83
95
|
r1, r2 = row_range
|
|
84
96
|
if r1 > r2:
|
|
85
|
-
|
|
97
|
+
row_range = (r2, r1)
|
|
98
|
+
r1, r2 = r2, r1
|
|
86
99
|
if r1 < 0:
|
|
87
|
-
raise ValueError()
|
|
100
|
+
raise ValueError("Row range must be between 0 ~ last-index.")
|
|
88
101
|
if r2 > self._num_column:
|
|
89
|
-
raise ValueError()
|
|
102
|
+
raise ValueError("Row range must be between 0 ~ last-index.")
|
|
90
103
|
|
|
91
104
|
# string style to Style class
|
|
92
105
|
if isinstance(style, str):
|
|
@@ -101,6 +114,26 @@ class GridLayout:
|
|
|
101
114
|
style = self._default_style
|
|
102
115
|
if textstyle is None:
|
|
103
116
|
textstyle = self._default_textstyle
|
|
117
|
+
if textangle is None:
|
|
118
|
+
textangle = self._default_textangle
|
|
119
|
+
|
|
120
|
+
# set align left bottom
|
|
121
|
+
if style is None:
|
|
122
|
+
style = self._default_style
|
|
123
|
+
if style is None:
|
|
124
|
+
style = dtheme.rectanglestyles.get()
|
|
125
|
+
else:
|
|
126
|
+
style = style.copy()
|
|
127
|
+
style.halign = "left"
|
|
128
|
+
style.valign = "bottom"
|
|
129
|
+
|
|
130
|
+
# set text angle and xy_shift
|
|
131
|
+
if textstyle is None:
|
|
132
|
+
textstyle = dtheme.rectangletextstyles.get()
|
|
133
|
+
if textangle is not None:
|
|
134
|
+
textstyle.angle = textangle
|
|
135
|
+
if text_xy_shift is not None:
|
|
136
|
+
textstyle.xy_shift = text_xy_shift
|
|
104
137
|
|
|
105
138
|
# push
|
|
106
139
|
item = _GridLayoutItem(
|
|
@@ -113,6 +146,7 @@ class GridLayout:
|
|
|
113
146
|
)
|
|
114
147
|
self._items.append(item)
|
|
115
148
|
|
|
149
|
+
@error_handler
|
|
116
150
|
def draw(
|
|
117
151
|
self,
|
|
118
152
|
xy: Tuple[float, float],
|
|
@@ -154,6 +188,7 @@ class GridLayout:
|
|
|
154
188
|
outer_style=outer_style,
|
|
155
189
|
)
|
|
156
190
|
|
|
191
|
+
@error_handler
|
|
157
192
|
def draw_flexible( # noqa: C901
|
|
158
193
|
self,
|
|
159
194
|
xy: Tuple[float, float],
|
|
@@ -182,13 +217,13 @@ class GridLayout:
|
|
|
182
217
|
"""
|
|
183
218
|
# validate
|
|
184
219
|
if len(column_widths) != self._num_column:
|
|
185
|
-
raise ValueError()
|
|
220
|
+
raise ValueError('Length of arg "column_widths" does not match to num of columns')
|
|
186
221
|
if len(column_margins) != self._num_column + 1:
|
|
187
|
-
raise ValueError()
|
|
222
|
+
raise ValueError('Length of arg "column_margins" does not match to num of columns + 1')
|
|
188
223
|
if len(row_heights) != self._num_row:
|
|
189
|
-
raise ValueError()
|
|
224
|
+
raise ValueError('Length of arg "row_heights" does not match to num of rows')
|
|
190
225
|
if len(row_margins) != self._num_row + 1:
|
|
191
|
-
raise ValueError()
|
|
226
|
+
raise ValueError('Length of arg "row_margins" does not match to num of rows + 1')
|
|
192
227
|
|
|
193
228
|
# draw outer rectangle
|
|
194
229
|
if outer_style is not None:
|
|
@@ -239,20 +274,6 @@ class GridLayout:
|
|
|
239
274
|
width = width - margin_left - margin_right
|
|
240
275
|
height = height - margin_bottom - margin_top
|
|
241
276
|
|
|
242
|
-
# set align left bottom
|
|
243
|
-
if style is None:
|
|
244
|
-
style = self._default_style
|
|
245
|
-
if style is None:
|
|
246
|
-
style = dtheme.rectanglestyles.get()
|
|
247
|
-
else:
|
|
248
|
-
style = style.copy()
|
|
249
|
-
style.halign = "left"
|
|
250
|
-
style.valign = "bottom"
|
|
251
|
-
|
|
252
|
-
# apply default text style if specified text style is None
|
|
253
|
-
if textstyle is None:
|
|
254
|
-
textstyle = self._default_textstyle
|
|
255
|
-
|
|
256
277
|
# draw
|
|
257
278
|
rectangle(
|
|
258
279
|
xy=(xy[0] + lb_xy[0], xy[1] + lb_xy[1]),
|
|
@@ -270,6 +291,6 @@ class _GridLayoutItem:
|
|
|
270
291
|
column_range: Tuple[int, int]
|
|
271
292
|
row_range: Tuple[int, int]
|
|
272
293
|
r: int
|
|
273
|
-
style:
|
|
294
|
+
style: ShapeStyle
|
|
274
295
|
text: str
|
|
275
|
-
textstyle:
|
|
296
|
+
textstyle: ShapeTextStyle
|