Skip to content

Commit 5aced1d

Browse files
authored
Merge pull request #5 from jepler/bundle-images
Bundle images
2 parents 2a2b59c + 746d3cf commit 5aced1d

File tree

3 files changed

+133
-60
lines changed

3 files changed

+133
-60
lines changed

create_requirement_images.py

Lines changed: 97 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
from multiprocessing import Pool
1212
import json
1313
import os
14-
import traceback
1514

15+
import click
1616
from PIL import Image, ImageDraw, ImageFont
1717

1818
from get_imports import (
1919
get_libs_for_project,
2020
get_files_for_project,
21+
get_libs_for_example,
22+
get_files_for_example,
2123
get_learn_guide_cp_projects,
2224
)
2325

@@ -40,20 +42,26 @@
4042
bundle_data = json.load(f)
4143
f.close()
4244

43-
font = ImageFont.truetype("Roboto-Regular.ttf", 24)
44-
right_triangle = Image.open("img/right_triangle.png")
45-
down_triangle = Image.open("img/down_triangle.png")
4645

47-
folder_icon = Image.open("img/folder.png")
48-
folder_hidden_icon = Image.open("img/folder_hidden.png")
49-
file_icon = Image.open("img/file.png")
50-
file_hidden_icon = Image.open("img/file_hidden.png")
51-
file_empty_icon = Image.open("img/file_empty.png")
52-
file_empty_hidden_icon = Image.open("img/file_empty_hidden.png")
46+
def asset_path(asset_name):
47+
"""Return the location of a file shipped with the screenshot maker"""
48+
return os.path.join(os.path.dirname(__file__), asset_name)
5349

54-
file_image_icon = Image.open("img/file_image.png")
55-
file_music_icon = Image.open("img/file_music.png")
56-
file_font_icon = Image.open("img/file_font.png")
50+
51+
font = ImageFont.truetype(asset_path("Roboto-Regular.ttf"), 24)
52+
right_triangle = Image.open(asset_path("img/right_triangle.png"))
53+
down_triangle = Image.open(asset_path("img/down_triangle.png"))
54+
55+
folder_icon = Image.open(asset_path("img/folder.png"))
56+
folder_hidden_icon = Image.open(asset_path("img/folder_hidden.png"))
57+
file_icon = Image.open(asset_path("img/file.png"))
58+
file_hidden_icon = Image.open(asset_path("img/file_hidden.png"))
59+
file_empty_icon = Image.open(asset_path("img/file_empty.png"))
60+
file_empty_hidden_icon = Image.open(asset_path("img/file_empty_hidden.png"))
61+
62+
file_image_icon = Image.open(asset_path("img/file_image.png"))
63+
file_music_icon = Image.open(asset_path("img/file_music.png"))
64+
file_font_icon = Image.open(asset_path("img/file_font.png"))
5765

5866
FILE_TYPE_ICON_MAP = {
5967
"py": file_icon,
@@ -73,7 +81,7 @@
7381

7482

7583
def generate_requirement_image(
76-
learn_guide_project,
84+
project_files, libs, image_name
7785
): # pylint: disable=too-many-statements
7886
"""Generate a single requirement image"""
7987

@@ -149,7 +157,7 @@ def make_line(
149157
font=font,
150158
)
151159

152-
def make_header(position, learn_guide_project):
160+
def make_header(position, project_files):
153161
# Static files
154162
make_line("CIRCUITPY", position)
155163
make_line(
@@ -181,24 +189,17 @@ def make_header(position, learn_guide_project):
181189
)
182190

183191
# dynamic files from project dir in learn guide repo
184-
project_files = get_files_for_project(learn_guide_project)
185192
rows_added = 0
186193
project_files_to_draw = []
187194
project_folders_to_draw = []
188195
for cur_file in project_files:
189196
if "." in cur_file[-5:]:
190197
cur_extension = cur_file.split(".")[-1]
191198
if cur_extension in SHOWN_FILETYPES:
192-
if cur_file != "main.py":
193-
project_files_to_draw.append(cur_file)
199+
project_files_to_draw.append(cur_file)
194200
else:
195201
project_folders_to_draw.append(cur_file)
196202

197-
try:
198-
project_files_to_draw.remove("code.py")
199-
except ValueError:
200-
pass
201-
202203
for i, file in enumerate(sorted(project_files_to_draw)):
203204
cur_file_extension = file.split(".")[-1]
204205

@@ -291,50 +292,87 @@ def make_libraries(libraries, position):
291292
triangle_icon=triangle_icon,
292293
)
293294

294-
try:
295-
libs = get_libs_for_project(learn_guide_project)
296-
final_list_to_render = sort_libraries(libs)
295+
final_list_to_render = sort_libraries(libs)
297296

298-
project_file_list = get_files_for_project(learn_guide_project)
297+
if "code.py" in project_files:
298+
project_files.remove("code.py")
299299

300-
project_files_count = len(project_file_list)
300+
if "main.py" in project_files:
301+
project_files.remove("main.py")
301302

302-
if "code.py" in project_file_list:
303-
project_files_count -= 1
303+
project_files_count = len(project_files)
304304

305-
if "main.py" in project_file_list:
306-
project_files_count -= 1
305+
image_height = (
306+
PADDING * 2
307+
+ 7 * LINE_SPACING
308+
+ len(final_list_to_render) * LINE_SPACING
309+
+ (project_files_count) * LINE_SPACING
310+
)
311+
img = Image.new("RGB", (OUT_WIDTH, image_height), "#303030")
312+
draw = ImageDraw.Draw(img)
307313

