Skip to main content

A flexible and extensible tab manager widget for tkinter applications built on top of ttk.Notebook

Project description

PyPI version Python License

FlexTabs

A flexible and extensible tab manager widget for tkinter applications built on top of ttk.Notebook. While ttk.Notebook provides the basic tab interface, FlexTabs extends it with multiple tab opening mechanisms (toolbar, sidebar, menu), advanced tab management features, and customizable behavior options.

Why FlexTabs?

While tkinter's ttk.Notebook is great for basic tabbed interfaces, it has limitations:

  • Only supports tabs displayed at the top of the widget
  • No built-in way to dynamically open/close tabs from external UI elements
  • Limited customization for tab opening mechanisms
  • No built-in support for unclosable tabs, confirmations, or notifications

FlexTabs solves these problems by wrapping ttk.Notebook with a comprehensive management layer that provides modern tab interface patterns commonly seen in IDEs, browsers, and professional applications. It handles tab state retention after open and close actions. New opened tabs are appended to the end, and opener buttons can manage both opening and switching between opened tabs.

Architecture

FlexTabs is built as a wrapper around tkinter's ttk.Notebook widget:

┌─────────────────────────────────────────┐
│              TabManager                 │
│  ┌─────────────┐  ┌─────────────────┐   │
│  │ TabOpener   │  │  ttk.Notebook   │   │
│  │ (Sidebar/   │  │                 │   │
│  │ Toolbar/    │  │ ┌─────────────┐ │   │
│  │ Menu)       │  │ │ TabContent  │ │   │
│  │             │  │ │   Frame     │ │   │
│  │ [Home]      │  │ └─────────────┘ │   │
│  │ [Settings]  │  │ ┌─────────────┐ │   │
│  │ [Help]      │  │ │ TabContent  │ │   │
│  │             │  │ │   Frame     │ │   │
│  └─────────────┘  │ └─────────────┘ │   │
│                   └─────────────────┘   │
└─────────────────────────────────────────┘
  • TabManager: Main widget that coordinates everything
  • TabOpener: External UI elements (sidebar, toolbar, menu) for opening tabs
  • ttk.Notebook: The actual tab container (unchanged tkinter behavior)
  • TabContent: Your custom content classes that populate each tab

Features

  • Built on ttk.Notebook: Full compatibility with existing tkinter tab functionality
  • Multiple Tab Openers: Toolbar, Sidebar, and Menu-based tab opening
  • Flexible Tab Management: Open, close, and switch between tabs programmatically
  • Customizable Styling: Configure appearance of tabs, buttons, and notifications
  • Keyboard Shortcuts: Built-in shortcuts for tab navigation and custom tab shortcuts
  • Close Confirmation: Optional confirmation dialogs before closing tabs
  • Toast Notifications: Built-in notification system for user feedback
  • Tooltips: Hover tooltips for tab buttons
  • Event Callbacks: Hook into tab lifecycle events
  • Runtime Configuration: Add/remove tabs dynamically
  • Unclosable Tabs: Mark tabs as permanent with special styling

Installation

pip install flextabs

Or clone the repository:

git clone https://github.com/MS-32154/flextabs.git
cd flextabs
pip install -e .

Running Tests

pytest

Running the Demo

python3 -m flextabs

Quick Start

Here's how you would create tabs with vanilla tkinter vs. FlexTabs:

Traditional ttk.Notebook approach:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()
notebook = ttk.Notebook(root)

# Manually create and add tabs
frame1 = ttk.Frame(notebook)
notebook.add(frame1, text="Tab 1")
tk.Label(frame1, text="Content 1").pack()

frame2 = ttk.Frame(notebook)
notebook.add(frame2, text="Tab 2")
tk.Label(frame2, text="Content 2").pack()

notebook.pack(fill=tk.BOTH, expand=True)
root.mainloop()

FlexTabs approach:

import tkinter as tk
from flextabs import TabManager, TabConfig, TabContent

class MyTabContent(TabContent):
    def setup_content(self):
        tk.Label(self.frame, text=f"Content for {self.config.title}").pack()

