"""图形界面入口:``python -m recorder.gui``(或双击 启动录音机.bat)。 负责三件事:High-DPI 适配、深色主题样式表、以及缺依赖时的友好提示。 """ from __future__ import annotations import sys QSS = """ * { font-family: "Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI", sans-serif; } QWidget { background: #0F1218; color: #E8ECF4; font-size: 12px; } QMainWindow, QDialog { background: #0F1218; } QMenuBar { background: #12161D; border-bottom: 1px solid #2A313D; } QMenuBar::item { padding: 5px 12px; background: transparent; } QMenuBar::item:selected { background: #1D222C; border-radius: 4px; } QMenu { background: #161A22; border: 1px solid #2A313D; padding: 4px; } QMenu::item { padding: 6px 22px; border-radius: 4px; } QMenu::item:selected { background: #23303F; } QGroupBox { background: #161A22; border: 1px solid #2A313D; border-radius: 8px; margin-top: 12px; padding-top: 8px; font-weight: 600; } QGroupBox::title { subcontrol-origin: margin; left: 12px; padding: 0 6px; color: #9FB0CB; } QFrame#card { background: #161A22; border: 1px solid #2A313D; border-radius: 10px; } QLabel { background: transparent; } QPushButton { background: #232A36; border: 1px solid #323A48; border-radius: 6px; padding: 6px 12px; color: #E8ECF4; } QPushButton:hover { background: #2A3341; border-color: #3E4A5C; } QPushButton:pressed { background: #1B2129; } QPushButton:disabled { color: #5C6577; background: #1A1F27; border-color: #262D38; } QComboBox, QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit { background: #1B2029; border: 1px solid #2E3644; border-radius: 6px; padding: 5px 8px; selection-background-color: #2C5F9E; } QComboBox:hover, QLineEdit:hover, QSpinBox:hover, QDoubleSpinBox:hover { border-color: #3E4A5C; } QComboBox::drop-down { border: none; width: 18px; } QComboBox QAbstractItemView { background: #1B2029; border: 1px solid #2E3644; selection-background-color: #23303F; outline: none; } QComboBox::down-arrow { image: none; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid #8B94A7; margin-right: 6px; } QCheckBox { spacing: 7px; } QCheckBox::indicator { width: 15px; height: 15px; border: 1px solid #3A4352; border-radius: 4px; background: #1B2029; } QCheckBox::indicator:checked { background: #4EA1FF; border-color: #4EA1FF; } QSlider::groove:horizontal { height: 4px; background: #2A313D; border-radius: 2px; } QSlider::sub-page:horizontal { background: #4EA1FF; border-radius: 2px; } QSlider::handle:horizontal { background: #E8ECF4; width: 14px; margin: -6px 0; border-radius: 7px; } QScrollArea { background: transparent; border: none; } QScrollBar:vertical { background: #12161D; width: 10px; border-radius: 5px; } QScrollBar::handle:vertical { background: #333B49; border-radius: 5px; min-height: 30px; } QScrollBar::handle:vertical:hover { background: #414B5C; } QScrollBar::add-line, QScrollBar::sub-line { height: 0; } QStatusBar { background: #12161D; border-top: 1px solid #2A313D; color: #8B94A7; } QStatusBar QLabel { color: #8B94A7; } QSplitter::handle { background: transparent; width: 6px; } QProgressBar { background: #1B2029; border: 1px solid #2E3644; border-radius: 6px; text-align: center; color: #E8ECF4; height: 16px; } QProgressBar::chunk { background: #4EA1FF; border-radius: 5px; } QToolTip { background: #1B2029; color: #E8ECF4; border: 1px solid #3E4A5C; padding: 4px 6px; border-radius: 4px; } QTabBar::tab { background: #1B2029; padding: 6px 14px; border: 1px solid #2E3644; border-bottom: none; border-top-left-radius: 6px; border-top-right-radius: 6px; } QTabBar::tab:selected { background: #232A36; } """ def _check_dependencies() -> str | None: """返回缺失依赖的说明,全部就绪时返回 None。""" missing = [] try: import numpy # noqa: F401 (仅用于探测依赖是否就绪) except Exception: missing.append("numpy(数值运算)") try: import PyQt5 # noqa: F401 (仅用于探测依赖是否就绪) except Exception: missing.append("PyQt5(图形界面)") from . import engine if engine.sd is None: missing.append("sounddevice(PortAudio 音频后端)") if not missing: return None return ("缺少以下依赖,无法启动图形界面:\n\n · " + "\n · ".join(missing) + "\n\n请运行:python install_deps.py\n" "(在项目目录下执行,会把依赖装到项目内的 _vendor 目录)") def run(argv: list[str] | None = None) -> int: argv = list(sys.argv if argv is None else argv) problem = _check_dependencies() if problem: print(problem, file=sys.stderr) try: from PyQt5.QtWidgets import QApplication as _QA, QMessageBox except Exception: return 2 _app = _QA(argv) QMessageBox.critical(None, "缺少依赖", problem) return 2 from PyQt5.QtCore import Qt from PyQt5.QtGui import QFont from PyQt5.QtWidgets import QApplication # High-DPI 相关属性必须在 QApplication 实例化之前设置 QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) from .window import MainWindow app = QApplication(argv) app.setApplicationName("RecorderStudio") app.setOrganizationName("RecorderStudio") app.setStyleSheet(QSS) app.setFont(QFont("Microsoft YaHei UI", 9)) win = MainWindow() win.show() return app.exec_() if __name__ == "__main__": sys.exit(run())