snakeflex 0.1.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 Matthew Toomey
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,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: snakeflex
3
+ Version: 0.1.0
4
+ Summary: Flexbox and CSS Grid-inspired layout utilities for tkinter and customtkinter
5
+ Author: SnakePlayer contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/toomeydev/snakeflex
8
+ Project-URL: Repository, https://github.com/toomeydev/snakeflex
9
+ Project-URL: Issues, https://github.com/toomeydev/snakeflex/issues
10
+ Keywords: tkinter,customtkinter,layout,flexbox,css-grid,gui
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: Software Development :: User Interfaces
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: license-file
26
+
27
+ # snakeflex
28
+
29
+ **Flexbox and CSS Grid-inspired layout utilities for tkinter and customtkinter.**
30
+
31
+ `snakeflex` implements Flexbox inspired functionality in the context of building user interfaces with Tkinter and/or CustomTkinter and Python. It works as
32
+ a drop-in layout layer over standard `tkinter` and `customtkinter` widgets, without any external
33
+ dependencies.
34
+ ```bash
35
+ pip install snakeflex
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Quick start
41
+
42
+ ```python
43
+ import tkinter as tk
44
+ from snakeflex import FlexRow, FlexCol, GridFrame, Spacer
45
+
46
+ root = tk.Tk()
47
+
48
+ # Horizontal bar
49
+ bar = FlexRow(root, justify="space-between", align="center", gap=8)
50
+ bar.pack(fill="x")
51
+
52
+ title = tk.Label(bar, text="My App")
53
+ bar.add(title)
54
+ bar.add(Spacer()) # pushes right-hand items to the edge
55
+ bar.add(tk.Button(bar, text="Settings"))
56
+ bar.add(tk.Button(bar, text="Quit", command=root.quit))
57
+
58
+ # Vertical panel
59
+ panel = FlexCol(root, align="stretch", gap=4)
60
+ panel.pack(fill="both", expand=True)
61
+
62
+ panel.add(tk.Label(panel, text="Header"))
63
+ panel.add(tk.Text(panel), grow=1) # Text widget fills remaining height
64
+ panel.add(tk.Label(panel, text="Status"))
65
+
66
+ root.mainloop()
67
+ ```
68
+
69
+ ---
70
+
71
+ ## FlexRow / FlexCol
72
+
73
+ ```python
74
+ from snakeflex import FlexRow, FlexCol
75
+ ```
76
+
77
+ ### Parameters
78
+
79
+ | Parameter | Type | Default | Description |
80
+ |-------------|--------|---------------|-------------|
81
+ | `justify` | str | `"start"` | Main-axis alignment: `"start"` `"end"` `"center"` `"space-between"` `"space-around"` `"space-evenly"` |
82
+ | `align` | str | `"stretch"` | Cross-axis alignment: `"stretch"` `"start"` `"end"` `"center"` |
83
+ | `gap` | int | `0` | Pixels of space between children |
84
+ | `fg_color` | str | `"transparent"` | Background colour (customtkinter-compatible alias for `bg`) |
85
+ | `direction` | str | `"row"` | `FlexFrame` only: `"row"` `"column"` `"row-reverse"` `"column-reverse"` |
86
+
87
+ ### `.add(widget, *, grow=0, align=None)`
88
+
89
+ ```python
90
+ row = FlexRow(parent, justify="space-between", gap=8)
91
+ row.add(logo)
92
+ row.add(slider, grow=1) # slider expands to fill spare space
93
+ row.add(mute_btn)
94
+ ```
95
+
96
+ | Parameter | Default | Description |
97
+ |-----------|---------|-------------|
98
+ | `grow` | `0` | Proportional share of remaining space (like `flex-grow`) |
99
+ | `align` | `None` | Per-child cross-axis override |
100
+
101
+ Returns the widget for chaining.
102
+
103
+ <hr>
104
+
105
+ ## GridFrame
106
+
107
+ ```python
108
+ from snakeflex import GridFrame
109
+ ```
110
+
111
+ ### Template syntax
112
+
113
+ ```
114
+ "200px 1fr 2fr" - fixed 200 px | 1 share | 2 shares of remaining space
115
+ "1fr 1fr 1fr" - three equal columns
116
+ "auto 1fr" - content-sized column + fills the rest
117
+ "48px 1fr 28px" - fixed header | flexible content | fixed footer
118
+ ```
119
+
120
+ ### Usage
121
+
122
+ ```python
123
+ layout = GridFrame(parent,
124
+ columns="220px 1fr",
125
+ rows="48px 1fr 28px",
126
+ gap=4,
127
+ )
128
+ layout.add(topbar, col=0, row=0, colspan=2)
129
+ layout.add(sidebar, col=0, row=1)
130
+ layout.add(content, col=1, row=1)
131
+ layout.add(statusbar, col=0, row=2, colspan=2)
132
+ ```
133
+
134
+ ### Responsive breakpoints
135
+
136
+ ```python
137
+ layout.on_resize(800,
138
+ narrow=lambda: layout.hide_col(2), # collapse detail panel
139
+ wide=lambda: layout.show_col(2),
140
+ )
141
+ ```
142
+
143
+ <hr>
144
+
145
+ ## Spacer
146
+
147
+ An invisible, expanding spacer — equivalent to `<div style="flex: 1" />`.
148
+
149
+ ```python
150
+ bar = FlexRow(parent, justify="start", gap=8)
151
+ bar.add(logo)
152
+ bar.add(Spacer()) # everything after this is pushed right
153
+ bar.add(btn_a)
154
+ bar.add(btn_b)
155
+ ```
156
+
157
+ When added to a `FlexRow`/`FlexCol`, `Spacer` defaults to `grow=1`.
158
+
159
+ <br>
160
+
161
+ ## customtkinter compatibility
162
+
163
+ All containers accept `fg_color` as an alias for `bg` and default to
164
+ `"transparent"` so they blend seamlessly into CTk windows:
165
+
166
+ ```python
167
+ import customtkinter as ctk
168
+ from snakeflex import FlexRow
169
+
170
+ app = ctk.CTk()
171
+ bar = FlexRow(app, fg_color="transparent", gap=8)
172
+ bar.add(ctk.CTkLabel(bar, text="Hello"))
173
+ bar.add(ctk.CTkButton(bar, text="Go"), grow=1)
174
+ ```
175
+
176
+ <hr>
177
+
178
+ ## Installation
179
+
180
+ ```bash
181
+ pip install snakeflex
182
+ ```
183
+
@@ -0,0 +1,157 @@
1
+ # snakeflex
2
+
3
+ **Flexbox and CSS Grid-inspired layout utilities for tkinter and customtkinter.**
4
+
5
+ `snakeflex` implements Flexbox inspired functionality in the context of building user interfaces with Tkinter and/or CustomTkinter and Python. It works as
6
+ a drop-in layout layer over standard `tkinter` and `customtkinter` widgets, without any external
7
+ dependencies.
8
+ ```bash
9
+ pip install snakeflex
10
+ ```
11
+
12
+ ---
13
+
14
+ ## Quick start
15
+
16
+ ```python
17
+ import tkinter as tk
18
+ from snakeflex import FlexRow, FlexCol, GridFrame, Spacer
19
+
20
+ root = tk.Tk()
21
+
22
+ # Horizontal bar
23
+ bar = FlexRow(root, justify="space-between", align="center", gap=8)
24
+ bar.pack(fill="x")
25
+
26
+ title = tk.Label(bar, text="My App")
27
+ bar.add(title)
28
+ bar.add(Spacer()) # pushes right-hand items to the edge
29
+ bar.add(tk.Button(bar, text="Settings"))
30
+ bar.add(tk.Button(bar, text="Quit", command=root.quit))
31
+
32
+ # Vertical panel
33
+ panel = FlexCol(root, align="stretch", gap=4)
34
+ panel.pack(fill="both", expand=True)
35
+
36
+ panel.add(tk.Label(panel, text="Header"))
37
+ panel.add(tk.Text(panel), grow=1) # Text widget fills remaining height
38
+ panel.add(tk.Label(panel, text="Status"))
39
+
40
+ root.mainloop()
41
+ ```
42
+
43
+ ---
44
+
45
+ ## FlexRow / FlexCol
46
+
47
+ ```python
48
+ from snakeflex import FlexRow, FlexCol
49
+ ```
50
+
51
+ ### Parameters
52
+
53
+ | Parameter | Type | Default | Description |
54
+ |-------------|--------|---------------|-------------|
55
+ | `justify` | str | `"start"` | Main-axis alignment: `"start"` `"end"` `"center"` `"space-between"` `"space-around"` `"space-evenly"` |
56
+ | `align` | str | `"stretch"` | Cross-axis alignment: `"stretch"` `"start"` `"end"` `"center"` |
57
+ | `gap` | int | `0` | Pixels of space between children |
58
+ | `fg_color` | str | `"transparent"` | Background colour (customtkinter-compatible alias for `bg`) |
59
+ | `direction` | str | `"row"` | `FlexFrame` only: `"row"` `"column"` `"row-reverse"` `"column-reverse"` |
60
+
61
+ ### `.add(widget, *, grow=0, align=None)`
62
+
63
+ ```python
64
+ row = FlexRow(parent, justify="space-between", gap=8)
65
+ row.add(logo)
66
+ row.add(slider, grow=1) # slider expands to fill spare space
67
+ row.add(mute_btn)
68
+ ```
69
+
70
+ | Parameter | Default | Description |
71
+ |-----------|---------|-------------|
72
+ | `grow` | `0` | Proportional share of remaining space (like `flex-grow`) |
73
+ | `align` | `None` | Per-child cross-axis override |
74
+
75
+ Returns the widget for chaining.
76
+
77
+ <hr>
78
+
79
+ ## GridFrame
80
+
81
+ ```python
82
+ from snakeflex import GridFrame
83
+ ```
84
+
85
+ ### Template syntax
86
+
87
+ ```
88
+ "200px 1fr 2fr" - fixed 200 px | 1 share | 2 shares of remaining space
89
+ "1fr 1fr 1fr" - three equal columns
90
+ "auto 1fr" - content-sized column + fills the rest
91
+ "48px 1fr 28px" - fixed header | flexible content | fixed footer
92
+ ```
93
+
94
+ ### Usage
95
+
96
+ ```python
97
+ layout = GridFrame(parent,
98
+ columns="220px 1fr",
99
+ rows="48px 1fr 28px",
100
+ gap=4,
101
+ )
102
+ layout.add(topbar, col=0, row=0, colspan=2)
103
+ layout.add(sidebar, col=0, row=1)
104
+ layout.add(content, col=1, row=1)
105
+ layout.add(statusbar, col=0, row=2, colspan=2)
106
+ ```
107
+
108
+ ### Responsive breakpoints
109
+
110
+ ```python
111
+ layout.on_resize(800,
112
+ narrow=lambda: layout.hide_col(2), # collapse detail panel
113
+ wide=lambda: layout.show_col(2),
114
+ )
115
+ ```
116
+
117
+ <hr>
118
+
119
+ ## Spacer
120
+
121
+ An invisible, expanding spacer — equivalent to `<div style="flex: 1" />`.
122
+
123
+ ```python
124
+ bar = FlexRow(parent, justify="start", gap=8)
125
+ bar.add(logo)
126
+ bar.add(Spacer()) # everything after this is pushed right
127
+ bar.add(btn_a)
128
+ bar.add(btn_b)
129
+ ```
130
+
131
+ When added to a `FlexRow`/`FlexCol`, `Spacer` defaults to `grow=1`.
132
+
133
+ <br>
134
+
135
+ ## customtkinter compatibility
136
+
137
+ All containers accept `fg_color` as an alias for `bg` and default to
138
+ `"transparent"` so they blend seamlessly into CTk windows:
139
+
140
+ ```python
141
+ import customtkinter as ctk
142
+ from snakeflex import FlexRow
143
+
144
+ app = ctk.CTk()
145
+ bar = FlexRow(app, fg_color="transparent", gap=8)
146
+ bar.add(ctk.CTkLabel(bar, text="Hello"))
147
+ bar.add(ctk.CTkButton(bar, text="Go"), grow=1)
148
+ ```
149
+
150
+ <hr>
151
+
152
+ ## Installation
153
+
154
+ ```bash
155
+ pip install snakeflex
156
+ ```
157
+
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "snakeflex"
7
+ version = "0.1.0"
8
+ description = "Flexbox and CSS Grid-inspired layout utilities for tkinter and customtkinter"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "SnakePlayer contributors" }]
12
+ keywords = ["tkinter", "customtkinter", "layout", "flexbox", "css-grid", "gui"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ "Topic :: Software Development :: Libraries :: Python Modules",
24
+ "Topic :: Software Development :: User Interfaces",
25
+ ]
26
+ requires-python = ">=3.10"
27
+ dependencies = []
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/toomeydev/snakeflex"
31
+ Repository = "https://github.com/toomeydev/snakeflex"
32
+ Issues = "https://github.com/toomeydev/snakeflex/issues"
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
36
+
37
+ [tool.setuptools.package-data]
38
+ snakeflex = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,48 @@
1
+ """snakeflex — Flexbox and CSS Grid-inspired layout primitives for tkinter.
2
+
3
+ Works with standard tkinter and customtkinter widgets. Zero external
4
+ dependencies — just Python and tkinter.
5
+
6
+ Quick start
7
+ -----------
8
+ ::
9
+
10
+ from snakeflex import FlexRow, FlexCol, GridFrame, Spacer
11
+
12
+ # Horizontal bar: left label, growing spacer, right button
13
+ bar = FlexRow(parent, justify="space-between", align="center", gap=8)
14
+ bar.add(title_label)
15
+ bar.add(Spacer())
16
+ bar.add(settings_btn)
17
+
18
+ # Vertical panel: fixed header, growing content, fixed footer
19
+ panel = FlexCol(parent, align="stretch", gap=4)
20
+ panel.add(header, grow=0)
21
+ panel.add(content, grow=1)
22
+ panel.add(footer, grow=0)
23
+
24
+ # CSS Grid with fr units
25
+ layout = GridFrame(parent, columns="220px 1fr", rows="48px 1fr 28px")
26
+ layout.add(topbar, col=0, row=0, colspan=2)
27
+ layout.add(sidebar, col=0, row=1)
28
+ layout.add(main, col=1, row=1)
29
+ layout.add(statusbar, col=0, row=2, colspan=2)
30
+ """
31
+
32
+ from .flex import FlexFrame, FlexRow, FlexCol
33
+ from .grid_frame import GridFrame
34
+ from .spacer import Spacer
35
+ from .responsive import ResponsiveMixin
36
+
37
+ __all__ = [
38
+ "FlexFrame",
39
+ "FlexRow",
40
+ "FlexCol",
41
+ "GridFrame",
42
+ "Spacer",
43
+ "ResponsiveMixin",
44
+ ]
45
+
46
+ __version__ = "0.1.0"
47
+ __author__ = "SnakePlayer contributors"
48
+ __license__ = "MIT"
@@ -0,0 +1,71 @@
1
+ """snakeflex._base — shared base class for all layout containers.
2
+
3
+ Provides:
4
+ • fg_color kwarg → bg shim (customtkinter compatibility)
5
+ • transparent background resolution by walking the parent widget tree
6
+ • coalesced after(0) relayout scheduling
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import tkinter as tk
11
+ from typing import Optional
12
+
13
+
14
+ def _resolve_bg(parent: tk.Widget, fg_color: Optional[str]) -> Optional[str]:
15
+ """Map fg_color to a real tk bg colour string.
16
+
17
+ Rules
18
+ -----
19
+ None → don't set bg at all (inherit Tk default)
20
+ "transparent" → walk up the parent tree until a concrete bg is found
21
+ anything else → use as-is
22
+ """
23
+ if fg_color is None:
24
+ return None
25
+ if fg_color != "transparent":
26
+ return fg_color
27
+ # Walk up looking for a concrete colour.
28
+ node = parent
29
+ while node is not None:
30
+ try:
31
+ bg = node.cget("bg")
32
+ if bg and bg != "transparent":
33
+ return bg
34
+ except tk.TclError:
35
+ pass
36
+ node = getattr(node, "master", None)
37
+ return None # give up; Tk will use its default
38
+
39
+
40
+ class _FlexBase(tk.Frame):
41
+ """Base frame for FlexFrame and GridFrame.
42
+
43
+ Accepts ``fg_color`` as an alias for ``bg`` so code written against
44
+ customtkinter's CTkFrame API compiles without modification::
45
+
46
+ FlexRow(parent, fg_color="#1e1e2e")
47
+ FlexRow(parent, fg_color="transparent")
48
+ FlexRow(parent, bg="#1e1e2e") # plain tk style also works
49
+ """
50
+
51
+ def __init__(self, parent: tk.Widget, *, fg_color: Optional[str] = "transparent",
52
+ **kw) -> None:
53
+ # Resolve fg_color → bg before tk.Frame() runs so the frame is
54
+ # created with the right colour from the start.
55
+ if fg_color is not None and "bg" not in kw:
56
+ resolved = _resolve_bg(parent, fg_color)
57
+ if resolved is not None:
58
+ kw["bg"] = resolved
59
+
60
+ super().__init__(parent, **kw)
61
+ self._relayout_pending = False
62
+
63
+ def _schedule_relayout(self) -> None:
64
+ """Queue a relayout on the next idle tick, coalescing multiple calls."""
65
+ if not self._relayout_pending:
66
+ self._relayout_pending = True
67
+ self.after(0, self._do_relayout)
68
+
69
+ def _do_relayout(self) -> None:
70
+ """Subclasses override this to perform the actual grid reconfiguration."""
71
+ self._relayout_pending = False