# Create main window
root = tk.Tk()
root.title("FlexTabs Demo")
root.geometry("800x600")

# Define tabs with rich configuration
tab_configs = [
    TabConfig("tab1", "Home", MyTabContent, tooltip="Home page"),
    TabConfig("tab2", "Settings", MyTabContent, closable=False),
    TabConfig("tab3", "Help", MyTabContent, keyboard_shortcut="<Control-h>"),
]

# Create tab manager with sidebar opener
tab_manager = TabManager(
    root,
    tab_configs=tab_configs,
    opener_type="sidebar",  # External UI for opening tabs
    opener_config={"position": "left", "width": 200, "title": "Navigation"}
)
tab_manager.pack(fill=tk.BOTH, expand=True)

root.mainloop()

The key difference: FlexTabs separates tab definition from tab opening mechanism, allowing you to create professional interfaces where tabs can be opened from sidebars, toolbars, menus, or programmatically.

Tab Opener Types

1. Sidebar Opener

Perfect for navigation-style interfaces with vertical button layout.

tab_manager = TabManager(
    parent,
    tab_configs=tabs,
    opener_type="sidebar",
    opener_config={
        "position": "left",  # or "right"
        "width": 200,
        "title": "Navigation",
        "style": {"bg": "#f0f0f0"}
    }
)

2. Toolbar Opener

Great for ribbon-style interfaces with horizontal or vertical button layouts.

tab_manager = TabManager(
    parent,
    tab_configs=tabs,
    opener_type="toolbar",
    opener_config={
        "position": "top",  # "top", "bottom", "left", "right"
        "layout": "horizontal",  # or "vertical"
        "style": {"bg": "#e0e0e0"},
        "button_style": {"width": 15}
    }
)

3. Menu Opener

Integrates tabs into the application's menu bar.

tab_manager = TabManager(
    parent,
    tab_configs=tabs,
    opener_type="menu",
    opener_config={
        "menu_title": "Windows"  # Menu name in menu bar
    }
)

Tab Configuration

TabConfig Parameters

from flextabs import TabConfig

tab_config = TabConfig(
    id="unique_id",              # Unique identifier
    title="Tab Title",           # Display name
    content_class=MyTabContent,  # TabContent subclass
    icon="path/to/icon.png",     # Optional icon (not yet implemented)
    tooltip="Helpful tooltip",   # Hover tooltip text
    closable=True,              # Whether tab can be closed
    keyboard_shortcut="<Control-t>",  # Keyboard shortcut
    data={"key": "value"}       # Custom data dictionary
)

Creating Tab Content

Extend the TabContent class to create your tab content:

from flextabs import TabContent
import tkinter as tk
from tkinter import ttk

class MyTabContent(TabContent):
    def setup_content(self):
        """Required: Setup your tab's UI here"""
        # Access tab configuration
        title = self.config.title
        custom_data = self.config.data
        
        # Create UI elements
        ttk.Label(self.frame, text=f"Welcome to {title}").pack(pady=10)
        
        # Access the tab manager if needed
        manager = self.get_manager()
        close_btn = manager.add_close_button(self.frame, self.tab_id)
        close_btn.pack(pady=5)
    
    def on_tab_focus(self):
        """Optional: Called when tab gains focus"""
        print(f"Tab {self.tab_id} focused")
    
    def on_tab_blur(self):
        """Optional: Called when tab loses focus"""
        print(f"Tab {self.tab_id} blurred")
    
    def on_tab_close(self) -> bool:
        """Optional: Called before tab closes. Return False to prevent closing"""
        # Ask user for confirmation, save data, etc.
        return True  # Allow closing
    
    def cleanup(self):
        """Optional: Clean up resources when tab is destroyed"""
        super().cleanup()  # Always call parent cleanup

Advanced Configuration

Close Behavior

from flextabs import TabManager, CloseMode, CloseConfirmationType