308-
image_height = (
309-
PADDING * 2
310-
+ 7 * LINE_SPACING
311-
+ len(final_list_to_render) * LINE_SPACING
312-
+ (project_files_count) * LINE_SPACING
313-
)
314-
img = Image.new("RGB", (OUT_WIDTH, image_height), "#303030")
315-
draw = ImageDraw.Draw(img)
314+
make_background_highlights(
315+
7 + len(final_list_to_render) + project_files_count,
316+
offset=(PADDING, PADDING),
317+
)
316318

317-
make_background_highlights(
318-
7 + len(final_list_to_render) + project_files_count,
319-
offset=(PADDING, PADDING),
320-
)
319+
make_header((PADDING, PADDING), project_files)
320+
make_libraries(
321+
final_list_to_render,
322+
(PADDING, PADDING + (LINE_SPACING * (7 + project_files_count))),
323+
)
321324

322-
make_header((PADDING, PADDING), learn_guide_project)
323-
make_libraries(
324-
final_list_to_render,
325-
(PADDING, PADDING + (LINE_SPACING * (7 + project_files_count))),
326-
)
325+
img.save("generated_images/{}.png".format(image_name))
327326

328-
img.save(
329-
"generated_images/{}.png".format(learn_guide_project.replace("/", "_"))
330-
)
331-
except SyntaxError as exc:
332-
print(exc)
333-
traceback.print_exc()
334-
print("SyntaxError finding imports for {}".format(learn_guide_project))
335327

328+
def generate_learn_requirement_image( # pylint: disable=invalid-name
329+
learn_guide_project,
330+
):
331+
"""Generate an image for a single learn project"""
332+
image_name = learn_guide_project.replace("/", "_")
333+
libs = get_libs_for_project(learn_guide_project)
334+
project_files = get_files_for_project(learn_guide_project)
335+
generate_requirement_image(project_files, libs, image_name)
336+
337+
338+
def generate_example_requirement_image(example_path): # pylint: disable=invalid-name
339+
"""Generate an image for a library example"""
340+
image_name = "_".join(
341+
element
342+
for element in example_path.split("/")
343+
if element not in ("libraries", "drivers", "helpers", "examples")
344+
)
345+
libs = get_libs_for_example(example_path)
346+
project_files = get_files_for_example(example_path)
347+
generate_requirement_image(project_files, libs, image_name)
348+
349+
350+
@click.group(invoke_without_command=True)
351+
@click.pass_context
352+
def cli(ctx):
353+
"""Main entry point; invokes the learn subcommand if nothing is specified"""
354+
if ctx.invoked_subcommand is None:
355+
learn()
356+
357+
358+
@cli.command()
359+
def learn():
360+
"""Generate images for a learn-style repo"""
361+
with Pool() as pool:
362+
for _ in pool.imap(
363+
generate_learn_requirement_image, get_learn_guide_cp_projects()
364+
):
365+
pass
336366

337-
if __name__ == "__main__":
338-
with Pool() as p:
339-
for _ in p.imap(generate_requirement_image, get_learn_guide_cp_projects()):
367+
368+
@cli.command()
369+
@click.argument("paths", nargs=-1)
370+
def bundle(paths):
371+
"""Generate images for a bundle-style repo"""
372+
with Pool() as pool:
373+
for _ in pool.imap(generate_example_requirement_image, paths):
340374
pass
375+
376+
377+
if __name__ == "__main__":
378+
cli() # pylint: disable=no-value-for-parameter

get_imports.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
)
2121

2222
SHOWN_FILETYPES = ["py", "mpy", "bmp", "pcf", "bdf", "wav", "mp3", "json", "txt"]
23+
SHOWN_FILETYPES_EXAMPLE = [s for s in SHOWN_FILETYPES if s != "py"]
2324

2425

2526
def get_bundle(tag):
@@ -140,13 +141,46 @@ def get_libs_for_project(project_name):
140141
return found_libs
141142

142143

144+
def get_files_for_example(example_path):
145+
"""Get the set of files for a library example"""
146+
found_files = set(("code.py",))
147+
example_dir = os.path.dirname(example_path)
148+
for file in os.listdir(example_dir):
149+
if "." in file:
150+
cur_extension = file.split(".")[-1]
151+
if cur_extension in SHOWN_FILETYPES_EXAMPLE:
152+
# print(file)
153+
found_files.add(file)
154+
else:
155+
# add dir
156+
found_files.add(file)
157+
return found_files
158+
159+
160+
def get_libs_for_example(example_path):
161+
"""Get the set of libraries for a library example"""
162+
found_libs = set()
163+
found_imports = []
164+
found_imports = findimports.find_imports(example_path)
165+
166+
for cur_import in found_imports:
167+
cur_lib = cur_import.name.split(".")[0]
168+
if cur_lib in bundle_data:
169+
found_libs.add(cur_lib)
170+
171+
return found_libs
172+
173+
143174
def get_learn_guide_cp_projects():
144175
"""Get the list of all circuitpython projects, according to some heuristics"""
145176
for dirpath, dirnames, filenames in os.walk(LEARN_GUIDE_REPO):
177+
# Don't consider hidden directories
178+
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
179+
146180
# The top-level needs special treatment
147181
if dirpath == LEARN_GUIDE_REPO:
148-
dirnames.remove(".git")
149182
continue
183+
150184
# Skip this folder and all subfolders
151185
if ".circuitpython.skip" in filenames:
152186
del dirnames[:]

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
findimports
22
pillow
33
requests
4+
click

0 commit comments

Comments
 (0)