tabdock 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.
- tabdock-0.1.0/PKG-INFO +475 -0
- tabdock-0.1.0/README.md +462 -0
- tabdock-0.1.0/pyproject.toml +27 -0
- tabdock-0.1.0/setup.cfg +4 -0
- tabdock-0.1.0/tabdock/TabDock.py +143 -0
- tabdock-0.1.0/tabdock/__init__.py +20 -0
- tabdock-0.1.0/tabdock/_style_guide.py +3 -0
- tabdock-0.1.0/tabdock/connector_manager.py +183 -0
- tabdock-0.1.0/tabdock/dock.py +1091 -0
- tabdock-0.1.0/tabdock/hconnector.py +123 -0
- tabdock-0.1.0/tabdock/panel.py +710 -0
- tabdock-0.1.0/tabdock/panel_state.py +49 -0
- tabdock-0.1.0/tabdock/qt_themes_compat.py +67 -0
- tabdock-0.1.0/tabdock/tab.py +290 -0
- tabdock-0.1.0/tabdock/tabs/__init__.py +13 -0
- tabdock-0.1.0/tabdock/tabs/editor_tab.py +56 -0
- tabdock-0.1.0/tabdock/tabs/left_main_tab.py +44 -0
- tabdock-0.1.0/tabdock/tabs/quad_tab.py +62 -0
- tabdock-0.1.0/tabdock/tabs/standard_tab.py +65 -0
- tabdock-0.1.0/tabdock/tabs/top_bottom_tab.py +44 -0
- tabdock-0.1.0/tabdock/vconnector.py +119 -0
- tabdock-0.1.0/tabdock.egg-info/PKG-INFO +475 -0
- tabdock-0.1.0/tabdock.egg-info/SOURCES.txt +24 -0
- tabdock-0.1.0/tabdock.egg-info/dependency_links.txt +1 -0
- tabdock-0.1.0/tabdock.egg-info/requires.txt +5 -0
- tabdock-0.1.0/tabdock.egg-info/top_level.txt +1 -0
tabdock-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tabdock
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A PyQt6 dockable layout framework for Python desktop applications
|
|
5
|
+
Author-email: Tristan Winata <twinata@sas.upenn.edu>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: PyQt6
|
|
10
|
+
Provides-Extra: themes
|
|
11
|
+
Requires-Dist: qt-themes; extra == "themes"
|
|
12
|
+
Requires-Dist: qtpy; extra == "themes"
|
|
13
|
+
|
|
14
|
+
# TabDock UI Framework
|
|
15
|
+
|
|
16
|
+
A PyQt6 dockable layout framework. The UI is made up of tabs, each containing resizable docks, each dock containing one or more panels inspired largely by the design of game engines, IDEs, and art programs. Users can resize docks by dragging the dividers between them, rearrange panels within docks by dragging their tab buttons, and tear panels out into floating windows. There is also a shared state mechanism that lets panels communicate with each other even when they're in different docks. This framework is meant to be an easy-to-plug-in UI layer for any Python application.
|
|
17
|
+
|
|
18
|
+
Provides support for themes from:
|
|
19
|
+
https://github.com/beatreichenbach/qt-themes?tab=readme-ov-file
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Installation
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
pip install tabdock
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For theme support:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
pip install tabdock[themes]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Requires Python 3.11 or later.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
# Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import sys
|
|
43
|
+
from PyQt6.QtWidgets import QApplication, QMainWindow
|
|
44
|
+
from tabdock import TabDock, Panel, apply_theme
|
|
45
|
+
from tabdock.tabs import LeftMainTab
|
|
46
|
+
|
|
47
|
+
class MyPanel(Panel):
|
|
48
|
+
def __init__(self, parent, docked, x, y, w, h):
|
|
49
|
+
super().__init__(parent, docked, x, y, w, h)
|
|
50
|
+
self.initUI()
|
|
51
|
+
|
|
52
|
+
def initUI(self):
|
|
53
|
+
self.add_section_label("Controls")
|
|
54
|
+
self.next_row()
|
|
55
|
+
self.add_button("Run", callback=lambda: print("run"))
|
|
56
|
+
self.add_button("Stop", callback=lambda: print("stop"))
|
|
57
|
+
|
|
58
|
+
app = QApplication(sys.argv)
|
|
59
|
+
theme_kwargs = apply_theme("monokai")
|
|
60
|
+
|
|
61
|
+
window = QMainWindow()
|
|
62
|
+
TD = TabDock(available_panels=[MyPanel], **theme_kwargs)
|
|
63
|
+
window.setCentralWidget(TD)
|
|
64
|
+
|
|
65
|
+
tab = LeftMainTab(TD, "Main", 0, left_panels=[MyPanel], main_panels=[MyPanel])
|
|
66
|
+
TD.add_tab(tab)
|
|
67
|
+
|
|
68
|
+
window.show()
|
|
69
|
+
sys.exit(app.exec())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
# Architecture
|
|
75
|
+
|
|
76
|
+
The hierarchy from outermost to innermost is:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
TabDock — the main widget, sits inside QMainWindow
|
|
80
|
+
Tab — one screen layout, selected from the top bar
|
|
81
|
+
Dock — a resizable container that holds panels in its own tab row
|
|
82
|
+
Panel — the actual UI content you write
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Docks are positioned using ratios (0.0 to 1.0) relative to the Tab's size, so they resize correctly with the window. Connectors sit between adjacent docks and let the user drag to resize them.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
# Core Classes
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## TabDock
|
|
93
|
+
|
|
94
|
+
The root container. Pass it to `setCentralWidget`.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from tabdock import TabDock
|
|
98
|
+
|
|
99
|
+
TD = TabDock(
|
|
100
|
+
create_external_docks=True, # allow panels to be torn out into floating windows
|
|
101
|
+
min_dock_size=100, # minimum pixel size for any dock before splits are disabled
|
|
102
|
+
available_panels=[MyPanel], # panel classes the user can add via right-click menu
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Styling parameters (all optional, all cascade down to Tab, Dock, and Panel):
|
|
107
|
+
|
|
108
|
+
| Parameter | What it controls |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `tab_height` | Height of the top-level tab chooser bar |
|
|
111
|
+
| `tab_bar_bg` | Background of the tab chooser bar |
|
|
112
|
+
| `content_bg` | Background of the content area |
|
|
113
|
+
| `tab_text_color` | Text color on tab buttons |
|
|
114
|
+
| `active_tab_color` | Highlighted tab button color |
|
|
115
|
+
| `dock_bg` | Background of each dock |
|
|
116
|
+
| `dock_border_color` | Color of the dock border |
|
|
117
|
+
| `panel_bg` | Background of each panel |
|
|
118
|
+
| `accent_color` | Accent color used on interactive widgets |
|
|
119
|
+
|
|
120
|
+
Call `add_tab(tab)` to register a tab. Call `switch_tab(index)` to change the visible one.
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
## Tab
|
|
124
|
+
|
|
125
|
+
A Tab defines one screen layout. Subclass it and implement `initUI` to create docks and connectors.
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from tabdock import Tab, Dock, HConnector
|
|
129
|
+
|
|
130
|
+
class MyTab(Tab):
|
|
131
|
+
def initUI(self):
|
|
132
|
+
self.left = Dock(self, [MyPanel], 0, 0, 0.3, 1)
|
|
133
|
+
self.right = Dock(self, [MyPanel], 0.3, 0, 0.7, 1)
|
|
134
|
+
|
|
135
|
+
self.add_dock(self.left)
|
|
136
|
+
self.add_dock(self.right)
|
|
137
|
+
|
|
138
|
+
self.add_connector(HConnector(self, 0.3, [self.left], [self.right]))
|
|
139
|
+
|
|
140
|
+
self.left.raise_()
|
|
141
|
+
self.right.raise_()
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The four positional arguments to `Dock` are `x_ratio`, `y_ratio`, `w_ratio`, `h_ratio`. All are fractions of the Tab's width and height (0.0 to 1.0).
|
|
145
|
+
|
|
146
|
+
Always call `dock.raise_()` on every dock after adding connectors — this keeps dock tab bars clickable above the connector hit zones.
|
|
147
|
+
|
|
148
|
+
The constructor signature for a Tab is `(parent, name, index, **style_kwargs)`. Store any custom arguments as instance variables before calling `super().__init__()` because `super().__init__()` calls `initUI()`.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
## Dock
|
|
152
|
+
|
|
153
|
+
A Dock holds one or more panels and displays them in its own small tab row at the top. The user can right-click a dock to split it, add panels, or delete it.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
Dock(parent, panels, x_ratio, y_ratio, w_ratio, h_ratio)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- `panels` is a list of Panel classes (not instances). The dock instantiates them itself.
|
|
160
|
+
- The dock's tab bar shows one button per panel. Dragging a button reorders it or tears it into a new floating window.
|
|
161
|
+
|
|
162
|
+
You do not usually call methods on Dock directly from application code. Layout is handled by the Tab, and user interactions are handled internally.
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
## HConnector and VConnector
|
|
166
|
+
|
|
167
|
+
Connectors are the draggable dividers between docks.
|
|
168
|
+
|
|
169
|
+
- `HConnector` is a vertical line the user drags left/right.
|
|
170
|
+
- `VConnector` is a horizontal line the user drags up/down.
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from tabdock import HConnector, VConnector
|
|
174
|
+
|
|
175
|
+
HConnector(parent_tab, x_ratio, left_docks, right_docks)
|
|
176
|
+
VConnector(parent_tab, y_ratio, top_docks, bottom_docks)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Pass every dock that shares that edge on each side. When the user drags the connector, all listed docks on both sides resize together.
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
## Panel
|
|
183
|
+
|
|
184
|
+
A Panel is the leaf of the hierarchy — it contains your actual UI. Subclass it and implement `initUI`.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from tabdock import Panel
|
|
188
|
+
|
|
189
|
+
class MyPanel(Panel):
|
|
190
|
+
def __init__(self, parent, docked, x, y, w, h):
|
|
191
|
+
super().__init__(parent, docked, x, y, w, h)
|
|
192
|
+
self.initUI()
|
|
193
|
+
|
|
194
|
+
def initUI(self):
|
|
195
|
+
self.add_section_label("Controls")
|
|
196
|
+
self.next_row()
|
|
197
|
+
self.add_button("Run", callback=self.on_run)
|
|
198
|
+
self.add_button("Stop", callback=self.on_stop)
|
|
199
|
+
self.next_row()
|
|
200
|
+
self.add_slider(minimum=0, maximum=100, value=50)
|
|
201
|
+
|
|
202
|
+
def on_run(self):
|
|
203
|
+
print("running")
|
|
204
|
+
|
|
205
|
+
def on_stop(self):
|
|
206
|
+
print("stopped")
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Widgets are placed left to right in the current row. Call `next_row()` to move to the next line. All widgets inherit the panel's theme colors automatically.
|
|
210
|
+
|
|
211
|
+
Always store custom constructor arguments before calling `super().__init__()` because `super().__init__()` calls `initUI()`.
|
|
212
|
+
|
|
213
|
+
Widget factory methods:
|
|
214
|
+
|
|
215
|
+
| Method | What it creates |
|
|
216
|
+
|---|---|
|
|
217
|
+
| `add_label(text)` | Plain text label |
|
|
218
|
+
| `add_section_label(text)` | Bold section header |
|
|
219
|
+
| `add_button(text, callback)` | Push button |
|
|
220
|
+
| `add_dropdown(options, callback)` | Combo box |
|
|
221
|
+
| `add_text_input(placeholder, callback)` | Single-line text field |
|
|
222
|
+
| `add_number_input(placeholder, integers_only, positive_only, min_value, max_value, callback)` | Number-only text field |
|
|
223
|
+
| `add_checkbox(text, callback)` | Checkbox |
|
|
224
|
+
| `add_slider(minimum, maximum, value, callback)` | Horizontal slider |
|
|
225
|
+
| `add_progress_bar(minimum, maximum, value)` | Progress bar |
|
|
226
|
+
| `add_list(items, multi_select, callback)` | Scrollable list |
|
|
227
|
+
| `add_calendar(callback)` | Monthly calendar picker |
|
|
228
|
+
| `add_separator()` | Full-width horizontal divider line |
|
|
229
|
+
| `add_spacer(height)` | Vertical blank space |
|
|
230
|
+
| `next_row()` | Start a new row |
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
# Shared State Between Panel Instances
|
|
235
|
+
|
|
236
|
+
Every subclass of Panel has a shared state manager. All instances of the same panel class read and write from the same state, so changes in one dock are automatically reflected in any other dock showing the same panel type. (Note: you can use these functions without using a global key; doing so will make each instance of each panel different, and you will have to get values from the widget the classical way from PyQt).
|
|
237
|
+
|
|
238
|
+
To use this, pass a key name to the widget factory's state parameter:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
def initUI(self):
|
|
242
|
+
# This label will display the current value of "mode" in shared state
|
|
243
|
+
self.add_label("Mode: none", state_key="mode", state_format=lambda v: f"Mode: {v}")
|
|
244
|
+
self.next_row()
|
|
245
|
+
|
|
246
|
+
# Clicking this button toggles a boolean stored at "active"
|
|
247
|
+
# The button label changes to reflect the current state
|
|
248
|
+
self.add_button("Enable", bool_key="active", default=False,
|
|
249
|
+
on_text="Enabled", off_text="Enable")
|
|
250
|
+
self.next_row()
|
|
251
|
+
|
|
252
|
+
# This checkbox stays in sync with the same "active" key
|
|
253
|
+
self.add_checkbox("Active", bool_key="active")
|
|
254
|
+
self.next_row()
|
|
255
|
+
|
|
256
|
+
# This dropdown syncs its selected text to "mode"
|
|
257
|
+
self.add_dropdown(["Fast", "Slow", "Paused"], string_key="mode", default="Fast")
|
|
258
|
+
self.next_row()
|
|
259
|
+
|
|
260
|
+
# This list syncs its selection to "files"
|
|
261
|
+
self.add_list(["a.py", "b.py"], list_key="files", default=[])
|
|
262
|
+
```
|
|
263
|
+
Unilateral can change based on the key, but has no way of changing the key itself
|
|
264
|
+
Bilateral can change based on the key, and changes the key based on user input
|
|
265
|
+
Buttons are neither, as they do not require any key, nor will they automatically change a key
|
|
266
|
+
|
|
267
|
+
Note that multiple different widgets can have the same key if compatible
|
|
268
|
+
i.e., a label and a text_input can have the same key and the label will update based on the text_input
|
|
269
|
+
State key parameters by widget:
|
|
270
|
+
|
|
271
|
+
| Widget | Key parameter | Value type | Unilateral // Bilateral
|
|
272
|
+
|---|---|---|
|
|
273
|
+
| `add_label` | `state_key` | any (use `state_format` to convert) | Unilateral
|
|
274
|
+
| `add_button` | `NONE` | NONE | NONE
|
|
275
|
+
| `add_toggle_button` | `bool_key` | `bool` | Bilateral
|
|
276
|
+
| `add_checkbox` | `bool_key` | `bool` | Bilateral
|
|
277
|
+
| `add_text_input` | `string_key` | `str` | Bilateral
|
|
278
|
+
| `add_number_input` | `float_key` | `int` or `float` | Bilateral
|
|
279
|
+
| `add_dropdown` | `string_key` | `str` (selected text) | Bilateral
|
|
280
|
+
| `add_slider` | `int_key` | `int` | Bilateral
|
|
281
|
+
| `add_progress_bar` | `int_key` | `int` | Unilateral
|
|
282
|
+
| `add_list` | `list_key` | `list[str]` | Bilateral
|
|
283
|
+
| `add_calendar` | `string_key` | `str` ("YYYY-MM-DD") | Bilateral
|
|
284
|
+
|
|
285
|
+
The `default` parameter sets the initial value only if the key has never been set. The first instance to initialize wins; subsequent instances pick up the current value.
|
|
286
|
+
|
|
287
|
+
You can also read and write state directly:
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
self.state.get("active") # read a value
|
|
291
|
+
self.state.set("active", True) # write a value (notifies all subscribers)
|
|
292
|
+
self.state.has("active") # check if a key exists
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
# Built-in Tab Layouts
|
|
298
|
+
|
|
299
|
+
These are ready-made Tab subclasses. Import them from `tabdock.tabs`.
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
from tabdock.tabs import StandardTab, LeftMainTab, TopBottomTab, EditorTab, QuadTab
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
## StandardTab
|
|
307
|
+
|
|
308
|
+
Classic sidebar layout.
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
+--------+-----------+--------+
|
|
312
|
+
| | | |
|
|
313
|
+
| left | center | right |
|
|
314
|
+
| | | |
|
|
315
|
+
| +-----------+ |
|
|
316
|
+
| | bottom | |
|
|
317
|
+
+--------+-----------+--------+
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
StandardTab(TD, "My Tab", 0,
|
|
322
|
+
left_panels=[FilePanel],
|
|
323
|
+
center_panels=[EditorPanel],
|
|
324
|
+
right_panels=[InspectorPanel],
|
|
325
|
+
bottom_panels=[LogPanel],
|
|
326
|
+
left_ratio=0.20, right_ratio=0.20, bottom_ratio=0.30)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Docks accessible as `tab.left`, `tab.center`, `tab.right`, `tab.bottom`.
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
## LeftMainTab
|
|
333
|
+
|
|
334
|
+
Left sidebar with a single large main area.
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
+--------+--------------------+
|
|
338
|
+
| | |
|
|
339
|
+
| left | main |
|
|
340
|
+
| | |
|
|
341
|
+
+--------+--------------------+
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
LeftMainTab(TD, "My Tab", 0,
|
|
346
|
+
left_panels=[TreePanel],
|
|
347
|
+
main_panels=[ContentPanel],
|
|
348
|
+
left_ratio=0.25)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Docks accessible as `tab.left`, `tab.main`.
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
## TopBottomTab
|
|
355
|
+
|
|
356
|
+
Toolbar strip on top, large main area below.
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
+----------------------------+
|
|
360
|
+
| top |
|
|
361
|
+
+----------------------------+
|
|
362
|
+
| main |
|
|
363
|
+
+----------------------------+
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
TopBottomTab(TD, "My Tab", 0,
|
|
368
|
+
top_panels=[ToolbarPanel],
|
|
369
|
+
main_panels=[ViewPanel],
|
|
370
|
+
top_ratio=0.10)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Docks accessible as `tab.top`, `tab.main`.
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
## EditorTab
|
|
377
|
+
|
|
378
|
+
IDE-style: file tree on the left, editor in the center, console at the bottom.
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
+--------+--------------------+
|
|
382
|
+
| | main |
|
|
383
|
+
| left +--------------------+
|
|
384
|
+
| | bottom |
|
|
385
|
+
+--------+--------------------+
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
EditorTab(TD, "My Tab", 0,
|
|
390
|
+
left_panels=[FilesPanel],
|
|
391
|
+
main_panels=[EditorPanel],
|
|
392
|
+
bottom_panels=[ConsolePanel],
|
|
393
|
+
left_ratio=0.20, bottom_ratio=0.25)
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Docks accessible as `tab.left`, `tab.main`, `tab.bottom`.
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
## QuadTab
|
|
400
|
+
|
|
401
|
+
Four equal quadrants.
|
|
402
|
+
|
|
403
|
+
```
|
|
404
|
+
+-------------+-------------+
|
|
405
|
+
| | |
|
|
406
|
+
| top_left | top_right |
|
|
407
|
+
| | |
|
|
408
|
+
+-------------+-------------+
|
|
409
|
+
| | |
|
|
410
|
+
| bottom_left | bottom_right|
|
|
411
|
+
| | |
|
|
412
|
+
+-------------+-------------+
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
```python
|
|
416
|
+
QuadTab(TD, "My Tab", 0,
|
|
417
|
+
top_left_panels=[ChartPanel],
|
|
418
|
+
top_right_panels=[ChartPanel],
|
|
419
|
+
bottom_left_panels=[ChartPanel],
|
|
420
|
+
bottom_right_panels=[StatsPanel],
|
|
421
|
+
h_split=0.5, v_split=0.5)
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Docks accessible as `tab.top_left`, `tab.top_right`, `tab.bottom_left`, `tab.bottom_right`.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
# Theming
|
|
429
|
+
|
|
430
|
+
Themes are applied through `qt-themes` using the `apply_theme` helper (requires `pip install tabdock[themes]`):
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
from tabdock import apply_theme
|
|
434
|
+
|
|
435
|
+
theme_kwargs = apply_theme("monokai")
|
|
436
|
+
TD = TabDock(..., **theme_kwargs)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
The helper sets the global QPalette and returns a dict of color keyword arguments that flow through the cascade into every Dock and Panel. If `qt-themes` is not installed it returns an empty dict silently, so the app still runs with default colors.
|
|
440
|
+
|
|
441
|
+
To see available themes:
|
|
442
|
+
|
|
443
|
+
```python
|
|
444
|
+
from tabdock import get_available_themes
|
|
445
|
+
print(get_available_themes())
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
# File Structure
|
|
451
|
+
|
|
452
|
+
```
|
|
453
|
+
pyproject.toml
|
|
454
|
+
|
|
455
|
+
tabdock/
|
|
456
|
+
__init__.py public API exports
|
|
457
|
+
TabDock.py top-level container widget
|
|
458
|
+
tab.py Tab base class
|
|
459
|
+
dock.py Dock widget and drag logic
|
|
460
|
+
panel.py Panel base class and widget factories
|
|
461
|
+
panel_state.py shared observable state per panel class
|
|
462
|
+
hconnector.py horizontal (left/right) resizer
|
|
463
|
+
vconnector.py vertical (top/bottom) resizer
|
|
464
|
+
connector_manager.py mouse event handler for connectors
|
|
465
|
+
qt_themes_compat.py qt-themes integration
|
|
466
|
+
_style_guide.py default color constants
|
|
467
|
+
|
|
468
|
+
tabs/
|
|
469
|
+
__init__.py
|
|
470
|
+
standard_tab.py four-area layout
|
|
471
|
+
left_main_tab.py sidebar + main
|
|
472
|
+
top_bottom_tab.py toolbar + main
|
|
473
|
+
editor_tab.py IDE-style three-area layout
|
|
474
|
+
quad_tab.py 2x2 grid
|
|
475
|
+
```
|