tab_manager = TabManager(
    parent,
    tab_configs=tabs,
    
    # Close button behavior
    close_button_style="right_click",  # "right_click", "double_click", "both"
    
    # Close mode - which tabs can be closed with click
    close_mode=CloseMode.ACTIVE_ONLY,  # ACTIVE_ONLY, ANY_VISIBLE, BOTH
    
    # Close confirmation
    close_confirmation=True,
    close_confirmation_type=CloseConfirmationType.YESNO,  # NONE, YESNO, WARNING, INFO
    
    # Keyboard shortcuts
    enable_keyboard_shortcuts=True
)

Event Callbacks

def on_tab_opened(tab_id: str):
    print(f"Tab {tab_id} opened")

def on_tab_closed(tab_id: str):
    print(f"Tab {tab_id} closed")

def on_tab_switched(new_tab_id: str, old_tab_id: str):
    print(f"Switched from {old_tab_id} to {new_tab_id}")

def on_tab_error(tab_id: str, error: Exception):
    print(f"Error in tab {tab_id}: {error}")

tab_manager.on_tab_opened = on_tab_opened
tab_manager.on_tab_closed = on_tab_closed
tab_manager.on_tab_switched = on_tab_switched
tab_manager.on_tab_error = on_tab_error

Notebook Styling

FlexTabs uses ttk.Notebook internally, so all standard ttk styling applies:

import tkinter.ttk as ttk

# Create custom notebook style
style = ttk.Style()
style.configure("Custom.TNotebook", 
               background="lightgray",
               tabmargins=[0, 5, 0, 0])
style.configure("Custom.TNotebook.Tab", 
               padding=[20, 10],
               background="white")

tab_manager = TabManager(
    parent,
    tab_configs=tabs,
    notebook_config={
        "style": "Custom.TNotebook",  # Apply custom ttk style
        "padding": 5
    }
)

You can also access the underlying ttk.Notebook directly:

# Access the internal ttk.Notebook for advanced customization
internal_notebook = tab_manager.notebook
internal_notebook.configure(width=500, height=300)

API Reference

TabManager Methods

Tab Management

  • open_tab(tab_id: str) -> bool - Open a specific tab
  • close_tab(tab_id: str) -> bool - Close a specific tab
  • select_tab(tab_id: str) -> bool - Select/focus a specific tab
  • close_all_tabs() -> int - Close all open tabs

Tab Information

  • is_tab_open(tab_id: str) -> bool - Check if tab is open
  • get_open_tabs() -> list[str] - Get list of open tab IDs
  • get_current_tab() -> str | None - Get currently selected tab ID
  • get_tab_content(tab_id: str) -> TabContent | None - Get tab content instance

Runtime Configuration

  • add_tab_config(config: TabConfig) - Add new tab configuration
  • remove_tab_config(tab_id: str) -> bool - Remove tab configuration
  • set_close_mode(mode: CloseMode) - Change close mode
  • get_close_mode() -> CloseMode - Get current close mode

UI Helpers

  • add_close_button(parent: Widget, tab_id: str) -> ttk.Button - Add close button to tab content
  • show_notification(message: str, toast_type: str = "info", duration: int = 2000) - Show toast notification

Cleanup

  • cleanup() - Clean up all resources
  • destroy() - Destroy the widget

Built-in Keyboard Shortcuts

  • Ctrl+W - Close current tab
  • Ctrl+Tab - Next tab
  • Ctrl+Shift+Tab - Previous tab
  • Ctrl+1-9 - Select tab by index
  • Custom shortcuts defined in TabConfig.keyboard_shortcut

Enums

TabPosition

  • TOP, BOTTOM, LEFT, RIGHT - Opener positions

CloseMode

  • ACTIVE_ONLY - Only close currently active tab on click
  • ANY_VISIBLE - Close any visible tab on click
  • BOTH - Close active tab normally, any tab with Ctrl+click

CloseConfirmationType

  • NONE - No confirmation
  • YESNO - Yes/No dialog
  • WARNING - Warning dialog with OK/Cancel
  • INFO - Info dialog with OK

Examples

Complete Application Example

import tkinter as tk
from tkinter import ttk, messagebox
from flextabs import TabManager, TabConfig, TabContent

