pyTermTk

A Python terminal toolkit library. Provides a Qt-like API with layout managers and signal-slot functionality as a full-fledged TUI framework.

TUIQt-likeSignalsSlotsLayoutFramework

GitHub Overview

ceccopierangiolieugenio/pyTermTk

Python Terminal Toolkit - a Spiced Up Cross Compatible TUI Library 🌶️

Stars744
Watchers7
Forks31
Created:February 16, 2021
Language:Python
License:MIT License

Topics

asciiascii-graphicslibrarypyodidepytermtkpythonterminalterminal-emulatorterminal-graphicsterminal-multiplexertext-editortuitui-apptui-editortui-librarytutorialuiui-designui-libraryuser-interface

Star History

ceccopierangiolieugenio/pyTermTk Star History
Data as of: 7/25/2025, 06:23 AM

pyTermTk

pyTermTk is a Python terminal toolkit library. It adopts a Qt-like API and provides signal-slot functionality, layout managers, and event-driven programming as a full-fledged TUI framework. Its design is familiar to desktop application developers.

Key Features

Qt-like API

  • Familiar Design: Qt-like API reduces learning costs
  • Object-Oriented: Class-based design philosophy
  • Consistency: Similar method names and structure to Qt

Signal-Slot System

  • Event-Driven: Loose coupling through signals and slots
  • Custom Signals: Can define custom signals
  • Asynchronous Processing: Efficient event handling

Layout Management

  • Automatic Placement: Dynamic widget placement
  • Size Adjustment: Automatic size calculation
  • Nested Structure: Construction of complex layouts

Installation

pip install pyTermTk

Basic Usage

Simple Application

#!/usr/bin/env python3

import TermTk as ttk

root = ttk.TTk()

# Create main window
win = ttk.TTkWindow(parent=root, pos=(1,1), size=(50,20), title="Hello pyTermTk")

# Add label widget
label = ttk.TTkLabel(parent=win, pos=(2,2), text="Hello, World!")

# Add button
button = ttk.TTkButton(parent=win, pos=(2,4), text="Click Me!")

def button_clicked():
    label.setText("Button Clicked!")

# Connect signal-slot
button.clicked.connect(button_clicked)

root.mainloop()

Using Layout Managers

#!/usr/bin/env python3

import TermTk as ttk

root = ttk.TTk()

# Main window
win = ttk.TTkWindow(parent=root, pos=(1,1), size=(60,25), title="Layout Example")

# Vertical layout
vbox = ttk.TTkVBoxLayout()
win.setLayout(vbox)

# Header
header = ttk.TTkLabel(text="pyTermTk Layout Example")
vbox.addWidget(header)

# Horizontal layout
hbox = ttk.TTkHBoxLayout()

# Left panel
left_panel = ttk.TTkFrame()
left_layout = ttk.TTkVBoxLayout()
left_panel.setLayout(left_layout)

left_layout.addWidget(ttk.TTkLabel(text="Left Panel"))
left_layout.addWidget(ttk.TTkButton(text="Button 1"))
left_layout.addWidget(ttk.TTkButton(text="Button 2"))

# Right panel
right_panel = ttk.TTkFrame()
right_layout = ttk.TTkVBoxLayout()
right_panel.setLayout(right_layout)

right_layout.addWidget(ttk.TTkLabel(text="Right Panel"))
right_layout.addWidget(ttk.TTkLineEdit())
right_layout.addWidget(ttk.TTkTextEdit())

hbox.addWidget(left_panel)
hbox.addWidget(right_panel)

vbox.addLayout(hbox)

# Footer
footer = ttk.TTkLabel(text="Status: Ready")
vbox.addWidget(footer)

root.mainloop()

Advanced Features

Custom Widgets

#!/usr/bin/env python3

import TermTk as ttk

class CounterWidget(ttk.TTkWidget):
    # Custom signal definition
    valueChanged = ttk.pyTTkSignal(int)
    
    def __init__(self, initial_value=0, **kwargs):
        super().__init__(**kwargs)
        self._value = initial_value
        
        # Layout setup
        layout = ttk.TTkHBoxLayout()
        self.setLayout(layout)
        
        # Components
        self._label = ttk.TTkLabel(text=f"Count: {self._value}")
        self._dec_btn = ttk.TTkButton(text="-")
        self._inc_btn = ttk.TTkButton(text="+")
        
        layout.addWidget(self._dec_btn)
        layout.addWidget(self._label)
        layout.addWidget(self._inc_btn)
        
        # Signal connections
        self._inc_btn.clicked.connect(self._increment)
        self._dec_btn.clicked.connect(self._decrement)
    
    def _increment(self):
        self._value += 1
        self._update_display()
        self.valueChanged.emit(self._value)
    
    def _decrement(self):
        self._value -= 1
        self._update_display()
        self.valueChanged.emit(self._value)
    
    def _update_display(self):
        self._label.setText(f"Count: {self._value}")
    
    def value(self):
        return self._value
    
    def setValue(self, value):
        self._value = value
        self._update_display()
        self.valueChanged.emit(self._value)

# Using custom widget
root = ttk.TTk()
win = ttk.TTkWindow(parent=root, pos=(1,1), size=(50,15), title="Custom Widget")

layout = ttk.TTkVBoxLayout()
win.setLayout(layout)

counter = CounterWidget(initial_value=10)
status_label = ttk.TTkLabel(text="Value: 10")

def on_value_changed(value):
    status_label.setText(f"Value: {value}")

counter.valueChanged.connect(on_value_changed)

layout.addWidget(counter)
layout.addWidget(status_label)

