diff options
| -rw-r--r-- | gui.py | 394 | ||||
| -rw-r--r-- | pyproject.toml | 1 | ||||
| -rw-r--r-- | uv.lock | 62 |
3 files changed, 298 insertions, 159 deletions
@@ -1,236 +1,312 @@ -import tkinter as tk -from tkinter import messagebox, ttk, simpledialog -from typing import Iterable, List, Tuple, Dict, Any +from typing import Iterable, List, Tuple, Dict, Any, cast import sounddevice as sd +from PySide6.QtGui import QFont +from PySide6.QtCore import Qt +from PySide6.QtWidgets import ( + QApplication, + QCheckBox, + QComboBox, + QDialog, + QDialogButtonBox, + QFormLayout, + QGroupBox, + QHBoxLayout, + QInputDialog, + QLabel, + QLineEdit, + QMessageBox, + QTabWidget, + QVBoxLayout, + QWidget, +) +class _SettingsDialog(QDialog): + def __init__( + self, + settings: Dict[str, Any], + input_devices: List[Tuple[int, Dict[str, Any]]], + default_settings: Dict[str, Any], + model_choices: Iterable[str], + device_choices: Iterable[str], + compute_choices: Iterable[str], + task_choices: Iterable[str], + ) -> None: + super().__init__() + self.setWindowTitle("Settings") + self.setModal(True) + self.setMinimumWidth(700) -def select_settings( - settings: Dict[str, Any], - input_devices: List[Tuple[int, Dict[str, Any]]], - default_settings: Dict[str, Any], - model_choices: Iterable[str], - device_choices: Iterable[str], - compute_choices: Iterable[str], - task_choices: Iterable[str], -) -> Dict[str, Any]: - if not input_devices: - raise RuntimeError("No audio input devices found.") + self.selected_settings: Dict[str, Any] = {} - def get_value(key: str, fallback: Any) -> Any: - return settings.get(key, default_settings.get(key, fallback)) + def get_value(key: str, fallback: Any) -> Any: + return settings.get(key, default_settings.get(key, fallback)) - root = tk.Tk() - root.title("Settings") - root.resizable(False, False) + self.device_names = [dev["name"] for _idx, dev in input_devices] - notebook = ttk.Notebook(root) - notebook.pack(fill="both", expand=True, padx=10, pady=(10, 0)) + root_layout = QVBoxLayout(self) - # ------------------------------------------------------------------ # - # Tab 1 – Whisper # - # ------------------------------------------------------------------ # - whisper_tab = ttk.Frame(notebook, padding=10) - whisper_tab.columnconfigure(1, weight=1) - notebook.add(whisper_tab, text="Whisper") + tabs = QTabWidget(self) + root_layout.addWidget(tabs) - def add_row(parent: ttk.Frame, row: int, label_text: str, widget: tk.Widget) -> None: - ttk.Label(parent, text=label_text).grid(row=row, column=0, sticky="w", pady=4, padx=(0, 12)) - widget.grid(row=row, column=1, sticky="ew", pady=4) + # Whisper tab + whisper_tab = QWidget(self) + whisper_tab_layout = QVBoxLayout(whisper_tab) - device_options = [ - f"[{idx}] {dev['name']} ({dev.get('max_input_channels', 0)} ch)" - for idx, dev in input_devices - ] - device_names = [dev["name"] for _idx, dev in input_devices] - device_combo = ttk.Combobox(whisper_tab, values=device_options, state="readonly", width=60) - default_device_name = get_value("audio_device_name", "") - if default_device_name in device_names: - device_combo.current(device_names.index(default_device_name)) - else: - device_combo.current(0) - add_row(whisper_tab, 0, "Audio input device:", device_combo) + whisper_layout = QFormLayout() + whisper_layout.setLabelAlignment(Qt.AlignmentFlag.AlignLeft) - model_var = tk.StringVar(value=get_value("model_name", "medium")) - model_combo = ttk.Combobox(whisper_tab, values=list(model_choices), textvariable=model_var) - model_combo.set(model_var.get()) - add_row(whisper_tab, 1, "Model:", model_combo) + device_options = [ + f"[{idx}] {dev['name']} ({dev.get('max_input_channels', 0)} ch)" + for idx, dev in input_devices + ] + self.device_combo = QComboBox(whisper_tab) + self.device_combo.addItems(device_options) + self.device_combo.setEditable(False) + default_device_name = get_value("audio_device_name", "") + if default_device_name in self.device_names: + self.device_combo.setCurrentIndex(self.device_names.index(default_device_name)) + else: + self.device_combo.setCurrentIndex(0) + whisper_layout.addRow(QLabel("Audio input device:"), self.device_combo) - device_type_var = tk.StringVar(value=get_value("device", "cpu")) - device_type_combo = ttk.Combobox( - whisper_tab, values=list(device_choices), textvariable=device_type_var, state="readonly" - ) - device_type_combo.set(device_type_var.get()) - add_row(whisper_tab, 2, "Compute device:", device_type_combo) + self.model_combo = QComboBox(whisper_tab) + self.model_combo.addItems(list(model_choices)) + self.model_combo.setEditable(True) + default_model = str(get_value("model_name", "medium")) + if default_model in [self.model_combo.itemText(i) for i in range(self.model_combo.count())]: + self.model_combo.setCurrentText(default_model) + else: + self.model_combo.setEditText(default_model) + whisper_layout.addRow(QLabel("Model:"), self.model_combo) - compute_type_var = tk.StringVar(value=get_value("compute_type", "int8")) - compute_type_combo = ttk.Combobox(whisper_tab, values=list(compute_choices), textvariable=compute_type_var) - compute_type_combo.set(compute_type_var.get()) - add_row(whisper_tab, 3, "Compute type:", compute_type_combo) + self.device_type_combo = QComboBox(whisper_tab) + self.device_type_combo.addItems(list(device_choices)) + self.device_type_combo.setEditable(False) + default_device_type = str(get_value("device", "cpu")) + if default_device_type in [self.device_type_combo.itemText(i) for i in range(self.device_type_combo.count())]: + self.device_type_combo.setCurrentText(default_device_type) + elif self.device_type_combo.count() > 0: + self.device_type_combo.setCurrentIndex(0) + whisper_layout.addRow(QLabel("Compute device:"), self.device_type_combo) - task_var = tk.StringVar(value=get_value("task", "translate")) - task_combo = ttk.Combobox(whisper_tab, values=list(task_choices), textvariable=task_var, state="readonly") - task_combo.set(task_var.get()) - add_row(whisper_tab, 4, "Task:", task_combo) + self.task_combo = QComboBox(whisper_tab) + self.task_combo.addItems(list(task_choices)) + self.task_combo.setEditable(False) + default_task = str(get_value("task", "translate")) + if default_task in [self.task_combo.itemText(i) for i in range(self.task_combo.count())]: + self.task_combo.setCurrentText(default_task) + elif self.task_combo.count() > 0: + self.task_combo.setCurrentIndex(0) + whisper_layout.addRow(QLabel("Task:"), self.task_combo) - beam_size_var = tk.StringVar(value=str(get_value("beam_size", 3))) - add_row(whisper_tab, 5, "Beam size:", ttk.Entry(whisper_tab, textvariable=beam_size_var, width=10)) + whisper_tab_layout.addLayout(whisper_layout) - language_var = tk.StringVar(value=get_value("language", "")) - add_row(whisper_tab, 6, "Language (optional):", ttk.Entry(whisper_tab, textvariable=language_var)) + whisper_advanced_group = QGroupBox("Advanced settings", whisper_tab) + whisper_advanced_layout = QFormLayout(whisper_advanced_group) + whisper_advanced_layout.setLabelAlignment(Qt.AlignmentFlag.AlignLeft) - context_seconds_var = tk.StringVar(value=str(get_value("context_seconds", 10))) - add_row(whisper_tab, 7, "Context seconds:", ttk.Entry(whisper_tab, textvariable=context_seconds_var, width=10)) + self.compute_type_combo = QComboBox(whisper_tab) + self.compute_type_combo.addItems(list(compute_choices)) + self.compute_type_combo.setEditable(True) + default_compute = str(get_value("compute_type", "int8")) + if default_compute in [self.compute_type_combo.itemText(i) for i in range(self.compute_type_combo.count())]: + self.compute_type_combo.setCurrentText(default_compute) + else: + self.compute_type_combo.setEditText(default_compute) + whisper_advanced_layout.addRow(QLabel("Compute type:"), self.compute_type_combo) - update_interval_var = tk.StringVar(value=str(get_value("update_interval_seconds", 2))) - add_row(whisper_tab, 8, "Update interval (s):", ttk.Entry(whisper_tab, textvariable=update_interval_var, width=10)) + self.beam_size_edit = QLineEdit(str(get_value("beam_size", 3)), whisper_tab) + whisper_advanced_layout.addRow(QLabel("Beam size:"), self.beam_size_edit) - # ------------------------------------------------------------------ # - # Tab 2 – Ollama # - # ------------------------------------------------------------------ # - ollama_tab = ttk.Frame(notebook, padding=10) - ollama_tab.columnconfigure(1, weight=1) - notebook.add(ollama_tab, text="Ollama") + self.language_edit = QLineEdit(str(get_value("language", "")), whisper_tab) + whisper_advanced_layout.addRow(QLabel("Language (optional):"), self.language_edit) - use_ollama_cleanup_var = tk.BooleanVar(value=get_value("use_ollama_cleanup", True)) - add_row(ollama_tab, 0, "LLM subtitle cleanup:", ttk.Checkbutton(ollama_tab, variable=use_ollama_cleanup_var)) + self.context_seconds_edit = QLineEdit(str(get_value("context_seconds", 10)), whisper_tab) + whisper_advanced_layout.addRow(QLabel("Context seconds:"), self.context_seconds_edit) - ollama_device_var = tk.StringVar(value=get_value("ollama_device", "CPU")) - ollama_device_combo = ttk.Combobox( - ollama_tab, values=["CPU", "GPU"], textvariable=ollama_device_var, state="readonly", width=10 - ) - ollama_device_combo.set(ollama_device_var.get()) - add_row(ollama_tab, 1, "Ollama compute:", ollama_device_combo) + self.update_interval_edit = QLineEdit(str(get_value("update_interval_seconds", 2)), whisper_tab) + whisper_advanced_layout.addRow(QLabel("Update interval (s):"), self.update_interval_edit) + + whisper_tab_layout.addWidget(whisper_advanced_group) + tabs.addTab(whisper_tab, "Whisper") + + # Ollama tab + ollama_tab = QWidget(self) + ollama_tab_layout = QVBoxLayout(ollama_tab) + + ollama_layout = QFormLayout() + ollama_layout.setLabelAlignment(Qt.AlignmentFlag.AlignLeft) + + self.use_ollama_cleanup_checkbox = QCheckBox(ollama_tab) + self.use_ollama_cleanup_checkbox.setChecked(bool(get_value("use_ollama_cleanup", True))) + ollama_layout.addRow(QLabel("LLM subtitle cleanup:"), self.use_ollama_cleanup_checkbox) + + self.ollama_device_combo = QComboBox(ollama_tab) + self.ollama_device_combo.addItems(["CPU", "GPU"]) + self.ollama_device_combo.setEditable(False) + default_ollama_device = str(get_value("ollama_device", "CPU")) + if default_ollama_device in [self.ollama_device_combo.itemText(i) for i in range(self.ollama_device_combo.count())]: + self.ollama_device_combo.setCurrentText(default_ollama_device) + ollama_layout.addRow(QLabel("Ollama compute:"), self.ollama_device_combo) + + ollama_tab_layout.addLayout(ollama_layout) + + ollama_advanced_group = QGroupBox("Advanced settings", ollama_tab) + ollama_advanced_layout = QFormLayout(ollama_advanced_group) + ollama_advanced_layout.setLabelAlignment(Qt.AlignmentFlag.AlignLeft) - ollama_context_var = tk.StringVar(value=str(get_value("ollama_context_window", 6))) - add_row(ollama_tab, 2, "Context window (segments):", ttk.Entry(ollama_tab, textvariable=ollama_context_var, width=10)) + self.ollama_context_edit = QLineEdit(str(get_value("ollama_context_window", 6)), ollama_tab) + ollama_advanced_layout.addRow(QLabel("Context window (segments):"), self.ollama_context_edit) - ollama_batch_var = tk.StringVar(value=str(get_value("ollama_raw_batch_size", 3))) - add_row(ollama_tab, 3, "Batch size (lines per LLM call):", ttk.Entry(ollama_tab, textvariable=ollama_batch_var, width=10)) + self.ollama_batch_edit = QLineEdit(str(get_value("ollama_raw_batch_size", 3)), ollama_tab) + ollama_advanced_layout.addRow(QLabel("Batch size (lines per LLM call):"), self.ollama_batch_edit) - # ------------------------------------------------------------------ # - # Buttons # - # ------------------------------------------------------------------ # - button_frame = ttk.Frame(root, padding=(10, 6, 10, 10)) - button_frame.pack(fill="x") + ollama_tab_layout.addWidget(ollama_advanced_group) + tabs.addTab(ollama_tab, "Ollama") - selected_settings: Dict[str, Any] = {} + button_layout = QHBoxLayout() + root_layout.addLayout(button_layout) + button_box = QDialogButtonBox( + QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, + self, + ) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + button_layout.addWidget(button_box) - def on_ok() -> None: - nonlocal selected_settings + def _warn(self, title: str, text: str) -> None: + QMessageBox.warning(self, title, text) - selection = device_combo.current() + def accept(self) -> None: + selection = self.device_combo.currentIndex() if selection < 0: - messagebox.showwarning("Select a device", "Please select an audio input device.") + self._warn("Select a device", "Please select an audio input device.") return - model_name = model_var.get().strip() + model_name = self.model_combo.currentText().strip() if not model_name: - messagebox.showwarning("Model required", "Please select or enter a model name.") + self._warn("Model required", "Please select or enter a model name.") return try: - beam_size = int(beam_size_var.get().strip()) + beam_size = int(self.beam_size_edit.text().strip()) if beam_size <= 0: raise ValueError except ValueError: - messagebox.showwarning("Invalid beam size", "Beam size must be a positive integer.") + self._warn("Invalid beam size", "Beam size must be a positive integer.") return try: - context_seconds = float(context_seconds_var.get().strip()) + context_seconds = float(self.context_seconds_edit.text().strip()) if context_seconds <= 0: raise ValueError except ValueError: - messagebox.showwarning("Invalid context seconds", "Context seconds must be a positive number.") + self._warn("Invalid context seconds", "Context seconds must be a positive number.") return try: - update_interval_seconds = float(update_interval_var.get().strip()) + update_interval_seconds = float(self.update_interval_edit.text().strip()) if update_interval_seconds <= 0: raise ValueError except ValueError: - messagebox.showwarning("Invalid update interval", "Update interval must be a positive number.") + self._warn("Invalid update interval", "Update interval must be a positive number.") return try: - ollama_context_window = int(ollama_context_var.get().strip()) + ollama_context_window = int(self.ollama_context_edit.text().strip()) if ollama_context_window <= 0: raise ValueError except ValueError: - messagebox.showwarning("Invalid context window", "Context window must be a positive integer.") + self._warn("Invalid context window", "Context window must be a positive integer.") return try: - ollama_raw_batch_size = int(ollama_batch_var.get().strip()) + ollama_raw_batch_size = int(self.ollama_batch_edit.text().strip()) if ollama_raw_batch_size <= 0: raise ValueError except ValueError: - messagebox.showwarning("Invalid batch size", "Batch size must be a positive integer.") + self._warn("Invalid batch size", "Batch size must be a positive integer.") return - selected_settings = { - "audio_device_name": device_names[selection], + self.selected_settings = { + "audio_device_name": self.device_names[selection], "model_name": model_name, - "device": device_type_var.get().strip() or "cpu", - "compute_type": compute_type_var.get().strip() or "int8", - "task": task_var.get().strip() or "translate", + "device": self.device_type_combo.currentText().strip() or "cpu", + "compute_type": self.compute_type_combo.currentText().strip() or "int8", + "task": self.task_combo.currentText().strip() or "translate", "beam_size": beam_size, - "language": language_var.get().strip(), + "language": self.language_edit.text().strip(), "context_seconds": context_seconds, "update_interval_seconds": update_interval_seconds, - "use_ollama_cleanup": use_ollama_cleanup_var.get(), - "ollama_device": ollama_device_var.get(), + "use_ollama_cleanup": self.use_ollama_cleanup_checkbox.isChecked(), + "ollama_device": self.ollama_device_combo.currentText(), "ollama_context_window": ollama_context_window, "ollama_raw_batch_size": ollama_raw_batch_size, } - root.quit() + super().accept() - def on_cancel() -> None: - root.quit() - ok_button = ttk.Button(button_frame, text="OK", command=on_ok) - cancel_button = ttk.Button(button_frame, text="Cancel", command=on_cancel) - ok_button.pack(side="left", padx=(0, 6)) - cancel_button.pack(side="left") +def select_settings( + settings: Dict[str, Any], + input_devices: List[Tuple[int, Dict[str, Any]]], + default_settings: Dict[str, Any], + model_choices: Iterable[str], + device_choices: Iterable[str], + compute_choices: Iterable[str], + task_choices: Iterable[str], +) -> Dict[str, Any]: + if not input_devices: + raise RuntimeError("No audio input devices found.") + + app = QApplication.instance() + if app is None: + app = QApplication([]) + app = cast(QApplication, app) + app.setFont(QFont("Calibri", 12)) - root.protocol("WM_DELETE_WINDOW", on_cancel) - root.mainloop() - root.destroy() + dialog = _SettingsDialog( + settings=settings, + input_devices=input_devices, + default_settings=default_settings, + model_choices=model_choices, + device_choices=device_choices, + compute_choices=compute_choices, + task_choices=task_choices, + ) + result = dialog.exec() - if not selected_settings: + if result != int(QDialog.DialogCode.Accepted) or not dialog.selected_settings: raise SystemExit("No settings selected.") - return selected_settings + return dialog.selected_settings + + +def prompt_input_sample_rate(device_index: int, common_rates: Iterable[int]) -> int: + rates = list(common_rates) + while True: + prompt = ( + "Enter an input sample rate in Hz.\n" + f"Common values: {', '.join(str(r) for r in rates)}" + ) + raw, ok = QInputDialog.getText(None, "Select Sample Rate", prompt) + if not ok: + raise sd.PortAudioError("No supported input sample rate found for selected device.") + raw = raw.strip() + if not raw: + continue -def prompt_input_sample_rate(device_index: int, common_rates: Iterable[int] | None = None) -> int: - rates = list(common_rates) if common_rates is not None else [48000, 44100, 32000, 24000, 22050, 16000, 12000, 8000] - root = tk.Tk() - root.withdraw() - try: - while True: - prompt = ( - "Enter an input sample rate in Hz.\n" - f"Common values: {', '.join(str(r) for r in rates)}" + try: + rate = int(float(raw)) + except ValueError: + QMessageBox.warning(None, "Invalid value", "Sample rate must be a number.") + continue + + try: + sd.check_input_settings(device=device_index, channels=1, samplerate=rate, dtype="float32") + return rate + except sd.PortAudioError: + QMessageBox.warning( + None, + "Unsupported sample rate", + f"{rate} Hz is not supported by the selected device.", ) - raw = simpledialog.askstring("Select Sample Rate", prompt, parent=root) - if raw is None: - raise sd.PortAudioError("No supported input sample rate found for selected device.") - raw = raw.strip() - if not raw: - continue - try: - rate = int(float(raw)) - except ValueError: - messagebox.showwarning("Invalid value", "Sample rate must be a number.", parent=root) - continue - try: - sd.check_input_settings(device=device_index, channels=1, samplerate=rate, dtype="float32") - return rate - except sd.PortAudioError: - messagebox.showwarning( - "Unsupported sample rate", - f"{rate} Hz is not supported by the selected device.", - parent=root, - ) - finally: - root.destroy() diff --git a/pyproject.toml b/pyproject.toml index 0f28a93..0b7da0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,5 +9,6 @@ dependencies = [ "flask>=3.1.3", "flask-cors>=6.0.2", "ollama>=0.6.1", + "pyside6>=6.11.0", "sounddevice>=0.5.5", ] @@ -41,6 +41,7 @@ dependencies = [ { name = "flask" }, { name = "flask-cors" }, { name = "ollama" }, + { name = "pyside6" }, { name = "sounddevice" }, ] @@ -50,6 +51,7 @@ requires-dist = [ { name = "flask", specifier = ">=3.1.3" }, { name = "flask-cors", specifier = ">=6.0.2" }, { name = "ollama", specifier = ">=0.6.1" }, + { name = "pyside6", specifier = ">=6.11.0" }, { name = "sounddevice", specifier = ">=0.5.5" }, ] @@ -666,6 +668,54 @@ wheels = [ ] [[package]] +name = "pyside6" +version = "6.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyside6-addons" }, + { name = "pyside6-essentials" }, + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/95/f3f5a2799163b6658126d78a85bc1dec9eda88c75c26780556b26071a1d8/pyside6-6.11.0-cp310-abi3-macosx_13_0_universal2.whl", hash = "sha256:1f2735dc4f2bd4ec452ae50502c8a22128bba0aced35358a2bbc58384b820c6f", size = 571544, upload-time = "2026-03-23T12:47:20.263Z" }, + { url = "https://files.pythonhosted.org/packages/da/89/9a1f521051714e6694ebbe2b979ded279845ec8e25cb309ca3960158d74f/pyside6-6.11.0-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c642e2d25704ca746fd37f56feacf25c5aecc4cd40bef23d18eec81f87d9dc00", size = 571725, upload-time = "2026-03-23T12:47:21.727Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/f779d8bba00fcde31a7d7fb6b59347a70773c9cc8135592dea9972579877/pyside6-6.11.0-cp310-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:267b344c73580ac938ca63c611881fb42a3922ebfe043e271005f4f06c372c4e", size = 571722, upload-time = "2026-03-23T12:47:22.761Z" }, + { url = "https://files.pythonhosted.org/packages/ac/98/150e01a026df3e9697310236821fa825319bb4b9d6137539cb25a3032968/pyside6-6.11.0-cp310-abi3-win_amd64.whl", hash = "sha256:9092cb002ca43c64006afb2e0d0f6f51aef17aa737c33a45e502326a081ddcbc", size = 577988, upload-time = "2026-03-23T12:47:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/50/e7/55960f7c6b41d058e95cb4af02652c46c48702c506c8bbf12e99550e1fb3/pyside6-6.11.0-cp310-abi3-win_arm64.whl", hash = "sha256:b15f39acc2b8f46251a630acad0d97f9a0a0461f2baffcd66d7adfada8eb641e", size = 561372, upload-time = "2026-03-23T12:47:25.073Z" }, +] + +[[package]] +name = "pyside6-addons" +version = "6.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyside6-essentials" }, + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/df/241f311c61a46b7b1195927da77b2537692ee3442aa9ccd87981164ff78d/pyside6_addons-6.11.0-cp310-abi3-macosx_13_0_universal2.whl", hash = "sha256:d5eaa4643302e3a0fa94c5766234bee4073d7d5ab9c2b7fd222692a176faf182", size = 331554157, upload-time = "2026-03-23T12:40:40.497Z" }, + { url = "https://files.pythonhosted.org/packages/31/b9/e81172835ccc9d8b9792cc6bf7524a252a0db9a76ddd693de230402697f9/pyside6_addons-6.11.0-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ac6fe3d4ef4497dde3efc5e896b0acd53ff6c93be4bf485f045690f919419f35", size = 174948482, upload-time = "2026-03-23T12:41:05.379Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/426d9333782bf65ab2a20257d6b4b3af9b8d5d7a710da719865fab49d492/pyside6_addons-6.11.0-cp310-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:8ffb40222456078930816ebcac2f2511716d2acbc11716dd5acc5c365179a753", size = 170430798, upload-time = "2026-03-23T12:41:38.134Z" }, + { url = "https://files.pythonhosted.org/packages/35/9a/46d271fedfabad8c6dce2ebb69bb593745487ed33753a56a47c3ba4fdb1c/pyside6_addons-6.11.0-cp310-abi3-win_amd64.whl", hash = "sha256:413e6121c24f5ffdce376298059eddecff74aa6d638e94e0f6015b33d29b889e", size = 168723088, upload-time = "2026-03-23T12:42:00.668Z" }, + { url = "https://files.pythonhosted.org/packages/16/cd/1b28264f7dc9a642da2e4e7c02f67418d0949eb7ce329ae20869703c2630/pyside6_addons-6.11.0-cp310-abi3-win_arm64.whl", hash = "sha256:aaaee83385977a0fe134b2f4fbfb92b45a880d5b656e4d90a708eef10b1b6de8", size = 35698324, upload-time = "2026-03-23T12:42:13.748Z" }, +] + +[[package]] +name = "pyside6-essentials" +version = "6.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/00/8a8583d3429c737cc20e61a43eba8ab1ec13ddb101e99802c2ffeedf3b41/pyside6_essentials-6.11.0-cp310-abi3-macosx_13_0_universal2.whl", hash = "sha256:85d6ca87ef35fa6565d385ede72ae48420dd3f63113929d10fc800f6b0360e01", size = 108085251, upload-time = "2026-03-23T12:42:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a9/07c9e5c014b871c1b19caf8f994bcd50b345559b81f81671217b49559b67/pyside6_essentials-6.11.0-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:dc20e7afd5fc6fe51297db91cef997ce60844be578f7a49fc61b7ab9657a8849", size = 78316055, upload-time = "2026-03-23T12:43:04.19Z" }, + { url = "https://files.pythonhosted.org/packages/7c/35/f06b1b641d7600ec46374c16cd37c66fa4a22870326b4eb073a95471035f/pyside6_essentials-6.11.0-cp310-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:4854cb0a1b061e7a576d8fb7bb7cf9f49540d558b1acb7df0742a7afefe61e4e", size = 77380821, upload-time = "2026-03-23T12:43:24.649Z" }, + { url = "https://files.pythonhosted.org/packages/ff/37/ba95c6262836d2b286b4e05a9d16a5e870995d5d2503ac6adc6312208049/pyside6_essentials-6.11.0-cp310-abi3-win_amd64.whl", hash = "sha256:3b3362882ad9389357a80504e600180006a957731fec05786fced7b038461fdf", size = 75793322, upload-time = "2026-03-23T12:43:35.575Z" }, + { url = "https://files.pythonhosted.org/packages/53/27/d17f25e45820e633a70e6109b35991eda09a5e8000c2a306f0ab7538d48c/pyside6_essentials-6.11.0-cp310-abi3-win_arm64.whl", hash = "sha256:81ca603dbf21bc39f89bb42db215c25ebe0c879a1a4c387625c321d2730ec187", size = 56337457, upload-time = "2026-03-23T12:43:43.573Z" }, +] + +[[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } @@ -733,6 +783,18 @@ wheels = [ ] [[package]] +name = "shiboken6" +version = "6.11.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/1d/b56b7b694fbc871496435488d1f41c5068de546334850d722756511cef65/shiboken6-6.11.0-cp310-abi3-macosx_13_0_universal2.whl", hash = "sha256:d88e8a1eb705f2b9ad21db08a61ae1dc0c773e5cd86a069de0754c4cf1f9b43b", size = 476085, upload-time = "2026-03-23T12:47:05.724Z" }, + { url = "https://files.pythonhosted.org/packages/65/cb/4bb0c76011166230daa7c0074aeb3fdb3935c83ac1fef3789b85fcd1a8fc/shiboken6-6.11.0-cp310-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ad54e64f8192ddbdff0c54ac82b89edcd62ed623f502ea21c960541d19514053", size = 271055, upload-time = "2026-03-23T12:47:07.349Z" }, + { url = "https://files.pythonhosted.org/packages/f5/96/771a6e2b530f725303d16d78a321fa4876b98b4f3615c9851880df8c1a43/shiboken6-6.11.0-cp310-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:a10dc7718104ea2dc15d5b0b96909b77162ce1c76fcc6968e6df692b947a00e9", size = 267456, upload-time = "2026-03-23T12:47:08.689Z" }, + { url = "https://files.pythonhosted.org/packages/72/f7/44c0c42c3f5f29dec457fd46ea0552174bcb8aa75becf03bbd90308ba07b/shiboken6-6.11.0-cp310-abi3-win_amd64.whl", hash = "sha256:483ff78a73c7b3189ca924abc694318084f078bcfeaffa68e32024ff2d025ee1", size = 1222132, upload-time = "2026-03-23T12:47:10.143Z" }, + { url = "https://files.pythonhosted.org/packages/fb/99/6e5ee21db2d6af84bbbd7d871d441dafeb069c6de5667b1aa49891a77c66/shiboken6-6.11.0-cp310-abi3-win_arm64.whl", hash = "sha256:3bd76cf56105ab2d62ecaff630366f11264f69b88d488f10f048da9a065781f4", size = 1783186, upload-time = "2026-03-23T12:47:11.832Z" }, +] + +[[package]] name = "sounddevice" version = "0.5.5" source = { registry = "https://pypi.org/simple" } |
