Metadata-Version: 2.4
Name: qtwrap
Version: 1.0.2
Summary: A PyQt6 toolkit for wrapping scripts in a GUI with live stdout capture
Project-URL: Homepage, https://gitlab.com/malomr/qtwrap
Author-email: malomr <malomr@proton.me>
License-Expression: MIT
License-File: LICENSE
Keywords: gui,pyqt6,stdout
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.12
Requires-Dist: pyqt6==6.11.0
Description-Content-Type: text/markdown

# qtwrap

A PyQt6 toolkit for wrapping standard Python scripts in a GUI.

## Requirements

- Python >= 3.12

## Installation

```shell
pip install qtwrap
```
or, to add it to a `uv`-managed project:
```shell
uv add qtwrap
```

## Quick Start

A simple window allowing to capture the `stdout` and `stderr` of the given function with Exception handling.

```python
import time
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QTextEdit, QVBoxLayout, QComboBox
from qtwrap import LogLevel, DirectorySelector, ChoiceSelector, ScriptCaptureWindow

class MainWindow(ScriptCaptureWindow):
    def __init__(self, func):
        super().__init__(func)

        self.setWindowTitle("MainWindow")
        self.setGeometry(100, 100, 800, 600)

        widget = QWidget()
        layout = QVBoxLayout()

        self.__output = QTextEdit()
        self.__dir = DirectorySelector()
        self.__choice = ChoiceSelector("one", "two", "three")
        self.__btn = QPushButton("Start")

        layout.addWidget(self.__dir)
        layout.addWidget(self.__choice)
        layout.addWidget(self.__btn)
        layout.addWidget(self.__output)
        widget.setLayout(layout)

        self.setCentralWidget(widget)
        self.set_output_field(self.__output)
        self.__btn.clicked.connect(self.__start)

        self.show()

    def __args(self):
        return {
            "path": self.__dir.path(),
            "choices": self.__choice.choice()
        }

    def __start(self):
        if len(self.__dir.path()) == 0:
            self.status("The path must be set.", LogLevel.ERROR)
            return

        if len(self.__choice.choice()) == 0:
            self.status("Make a choice.", LogLevel.ERROR)
            return

        self._started(kwargs=self.__args())

def f(path: str, choices: list):
    for _ in range(5):
        print(f"{path=} | {choices=}")
        time.sleep(0.3)
    print(1/0)

app = QApplication([])
window = MainWindow(f)
app.exec()
```

## Features

- **Live output capture** — `stdout` and `stderr` from the wrapped function are streamed into the GUI as they're produced, including output from background `threading.Thread`s spawned inside it, not just the main call.
- **Non-blocking execution** — the wrapped function runs on a separate thread, so the GUI stays responsive while it works.
- **Automatic input locking** — all input widgets are disabled while the script runs and re-enabled automatically once it finishes, without any manual wiring per widget.
- **Exception handling** — unhandled exceptions, including ones raised inside child threads, are caught and displayed in the output field with a full traceback instead of crashing or printing to the console.
- **Ready-made input widgets** — `DirectorySelector`, `FileSelector`, and `ChoiceSelector` cover common script inputs out of the box.
- **Status bar & output saving** — built-in status messages for validation/errors, plus a menu action to save the captured output to a file.

## License

This project is licensed under the terms of the [LICENSE](LICENSE) file included in this repository.