root.mainloop()

Tabs and Menus

#!/usr/bin/env python3

import TermTk as ttk

root = ttk.TTk()

# Main window
win = ttk.TTkWindow(parent=root, pos=(1,1), size=(70,30), title="Tabs and Menus")

# Menu bar
menubar = ttk.TTkMenuBar(parent=win)

# File menu
file_menu = menubar.addMenu("File")
file_menu.addAction("New").triggered.connect(lambda: print("New file"))
file_menu.addAction("Open").triggered.connect(lambda: print("Open file"))
file_menu.addSeparator()
file_menu.addAction("Exit").triggered.connect(root.quit)

# Edit menu
edit_menu = menubar.addMenu("Edit")
edit_menu.addAction("Copy").triggered.connect(lambda: print("Copy"))
edit_menu.addAction("Paste").triggered.connect(lambda: print("Paste"))

# Tab widget
tab_widget = ttk.TTkTabWidget(parent=win, pos=(0,1), size=(70,28))

# Tab 1: Text editor
tab1 = ttk.TTkWidget()
tab1_layout = ttk.TTkVBoxLayout()
tab1.setLayout(tab1_layout)

text_edit = ttk.TTkTextEdit()
tab1_layout.addWidget(ttk.TTkLabel(text="Text Editor Tab"))
tab1_layout.addWidget(text_edit)

tab_widget.addTab(tab1, "Editor")

# Tab 2: Settings
tab2 = ttk.TTkWidget()
tab2_layout = ttk.TTkVBoxLayout()
tab2.setLayout(tab2_layout)

tab2_layout.addWidget(ttk.TTkLabel(text="Settings Tab"))
tab2_layout.addWidget(ttk.TTkCheckbox(text="Enable feature A"))
tab2_layout.addWidget(ttk.TTkCheckbox(text="Enable feature B"))

combo = ttk.TTkComboBox()
combo.addItems(["Option 1", "Option 2", "Option 3"])
tab2_layout.addWidget(combo)

tab_widget.addTab(tab2, "Settings")

# Tab 3: Information
tab3 = ttk.TTkWidget()
tab3_layout = ttk.TTkVBoxLayout()
tab3.setLayout(tab3_layout)

tab3_layout.addWidget(ttk.TTkLabel(text="Information Tab"))
tab3_layout.addWidget(ttk.TTkLabel(text="pyTermTk Version: 0.x.x"))
tab3_layout.addWidget(ttk.TTkLabel(text="Platform: Terminal"))

tab_widget.addTab(tab3, "Info")

root.mainloop()

Dialogs and Popups

#!/usr/bin/env python3

import TermTk as ttk

class MainWindow(ttk.TTkWindow):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        
        layout = ttk.TTkVBoxLayout()
        self.setLayout(layout)
        
        # Button group
        msg_btn = ttk.TTkButton(text="Show Message")
        input_btn = ttk.TTkButton(text="Show Input Dialog")
        confirm_btn = ttk.TTkButton(text="Show Confirmation")
        
        layout.addWidget(ttk.TTkLabel(text="Dialog Examples"))
        layout.addWidget(msg_btn)
        layout.addWidget(input_btn)
        layout.addWidget(confirm_btn)
        
        # Result display
        self.result_label = ttk.TTkLabel(text="Result will appear here")
        layout.addWidget(self.result_label)
        
        # Signal connections
        msg_btn.clicked.connect(self.show_message)
        input_btn.clicked.connect(self.show_input_dialog)
        confirm_btn.clicked.connect(self.show_confirmation)
    
    def show_message(self):
        dialog = ttk.TTkMessageBox(
            parent=self,
            title="Information",
            text="This is a message dialog!"
        )
        dialog.exec()
        self.result_label.setText("Message dialog shown")
    
    def show_input_dialog(self):
        dialog = ttk.TTkInputDialog(
            parent=self,
            title="Input",
            label="Enter your name:"
        )
        
        if dialog.exec() == ttk.TTkK.Accepted:
            text = dialog.textValue()
            self.result_label.setText(f"Input: {text}")
        else:
            self.result_label.setText("Input canceled")
    
    def show_confirmation(self):
        dialog = ttk.TTkMessageBox(
            parent=self,
            title="Confirmation",
            text="Are you sure?",
            standardButtons=ttk.TTkMessageBox.Yes | ttk.TTkMessageBox.No
        )
        
        result = dialog.exec()
        if result == ttk.TTkMessageBox.Yes:
            self.result_label.setText("Confirmed: Yes")
        else:
            self.result_label.setText("Confirmed: No")

root = ttk.TTk()
win = MainWindow(parent=root, pos=(1,1), size=(60,20), title="Dialog Example")
root.mainloop()

Comparison with Other Libraries

FeaturepyTermTkTextualUrwidPyTermGUI
API DesignQt-likeCustomCustomYAML-like
LayoutAutomaticCSS-likeManualConfiguration
Signal-Slot×××
Learning CurveLow for Qt usersMediumHighMedium
Feature RichnessHighHighHighMedium

Use Cases

  • Qt Porting: Terminal versions of Qt applications
  • Complex TUI Apps: Feature-rich terminal applications
  • Development Tools: IDE-like terminal tools
  • Management Interfaces: TUI tools for system administration

Community and Support

  • Development: Active ongoing development
  • Documentation: Comprehensive API reference
  • Examples: Samples for Qt-experienced developers
  • Qt Compatibility: High affinity with Qt API

pyTermTk is a modern framework that provides a familiar API for Qt developers and enables serious TUI application development.