class HomeTab(TabContent):
    def setup_content(self):
        ttk.Label(self.frame, text="Welcome to the Home Tab!", 
                 font=("TkDefaultFont", 16)).pack(pady=20)
        
        ttk.Button(self.frame, text="Open Settings", 
                  command=lambda: self.get_manager().open_tab("settings")).pack(pady=5)

class SettingsTab(TabContent):
    def setup_content(self):
        ttk.Label(self.frame, text="Settings", 
                 font=("TkDefaultFont", 14, "bold")).pack(pady=10)
        
        # Some settings widgets
        ttk.Checkbutton(self.frame, text="Enable notifications").pack(pady=2)
        ttk.Checkbutton(self.frame, text="Auto-save").pack(pady=2)
        
        # Add close button
        close_btn = self.get_manager().add_close_button(self.frame, self.tab_id)
        close_btn.pack(pady=10)

class DataTab(TabContent):
    def setup_content(self):
        self.data_modified = False
        
        ttk.Label(self.frame, text="Data Editor").pack(pady=10)
        
        self.text_area = tk.Text(self.frame, height=10, width=50)
        self.text_area.pack(pady=10, padx=20, fill=tk.BOTH, expand=True)
        self.text_area.bind('<KeyPress>', self.on_data_change)
    
    def on_data_change(self, event):
        self.data_modified = True
    
    def on_tab_close(self) -> bool:
        if self.data_modified:
            result = messagebox.askyesnocancel(
                "Unsaved Changes",
                "You have unsaved changes. Save before closing?",
                parent=self.frame
            )
            if result is None:  # Cancel
                return False
            elif result:  # Yes - save
                # Simulate saving
                self.get_manager().show_notification("Data saved!", "success")
        return True

def main():
    root = tk.Tk()
    root.title("FlexTabs Demo Application")
    root.geometry("900x600")
    
    # Define tabs
    tabs = [
        TabConfig("home", "Home", HomeTab, 
                 tooltip="Application home page",
                 keyboard_shortcut="<Control-h>"),
        TabConfig("settings", "Settings", SettingsTab,
                 tooltip="Application settings",
                 closable=False),  # Can't be closed
        TabConfig("data", "Data Editor", DataTab,
                 tooltip="Edit your data here",
                 keyboard_shortcut="<Control-d>"),
    ]
    
    # Create tab manager with sidebar
    tab_manager = TabManager(
        root,
        tab_configs=tabs,
        opener_type="sidebar",
        opener_config={
            "position": "left",
            "width": 180,
            "title": "Navigation",
            "style": {"bg": "#f8f9fa"}
        },
        close_confirmation=True,
        close_confirmation_type="yesno",
        enable_keyboard_shortcuts=True
    )
    
    # Set up event handlers
    def on_tab_opened(tab_id):
        tab_manager.show_notification(f"Opened {tabs[0].title}", "info")
    
    tab_manager.on_tab_opened = on_tab_opened
    tab_manager.pack(fill=tk.BOTH, expand=True)
    
    # Open home tab by default
    tab_manager.open_tab("home")
    
    root.mainloop()

if __name__ == "__main__":
    main()

Dynamic Tab Management

import tkinter as tk
from flextabs import TabManager, TabConfig, TabContent

class DynamicContent(TabContent):
    def setup_content(self):
        data = self.config.data
        tk.Label(self.frame, text=f"Dynamic tab: {data.get('content', 'No content')}").pack()

def create_dynamic_tab(tab_manager, counter):
    """Create a new tab dynamically"""
    tab_id = f"dynamic_{counter}"
    config = TabConfig(
        id=tab_id,
        title=f"Dynamic {counter}",
        content_class=DynamicContent,
        data={"content": f"This is dynamic tab #{counter}"}
    )
    
    tab_manager.add_tab_config(config)
    tab_manager.open_tab(tab_id)

# Usage in your application
root = tk.Tk()
tab_manager = TabManager(root, tab_configs=[], opener_type="toolbar")

# Add button to create new tabs
counter = 1
def add_tab():
    global counter
    create_dynamic_tab(tab_manager, counter)
    counter += 1

