dpgbaseapp 0.1.0__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.
dpgbaseapp/__init__.py ADDED
File without changes
dpgbaseapp/app.py ADDED
@@ -0,0 +1,47 @@
1
+ import dearpygui.dearpygui as dpg
2
+
3
+
4
+ class App:
5
+ title: str = 'dpgbaseapp'
6
+ docking: bool = False
7
+ width: int = 1400
8
+ height: int = 800
9
+ position: tuple[int, int] = (0, 0)
10
+
11
+ def setup(self):
12
+ pass
13
+
14
+ def render(self):
15
+ pass
16
+
17
+ def post_render(self):
18
+ pass
19
+
20
+ def between_frames(self):
21
+ pass
22
+
23
+ def shutdown(self):
24
+ pass
25
+
26
+ @classmethod
27
+ def run(cls):
28
+ app = cls()
29
+ dpg.create_context()
30
+ dpg.create_viewport(title=app.title, width=app.width, height=app.height)
31
+ dpg.set_viewport_pos(list(app.position))
32
+ dpg.configure_app(docking=app.docking, docking_space=app.docking)
33
+ dpg.setup_dearpygui()
34
+
35
+ app.setup()
36
+ app.render()
37
+ app.post_render()
38
+
39
+ dpg.show_viewport()
40
+ while dpg.is_dearpygui_running():
41
+
42
+ app.between_frames()
43
+
44
+ dpg.render_dearpygui_frame()
45
+
46
+ app.shutdown()
47
+ dpg.destroy_context()
@@ -0,0 +1,211 @@
1
+ import dataclasses
2
+
3
+
4
+ @dataclasses.dataclass
5
+ class Colorscheme:
6
+ name: str
7
+ Sub_0: tuple[int, int, int]
8
+ Sub_1: tuple[int, int, int]
9
+ Base: tuple[int, int, int]
10
+ Overlay: tuple[int, int, int]
11
+ Surface_0: tuple[int, int, int]
12
+ Surface_1: tuple[int, int, int]
13
+ Surface_2: tuple[int, int, int]
14
+ Text: tuple[int, int, int]
15
+ Subtext: tuple[int, int, int]
16
+ Rose: tuple[int, int, int]
17
+ Pink: tuple[int, int, int]
18
+ Mauve: tuple[int, int, int]
19
+ Peach: tuple[int, int, int]
20
+ Yellow: tuple[int, int, int]
21
+ Green: tuple[int, int, int]
22
+ Teal: tuple[int, int, int]
23
+ Sky: tuple[int, int, int]
24
+ Sapphire: tuple[int, int, int]
25
+ Blue: tuple[int, int, int]
26
+ Lavender: tuple[int, int, int]
27
+
28
+
29
+ Latte = Colorscheme(
30
+ 'Latte',
31
+ Sub_0 = (220, 224, 232),
32
+ Sub_1 = (230, 233, 239),
33
+ Base = (239, 241, 245),
34
+ Overlay = (156, 160, 176),
35
+ Surface_0 = (204, 208, 218),
36
+ Surface_1 = (188, 192, 204),
37
+ Surface_2 = (172, 176, 190),
38
+ Text = (76, 79, 105),
39
+ Subtext = (108, 111, 133),
40
+ Rose = (220, 138, 120),
41
+ Pink = (234, 118, 203),
42
+ Mauve = (136, 57, 239),
43
+ Peach = (254, 100, 11),
44
+ Yellow = (223, 142, 29),
45
+ Green = (64, 160, 43),
46
+ Teal = (23, 146, 153),
47
+ Sky = (4, 165, 229),
48
+ Sapphire = (32, 159, 181),
49
+ Blue = (30, 102, 245),
50
+ Lavender = (114, 135, 253),
51
+ )
52
+
53
+ Frappe = Colorscheme(
54
+ 'Frappe',
55
+ Rose = (242, 213, 207),
56
+ Pink = (244, 184, 228),
57
+ Mauve = (202, 158, 230),
58
+ Peach = (239, 159, 118),
59
+ Yellow = (229, 200, 144),
60
+ Green = (166, 209, 137),
61
+ Teal = (129, 200, 190),
62
+ Sky = (153, 209, 219),
63
+ Sapphire = (133, 193, 220),
64
+ Blue = (140, 170, 238),
65
+ Lavender = (186, 187, 241),
66
+ Text = (198, 208, 245),
67
+ Subtext = (165, 173, 206),
68
+ Overlay = (115, 121, 148),
69
+ Surface_2 = (98, 104, 128),
70
+ Surface_1 = (81, 87, 109),
71
+ Surface_0 = (65, 69, 89),
72
+ Base = (48, 52, 70),
73
+ Sub_1 = (41, 44, 60),
74
+ Sub_0 = (35, 38, 52),
75
+ )
76
+
77
+
78
+ Macchiato = Colorscheme(
79
+ 'Macchiato',
80
+ Rose = (244, 219, 214),
81
+ Pink = (245, 189, 230),
82
+ Mauve = (198, 160, 246),
83
+ Peach = (245, 169, 127),
84
+ Yellow = (238, 212, 159),
85
+ Green = (166, 218, 149),
86
+ Teal = (139, 213, 202),
87
+ Sky = (145, 215, 227),
88
+ Sapphire = (125, 196, 228),
89
+ Blue = (138, 173, 244),
90
+ Lavender = (183, 189, 248),
91
+ Text = (202, 211, 245),
92
+ Subtext = (165, 173, 203),
93
+ Overlay = (110, 115, 141),
94
+ Surface_2 = (91, 96, 120),
95
+ Surface_1 = (73, 77, 100),
96
+ Surface_0 = (54, 58, 79),
97
+ Base = (36, 39, 58),
98
+ Sub_1 = (30, 32, 48),
99
+ Sub_0 = (24, 25, 38),
100
+ )
101
+
102
+
103
+ Mocha = Colorscheme(
104
+ 'Mocha',
105
+ Rose = (245, 224, 220),
106
+ Pink = (245, 194, 231),
107
+ Mauve = (203, 166, 247),
108
+ Peach = (250, 179, 135),
109
+ Yellow = (249, 226, 175),
110
+ Green = (166, 227, 161),
111
+ Teal = (148, 226, 213),
112
+ Sky = (137, 220, 235),
113
+ Sapphire = (116, 199, 236),
114
+ Blue = (137, 180, 250),
115
+ Lavender = (180, 190, 254),
116
+ Text = (205, 214, 244),
117
+ Subtext = (166, 173, 200),
118
+ Overlay = (108, 112, 134),
119
+ Surface_2 = (88, 91, 112),
120
+ Surface_1 = (69, 71, 90),
121
+ Surface_0 = (49, 50, 68),
122
+ Base = (30, 30, 46),
123
+ Sub_1 = (24, 24, 37),
124
+ Sub_0 = (17, 17, 27),
125
+ )
126
+
127
+ Cthulu = Colorscheme(
128
+ 'Cthulu',
129
+ # --- structural ramp (dark -> light) ---
130
+ Sub_0 = (33, 35, 55), # Sunken Depths Grey #212337
131
+ Sub_1 = (50, 52, 73), # Shallow Depths Grey #323449
132
+ Base = (57, 59, 79), # interp Sub_1 -> Tidal (main bg)
133
+ Surface_0 = (63, 65, 84), # interp Sub_1 -> Tidal
134
+ Surface_1 = (69, 71, 89), # Tidal Surface #454759
135
+ Surface_2 = (80, 82, 96), # interp Tidal -> Murk Overlay
136
+ Overlay = (91, 92, 102), # Murk Overlay #5b5c66
137
+ # --- foreground ---
138
+ Subtext = (177, 187, 191), # interp Overlay -> Lighthouse White
139
+ Text = (235, 250, 250), # Lighthouse White #ebfafa
140
+ # --- accents ---
141
+ Rose = (241, 108, 117), # R'lyeh' Red #f16c75
142
+ Pink = (242, 101, 181), # Pustule Pink #f265b5
143
+ Mauve = (164, 140, 242), # Lovecraft Purple #a48cf2
144
+ Peach = (247, 198, 127), # Dreaming Orange #f7c67f
145
+ Yellow = (241, 252, 121), # Gold of Yuggoth #f1fc79
146
+ Green = (55, 244, 153), # Great Old One Green #37f499
147
+ Teal = (30, 227, 201), # interp Green -> Watery Tomb Blue
148
+ Sky = (4, 209, 249), # Watery Tomb Blue #04d1f9
149
+ Sapphire = (58, 169, 229), # interp Watery Tomb Blue -> The Old One Purple
150
+ Blue = (112, 129, 208), # The Old One Purple #7081d0
151
+ Lavender = (138, 135, 225), # interp The Old One Purple -> Lovecraft Purple
152
+ )
153
+
154
+
155
+ Abyss = Colorscheme(
156
+ 'Abyss',
157
+ # --- structural ramp (dark -> light) ---
158
+ Sub_0 = (23, 25, 40), # Void Black #171928
159
+ Sub_1 = (37, 39, 56), # Deep Sea Grey #252738
160
+ Base = (43, 45, 61), # interp Sub_1 -> Benthic (main bg)
161
+ Surface_0 = (48, 50, 66), # interp Sub_1 -> Benthic
162
+ Surface_1 = (53, 55, 70), # Benthic Surface #353746
163
+ Surface_2 = (62, 64, 76), # interp Benthic -> Hadal Overlay
164
+ Overlay = (71, 72, 82), # Hadal Overlay #474852
165
+ # --- foreground ---
166
+ Subtext = (158, 167, 171), # interp Overlay -> Pale Specter
167
+ Text = (216, 230, 230), # Pale Specter #d8e6e6
168
+ # --- accents ---
169
+ Rose = (204, 88, 96), # Crimson Omen #cc5860
170
+ Pink = (209, 84, 161), # Dreamrot Pink #d154a1
171
+ Mauve = (139, 117, 217), # Shadow Violet #8b75d9
172
+ Peach = (212, 166, 102), # Amber Ichor #d4a666
173
+ Yellow = (204, 214, 99), # Sulfur Yellow #ccd663
174
+ Green = (45, 204, 130), # Phosphor Green #2dcc82
175
+ Teal = (24, 177, 155), # interp Phosphor Green -> Abyssal Teal
176
+ Sky = (3, 150, 179), # Abyssal Teal #0396b3
177
+ Sapphire = (42, 124, 166), # interp Abyssal Teal -> Forgotten Rune
178
+ Blue = (80, 98, 153), # Forgotten Rune #506299
179
+ Lavender = (110, 108, 185), # interp Forgotten Rune -> Shadow Violet
180
+ )
181
+
182
+
183
+ Dusk = Colorscheme(
184
+ 'Dusk',
185
+ # --- structural ramp (light -> dark, like Latte) ---
186
+ Base = (240, 243, 244), # Pale Shore #f0f3f4 (main bg)
187
+ Sub_1 = (226, 230, 232), # Coastal Mist #e2e6e8
188
+ Sub_0 = (213, 217, 219), # Tidal Flat #d5d9db
189
+ Surface_0 = (201, 203, 205),# Dusk Haze #c9cbcd
190
+ Surface_1 = (183, 185, 188),# interp Dusk Haze -> Abyssal Ink
191
+ Surface_2 = (165, 167, 170),# interp Dusk Haze -> Abyssal Ink
192
+ Overlay = (147, 149, 153), # interp Dusk Haze -> Abyssal Ink
193
+ # --- foreground ---
194
+ Subtext = (103, 105, 111), # interp Dusk Haze -> Abyssal Ink
195
+ Text = (30, 32, 41), # Abyssal Ink #1e2029
196
+ # --- accents ---
197
+ Rose = (251, 91, 102), # Dusk Crimson #fb5b66
198
+ Pink = (251, 91, 182), # Fading Rose #fb5bb6
199
+ Mauve = (138, 105, 247), # Vesper Violet #8a69f7
200
+ Peach = (255, 175, 77), # Ember Glow #ffaf4d
201
+ Yellow = (255, 249, 82), # Last Light Yellow #fff952
202
+ Green = (56, 255, 159), # Dusk Moss #38ff9f
203
+ Teal = (33, 235, 207), # interp Dusk Moss -> Twilight Teal
204
+ Sky = (10, 214, 255), # Twilight Teal #0ad6ff
205
+ Sapphire = (51, 165, 238), # interp Twilight Teal -> Faded Rune
206
+ Blue = (91, 115, 220), # Faded Rune #5b73dc
207
+ Lavender = (115, 110, 234), # interp Faded Rune -> Vesper Violet
208
+ )
209
+
210
+
211
+ default_colorschemes: tuple[Colorscheme, ...] = (Latte, Frappe, Macchiato, Mocha, Cthulu, Abyss, Dusk)
dpgbaseapp/demo.py ADDED
@@ -0,0 +1,132 @@
1
+ import math
2
+ import typing
3
+
4
+ import dearpygui.dearpygui as dpg
5
+
6
+ from dpgbaseapp.app import App
7
+ from dpgbaseapp.style_selector import StyleSelector
8
+
9
+
10
+ class Demo(App):
11
+ title: str = "DPGBaseApp Demo"
12
+
13
+
14
+ def cb_on_drag(self, sender, app_data, user_data):
15
+ pass
16
+
17
+ def cb_on_drop(self, sender, app_data, user_data):
18
+ dpg.push_container_stack("drop_zone")
19
+ dpg.add_text(f"Payload {app_data} dropped")
20
+
21
+ def cb_link(self, sender, app_data):
22
+ dpg.add_node_link(app_data[0], app_data[1], parent=sender)
23
+
24
+ def cb_delink(self, sender, app_data):
25
+ dpg.delete_item(app_data)
26
+
27
+ def __init__(self):
28
+ self.style_selector = StyleSelector.factory()
29
+ self.targeted_style_selector = StyleSelector.factory(target="row_2")
30
+
31
+ self.sindatax = []
32
+ self.sindatay = []
33
+ for i in range(0, 500):
34
+ self.sindatax.append(i / 1000)
35
+ self.sindatay.append(0.5 + 0.5 * math.sin(50 * i / 1000))
36
+
37
+ @typing.override
38
+ def setup(self):
39
+ self.style_selector.apply()
40
+
41
+ with dpg.viewport_menu_bar():
42
+ with dpg.menu(label='Style Selector'):
43
+ dpg.add_menu_item(label='Global', callback=self.style_selector.cb_show)
44
+ dpg.add_menu_item(label='Targeted', callback=self.targeted_style_selector.cb_show)
45
+
46
+ @typing.override
47
+ def render(self):
48
+ with dpg.window(label="Example Window", tag="window"):
49
+ dpg.add_spacer()
50
+ dpg.add_spacer()
51
+ with dpg.table(
52
+ header_row=False,
53
+ policy=dpg.mvTable_SizingStretchSame,
54
+ borders_outerH=True, borders_innerV=True, borders_outerV=True
55
+ ):
56
+ dpg.add_table_column(tag="col_1")
57
+ dpg.add_table_column(tag="col_2")
58
+
59
+ with dpg.table_row(tag="row_1"):
60
+ with dpg.group():
61
+ dpg.add_text("Hello, world")
62
+ dpg.add_input_text(default_value="Multiline\nText", multiline=True)
63
+ dpg.add_input_text(default_value="The quick brown fox jumps over the lazy dog.")
64
+ dpg.add_slider_float(default_value=0.273, max_value=1)
65
+ dpg.add_input_text(
66
+ label="Filter (inc, -exc)",
67
+ callback=lambda _, filter_string: dpg.set_value("filter_id", filter_string)
68
+ )
69
+ with dpg.filter_set(id="filter_id"):
70
+ dpg.add_text("aaa1.c", filter_key="aaa1.c", bullet=True)
71
+ dpg.add_text("bbb1.c", filter_key="bbb1.c", bullet=True)
72
+ dpg.add_text("ccc1.c", filter_key="ccc1.c", bullet=True)
73
+
74
+ with dpg.group():
75
+ with dpg.plot(label="Multi Axes Plot", height=300, width=400):
76
+ dpg.add_plot_legend()
77
+ dpg.add_plot_axis(dpg.mvXAxis, label="x")
78
+ dpg.add_plot_axis(dpg.mvYAxis, label="y1")
79
+ dpg.add_line_series(self.sindatax, self.sindatay, label="y1 lines", parent=dpg.last_item())
80
+ dpg.add_plot_axis(dpg.mvYAxis2, label="y2")
81
+ dpg.add_stem_series(self.sindatax, self.sindatay, label="y2 stem", parent=dpg.last_item())
82
+ dpg.add_plot_axis(dpg.mvYAxis3, label="y3 scatter")
83
+ dpg.add_scatter_series(self.sindatax, self.sindatay, label="y3", parent=dpg.last_item())
84
+
85
+ with dpg.table_row(tag="row_2"):
86
+ with dpg.group():
87
+ with dpg.group(horizontal=True):
88
+ with dpg.group():
89
+ for i in range(1, 6):
90
+ button = dpg.add_button(label=f"Drag Me", drag_callback=self.cb_on_drag)
91
+ with dpg.drag_payload(parent=button, user_data=i, drag_data=i):
92
+ dpg.add_text(f"Drag Payload {i}")
93
+ dpg.add_child_window(tag="drop_zone", drop_callback=self.cb_on_drop, width=300, height=200)
94
+ with dpg.node_editor(callback=self.cb_link, delink_callback=self.cb_delink, width=519, height=200):
95
+ with dpg.node(label="Node 1", pos=(0, 0)):
96
+ with dpg.node_attribute(label="Node A1"):
97
+ dpg.add_input_float(label="F1", width=150)
98
+ with dpg.node_attribute(label="Node A2", attribute_type=dpg.mvNode_Attr_Output):
99
+ dpg.add_input_float(label="F2", width=150)
100
+ with dpg.node(label="Node 2", pos=(270, 50)):
101
+ with dpg.node_attribute(label="Node A3"):
102
+ dpg.add_input_float(label="F3", width=200)
103
+ with dpg.node_attribute(label="Node A4", attribute_type=dpg.mvNode_Attr_Output):
104
+ dpg.add_input_float(label="F4", width=200)
105
+
106
+ with dpg.group():
107
+ dpg.add_listbox(items=["a", "b", "c"])
108
+ dpg.add_combo(items=["aaa", "bbb", "ccc"])
109
+ dpg.add_radio_button(items=["x", "y", "z"], horizontal=True)
110
+ with dpg.group(horizontal=True):
111
+ dpg.add_button(label="Button 1")
112
+ dpg.add_button(label="Button 2")
113
+ with dpg.group(horizontal=True):
114
+ dpg.add_selectable(label="Selectable 1", width=100)
115
+ dpg.add_selectable(label="Selectable 2", width=100)
116
+ dpg.add_selectable(label="Selectable 3", width=100)
117
+ with dpg.group(horizontal=True):
118
+ dpg.add_checkbox(label="Check Me", default_value=True)
119
+ dpg.add_checkbox(label="Check You", default_value=False)
120
+ with dpg.tab_bar():
121
+ with dpg.tab(label="Tab 1"):
122
+ dpg.add_text("tab 1 content")
123
+ with dpg.tab(label="Tab 2"):
124
+ dpg.add_text("tab 2 content")
125
+
126
+ @typing.override
127
+ def post_render(self):
128
+ dpg.set_primary_window("window", True)
129
+
130
+
131
+ if __name__ == "__main__":
132
+ Demo.run()
dpgbaseapp/fonts.py ADDED
@@ -0,0 +1,186 @@
1
+ """
2
+ Helpers for dealing with fonts. Works off of a base 'fonts' path - which must be structured as such:
3
+ fonts/
4
+ FontName1-Variant1.ttf
5
+ FontName1-Variant2.ttf
6
+ FontName2-Variant1.otf
7
+ FontName2-Variant2.otf
8
+
9
+ Opinionated
10
+ - All variants of a font must share the same extension.
11
+ - Variants must exist in FONT_NAME_SORT_LIST below (case insensitive)
12
+ """
13
+ import dataclasses
14
+ import functools
15
+ import pathlib
16
+ import typing
17
+
18
+ import dearpygui.dearpygui as dpg
19
+
20
+
21
+ FONT_NAMES_BY_WEIGHT = {
22
+ 100: ('HAIRLINE', 'THIN', ),
23
+ 200: ('EXTRALIGHT', 'ULTRALIGHT', ),
24
+ 300: ('LIGHT', ),
25
+ 350: ('SEMILIGHT', 'DEMILIGHT', ),
26
+ 380: ('BOOK', ),
27
+ 400: ('REGULAR', 'NORMAL', 'ROMAN', 'TEXT', ),
28
+ 500: ('MEDIUM', ),
29
+ 600: ('SEMIBOLD', 'DEMIBOLD', 'DEMI', ),
30
+ 700: ('BOLD', ),
31
+ 800: ('EXTRABOLD', 'ULTRABOLD', ),
32
+ 850: ('HEAVY', ),
33
+ 900: ('BLACK', ),
34
+ 950: ('EXTRABLACK', 'ULTRABLACK', )
35
+ }
36
+
37
+ FONT_WEIGHTS_BY_NAME = {
38
+ name: weight
39
+ for weight, names in FONT_NAMES_BY_WEIGHT.items()
40
+ for name in names
41
+ }
42
+
43
+ ITALIC_NAMES = ('ITALIC', 'OBLIQUE')
44
+
45
+ FONT_VARIANT_SORT_LIST: list[str] = []
46
+ for names in FONT_NAMES_BY_WEIGHT.values():
47
+ FONT_VARIANT_SORT_LIST.extend(names)
48
+ for name in names:
49
+ for italic_name in ITALIC_NAMES:
50
+ FONT_VARIANT_SORT_LIST.append(f'{name}{italic_name}')
51
+ if 'REGULAR' in names:
52
+ for italic_name in ITALIC_NAMES:
53
+ FONT_VARIANT_SORT_LIST.append(italic_name)
54
+
55
+ DEFAULT_FONT_SIZES: tuple[int, ...] = tuple(range(2, 65, 2))
56
+
57
+
58
+ @dataclasses.dataclass
59
+ class FontVariantDescriptors:
60
+ variant: str
61
+
62
+ @property
63
+ def upper(self) -> str:
64
+ return self.variant.upper()
65
+
66
+ @property
67
+ def italic(self) -> bool:
68
+ for italic_name in ITALIC_NAMES:
69
+ if self.upper == italic_name:
70
+ return True
71
+ if self.upper.endswith(italic_name):
72
+ return True
73
+ return False
74
+
75
+ @property
76
+ def weight_name(self) -> str:
77
+ for italic_name in ITALIC_NAMES:
78
+ if self.upper == italic_name:
79
+ return 'REGULAR'
80
+ if self.upper.endswith(italic_name):
81
+ return self.upper[:-len(italic_name)]
82
+ return self.upper
83
+
84
+ @property
85
+ def weight(self) -> int:
86
+ return FONT_WEIGHTS_BY_NAME[self.weight_name]
87
+
88
+
89
+ @dataclasses.dataclass(frozen=True)
90
+ class RealizedFontConfig:
91
+ name: str
92
+ variant: str
93
+ size: int
94
+
95
+
96
+ @dataclasses.dataclass
97
+ class Font:
98
+ name: str
99
+ extension: typing.Literal['.ttf', '.otf']
100
+ variants: list[str] = dataclasses.field(default_factory=list)
101
+
102
+ @functools.cached_property
103
+ def default_variant(self):
104
+ return self.find_nearest_variant('regular')
105
+
106
+ @functools.cached_property
107
+ def descriptors(self) -> list[FontVariantDescriptors]:
108
+ return [FontVariantDescriptors(variant) for variant in self.variants]
109
+
110
+ def find_nearest_variant(self, variant: str) -> str:
111
+ if variant in self.variants:
112
+ return variant
113
+
114
+ target = FontVariantDescriptors(variant)
115
+ descriptors = [descriptor for descriptor in self.descriptors if descriptor.italic == target.italic]
116
+ # If no descriptors match 'Italicness', fall back to all descriptors and search by weight alone
117
+ if len(descriptors) == 0:
118
+ descriptors = self.descriptors
119
+
120
+ sorted_descriptors = sorted(
121
+ descriptors,
122
+ key=lambda d: abs(d.weight - target.weight),
123
+ )
124
+ return sorted_descriptors[0].variant
125
+
126
+ @dataclasses.dataclass
127
+ class FontLibrary:
128
+ path: pathlib.Path
129
+ fonts: dict[str, Font] = dataclasses.field(default_factory=dict)
130
+ sizes: tuple[int, ...] = DEFAULT_FONT_SIZES
131
+ realized_fonts: dict[RealizedFontConfig, str | int] = dataclasses.field(default_factory=dict)
132
+
133
+ @classmethod
134
+ def factory(cls, path: pathlib.Path) -> typing.Self:
135
+ library = cls(path)
136
+ for entry in path.iterdir():
137
+ if entry.is_file() and entry.suffix in ('.ttf', '.otf'):
138
+ library.add_font(entry)
139
+ library.sort_all_variants()
140
+ return library
141
+
142
+ def add_font(self, font: pathlib.Path):
143
+ name_and_variant, extension = font.stem, font.suffix
144
+ assert extension in ('.ttf', '.otf')
145
+ name, variant = name_and_variant.split('-')
146
+ if name not in self.fonts:
147
+ self.fonts[name] = Font(name, extension)
148
+ self.fonts[name].variants.append(variant)
149
+
150
+ def sort_all_variants(self):
151
+ for font in self.fonts.values():
152
+ font.variants = sorted(
153
+ font.variants,
154
+ key=lambda variant: FONT_VARIANT_SORT_LIST.index(variant.upper()),
155
+ )
156
+
157
+ def _resolve_file(self, config: RealizedFontConfig) -> str:
158
+ font = self.fonts[config.name]
159
+ filename = f'{config.name}-{config.variant}{font.extension}'
160
+ return str(self.path / filename)
161
+
162
+ def realize_font(self, config: RealizedFontConfig) -> str | int:
163
+ if config in self.realized_fonts:
164
+ return self.realized_fonts[config]
165
+
166
+ file = self._resolve_file(config)
167
+ with dpg.font_registry():
168
+ font = dpg.add_font(file, config.size)
169
+
170
+ self.realized_fonts[config] = font
171
+
172
+ return font
173
+
174
+ @property
175
+ def default_realized_font_config(self) -> RealizedFontConfig:
176
+ name = next(iter(self.fonts))
177
+ variant = self.fonts[name].default_variant
178
+ size = 14
179
+
180
+ return RealizedFontConfig(name, variant, size)
181
+
182
+
183
+ if __name__ == '__main__':
184
+ fonts_path = pathlib.Path(__file__).parent.parent.parent / 'fonts'
185
+ library = FontLibrary.factory(fonts_path)
186
+ print(library.fonts)
@@ -0,0 +1,179 @@
1
+ import dataclasses
2
+ import functools
3
+ import pathlib
4
+ import random
5
+ import types
6
+ import typing
7
+
8
+ import dearpygui.dearpygui as dpg
9
+
10
+ from dpgbaseapp.fonts import FontLibrary
11
+ from dpgbaseapp.fonts import RealizedFontConfig
12
+ from dpgbaseapp.themes import Theme, default_themes
13
+
14
+
15
+ @dataclasses.dataclass
16
+ class StyleSelector:
17
+ theme: Theme
18
+ themes: tuple[Theme, ...]
19
+ font_library: FontLibrary
20
+ font_config: RealizedFontConfig
21
+ target: str | int | None = None
22
+ prefix: str | int = dataclasses.field(
23
+ default_factory=functools.partial(
24
+ random.randint,
25
+ 1999999,
26
+ 9999999,
27
+ ),
28
+ )
29
+ _labels: types.SimpleNamespace = dataclasses.field(default_factory=types.SimpleNamespace)
30
+ _rendered: bool = False
31
+
32
+ @classmethod
33
+ def factory(
34
+ cls,
35
+ theme: Theme | None = None,
36
+ themes: tuple[Theme, ...] | None = None,
37
+ font_path: pathlib.Path | None = None,
38
+ font_library: FontLibrary | None = None,
39
+ font_config: RealizedFontConfig | None = None,
40
+ target: str | int | None = None,
41
+ prefix: str | int | None = None,
42
+ ):
43
+ kwargs: dict[str, typing.Any] = {}
44
+ if themes is None:
45
+ kwargs['themes'] = default_themes
46
+ else:
47
+ kwargs['themes'] = themes
48
+
49
+ if theme is None:
50
+ kwargs['theme'] = kwargs['themes'][0]
51
+ else:
52
+ kwargs['theme'] = theme
53
+
54
+ if font_library is not None:
55
+ kwargs['font_library'] = font_library
56
+ else:
57
+ if font_path is None:
58
+ font_path = pathlib.Path.home() / '.local/share/dpgbaseapp/fonts'
59
+
60
+ kwargs['font_library'] = FontLibrary.factory(font_path)
61
+
62
+ if font_config is None:
63
+ kwargs['font_config'] = kwargs['font_library'].default_realized_font_config
64
+ else:
65
+ kwargs['font_config'] = font_config
66
+
67
+ kwargs['target'] = target
68
+
69
+ if prefix is not None:
70
+ kwargs['prefix'] = prefix
71
+
72
+ return cls(**kwargs) # pyright: ignore[reportAny]
73
+
74
+ def tag(self, key: str) -> str:
75
+ return f'{self.prefix}_styleselector_{key}'
76
+
77
+ def apply(self):
78
+ self.theme.bind(self.target)
79
+ font = self.font_library.realize_font(self.font_config)
80
+ if self.target is not None:
81
+ dpg.bind_item_font(self.target, font)
82
+ else:
83
+ dpg.bind_font(font)
84
+
85
+ def cb_show(self, sender, app_data, user_data):
86
+ self.render()
87
+ dpg.show_item(self.tag('window'))
88
+
89
+
90
+
91
+ def cb_theme(self, sender: str | int, app_data: typing.Any, user_data: typing.Any) -> None:
92
+ for theme in self.themes:
93
+ if theme.name == app_data:
94
+ self.theme = theme
95
+ self.apply()
96
+ return
97
+ raise ValueError(app_data)
98
+
99
+ def cb_fontname(self, sender: str | int, app_data: typing.Any, user_data: typing.Any):
100
+ name = app_data
101
+ variants = self.font_library.fonts[name].variants
102
+ variant = self.font_library.fonts[name].find_nearest_variant(self.font_config.variant)
103
+
104
+ dpg.configure_item(
105
+ self.tag('fontvariant'),
106
+ items=list(variants),
107
+ default_value=variant,
108
+ )
109
+
110
+ self.font_config = dataclasses.replace(
111
+ self.font_config,
112
+ name=name,
113
+ variant=variant,
114
+ )
115
+ self.apply()
116
+
117
+ def cb_fontvariant(self, sender: str | int, app_data: typing.Any, user_data: typing.Any):
118
+ self.font_config = dataclasses.replace(
119
+ self.font_config,
120
+ variant=app_data,
121
+ )
122
+ self.apply()
123
+
124
+ def cb_fontsize(self, sender: str | int, app_data: typing.Any, user_data: typing.Any):
125
+ self.font_config = dataclasses.replace(
126
+ self.font_config,
127
+ size=int(app_data),
128
+ )
129
+ self.apply()
130
+
131
+ def render(self) -> str | int:
132
+ if self._rendered:
133
+ return
134
+
135
+ self._rendered = True
136
+
137
+ with dpg.window(label='StyleSelector', width=800, height=400, tag=self.tag('window')) as window:
138
+ with dpg.group(horizontal=True):
139
+ with dpg.group():
140
+ self._labels.theme = dpg.add_text('Theme')
141
+ dpg.add_listbox(
142
+ items=list(theme.name for theme in self.themes),
143
+ callback=self.cb_theme,
144
+ tag=self.tag('theme'),
145
+ width=250,
146
+ num_items=10,
147
+ default_value=self.theme.name,
148
+ )
149
+ with dpg.group():
150
+ self._labels.fontname = dpg.add_text('Font Name')
151
+ dpg.add_listbox(
152
+ items=list(self.font_library.fonts),
153
+ callback=self.cb_fontname,
154
+ tag=self.tag('fontname'),
155
+ width=250,
156
+ num_items=10,
157
+ default_value=self.font_config.name,
158
+ )
159
+ with dpg.group():
160
+ self._labels.fontvariant = dpg.add_text('Font Variant')
161
+ dpg.add_listbox(
162
+ items=list(self.font_library.fonts[self.font_config.name].variants),
163
+ callback=self.cb_fontvariant,
164
+ tag=self.tag('fontvariant'),
165
+ width=200,
166
+ num_items=10,
167
+ default_value=self.font_config.variant,
168
+ )
169
+ with dpg.group():
170
+ self._labels.fontsize = dpg.add_text('Font Size')
171
+ dpg.add_listbox(
172
+ items=list(map(str, self.font_library.sizes)),
173
+ callback=self.cb_fontsize,
174
+ tag=self.tag('fontsize'),
175
+ width=60,
176
+ num_items=10,
177
+ default_value=str(self.font_config.size),
178
+ )
179
+ return window
dpgbaseapp/themes.py ADDED
@@ -0,0 +1,316 @@
1
+ import dataclasses
2
+
3
+ import dearpygui.dearpygui as dpg
4
+
5
+ from dpgbaseapp.colorschemes import (
6
+ Colorscheme,
7
+ default_colorschemes,
8
+ )
9
+
10
+
11
+
12
+ type ThemeColor = tuple[int, int, int] | tuple[int, int, int, int]
13
+
14
+ @dataclasses.dataclass
15
+ class ThemeColors:
16
+ Text: ThemeColor
17
+ TextDisabled: ThemeColor
18
+ WindowBg: ThemeColor
19
+ ChildBg: ThemeColor
20
+ PopupBg: ThemeColor
21
+ Border: ThemeColor
22
+ BorderShadow: ThemeColor
23
+ FrameBg: ThemeColor
24
+ FrameBgHovered: ThemeColor
25
+ FrameBgActive: ThemeColor
26
+ TitleBg: ThemeColor
27
+ TitleBgActive: ThemeColor
28
+ TitleBgCollapsed: ThemeColor
29
+ MenuBarBg: ThemeColor
30
+ ScrollbarBg: ThemeColor
31
+ ScrollbarGrab: ThemeColor
32
+ ScrollbarGrabHovered: ThemeColor
33
+ ScrollbarGrabActive: ThemeColor
34
+ CheckMark: ThemeColor
35
+ SliderGrab: ThemeColor
36
+ SliderGrabActive: ThemeColor
37
+ Button: ThemeColor
38
+ ButtonHovered: ThemeColor
39
+ ButtonActive: ThemeColor
40
+ Header: ThemeColor
41
+ HeaderHovered: ThemeColor
42
+ HeaderActive: ThemeColor
43
+ Separator: ThemeColor
44
+ SeparatorHovered: ThemeColor
45
+ SeparatorActive: ThemeColor
46
+ ResizeGrip: ThemeColor
47
+ ResizeGripHovered: ThemeColor
48
+ ResizeGripActive: ThemeColor
49
+ InputTextCursor: ThemeColor
50
+ TabHovered: ThemeColor
51
+ Tab: ThemeColor
52
+ TabSelected: ThemeColor
53
+ TabSelectedOverline: ThemeColor
54
+ TabDimmed: ThemeColor
55
+ TabDimmedSelected: ThemeColor
56
+ TabDimmedSelectedOverline: ThemeColor
57
+ DockingPreview: ThemeColor
58
+ DockingEmptyBg: ThemeColor
59
+ PlotLines: ThemeColor
60
+ PlotLinesHovered: ThemeColor
61
+ PlotHistogram: ThemeColor
62
+ PlotHistogramHovered: ThemeColor
63
+ TableHeaderBg: ThemeColor
64
+ TableBorderStrong: ThemeColor
65
+ TableBorderLight: ThemeColor
66
+ TableRowBg: ThemeColor
67
+ TableRowBgAlt: ThemeColor
68
+ TextSelectedBg: ThemeColor
69
+ TreeLines: ThemeColor
70
+ DragDropTarget: ThemeColor
71
+ DragDropTargetBg: ThemeColor
72
+ UnsavedMarker: ThemeColor
73
+ NavCursor: ThemeColor
74
+ NavWindowingHighlight : ThemeColor
75
+ NavWindowingDimBg: ThemeColor
76
+ ModalWindowDimBg: ThemeColor
77
+ Plot_FrameBg: ThemeColor
78
+ Plot_PlotBg: ThemeColor
79
+ Plot_PlotBorder: ThemeColor
80
+ Plot_LegendBg: ThemeColor
81
+ Plot_LegendBorder: ThemeColor
82
+ Plot_LegendText: ThemeColor
83
+ Plot_TitleText: ThemeColor
84
+ Plot_InlayText: ThemeColor
85
+ Plot_AxisText: ThemeColor
86
+ Plot_AxisGrid: ThemeColor
87
+ Plot_AxisTick: ThemeColor
88
+ Plot_Selection: ThemeColor
89
+ Plot_Crosshairs: ThemeColor
90
+ Node_NodeBackground: ThemeColor
91
+ Node_NodeBackgroundHovered: ThemeColor
92
+ Node_NodeBackgroundSelected : ThemeColor
93
+ Node_NodeOutline: ThemeColor
94
+ Node_TitleBar: ThemeColor
95
+ Node_TitleBarHovered: ThemeColor
96
+ Node_TitleBarSelected: ThemeColor
97
+ Node_Link: ThemeColor
98
+ Node_LinkHovered: ThemeColor
99
+ Node_LinkSelected: ThemeColor
100
+ Node_Pin: ThemeColor
101
+ Node_PinHovered: ThemeColor
102
+ Node_BoxSelector: ThemeColor
103
+ Node_BoxSelectorOutline: ThemeColor
104
+ Node_GridBackground: ThemeColor
105
+ Node_GridLine: ThemeColor
106
+ Nodes_GridLinePrimary: ThemeColor
107
+ Nodes_MiniMapBackground: ThemeColor
108
+ Nodes_MiniMapBackgroundHovered: ThemeColor
109
+ Nodes_MiniMapOutline: ThemeColor
110
+ Nodes_MiniMapOutlineHovered : ThemeColor
111
+ Nodes_MiniMapNodeBackground : ThemeColor
112
+ Nodes_MiniMapNodeBackgroundSelected: ThemeColor
113
+ Nodes_MiniMapNodeOutline: ThemeColor
114
+ Nodes_MiniMapLink: ThemeColor
115
+ Nodes_MiniMapLinkSelected: ThemeColor
116
+ Nodes_MiniMapCanvas: ThemeColor
117
+ Nodes_MiniMapCanvasOutline: ThemeColor
118
+
119
+
120
+ @dataclasses.dataclass
121
+ class Theme:
122
+ name: str
123
+ colors: ThemeColors
124
+ colorscheme: Colorscheme
125
+ _theme_id: int | str | None = None
126
+
127
+ def initialize(self):
128
+ if self._theme_id is not None:
129
+ return
130
+ with dpg.theme() as theme_id:
131
+ with dpg.theme_component(0):
132
+ for field in dataclasses.fields(self.colors):
133
+ if field.name == '_theme_id':
134
+ continue
135
+ name = field.name
136
+ value = getattr(self.colors, field.name)
137
+ if name.startswith('Plot_'):
138
+ attr_name = f'mvPlotCol_{name[5:]}'
139
+ category = dpg.mvThemeCat_Plots
140
+ elif name.startswith('Node_'):
141
+ attr_name = f'mvNodeCol_{name[5:]}'
142
+ category = dpg.mvThemeCat_Nodes
143
+ elif name.startswith('Nodes_'):
144
+ attr_name = f'mvNodesCol_{name[6:]}'
145
+ category = dpg.mvThemeCat_Nodes
146
+ else:
147
+ attr_name = f'mvThemeCol_{name}'
148
+ category = dpg.mvThemeCat_Core
149
+
150
+ dpg.add_theme_color(getattr(dpg, attr_name), value, category=category)
151
+ self._theme_id = theme_id
152
+
153
+ def bind(self, target: int | str | None = None):
154
+ self.initialize()
155
+ assert self._theme_id is not None
156
+ if target is None:
157
+ dpg.bind_theme(self._theme_id)
158
+ else:
159
+ dpg.bind_item_theme(target, self._theme_id)
160
+
161
+
162
+ def create_theme(colorscheme: Colorscheme) -> Theme:
163
+ """Build a Dear PyGui ``Theme`` from a ``Colorscheme``.
164
+
165
+ ``Base`` carries the main background,
166
+ the ``Sub_0``/``Crust`` shades sit "behind" it (menus, title bars, popups),
167
+ and the ``Surface`` ramp layers up for interactive states
168
+ (rest -> hovered -> active). Foreground uses ``Text`` with the ``Overlay``
169
+ ramp for muted/disabled content.
170
+
171
+ Accents are deliberately spread around for some colour: ``Mauve`` is the
172
+ primary accent (selection, slider, tab overline, nav), with checkmarks in
173
+ ``Green``, separators/links in ``Blue``/``Sky``, plots in ``Peach``/``Yellow``,
174
+ drag-drop in ``Yellow`` and node titles cycling through the warm accents.
175
+ """
176
+
177
+ def a(color: tuple[int, int, int], alpha: int) -> ThemeColor:
178
+ """Same colour with an explicit alpha (0-255)."""
179
+ return (color[0], color[1], color[2], alpha)
180
+
181
+ accent = colorscheme.Mauve # primary accent
182
+ accent_alt = colorscheme.Blue # secondary accent
183
+
184
+ colors = ThemeColors(
185
+ # --- ImGui: text & windows ---
186
+ Text=colorscheme.Text,
187
+ TextDisabled=colorscheme.Overlay,
188
+ WindowBg=colorscheme.Base,
189
+ ChildBg=a(colorscheme.Base, 0),
190
+ PopupBg=colorscheme.Sub_1,
191
+ Border=colorscheme.Surface_0,
192
+ BorderShadow=a(colorscheme.Sub_0, 0),
193
+ # --- frames / inputs ---
194
+ FrameBg=colorscheme.Surface_0,
195
+ FrameBgHovered=colorscheme.Surface_1,
196
+ FrameBgActive=colorscheme.Surface_2,
197
+ # --- title bars ---
198
+ TitleBg=colorscheme.Sub_1,
199
+ TitleBgActive=colorscheme.Surface_0,
200
+ TitleBgCollapsed=a(colorscheme.Sub_1, 180),
201
+ MenuBarBg=colorscheme.Sub_0,
202
+ # --- scrollbar ---
203
+ ScrollbarBg=colorscheme.Sub_1,
204
+ ScrollbarGrab=colorscheme.Surface_0,
205
+ ScrollbarGrabHovered=colorscheme.Surface_1,
206
+ ScrollbarGrabActive=colorscheme.Surface_2,
207
+ # --- checks & sliders (splash: green check, mauve/pink slider) ---
208
+ CheckMark=colorscheme.Green,
209
+ SliderGrab=accent,
210
+ SliderGrabActive=colorscheme.Pink,
211
+ # --- buttons (neutral surfaces, colour lives in the accents) ---
212
+ Button=colorscheme.Surface_0,
213
+ ButtonHovered=colorscheme.Surface_1,
214
+ ButtonActive=colorscheme.Surface_2,
215
+ # --- headers (selectables / tree nodes) ---
216
+ Header=a(accent, 90),
217
+ HeaderHovered=a(accent, 130),
218
+ HeaderActive=a(accent, 170),
219
+ # --- separators (splash: blue) ---
220
+ Separator=colorscheme.Surface_0,
221
+ SeparatorHovered=accent_alt,
222
+ SeparatorActive=accent_alt,
223
+ # --- resize grips ---
224
+ ResizeGrip=a(accent, 80),
225
+ ResizeGripHovered=a(accent, 140),
226
+ ResizeGripActive=a(accent, 200),
227
+ InputTextCursor=colorscheme.Rose,
228
+ # --- tabs (splash: mauve overline) ---
229
+ TabHovered=a(accent, 150),
230
+ Tab=colorscheme.Sub_1,
231
+ TabSelected=colorscheme.Surface_0,
232
+ TabSelectedOverline=accent,
233
+ TabDimmed=colorscheme.Sub_0,
234
+ TabDimmedSelected=colorscheme.Sub_1,
235
+ TabDimmedSelectedOverline=a(accent, 120),
236
+ # --- docking ---
237
+ DockingPreview=a(accent, 100),
238
+ DockingEmptyBg=colorscheme.Sub_0,
239
+ # --- plot lines (splash: blue/sky, peach/yellow) ---
240
+ PlotLines=colorscheme.Blue,
241
+ PlotLinesHovered=colorscheme.Sky,
242
+ PlotHistogram=colorscheme.Peach,
243
+ PlotHistogramHovered=colorscheme.Yellow,
244
+ # --- tables ---
245
+ TableHeaderBg=colorscheme.Surface_0,
246
+ TableBorderStrong=colorscheme.Surface_1,
247
+ TableBorderLight=colorscheme.Surface_0,
248
+ TableRowBg=a(colorscheme.Base, 0),
249
+ TableRowBgAlt=a(colorscheme.Surface_0, 60),
250
+ # --- selection / misc ---
251
+ TextSelectedBg=a(accent, 80),
252
+ TreeLines=colorscheme.Overlay,
253
+ DragDropTarget=colorscheme.Yellow,
254
+ DragDropTargetBg=a(colorscheme.Yellow, 80),
255
+ UnsavedMarker=colorscheme.Peach,
256
+ NavCursor=accent,
257
+ NavWindowingHighlight=a(colorscheme.Text, 180),
258
+ NavWindowingDimBg=a(colorscheme.Sub_0, 150),
259
+ ModalWindowDimBg=a(colorscheme.Sub_0, 150),
260
+ # --- ImPlot ---
261
+ Plot_FrameBg=a(colorscheme.Base, 0),
262
+ Plot_PlotBg=colorscheme.Sub_1,
263
+ Plot_PlotBorder=colorscheme.Surface_0,
264
+ Plot_LegendBg=a(colorscheme.Sub_1, 230),
265
+ Plot_LegendBorder=colorscheme.Surface_1,
266
+ Plot_LegendText=colorscheme.Text,
267
+ Plot_TitleText=colorscheme.Text,
268
+ Plot_InlayText=colorscheme.Subtext,
269
+ Plot_AxisText=colorscheme.Subtext,
270
+ Plot_AxisGrid=colorscheme.Surface_0,
271
+ Plot_AxisTick=colorscheme.Surface_1,
272
+ Plot_Selection=a(colorscheme.Yellow, 90),
273
+ Plot_Crosshairs=a(colorscheme.Text, 128),
274
+ # --- imnodes ---
275
+ Node_NodeBackground=colorscheme.Surface_0,
276
+ Node_NodeBackgroundHovered=colorscheme.Surface_1,
277
+ Node_NodeBackgroundSelected=colorscheme.Surface_2,
278
+ Node_NodeOutline=colorscheme.Overlay,
279
+ # node title bars cycle through warm accents
280
+ Node_TitleBar=accent,
281
+ Node_TitleBarHovered=colorscheme.Pink,
282
+ Node_TitleBarSelected=colorscheme.Lavender,
283
+ # links in cool accents
284
+ Node_Link=colorscheme.Blue,
285
+ Node_LinkHovered=colorscheme.Sky,
286
+ Node_LinkSelected=colorscheme.Sapphire,
287
+ # pins in teal/green
288
+ Node_Pin=colorscheme.Teal,
289
+ Node_PinHovered=colorscheme.Green,
290
+ Node_BoxSelector=a(accent_alt, 30),
291
+ Node_BoxSelectorOutline=a(accent_alt, 150),
292
+ Node_GridBackground=colorscheme.Sub_1,
293
+ Node_GridLine=colorscheme.Surface_0,
294
+ Nodes_GridLinePrimary=colorscheme.Surface_1,
295
+ Nodes_MiniMapBackground=a(colorscheme.Sub_1, 150),
296
+ Nodes_MiniMapBackgroundHovered=a(colorscheme.Sub_1, 200),
297
+ Nodes_MiniMapOutline=colorscheme.Surface_0,
298
+ Nodes_MiniMapOutlineHovered=colorscheme.Surface_1,
299
+ Nodes_MiniMapNodeBackground=colorscheme.Surface_1,
300
+ Nodes_MiniMapNodeBackgroundSelected=accent,
301
+ Nodes_MiniMapNodeOutline=colorscheme.Overlay,
302
+ Nodes_MiniMapLink=colorscheme.Blue,
303
+ Nodes_MiniMapLinkSelected=colorscheme.Sapphire,
304
+ Nodes_MiniMapCanvas=a(colorscheme.Sub_0, 100),
305
+ Nodes_MiniMapCanvasOutline=colorscheme.Surface_0,
306
+ )
307
+
308
+ return Theme(
309
+ colorscheme.name,
310
+ colors,
311
+ colorscheme
312
+ )
313
+
314
+
315
+
316
+ default_themes = [create_theme(colorscheme) for colorscheme in default_colorschemes]
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: dpgbaseapp
3
+ Version: 0.1.0
4
+ Summary: A set of base structures to facilitate building DearPyGui apps
5
+ Author: Zachary Parmley
6
+ Author-email: Zachary Parmley <zachary.craig@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Dist: dearpygui>=2.3.1
10
+ Requires-Dist: rich>=15.0.0
11
+ Requires-Python: >=3.12
12
+ Project-URL: Homepage, https://github.com/zparmley/dpgbaseapp
13
+ Project-URL: Repository, https://github.com/zparmley/dpgbaseapp
14
+ Description-Content-Type: text/markdown
15
+
16
+ # DPGBaseApp
17
+
18
+ A set of base structures to facilitate building DPG apps
@@ -0,0 +1,11 @@
1
+ dpgbaseapp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ dpgbaseapp/app.py,sha256=lYk969oCsindtYMO4DAtNP3GbKBVAN0vRgWIqHj7v5s,968
3
+ dpgbaseapp/colorschemes.py,sha256=uRpTvUD7cQ0b0WLGHEoS5umItl9kJ8mt0B6uq8_8TBg,7346
4
+ dpgbaseapp/demo.py,sha256=kYNjVHx9qOFZVP0Q-DIk5hWZL7sQtKqluMyTiEiuGNw,6568
5
+ dpgbaseapp/fonts.py,sha256=sF82NKSiIbBqN34sZqCo93x9XDHhGiA5l3mOnKHG_j4,5670
6
+ dpgbaseapp/style_selector.py,sha256=JrnjsVzQY7aWiAvNSabUaRzPVfkGmKNR5T5C0aw5SYw,6044
7
+ dpgbaseapp/themes.py,sha256=3vyLPskfT6lHxYwMbVzExR6-WcP4FJSesdR7CHs7sV4,11632
8
+ dpgbaseapp-0.1.0.dist-info/licenses/LICENSE,sha256=44sQQ8dpyxE3oIBtkDUlt1UJj4yPMdcz7O36XTb6mZ4,1072
9
+ dpgbaseapp-0.1.0.dist-info/WHEEL,sha256=8ZlpUMJ7mlDirmlHRhDirEx_nPnARrwDjeE92mlk68E,81
10
+ dpgbaseapp-0.1.0.dist-info/METADATA,sha256=R1mQNa5dXO4-1svsDr7qu1PO4SUflzXasSMM0H5HavA,574
11
+ dpgbaseapp-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.21
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zachary Parmley
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.