tsignal-python

TSignal

TSignal is a lightweight, pure-Python signal/slot library that provides thread-safe, asyncio-compatible event handling inspired by the Qt signal/slot pattern—but without the heavyweight Qt dependencies. It enables clean decoupling of components, seamless thread-to-thread communication, and flexible asynchronous/synchronous slot handling.

Key Features

Why TSignal?

Modern Python applications often rely on asynchronous operations and multi-threading. Traditional event frameworks either require large external dependencies or lack seamless async/thread support. TSignal provides:

Key Benefits

Async-Ready

Thread-Safe by Design

Flexible Slots

Robust Testing & Examples

Installation

TSignal requires Python 3.10 or higher.

git clone https://github.com/TSignalDev/tsignal-python.git
cd tsignal-python
pip install -e .

For development (includes tests and linting tools):

pip install -e ".[dev]

Quick Start

Basic Example

from tsignal import t_with_signals, t_signal, t_slot

@t_with_signals
class Counter:
    def __init__(self):
        self.count = 0
    
    @t_signal
    def count_changed(self):
        pass
    
    def increment(self):
        self.count += 1
        self.count_changed.emit(self.count)

@t_with_signals
class Display:
    @t_slot
    async def on_count_changed(self, value):
        print(f"Count is now: {value}")

# Connect and use
counter = Counter()
display = Display()
counter.count_changed.connect(display, display.on_count_changed)
counter.increment()  # Will print: "Count is now: 1"

Asynchronous Slot Example

@t_with_signals
class AsyncDisplay:
    @t_slot
    async def on_count_changed(self, value):
        await asyncio.sleep(1)  # Simulate async operation
        print(f"Count updated to: {value}")

# Usage in async context
async def main():
    counter = Counter()
    display = AsyncDisplay()
    
    counter.count_changed.connect(display, display.on_count_changed)
    counter.increment()
    
    # Wait for async processing
    await asyncio.sleep(1.1)

asyncio.run(main())

Core Concepts

Signals and Slots

Thread Safety and Connection Types

TSignal automatically detects whether the signal emission and slot execution occur in the same thread or different threads:

This mechanism frees you from manually dispatching calls across threads.

Worker Threads

For background work, TSignal provides a @t_with_worker decorator that:

Worker Example

from tsignal import t_with_worker, t_signal

@t_with_worker
class DataProcessor:
    @t_signal
    def processing_done(self):
        """Emitted when processing completes"""

    async def run(self, *args, **kwargs):
        # The main entry point for the worker thread’s event loop
        # Wait for tasks or stopping signal
        await self._tsignal_stopping.wait()

    async def process_data(self, data):
        # Perform heavy computation in the worker thread
        result = await heavy_computation(data)
        self.processing_done.emit(result)

processor = DataProcessor()
processor.start()

# Queue a task to run in the worker thread:
processor.queue_task(processor.process_data(some_data))

# Stop the worker gracefully
processor.stop()

From Basics to Practical Use Cases

We’ve expanded TSignal’s examples to guide you from simple demos to full-fledged applications. Each example has its own GitHub link with fully commented code.

For detailed explanations, code walkthroughs, and architecture diagrams of these examples, check out our Examples Documentation.

Basic Signal/Slot Examples

Multi-Threading and Workers

Stock Monitor (Console & GUI)

Stock Monitor Console

Stock Monitor Console: Real-time price updates, alert configuration, and notification history in action

Stock Monitor UI

Stock Monitor UI: Real-time price updates, alert configuration, and notification history in action

Together, these examples highlight TSignal’s versatility—covering everything from quick demos to production-like patterns with threads, queues, and reactive UI updates.

Documentation and Example

Get Started

  1. Visit the GitHub Repository
    • Find installation instructions, usage docs, and a variety of real-world examples.
  2. Try the Python Release
    • Run pip install tsignal (requires Python 3.10+).
    • Check out the docs and examples in the repository’s examples/ folder.
  3. Contribute / Give Feedback
    • We appreciate your thoughts on the current Python version.
    • We welcome contributions! Please read our Contributing Guidelines before submitting PRs.

License

TSignal is licensed under the MIT License. See LICENSE for details.