tk.Button(root, text="Add Tab", command=add_tab).pack()
tab_manager.pack(fill=tk.BOTH, expand=True)

Styling and Customization

Custom Tooltip Styling

The library includes built-in tooltips that can be customized by modifying the TooltipWidget class or by styling the underlying tkinter widgets.

Toast Notifications

Built-in toast notification system with four types:

  • info (blue) - General information
  • warning (yellow) - Warnings
  • error (red) - Error messages
  • success (green) - Success messages

TTK Styling and Compatibility

FlexTabs preserves full compatibility with ttk.Notebook styling and behavior:

import tkinter.ttk as ttk

# All standard ttk.Notebook features work
style = ttk.Style()
style.configure("Custom.TNotebook", background="lightgray")
style.configure("Custom.TNotebook.Tab", padding=[20, 10])

# FlexTabs respects ttk themes
style.theme_use('clam')  # or 'alt', 'default', 'classic'

tab_manager = TabManager(
    parent,
    tab_configs=tabs,
    notebook_config={"style": "Custom.TNotebook"}
)

# Access underlying ttk.Notebook for direct manipulation
notebook = tab_manager.notebook
print(f"Current tab: {notebook.select()}")
print(f"All tabs: {notebook.tabs()}")

Migrating from ttk.Notebook

If you have existing ttk.Notebook code, migration is straightforward:

Before (ttk.Notebook):

notebook = ttk.Notebook(parent)
frame = ttk.Frame(notebook)
notebook.add(frame, text="My Tab")
# Content goes directly in frame

After (FlexTabs):

class MyTab(TabContent):
    def setup_content(self):
        # Content goes in self.frame
        pass

config = TabConfig("my_tab", "My Tab", MyTab)
tab_manager = TabManager(parent, [config], opener_type="toolbar")

Requirements

  • Python 3.8+
  • tkinter (usually included with Python)

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

Version 0.1.1

  • Initial release
  • Support for Toolbar, Sidebar, and Menu openers
  • Tab lifecycle management
  • Keyboard shortcuts
  • Close confirmation dialogs
  • Toast notifications
  • Tooltip support
  • Runtime tab configuration

Support

If you encounter any issues or have questions, please:

  1. Check the examples section
  2. Search existing GitHub Issues
  3. Create a new issue with:
    • Python version
    • Operating system
    • Minimal code example reproducing the issue
    • Full error traceback (if applicable)

Roadmap

  • Icon support for tabs and buttons
  • Drag and drop tab reordering
  • Tab groups and separators
  • Custom tab close buttons
  • Persistent tab state
  • Theme system
  • Animation effects
  • Tab overflow handling

FlexTabs – Making Tkinter tab management flexible and powerful! 🚀

© 2025 MS-32154. All rights reserved. Licensed under the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flextabs-0.1.1.tar.gz (38.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flextabs-0.1.1-py3-none-any.whl (36.2 kB view details)

Uploaded Python 3

File details

Details for the file flextabs-0.1.1.tar.gz.

File metadata

  • Download URL: flextabs-0.1.1.tar.gz
  • Upload date:
  • Size: 38.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.5

File hashes

Hashes for flextabs-0.1.1.tar.gz
Algorithm Hash digest
SHA256 89b74c50d5fc2041d28ddaa08712570ea81898ede8cf6b09b445decacc84a976
MD5 8bdeef3267ec336c12c44e032b7e4771
BLAKE2b-256 fc45b424a6714e516809ea3a71a2c9d2900fb87ea4e59ca5cc1dc80e51bbb317

See more details on using hashes here.

File details

Details for the file flextabs-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: flextabs-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 36.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.5

File hashes

Hashes for flextabs-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1e37e8de3ff77e31bbf1f5ff1b7af95b9c8bbeaf4b649194f3c8827213a84a23
MD5 89abf76ca71e95b78c8bd510f05e0213
BLAKE2b-256 ce48c5e7c4b8a2723b4c3a2284de18f5cf32717f6ad1c9a34c32951b293ab236

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page