A flexible and customizable docking framework for PySide6 applications.
Project description
JCDock
A flexible and customizable docking framework for PySide6 applications, inspired by modern IDEs.
JCDock allows you to create complex user interfaces where widgets can be docked into containers, undocked into floating windows, and rearranged dynamically by the user through an intuitive drag-and-drop interface.
Features
For detailed API documentation, see the wiki/ directory.
- Advanced Docking: Dock widgets to the top, bottom, left, right, or center of other widgets and containers with visual overlay guides.
- Floating Windows: Undock any widget or group of widgets into its own floating window with native-like appearance including drop shadows and proper window management.
- Tearable Tabs: Users can tear individual tabs away from a tab group to instantly create new floating windows with smooth visual feedback.
- Persistent Layouts: Save and restore complete application layouts with automatic widget recreation through the registry system.
- Nested Splitters: Automatically create and manage complex nested horizontal and vertical splitter layouts.
- Multi-Monitor Support: Full support for dragging and docking across multiple monitors with proper coordinate handling.
- Performance Optimized: Built-in caching systems for icons and hit-testing to ensure smooth performance even with complex layouts.
- Customizable Appearance: Easily customize title bar colors, widget styling, and visual effects.
- Signal System: Comprehensive event system to track widget docking, undocking, and layout changes.
- Enhanced Toolbar Support: Full QMainWindow-style toolbar system with multi-area support (top, bottom, left, right), breaks for row/column organization, precise insertion control, and automatic persistence across sessions.
- Multi-Area Layout: Support for complex layouts with multiple independent docking areas.
Installation
JCDock is available on PyPI and can be installed using pip. Choose the installation method that best fits your needs:
Option 1: Install from PyPI (Recommended for Users)
For most users who want to use JCDock in their applications:
pip install JCDock
This installs the latest stable release directly from PyPI.
Option 2: Install from Source (For Development)
For developers who want to contribute to JCDock or need the latest development features:
# 1. Clone the repository from GitHub
git clone https://github.com/JustiSoft/JCDock.git
# 2. Navigate into the cloned directory
cd JCDock
# 3. Install in "editable" mode
pip install -e .
Using the -e or --editable flag is recommended for development. It installs the package by creating a link to the source code, so any changes you make to the code will be immediately reflected in your environment.
Simplified Architecture
JCDock uses a streamlined, single-method architecture that eliminates complexity while providing full docking capabilities.
Universal API Design
One method handles all scenarios:
manager = DockingManager()
# Main windows, floating widgets, persistent widgets - all use the same method
window = manager.create_window(
content=widget, # Any QWidget (optional for main windows)
title="My Window",
is_main_window=True, # For main application window
persist=True, # For layout persistence
x=100, y=100, width=400, height=300
)
Key Architecture Benefits
- Single Entry Point:
create_window()replaces multiple creation methods - Automatic Management: DockPanel wrapping and DockContainer creation handled internally
- Persistent by Design:
@persistabledecorator +persist=Trueenables layout restoration - User-Friendly: No need to understand internal component relationships
Implementation Components (Internal)
- DockingManager: Public API facade and central coordinator
- DockPanel: Automatic wrapper for making widgets dockable
- DockContainer: Internal container implementation for windows
- Widget Registry: Automatic widget type management via
@persistable
Specialized Systems
Why This Architecture?
Before: Multiple complex methods, manual component management
# Old complex approach (deprecated)
container, panel = manager.create_simple_floating_widget(...)
other_container = manager.create_floating_widget_from_key(...)
manager.register_widget(...)
# Multiple methods, complex relationships
Now: Single universal method, automatic management
# New streamlined approach
window = manager.create_window(content=widget, title="Widget", persist=True)
# One method, automatic internal management
Internal System Overview (Advanced)
The simplified API is built on a sophisticated internal architecture:
- State Management: Coordinated state machine prevents conflicts
- Performance Optimization: Automatic caching and throttling systems
- Layout Persistence: Binary serialization with widget state preservation
- Drag & Drop: Visual overlay system with smooth interactions
- Multi-Platform: Native window effects and cross-platform compatibility
Development Philosophy
JCDock prioritizes developer experience over architectural exposure:
- Simple API Surface: One creation method vs. many specialized methods
- Automatic Lifecycle: Internal components managed transparently
- Sensible Defaults: Minimal configuration required for common scenarios
- Progressive Enhancement: Advanced features available when needed
- Future-Proof: Internal changes don't break application code
Basic Usage
Here's the simplest possible example showing how to create a floating dockable widget:
import sys
from PySide6.QtWidgets import QApplication, QLabel
from PySide6.QtCore import Qt
from JCDock import DockingManager
if __name__ == "__main__":
app = QApplication(sys.argv)
# 1. Create the Docking Manager
manager = DockingManager()
# 2. Create content and make it a floating dockable widget
content = QLabel("Hello, JCDock!")
content.setAlignment(Qt.AlignCenter)
window = manager.create_window(
content=content,
title="My Widget",
persist=True
)
window.show()
sys.exit(app.exec())
Creating a Main Window with Docking
For a more complete application with a main window and multiple dockable widgets:
import sys
from PySide6.QtWidgets import QApplication, QLabel, QTextEdit
from PySide6.QtCore import Qt
from JCDock import DockingManager
if __name__ == "__main__":
app = QApplication(sys.argv)
# 1. Create the Docking Manager
manager = DockingManager()
# 2. Create a main window
main_window = manager.create_window(
title="My Application",
is_main_window=True,
persist=True,
x=100,
y=100,
width=1000,
height=600
)
# 3. Create widget content
project_content = QLabel("Project Explorer")
editor_content = QTextEdit("Your code here...")
# 4. Register widgets with manager
manager.register_widget(project_content)
manager.register_widget(editor_content)
# 5. Dock widgets to create layout
manager.dock_widget(project_content, main_window, "left")
manager.dock_widget(editor_content, main_window, "center")
main_window.show()
sys.exit(app.exec())
Toolbar Integration Example
JCDock provides comprehensive toolbar support with automatic persistence:
import sys
from PySide6.QtWidgets import QApplication, QTextEdit, QToolBar, QAction
from PySide6.QtCore import Qt
from JCDock import DockingManager
if __name__ == "__main__":
app = QApplication(sys.argv)
# 1. Create the Docking Manager
manager = DockingManager()
# 2. Create main window with toolbar support
main_window = manager.create_window(
title="Text Editor with Toolbars",
is_main_window=True,
persist=True,
width=1000,
height=600
)
# 3. Create editor content
editor = QTextEdit("Start typing...")
manager.register_widget(editor)
manager.dock_widget(editor, main_window, "center")
# 4. Add comprehensive toolbar layout
# File operations toolbar
file_toolbar = main_window.addToolBar("File")
file_toolbar.addAction("New")
file_toolbar.addAction("Open")
file_toolbar.addAction("Save")
# Edit operations toolbar (same row)
edit_toolbar = main_window.addToolBar("Edit")
edit_toolbar.addAction("Cut")
edit_toolbar.addAction("Copy")
edit_toolbar.addAction("Paste")
# Create toolbar break for new row
main_window.addToolBarBreak()
# Format toolbar (new row)
format_toolbar = main_window.addToolBar("Format")
format_toolbar.addAction("Bold")
format_toolbar.addAction("Italic")
format_toolbar.addAction("Underline")
# Status toolbar at bottom
status_toolbar = main_window.addToolBar("Status", Qt.BottomToolBarArea)
status_toolbar.addAction("Status: Ready")
# 5. Demonstrate dynamic toolbar insertion
# Insert priority toolbar before edit toolbar
priority_toolbar = main_window.insertToolBar(edit_toolbar, "Priority")
priority_toolbar.addAction("Important Action")
# Add break before priority toolbar to reorganize layout
main_window.insertToolBarBreak(priority_toolbar)
main_window.show()
# Toolbar state is automatically saved/restored with layout
sys.exit(app.exec())
This example demonstrates:
- Multi-area toolbar support: Top and bottom toolbar areas
- Toolbar breaks: Creating rows/columns within areas
- Dynamic insertion: Adding toolbars between existing ones
- Automatic persistence: Toolbar layouts saved with application state
Complete Examples and Testing
Comprehensive Test Suite
JCDock includes a modular test suite that demonstrates all framework capabilities and serves as both a testing framework and reference implementation. To run the test suite:
# From project root directory
cd src/JCDock/Examples
../../../.venv/Scripts/python.exe run_test_suite.py
Example of JCDock in action showing floating windows, docked panels, and tearable tabs
The test suite (src/JCDock/Examples/test_suite/), which can be run from the Examples directory: provides comprehensive testing through a modular architecture:
Core Components
- Entry Point:
main.pyfor configuration and execution - Application Core:
app.pyhandling test orchestration
Specialized Managers
- Test Manager: Test execution and validation (
managers/test_manager.py) - UI Manager: Menu system and interaction (
managers/ui_manager.py) - Layout Manager: Layout persistence testing (
managers/layout_manager.py)
Test Widgets
- Base Widgets: Registry and decorator examples (
widgets/test_widgets.py) - Complex Widgets: Financial dashboard demos (
widgets/financial_widgets.py)
Testing Features
- Registry Testing:
@persistabledecorator and widget registration - API Coverage: Both registry and instance-based operations
- Layout Testing: Serialization and state preservation
- Performance: Visual feedback and drag operation metrics
- Event System: Signal listeners and lifecycle events
Key Testing Features
- Registry-based widget creation using
@persistabledecorators - Both API paths: "By Type" (registry-based) and "By Instance" (existing widgets)
- Comprehensive testing functions for all API methods including widget finding, listing, docking operations, and state management
- Signal system usage with event listeners for layout changes
- Interactive menu system for testing different features and operations
- Layout persistence testing with automatic save/load validation
- Performance monitoring and drag operation testing
Layout Persistence
The test suite demonstrates automatic layout persistence using the standard layout file location:
layouts/
└── jcdock_layout.ini # Automatically saved/loaded layout file
└── toolbar_demo_layout.bin # Example toolbar layout persistence
The layout file preserves:
- Widget positions and container hierarchies
- Window geometry and multi-monitor positioning
- Widget state (custom data via
get_dock_state()/set_dock_state()) - Tab ordering and splitter proportions
- Toolbar layouts including breaks, insertion points, and custom item states
Advanced Features
Layout Persistence
JCDock automatically supports saving and restoring layouts when you use the registry system with @persistable decorated widgets:
from PySide6.QtWidgets import QLabel, QTextEdit
from JCDock import persistable
# Register widget types for automatic layout persistence
@persistable("project_explorer", "Project Explorer")
class ProjectWidget(QLabel):
def __init__(self):
super().__init__()
self.setText("Project Files")
@persistable("code_editor", "Code Editor")
class EditorWidget(QTextEdit):
def __init__(self):
super().__init__()
self.setPlainText("# Your code here")
# Create widgets using registered types
project = ProjectWidget()
editor = EditorWidget()
# Create windows for widgets
project_window = manager.create_window(
content=project,
key="project_explorer",
title="Project Explorer",
persist=True
)
editor_window = manager.create_window(
content=editor,
key="code_editor",
title="Code Editor",
persist=True
)
# Save and restore layouts (binary format)
layout_data = manager.save_layout_to_bytearray()
# Later, restore the exact same layout
manager.load_layout_from_bytearray(layout_data)
The registry system automatically handles widget recreation during layout restoration - no manual factory functions needed!
Signal System
Connect to docking events to respond to layout changes:
# Connect to various signals
manager.signals.widget_docked.connect(lambda widget, container:
print(f"Widget '{widget.windowTitle()}' docked"))
manager.signals.widget_undocked.connect(lambda widget:
print(f"Widget '{widget.windowTitle()}' undocked"))
manager.signals.widget_closed.connect(lambda persistent_id:
print(f"Widget '{persistent_id}' closed"))
manager.signals.layout_changed.connect(lambda:
print("Layout changed - save state, update UI, etc."))
Available Signals:
widget_docked(widget, container)- Widget docked into a containerwidget_undocked(widget)- Widget undocked to floating windowwidget_closed(persistent_id)- Widget closed and removedlayout_changed()- Any layout modification occurredapplication_closing(layout_data)- Application closing with current layout data
API Reference
Public API (Recommended)
The streamlined public API focuses on the universal create_window() method:
from JCDock import DockingManager, persistable
manager = DockingManager()
window = manager.create_window(content=widget, title="Title", persist=True)
Available Public Imports:
DockingManager- Central API for all operationspersistable- Decorator for widgets with layout persistenceget_registry()- Access to the global widget registry
Internal Components
These components are documented for reference but should not be accessed directly:
WidgetFactory- Internal widget creation (useDockingManager.create_window())DockContainer- Internal container implementation (created bycreate_window())DockPanel- Internal widget wrapper (created automatically)- Various utility classes (caching, performance monitoring, etc.)
Migration from Older APIs
If you're using older JCDock versions, replace deprecated methods:
create_simple_floating_widget()→create_window()create_floating_widget_from_key()→create_window()with registry@dockable→@persistable
Additional Documentation
- wiki/ - Comprehensive API documentation and usage guides
- CLAUDE.md - Development guidelines and context for development tools
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jcdock-1.1.0.tar.gz.
File metadata
- Download URL: jcdock-1.1.0.tar.gz
- Upload date:
- Size: 134.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a39ac8596ea113020eb5b3f83adb1caf1904e7812706feff734b92d77de64d8
|
|
| MD5 |
96f4e505db79d3176fe14dbd0ee5ef47
|
|
| BLAKE2b-256 |
8bd511a131a048be85a0ecc8c55402f321546220372958df52e8ae56785b2e4e
|
File details
Details for the file jcdock-1.1.0-py3-none-any.whl.
File metadata
- Download URL: jcdock-1.1.0-py3-none-any.whl
- Upload date:
- Size: 153.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48ba6afb267c2025af23fbc4376aeb8d30d4271b89f4aae947d610d4531b0210
|
|
| MD5 |
37638c3361554e7041f2eca012b095a9
|
|
| BLAKE2b-256 |
aaad4d8663c2e0ae724c39308e32a1181e1cddb70a745e1a761b6576563d1491
|