responds-forms 0.4__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.
@@ -0,0 +1,396 @@
1
+ from __future__ import annotations
2
+
3
+ from pyflutter_ui.ui.core import (
4
+ Element, inflate_widget, StatelessWidget, BuildContext, FlutterWidget,
5
+ )
6
+ from pyflutter_ui.ui.progress import LinearProgress
7
+ from pyflutter_ui.ui.widgets import Column
8
+ from tkinter import ttk
9
+ import tkinter as tk
10
+ from typing import Callable, Generic, cast, TypeVar
11
+
12
+ T = TypeVar('T')
13
+
14
+
15
+ # ============================================================
16
+ # SCROLL VIEW
17
+ # ============================================================
18
+
19
+ class ScrollView(StatelessWidget):
20
+
21
+ def __init__(self, child: FlutterWidget, *, key: str | None = None) -> None:
22
+ super().__init__(key)
23
+ self.child: FlutterWidget = child
24
+
25
+ def build(self, context: BuildContext) -> FlutterWidget:
26
+ return self
27
+
28
+ def create_element(self) -> Element:
29
+ return ScrollViewElement(self)
30
+
31
+
32
+ class ScrollViewElement(Element[ttk.Frame]):
33
+
34
+ def __init__(self, widget: ScrollView) -> None:
35
+ super().__init__(widget)
36
+
37
+ def update(self, new_widget: FlutterWidget) -> None:
38
+ context = self.get_context() # salva ANTES de trocar o widget
39
+ self.widget_flutter = cast(ScrollView, new_widget)
40
+ self.widget_flutter.context = context
41
+ if context:
42
+ self.unmount()
43
+ self.mount(context)
44
+
45
+ def get_widget_flutter(self) -> ScrollView:
46
+ return self.widget_flutter
47
+
48
+ def mount(self, context: BuildContext) -> ttk.Frame:
49
+ super().mount(context)
50
+ container = ttk.Frame(context.parent_tk_widget)
51
+ container.pack(fill="both", expand=True)
52
+ canvas = tk.Canvas(container)
53
+
54
+ scrollbar = ttk.Scrollbar(
55
+ container, orient="vertical", command=canvas.yview
56
+ )
57
+ content = ttk.Frame(canvas)
58
+
59
+ content.bind(
60
+ "<Configure>",
61
+ lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
62
+ )
63
+ canvas.create_window((0, 0), window=content, anchor="nw")
64
+ canvas.configure(yscrollcommand=scrollbar.set)
65
+ canvas.pack(side="left", fill="both", expand=True)
66
+ scrollbar.pack(side="right", fill="y")
67
+
68
+ self._child_element = self.get_widget_flutter().child.create_element()
69
+ child_context = BuildContext(
70
+ app=context.app,
71
+ parent_widget=content,
72
+ parent_context=context
73
+ )
74
+ self._child_element.mount(child_context)
75
+
76
+ self.widget_tk = container
77
+ return container
78
+
79
+ def unmount(self) -> None:
80
+ if self._child_element is not None:
81
+ self._child_element.unmount()
82
+ self._child_element = None
83
+ super().unmount()
84
+
85
+
86
+ # ============================================================
87
+ # SCROLLABLE COLUMN
88
+ # ============================================================
89
+
90
+ class ScrollableColumn(StatelessWidget):
91
+
92
+ def __init__(
93
+ self,
94
+ children: list[FlutterWidget],
95
+ *,
96
+ scrollbar: bool = True,
97
+ mousewheel: bool = True,
98
+ padding: int = 0,
99
+ key: str | None = None
100
+ ) -> None:
101
+ super().__init__(key)
102
+
103
+ self.children = children
104
+ self.scrollbar = scrollbar
105
+ self.mousewheel = mousewheel
106
+ self.padding = padding
107
+
108
+ def build(
109
+ self,
110
+ context: BuildContext
111
+ ) -> FlutterWidget:
112
+ return self
113
+
114
+ def create_element(self) -> Element:
115
+ return ScrollableColumnElement(self)
116
+
117
+
118
+ class ScrollableColumnElement(Element[ttk.Frame]):
119
+
120
+ def __init__(self, widget: ScrollableColumn) -> None:
121
+ super().__init__(widget)
122
+
123
+ self._child_elements: list[Element] = []
124
+ self._canvas: tk.Canvas | None = None
125
+ self._content: ttk.Frame | None = None
126
+ self._canvas_window = None
127
+
128
+ def get_widget_flutter(self) -> ScrollableColumn:
129
+ return self.widget_flutter
130
+
131
+ def mount(self, context: BuildContext) -> ttk.Frame:
132
+ super().mount(context)
133
+
134
+ # ====================================================
135
+ # CONTAINER PRINCIPAL
136
+ # ====================================================
137
+ container = ttk.Frame(context.parent_tk_widget)
138
+ container.pack(fill="both", expand=True)
139
+
140
+ # ====================================================
141
+ # CANVAS
142
+ # ====================================================
143
+ canvas = tk.Canvas(container, highlightthickness=0, borderwidth=0)
144
+ self._canvas = canvas
145
+
146
+ # ====================================================
147
+ # SCROLLBAR
148
+ # ====================================================
149
+
150
+ scrollbar = None
151
+
152
+ if self.get_widget_flutter().scrollbar:
153
+ scrollbar = ttk.Scrollbar(container, orient="vertical", command=canvas.yview)
154
+ scrollbar.pack(side="right", fill="y")
155
+ canvas.configure(yscrollcommand=scrollbar.set)
156
+
157
+ # ====================================================
158
+ # ÁREA DE CONTEÚDO
159
+ # ====================================================
160
+ self._content = ttk.Frame(canvas)
161
+ self._canvas_window = canvas.create_window(
162
+ (0, 0),
163
+ window=self._content,
164
+ anchor="nw"
165
+ )
166
+
167
+ # ====================================================
168
+ # ATUALIZA SCROLL REGION
169
+ # ====================================================
170
+ def update_scroll_region(event=None):
171
+ if not canvas.winfo_exists():
172
+ return
173
+ canvas.configure(scrollregion=canvas.bbox("all"))
174
+
175
+
176
+ self._content.bind("<Configure>", update_scroll_region)
177
+
178
+ # ====================================================
179
+ # MANTÉM A LARGURA DO CONTEÚDO IGUAL À DO CANVAS
180
+ # ====================================================
181
+ def update_content_width(event):
182
+ if not canvas.winfo_exists():
183
+ return
184
+ canvas.itemconfigure(self._canvas_window, width=event.width)
185
+ canvas.configure(scrollregion=canvas.bbox("all"))
186
+
187
+
188
+ canvas.bind("<Configure>", update_content_width)
189
+
190
+ # ====================================================
191
+ # CANVAS
192
+ # ====================================================
193
+ canvas.pack(side="left", fill="both", expand=True)
194
+
195
+ # ====================================================
196
+ # MOUSE WHEEL
197
+ # ====================================================
198
+
199
+ if self.get_widget_flutter().mousewheel:
200
+
201
+ def mousewheel(event):
202
+
203
+ if event.delta != 0:
204
+ canvas.yview_scroll(int(-event.delta / 120), "units")
205
+
206
+ canvas.bind("<MouseWheel>", mousewheel)
207
+ self._content.bind("<MouseWheel>", mousewheel)
208
+
209
+ # Linux
210
+ canvas.bind("<Button-4>", lambda event: canvas.yview_scroll(-1, "units"))
211
+
212
+ canvas.bind(
213
+ "<Button-5>",
214
+ lambda event: canvas.yview_scroll(1, "units")
215
+ )
216
+
217
+ self._content.bind(
218
+ "<Button-4>",
219
+ lambda event: canvas.yview_scroll(-1, "units")
220
+ )
221
+
222
+ self._content.bind(
223
+ "<Button-5>",
224
+ lambda event: canvas.yview_scroll(1, "units")
225
+ )
226
+
227
+ # ====================================================
228
+ # CONTEÚDO VERTICAL
229
+ # ====================================================
230
+
231
+ content_inner = ttk.Frame(self._content)
232
+
233
+ content_inner.pack(
234
+ fill="x",
235
+ expand=False,
236
+ padx=self.get_widget_flutter().padding,
237
+ pady=self.get_widget_flutter().padding
238
+ )
239
+
240
+ # ====================================================
241
+ # MONTA OS WIDGETS
242
+ # ====================================================
243
+
244
+ for child in self.get_widget_flutter().children:
245
+
246
+ child_element = inflate_widget(child)
247
+ child_context = context.create_child_context(content_inner)
248
+ child_element.mount(child_context)
249
+ self._child_elements.append(child_element)
250
+
251
+ # ====================================================
252
+ # FORÇA ATUALIZAÇÃO DO SCROLL REGION
253
+ # ====================================================
254
+ self._content.update_idletasks()
255
+ canvas.configure(scrollregion=canvas.bbox("all"))
256
+ self.widget_tk = container
257
+ return container
258
+
259
+ def update(self, new_widget: FlutterWidget) -> None:
260
+
261
+ context = self._parent_context
262
+ self.widget_flutter = cast(ScrollableColumn, new_widget)
263
+
264
+ if context is not None:
265
+ self.unmount()
266
+ self.mount(context)
267
+
268
+ def unmount(self) -> None:
269
+
270
+ for child_element in self._child_elements:
271
+
272
+ try:
273
+ child_element.unmount()
274
+ except Exception:
275
+ pass
276
+
277
+ self._child_elements.clear()
278
+
279
+ self._canvas = None
280
+ self._content = None
281
+ self._canvas_window = None
282
+ super().unmount()
283
+
284
+
285
+ class ScrollableColumnOrProgress(StatelessWidget):
286
+ """
287
+ Exibe uma lista vertical e rolável de FlutterWidgets.
288
+
289
+ Enquanto `running` for True:
290
+ exibe o LinearProgress.
291
+
292
+ Enquanto `running` for False:
293
+ exibe os widgets dentro de um ScrollView.
294
+
295
+ Os widgets podem ser qualquer FlutterWidget:
296
+ Text
297
+ Button
298
+ Row
299
+ Column
300
+ Card
301
+ Container
302
+ etc.
303
+ """
304
+
305
+ def __init__(
306
+ self,
307
+ running: bool,
308
+ children: list[FlutterWidget],
309
+ *,
310
+ padding: int = 0,
311
+ ) -> None:
312
+ super().__init__()
313
+
314
+ self.running = running
315
+ self.children = children
316
+ self.padding = padding
317
+
318
+ def build(self, context: BuildContext) -> FlutterWidget:
319
+
320
+ if self.running:
321
+ return LinearProgress(indeterminate=True)
322
+
323
+ return ScrollView(
324
+ Column(self.children)
325
+ )
326
+
327
+
328
+ # ============================================================
329
+ # LIST BUILDER
330
+ # ============================================================
331
+
332
+ class ListBuilder(StatelessWidget, Generic[T]):
333
+
334
+ def __init__(
335
+ self,
336
+ items: list[T],
337
+ item_builder: Callable[[BuildContext, int, T], FlutterWidget]
338
+ ) -> None:
339
+ super().__init__()
340
+ self.items: list[T] = items
341
+ self.item_builder: Callable[[BuildContext, int, T], FlutterWidget] = item_builder
342
+
343
+ def build(self, context: BuildContext) -> ListBuilder[T]:
344
+ return self
345
+
346
+ def create_element(self) -> ListBuilderElement:
347
+ return ListBuilderElement(self)
348
+
349
+
350
+ class ListBuilderElement(Element[ttk.Frame]):
351
+
352
+ def __init__(self, widget: ListBuilder) -> None:
353
+ super().__init__(widget)
354
+ self._child_elements: list[Element] = []
355
+ self._parent_context: BuildContext | None = None
356
+
357
+ def get_widget(self) -> ListBuilder:
358
+ return self.widget
359
+
360
+ def mount(self, context: BuildContext) -> ttk.Frame:
361
+ super().mount(context)
362
+ self._parent_context = context
363
+
364
+ if not context.parent_tk_widget.winfo_exists():
365
+ return None
366
+
367
+ frame = ttk.Frame(context.parent_tk_widget)
368
+ frame.pack(fill="both", expand=True)
369
+
370
+ self._child_elements = []
371
+ for index, item in enumerate(self.get_widget().items):
372
+ widget = self.get_widget().item_builder(context, index, item)
373
+ child_element = widget.create_element()
374
+ child_context = BuildContext(
375
+ app=context.app, parent_widget=frame, parent_context=context
376
+ )
377
+ child_element.mount(child_context)
378
+ self._child_elements.append(child_element)
379
+
380
+ self.tk_widget = frame
381
+ return frame
382
+
383
+ def update(self, new_widget: FlutterWidget) -> None:
384
+ context = self._parent_context
385
+ self.widget = new_widget
386
+ if context is not None and context.parent_tk_widget.winfo_exists():
387
+ self.unmount()
388
+ self.mount(context)
389
+
390
+ def unmount(self) -> None:
391
+ for child_element in self._child_elements:
392
+ child_element.unmount()
393
+ self._child_elements = []
394
+ super().unmount()
395
+
396
+
@@ -0,0 +1,80 @@
1
+ # progress.py
2
+ from __future__ import annotations
3
+ from tkinter import ttk
4
+
5
+ from pyflutter_ui.ui.core import (
6
+ BuildContext, Element, FlutterWidget, StatelessWidget, )
7
+
8
+
9
+ # ============================================================
10
+ # LINEAR PROGRESS
11
+ # ============================================================
12
+
13
+ class LinearProgress(StatelessWidget):
14
+
15
+ def __init__(self, value: float = 0.0, indeterminate: bool = False) -> None:
16
+ super().__init__()
17
+ self.value = value
18
+ self.indeterminate = indeterminate
19
+
20
+ def build(self, context: BuildContext) -> FlutterWidget:
21
+ return self
22
+
23
+ def create_element(self) -> Element:
24
+ return LinearProgressElement(self)
25
+
26
+
27
+ class LinearProgressElement(Element[ttk.Progressbar]):
28
+
29
+ def __init__(self, widget: LinearProgress) -> None:
30
+ super().__init__(widget)
31
+
32
+ def get_widget_flutter(self) -> LinearProgress:
33
+ return self.widget_flutter
34
+
35
+ def mount(self, context: BuildContext) -> ttk.Progressbar:
36
+ super().mount(context)
37
+ mode = "indeterminate" if self.get_widget_flutter().indeterminate else "determinate"
38
+ progress = ttk.Progressbar(context.parent_tk_widget, mode=mode)
39
+
40
+ if self.get_widget_flutter().indeterminate:
41
+ progress.start(9)
42
+ else:
43
+ progress["value"] = max(0, min(100, self.widget_flutter.value * 100))
44
+
45
+ progress.pack(fill="x", expand=True, padx=1, pady=1)
46
+ self.widget_tk = progress
47
+ return progress
48
+
49
+
50
+ # ============================================================
51
+ # CIRCULAR PROGRESS
52
+ # ============================================================
53
+ class CircularProgress(StatelessWidget):
54
+
55
+ def __init__(self) -> None:
56
+ super().__init__()
57
+
58
+ def build(self, context: BuildContext) -> FlutterWidget:
59
+ return self
60
+
61
+ def create_element(self) -> Element:
62
+ return CircularProgressElement(self)
63
+
64
+
65
+ class CircularProgressElement(Element[ttk.Progressbar]):
66
+
67
+ def __init__(self, widget: CircularProgress) -> None:
68
+ super().__init__(widget)
69
+
70
+ def get_widget_flutter(self) -> CircularProgress:
71
+ return self.widget_flutter
72
+
73
+ def mount(self, context: BuildContext) -> ttk.Progressbar:
74
+ super().mount(context)
75
+ progress = ttk.Progressbar(context.parent_tk_widget, mode="indeterminate", length=40)
76
+ progress.start()
77
+ progress.pack()
78
+ self.widget_tk = progress
79
+ return progress
80
+
@@ -0,0 +1,109 @@
1
+ # tables.py
2
+ from __future__ import annotations
3
+
4
+ from tkinter import ttk
5
+ from typing import Callable, cast
6
+
7
+ from pyflutter_ui.ui.core import (
8
+ BuildContext, Element, FlutterWidget, StatelessWidget,
9
+ )
10
+
11
+ # ============================================================
12
+ # TABLEVIEW
13
+ # ============================================================
14
+
15
+ class TableView(StatelessWidget):
16
+
17
+ def __init__(
18
+ self, *,
19
+ columns: list[str],
20
+ rows: list[list[object]],
21
+ headings: dict[str, str] | None = None,
22
+ on_select: Callable[[list[object]], None] | None = None,
23
+ height: int = 10
24
+ ) -> None:
25
+ super().__init__()
26
+
27
+ self.columns = columns
28
+ self.rows = rows
29
+ self.headings = headings or {}
30
+ self.on_select = on_select
31
+ self.height = height
32
+
33
+ def build(self, context: BuildContext) -> TableView:
34
+ return self
35
+
36
+ def create_element(self) -> Element:
37
+ return TreeViewElement(self)
38
+
39
+
40
+ class TreeViewElement(Element[ttk.Treeview]):
41
+
42
+ def __init__(self, widget: TableView) -> None:
43
+ super().__init__(widget)
44
+
45
+ def get_widget_flutter(self) -> TableView:
46
+ return self.widget_flutter
47
+
48
+ def update(self, new_widget: FlutterWidget) -> None:
49
+
50
+ self.widget_flutter = cast(TableView, new_widget)
51
+ if self.widget_tk is None:
52
+ return
53
+
54
+ tree = cast(ttk.Treeview, self.widget_tk)
55
+
56
+ for item in tree.get_children():
57
+ tree.delete(item)
58
+
59
+ for row in self.widget_flutter.rows:
60
+ tree.insert("", "end", values=[str(v) for v in row])
61
+
62
+ def mount(self, context: BuildContext) -> ttk.Treeview:
63
+ super().mount(context)
64
+
65
+ frame = ttk.Frame(context.parent_tk_widget)
66
+ frame.pack(fill="both", expand=True)
67
+
68
+ # Scrollbar horizontal — precisa ser criada ANTES do tree.pack
69
+ scrollbar_x = ttk.Scrollbar(frame, orient="horizontal")
70
+ scrollbar_y = ttk.Scrollbar(frame, orient="vertical")
71
+
72
+ tree = ttk.Treeview(
73
+ frame,
74
+ columns=self.get_widget_flutter().columns,
75
+ show="headings",
76
+ height=self.get_widget_flutter().height,
77
+ xscrollcommand=scrollbar_x.set,
78
+ yscrollcommand=scrollbar_y.set,
79
+ )
80
+
81
+ scrollbar_x.configure(command=tree.xview)
82
+ scrollbar_y.configure(command=tree.yview)
83
+
84
+ for column in self.get_widget_flutter().columns:
85
+ label = self.get_widget_flutter().headings.get(column, column)
86
+ tree.heading(column, text=label)
87
+ # minwidth garante que haja conteúdo para rolar na horizontal
88
+ tree.column(column, anchor="w", width=120, minwidth=80, stretch=False)
89
+
90
+ for row in self.get_widget_flutter().rows:
91
+ tree.insert("", "end", values=[str(v) for v in row])
92
+
93
+ # Ordem de pack importa: scrollbars nas bordas, tree no centro
94
+ scrollbar_y.pack(side="right", fill="y")
95
+ scrollbar_x.pack(side="bottom", fill="x")
96
+ tree.pack(side="left", fill="both", expand=True)
97
+
98
+ if self.get_widget_flutter().on_select:
99
+ def _on_select(event):
100
+ selection = tree.selection()
101
+ if not selection:
102
+ return
103
+ values = list(tree.item(selection[0], "values"))
104
+ self.get_widget_flutter().on_select(values)
105
+
106
+ tree.bind("<<TreeviewSelect>>", _on_select)
107
+
108
+ self.widget_tk = tree
109
+ return tree