Initial commit: RecorderStudio:PyQt5 高保真录音软件,无损 WAV / WASAPI 独占、BS.1770 响度与 ffmpeg 交叉验证

This commit is contained in:
WpyQwq
2026-09-19 11:56:01 +08:00
commit b7f9098ccd
62 changed files with 10796 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
"""图形界面入口:``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())