pibooth-template-chooser 1.0.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ceeeeb
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: pibooth_template_chooser
3
+ Version: 1.0.0
4
+ Summary: Pibooth plugin letting guests choose the picture template on the wait screen
5
+ Home-page: https://github.com/ceeeeb/pibooth-template-chooser
6
+ Author: Ceeeeb
7
+ License: MIT license
8
+ Keywords: Raspberry Pi,photobooth,pibooth,template
9
+ Platform: unix
10
+ Platform: linux
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: pibooth-ceeeeb>=2.0.8.3
14
+ Dynamic: author
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: keywords
19
+ Dynamic: license
20
+ Dynamic: license-file
21
+ Dynamic: platform
22
+ Dynamic: requires-dist
23
+ Dynamic: summary
24
+
25
+ # pibooth-template-chooser
26
+
27
+ Pibooth plugin letting guests choose the picture template on the wait screen.
28
+
29
+ The wait screen shows the selected template in place of the last picture:
30
+ the first capture of the last picture assembled again in the single photo page
31
+ of the template, or grey photo holders until a picture exists. A discreet
32
+ caption at the top gives its name and position in the list. Guests change the
33
+ template by swiping horizontally on the screen, by tapping the `<` `>` arrows
34
+ of the caption, or with the arrow keys. The chosen template is used for the
35
+ next pictures; the saved and printed picture is not changed.
36
+
37
+ The templates are the draw.io files of the
38
+ [pibooth-picture-template](https://github.com/pibooth/pibooth-picture-template)
39
+ plugin, which must be installed (with `pip install --no-deps` to keep the
40
+ pibooth fork).
41
+
42
+ ## Configuration
43
+
44
+ ```ini
45
+ [TEMPLATE_CHOOSER]
46
+ # Directory of the picture templates guests choose from on the wait screen
47
+ directory = ~/.config/pibooth/templates
48
+ ```
49
+
50
+ Every `*.xml` file of the directory is offered. The template set in
51
+ `[PICTURE] template` is selected at startup when it belongs to the directory.
52
+
53
+ ## Template style
54
+
55
+ A template may come with a style file of the same name (`hollywood.xml` and
56
+ `hollywood.cfg`) giving the name displayed in the caption and the style of the
57
+ texts, which replaces the `[PICTURE]` values while the template is selected:
58
+
59
+ ```ini
60
+ [TEMPLATE]
61
+ name = Hollywood
62
+ text_colors = ((255, 255, 255), (230, 200, 205))
63
+ text_fonts = ('../fonts/Montserrat-Bold.ttf', '../fonts/Montserrat-SemiBold.ttf')
64
+ text_alignments = center
65
+ ```
66
+
67
+ Relative font paths are resolved from the style file directory. Without a
68
+ style file, the name comes from the file name and the `[PICTURE]` style is kept.
69
+
70
+ The texts themselves (`footer_text1`, `footer_text2`) stay in `pibooth.cfg`,
71
+ shared by all the templates of an event.
@@ -0,0 +1,47 @@
1
+ # pibooth-template-chooser
2
+
3
+ Pibooth plugin letting guests choose the picture template on the wait screen.
4
+
5
+ The wait screen shows the selected template in place of the last picture:
6
+ the first capture of the last picture assembled again in the single photo page
7
+ of the template, or grey photo holders until a picture exists. A discreet
8
+ caption at the top gives its name and position in the list. Guests change the
9
+ template by swiping horizontally on the screen, by tapping the `<` `>` arrows
10
+ of the caption, or with the arrow keys. The chosen template is used for the
11
+ next pictures; the saved and printed picture is not changed.
12
+
13
+ The templates are the draw.io files of the
14
+ [pibooth-picture-template](https://github.com/pibooth/pibooth-picture-template)
15
+ plugin, which must be installed (with `pip install --no-deps` to keep the
16
+ pibooth fork).
17
+
18
+ ## Configuration
19
+
20
+ ```ini
21
+ [TEMPLATE_CHOOSER]
22
+ # Directory of the picture templates guests choose from on the wait screen
23
+ directory = ~/.config/pibooth/templates
24
+ ```
25
+
26
+ Every `*.xml` file of the directory is offered. The template set in
27
+ `[PICTURE] template` is selected at startup when it belongs to the directory.
28
+
29
+ ## Template style
30
+
31
+ A template may come with a style file of the same name (`hollywood.xml` and
32
+ `hollywood.cfg`) giving the name displayed in the caption and the style of the
33
+ texts, which replaces the `[PICTURE]` values while the template is selected:
34
+
35
+ ```ini
36
+ [TEMPLATE]
37
+ name = Hollywood
38
+ text_colors = ((255, 255, 255), (230, 200, 205))
39
+ text_fonts = ('../fonts/Montserrat-Bold.ttf', '../fonts/Montserrat-SemiBold.ttf')
40
+ text_alignments = center
41
+ ```
42
+
43
+ Relative font paths are resolved from the style file directory. Without a
44
+ style file, the name comes from the file name and the `[PICTURE]` style is kept.
45
+
46
+ The texts themselves (`footer_text1`, `footer_text2`) stay in `pibooth.cfg`,
47
+ shared by all the templates of an event.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: pibooth_template_chooser
3
+ Version: 1.0.0
4
+ Summary: Pibooth plugin letting guests choose the picture template on the wait screen
5
+ Home-page: https://github.com/ceeeeb/pibooth-template-chooser
6
+ Author: Ceeeeb
7
+ License: MIT license
8
+ Keywords: Raspberry Pi,photobooth,pibooth,template
9
+ Platform: unix
10
+ Platform: linux
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: pibooth-ceeeeb>=2.0.8.3
14
+ Dynamic: author
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: keywords
19
+ Dynamic: license
20
+ Dynamic: license-file
21
+ Dynamic: platform
22
+ Dynamic: requires-dist
23
+ Dynamic: summary
24
+
25
+ # pibooth-template-chooser
26
+
27
+ Pibooth plugin letting guests choose the picture template on the wait screen.
28
+
29
+ The wait screen shows the selected template in place of the last picture:
30
+ the first capture of the last picture assembled again in the single photo page
31
+ of the template, or grey photo holders until a picture exists. A discreet
32
+ caption at the top gives its name and position in the list. Guests change the
33
+ template by swiping horizontally on the screen, by tapping the `<` `>` arrows
34
+ of the caption, or with the arrow keys. The chosen template is used for the
35
+ next pictures; the saved and printed picture is not changed.
36
+
37
+ The templates are the draw.io files of the
38
+ [pibooth-picture-template](https://github.com/pibooth/pibooth-picture-template)
39
+ plugin, which must be installed (with `pip install --no-deps` to keep the
40
+ pibooth fork).
41
+
42
+ ## Configuration
43
+
44
+ ```ini
45
+ [TEMPLATE_CHOOSER]
46
+ # Directory of the picture templates guests choose from on the wait screen
47
+ directory = ~/.config/pibooth/templates
48
+ ```
49
+
50
+ Every `*.xml` file of the directory is offered. The template set in
51
+ `[PICTURE] template` is selected at startup when it belongs to the directory.
52
+
53
+ ## Template style
54
+
55
+ A template may come with a style file of the same name (`hollywood.xml` and
56
+ `hollywood.cfg`) giving the name displayed in the caption and the style of the
57
+ texts, which replaces the `[PICTURE]` values while the template is selected:
58
+
59
+ ```ini
60
+ [TEMPLATE]
61
+ name = Hollywood
62
+ text_colors = ((255, 255, 255), (230, 200, 205))
63
+ text_fonts = ('../fonts/Montserrat-Bold.ttf', '../fonts/Montserrat-SemiBold.ttf')
64
+ text_alignments = center
65
+ ```
66
+
67
+ Relative font paths are resolved from the style file directory. Without a
68
+ style file, the name comes from the file name and the `[PICTURE]` style is kept.
69
+
70
+ The texts themselves (`footer_text1`, `footer_text2`) stay in `pibooth.cfg`,
71
+ shared by all the templates of an event.
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pibooth_template_chooser.py
4
+ setup.py
5
+ pibooth_template_chooser.egg-info/PKG-INFO
6
+ pibooth_template_chooser.egg-info/SOURCES.txt
7
+ pibooth_template_chooser.egg-info/dependency_links.txt
8
+ pibooth_template_chooser.egg-info/entry_points.txt
9
+ pibooth_template_chooser.egg-info/not-zip-safe
10
+ pibooth_template_chooser.egg-info/requires.txt
11
+ pibooth_template_chooser.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [pibooth]
2
+ pibooth_template_chooser = pibooth_template_chooser
@@ -0,0 +1 @@
1
+ pibooth-ceeeeb>=2.0.8.3
@@ -0,0 +1 @@
1
+ pibooth_template_chooser
@@ -0,0 +1,359 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """Pibooth plugin letting guests choose the picture template on the wait screen.
4
+
5
+ The templates are the draw.io files of the ``pibooth-picture-template`` plugin,
6
+ read from a directory. A template may come with a style file of the same name
7
+ (``hollywood.xml`` and ``hollywood.cfg``) giving its displayed name and the
8
+ fonts and colors of its texts::
9
+
10
+ [TEMPLATE]
11
+ name = Hollywood
12
+ text_colors = ((255, 255, 255), (230, 200, 205))
13
+ text_fonts = ('../fonts/Montserrat-Bold.ttf', '../fonts/Montserrat-SemiBold.ttf')
14
+
15
+ Relative font paths are resolved from the style file directory.
16
+ """
17
+
18
+ import ast
19
+ import base64
20
+ import glob
21
+ import os.path as osp
22
+ from configparser import ConfigParser
23
+ from io import BytesIO
24
+
25
+ import pygame
26
+ from PIL import Image, ImageDraw
27
+
28
+ import pibooth
29
+ from pibooth.utils import LOGGER
30
+ from pibooth_picture_template import TemplateParser, TemplatePictureFactory, TemplateShapeParser
31
+
32
+ __version__ = "1.0.0"
33
+
34
+ SECTION = 'TEMPLATE_CHOOSER'
35
+ STYLED_OPTIONS = ('text_colors', 'text_fonts', 'text_alignments')
36
+ CAPTION_HEIGHT_RATIO = 0.07
37
+ PLACEHOLDER_SIZE = 1200
38
+ # A horizontal move longer than this share of the screen width is a swipe
39
+ SWIPE_RATIO = 0.08
40
+ PREVIOUS_KEYS = (pygame.K_UP, pygame.K_LEFT)
41
+ NEXT_KEYS = (pygame.K_DOWN, pygame.K_RIGHT)
42
+
43
+
44
+ class TemplateChoice(object):
45
+
46
+ """A template file, its parsed layout and its text style."""
47
+
48
+ def __init__(self, path):
49
+ self.path = path
50
+ self.parser = TemplateParser(path)
51
+ self.name = osp.splitext(osp.basename(path))[0].replace('-', ' ').capitalize()
52
+ self.style = {}
53
+ style_path = osp.splitext(path)[0] + '.cfg'
54
+ if osp.isfile(style_path):
55
+ self._read_style(style_path)
56
+ self.placeholder = render_placeholder(self.parser)
57
+
58
+ def _read_style(self, style_path):
59
+ style = ConfigParser()
60
+ style.read(style_path, encoding='utf-8')
61
+ if not style.has_section('TEMPLATE'):
62
+ return
63
+ self.name = style.get('TEMPLATE', 'name', fallback=self.name)
64
+ for option in STYLED_OPTIONS:
65
+ if style.has_option('TEMPLATE', option):
66
+ self.style[option] = style.get('TEMPLATE', option)
67
+ if 'text_fonts' in self.style:
68
+ self.style['text_fonts'] = resolve_fonts(self.style['text_fonts'], osp.dirname(style_path))
69
+
70
+
71
+ def resolve_fonts(value, directory):
72
+ """Make the relative font paths of a style file absolute."""
73
+ fonts = ast.literal_eval(value)
74
+ if isinstance(fonts, str):
75
+ fonts = (fonts,)
76
+ resolved = []
77
+ for font in fonts:
78
+ candidate = osp.normpath(osp.join(directory, font))
79
+ resolved.append(candidate if not osp.isabs(font) and osp.isfile(candidate) else font)
80
+ return str(tuple(resolved))
81
+
82
+
83
+ def render_placeholder(parser):
84
+ """Draw the page of the smallest capture number, the one guests are shown
85
+ until a picture exists: embedded images as they are, capture holders in grey.
86
+ """
87
+ orientation, number = min(((orientation, number) for orientation, pages in parser.data.items()
88
+ for number in pages), key=lambda item: item[1])
89
+ width, height = parser.get_size(number, orientation)
90
+ image = Image.new('RGB', (width, height), (255, 255, 255))
91
+ draw = ImageDraw.Draw(image)
92
+ for shape in parser.get_rects(number, orientation):
93
+ box = (shape.x, shape.y, shape.x + shape.width, shape.y + shape.height)
94
+ if shape.type == TemplateShapeParser.TYPE_IMAGE:
95
+ data = base64.b64decode(shape.image.split(',', 1)[1])
96
+ picture = Image.open(BytesIO(data)).convert('RGBA').resize((shape.width, shape.height))
97
+ image.paste(picture, box[:2], picture)
98
+ elif shape.type == TemplateShapeParser.TYPE_CAPTURE:
99
+ draw.rectangle(box, fill=(170, 170, 175))
100
+ image.thumbnail((PLACEHOLDER_SIZE, PLACEHOLDER_SIZE))
101
+ return image
102
+
103
+
104
+ class TemplateChooser(object):
105
+
106
+ """Keep the list of templates and apply the selected one to the configuration."""
107
+
108
+ def __init__(self, cfg, choices):
109
+ self.cfg = cfg
110
+ self.choices = choices
111
+ self.defaults = {option: cfg.get('PICTURE', option) for option in STYLED_OPTIONS}
112
+ current = cfg.getpath('PICTURE', 'template')
113
+ paths = [choice.path for choice in choices]
114
+ self.index = paths.index(current) if current in paths else 0
115
+ self.apply()
116
+
117
+ @property
118
+ def current(self):
119
+ return self.choices[self.index]
120
+
121
+ def select(self, step):
122
+ self.index = (self.index + step) % len(self.choices)
123
+ self.apply()
124
+ LOGGER.info("Template selected: %s", self.current.name)
125
+
126
+ def apply(self):
127
+ """Point the picture-template plugin to the selected template, and
128
+ set its text style (or restore the configured one)."""
129
+ choice = self.current
130
+ self.cfg.set('PICTURE', 'template', choice.path)
131
+ self.cfg.template = choice.parser
132
+ for option in STYLED_OPTIONS:
133
+ self.cfg.set('PICTURE', option, choice.style.get(option, self.defaults[option]))
134
+
135
+
136
+ class Preview(object):
137
+
138
+ """First capture and texts of the last picture, assembled again in the
139
+ single photo page of any template to show guests what they choose. The
140
+ capture is kept after the other plugins processed it (background removal),
141
+ so previews cost no processing."""
142
+
143
+ MAX_CAPTURE_WIDTH = 1600
144
+
145
+ def __init__(self):
146
+ self.images = self.texts = None
147
+ self.opt_index = 0
148
+ self.cache = {}
149
+ self.waiting = False
150
+
151
+ def expect(self):
152
+ """Keep the next picture factory, the one of the final picture."""
153
+ self.waiting = True
154
+
155
+ def store(self, factory, opt_index):
156
+ self.waiting = False
157
+ self.images = [self._reduce(factory._images[0])]
158
+ self.texts = [text[0] for text in factory._texts]
159
+ self.opt_index = opt_index
160
+ self.cache = {}
161
+
162
+ def _reduce(self, image):
163
+ image = image.copy()
164
+ image.thumbnail((self.MAX_CAPTURE_WIDTH, self.MAX_CAPTURE_WIDTH))
165
+ return image
166
+
167
+ def build(self, choice, cfg):
168
+ """Return the last capture assembled in the given template, its
169
+ placeholder while no picture exists, or None if it can not be built."""
170
+ if not self.images:
171
+ return choice.placeholder
172
+ if choice.path not in self.cache:
173
+ try:
174
+ self.cache[choice.path] = self._assemble(choice.parser, cfg)
175
+ except Exception as ex: # No page for this capture number, broken template...
176
+ LOGGER.warning("No preview of template '%s': %s", choice.name, ex)
177
+ self.cache[choice.path] = None
178
+ return self.cache[choice.path]
179
+
180
+ def _assemble(self, parser, cfg):
181
+ orientation = parser.get_best_orientation(self.images)
182
+ factory = TemplatePictureFactory(parser, orientation, *self.images)
183
+ factory.set_background(cfg.gettuple('PICTURE', 'backgrounds', ('color', 'path'), 2)[self.opt_index])
184
+ if cfg.getboolean('PICTURE', 'captures_cropping'):
185
+ factory.set_cropping()
186
+ count = len(self.texts)
187
+ styles = zip(self.texts, cfg.gettuple('PICTURE', 'text_fonts', str, count),
188
+ cfg.gettuple('PICTURE', 'text_colors', 'color', count),
189
+ cfg.gettuple('PICTURE', 'text_alignments', str, count))
190
+ for text, font, color, align in styles:
191
+ if text:
192
+ factory.add_text(text, font, color, align)
193
+ return factory.build()
194
+
195
+
196
+ class Caption(object):
197
+
198
+ """Discreet label at the top of the wait screen, "< name 2/9 >": the
199
+ picture below already shows the template. Its arrows can be tapped."""
200
+
201
+ def __init__(self):
202
+ self.rect = self.previous_rect = self.next_rect = None
203
+
204
+ def draw(self, surface, chooser):
205
+ screen = surface.get_rect()
206
+ font = pygame.font.Font(None, int(screen.height * CAPTION_HEIGHT_RATIO))
207
+ text = font.render("{} {}/{}".format(chooser.current.name, chooser.index + 1, len(chooser.choices)),
208
+ True, (255, 255, 255))
209
+ arrow_width = text.get_height() * 2
210
+ self.rect = pygame.Rect(0, 0, text.get_width() + 2 * arrow_width, int(text.get_height() * 1.5))
211
+ self.rect.midtop = (screen.centerx, int(screen.height * 0.02))
212
+ self.previous_rect = pygame.Rect(self.rect.left, self.rect.top, arrow_width, self.rect.height)
213
+ self.next_rect = pygame.Rect(self.rect.right - arrow_width, self.rect.top, arrow_width, self.rect.height)
214
+
215
+ background = pygame.Surface(self.rect.size, pygame.SRCALPHA)
216
+ pygame.draw.rect(background, (0, 0, 0, 150), background.get_rect(), border_radius=self.rect.height // 2)
217
+ surface.blit(background, self.rect.topleft)
218
+ surface.blit(text, text.get_rect(center=self.rect.center))
219
+ for rect, label in ((self.previous_rect, '<'), (self.next_rect, '>')):
220
+ arrow = font.render(label, True, (255, 255, 255))
221
+ surface.blit(arrow, arrow.get_rect(center=rect.center))
222
+
223
+ def step_for(self, pos):
224
+ """Return -1 or 1 if the position hits an arrow, 0 elsewhere on the
225
+ label, None outside of it (or before it is drawn)."""
226
+ if self.rect is None or not self.rect.collidepoint(pos):
227
+ return None
228
+ if self.previous_rect.collidepoint(pos):
229
+ return -1
230
+ return 1 if self.next_rect.collidepoint(pos) else 0
231
+
232
+
233
+ def event_position(event, surface):
234
+ if event.type in (pygame.FINGERDOWN, pygame.FINGERUP):
235
+ width, height = surface.get_size()
236
+ return (event.x * width, event.y * height)
237
+ return event.pos
238
+
239
+
240
+ def is_touch(event):
241
+ return (event.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP) and event.button in (1, 2, 3)) \
242
+ or event.type in (pygame.FINGERDOWN, pygame.FINGERUP)
243
+
244
+
245
+ class Swipe(object):
246
+
247
+ """Track a touch from press to release to recognize horizontal swipes."""
248
+
249
+ def __init__(self):
250
+ self.start = None
251
+
252
+ def press(self, pos):
253
+ self.start = pos
254
+
255
+ def release(self, pos, width):
256
+ """Return 1 for a swipe to the left (next), -1 to the right, 0 otherwise."""
257
+ start, self.start = self.start, None
258
+ if start is None:
259
+ return 0
260
+ dx, dy = pos[0] - start[0], pos[1] - start[1]
261
+ if abs(dx) < width * SWIPE_RATIO or abs(dx) < abs(dy):
262
+ return 0
263
+ return 1 if dx < 0 else -1
264
+
265
+
266
+ def selection_step(caption, swipe, surface, events):
267
+ """Return the requested move in the template list (arrow keys, swipe,
268
+ caption arrows). The touches used for it are consumed so that they trigger
269
+ no capture; a simple tap elsewhere keeps its pibooth meaning."""
270
+ step, consumed = 0, []
271
+ for event in events:
272
+ if event.type == pygame.KEYDOWN and event.key in PREVIOUS_KEYS + NEXT_KEYS:
273
+ step += -1 if event.key in PREVIOUS_KEYS else 1
274
+ consumed.append(event)
275
+ elif is_touch(event):
276
+ pos = event_position(event, surface)
277
+ if event.type in (pygame.MOUSEBUTTONDOWN, pygame.FINGERDOWN):
278
+ swipe.press(pos)
279
+ continue
280
+ swiped = swipe.release(pos, surface.get_width())
281
+ touched = caption.step_for(pos)
282
+ if swiped or touched is not None:
283
+ step += swiped or touched
284
+ consumed.append(event)
285
+ for event in consumed:
286
+ events.remove(event)
287
+ return step
288
+
289
+
290
+ @pibooth.hookimpl
291
+ def pibooth_configure(cfg):
292
+ cfg.add_option(SECTION, 'directory', '~/.config/pibooth/templates',
293
+ "Directory of the picture templates guests choose from on the wait screen")
294
+
295
+
296
+ @pibooth.hookimpl
297
+ def pibooth_startup(cfg, app):
298
+ paths = sorted(glob.glob(osp.join(cfg.getpath(SECTION, 'directory'), '*.xml')))
299
+ choices = []
300
+ for path in paths:
301
+ try:
302
+ choices.append(TemplateChoice(path))
303
+ except Exception as ex: # A broken template must not prevent the others
304
+ LOGGER.warning("Template '%s' ignored: %s", path, ex)
305
+ app.template_chooser = TemplateChooser(cfg, choices) if choices else None
306
+ app.template_caption = Caption()
307
+ app.template_swipe = Swipe()
308
+ app.template_preview = Preview()
309
+ cfg.template_preview = app.template_preview # The factory hook only receives cfg
310
+ LOGGER.info("Template chooser: %s templates", len(choices))
311
+ if app.template_chooser and app.previous_picture is None:
312
+ app.previous_picture = app.template_chooser.current.placeholder
313
+
314
+
315
+ @pibooth.hookimpl
316
+ def state_wait_validate(app):
317
+ """Enter the wait state again so that every plugin redraws the screen
318
+ around the new preview (picture, gallery QR code...)."""
319
+ if getattr(app, 'template_redraw', False):
320
+ app.template_redraw = False
321
+ return 'wait'
322
+
323
+
324
+ @pibooth.hookimpl
325
+ def state_processing_enter(app):
326
+ if getattr(app, 'template_chooser', None):
327
+ app.template_preview.expect()
328
+
329
+
330
+ @pibooth.hookimpl(hookwrapper=True, tryfirst=True)
331
+ def pibooth_setup_picture_factory(cfg, opt_index, factory):
332
+ """Keep the captures and texts of the final picture, once the other
333
+ plugins have set them up: as the outermost wrapper, this one resumes last,
334
+ after pibooth added the texts. The next calls build animation frames."""
335
+ outcome = yield
336
+ preview = getattr(cfg, 'template_preview', None)
337
+ if preview and preview.waiting:
338
+ preview.store(outcome.get_result(), opt_index)
339
+
340
+
341
+ @pibooth.hookimpl(hookwrapper=True)
342
+ def state_wait_do(cfg, app, win, events):
343
+ """Handle the selection before the other plugins see the events, and draw
344
+ the caption once they have drawn the screen."""
345
+ chooser = getattr(app, 'template_chooser', None)
346
+ if chooser:
347
+ step = selection_step(app.template_caption, app.template_swipe, win.surface, events)
348
+ if step:
349
+ chooser.select(step)
350
+ preview = app.template_preview.build(chooser.current, cfg)
351
+ if preview:
352
+ # Displayed only: the saved and printed file is the real picture
353
+ app.previous_picture = preview
354
+ app.previous_animated = None
355
+ app.template_redraw = True
356
+ yield
357
+ if chooser:
358
+ app.template_caption.draw(win.surface, chooser)
359
+ pygame.display.update()
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from setuptools import setup
5
+
6
+ setup(
7
+ name='pibooth_template_chooser',
8
+ version='1.0.0',
9
+ description="Pibooth plugin letting guests choose the picture template on the wait screen",
10
+ long_description=open('README.md', encoding='utf-8').read(),
11
+ long_description_content_type='text/markdown',
12
+ author="Ceeeeb",
13
+ url="https://github.com/ceeeeb/pibooth-template-chooser",
14
+ license='MIT license',
15
+ platforms=['unix', 'linux'],
16
+ keywords=['Raspberry Pi', 'photobooth', 'pibooth', 'template'],
17
+ py_modules=['pibooth_template_chooser'],
18
+ # pibooth-picture-template requires the upstream pibooth: install it with --no-deps
19
+ install_requires=['pibooth-ceeeeb>=2.0.8.3'],
20
+ zip_safe=False,
21
+ entry_points={'pibooth': ["pibooth_template_chooser = pibooth_template_chooser"]},
22
+ )