[feature] 初始化仓库
This commit is contained in:
commit
9145e72263
5
.cursorrules
Normal file
5
.cursorrules
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
You are an expert in Python, Pysie6 application development.
|
||||||
|
Key Principles
|
||||||
|
- Use precise, efficient, and concise Python code to develop Pyside6 applications.
|
||||||
|
- Follow the principle of minimal changes for each modification.
|
||||||
|
- Prioritize readability and maintainability.
|
||||||
8
Avas_System.code-workspace
Normal file
8
Avas_System.code-workspace
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
BIN
__pycache__/Ui_widget.cpython-313.pyc
Normal file
BIN
__pycache__/Ui_widget.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/audio_filter_widget.cpython-313.pyc
Normal file
BIN
__pycache__/audio_filter_widget.cpython-313.pyc
Normal file
Binary file not shown.
BIN
data/database.db
Normal file
BIN
data/database.db
Normal file
Binary file not shown.
0
database/__init__.py
Normal file
0
database/__init__.py
Normal file
BIN
database/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
database/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
database/__pycache__/db_manager.cpython-313.pyc
Normal file
BIN
database/__pycache__/db_manager.cpython-313.pyc
Normal file
Binary file not shown.
BIN
database/__pycache__/models.cpython-313.pyc
Normal file
BIN
database/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
165
database/db_manager.py
Normal file
165
database/db_manager.py
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import sqlite3
|
||||||
|
from typing import List, Optional, Tuple
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
from .models import FilterData, ParamData, ConfigData
|
||||||
|
|
||||||
|
class DatabaseManager:
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls, db_path: str = None):
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = super(DatabaseManager, cls).__new__(cls)
|
||||||
|
if db_path is None:
|
||||||
|
app_dir = Path(os.path.dirname(os.path.abspath(__file__))).parent
|
||||||
|
data_dir = app_dir / 'data'
|
||||||
|
db_path = str(data_dir / 'database.db')
|
||||||
|
|
||||||
|
cls._instance.db_path = db_path
|
||||||
|
cls._instance._init_database()
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.init_database()
|
||||||
|
|
||||||
|
def _init_database(self):
|
||||||
|
Path(self.db_path).parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
conn.executescript("""
|
||||||
|
CREATE TABLE IF NOT EXISTS configs (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS params (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
config_id INTEGER,
|
||||||
|
delay_data1 FLOAT,
|
||||||
|
ENC_volume_data1 FLOAT,
|
||||||
|
ENT_mx_right_data FLOAT,
|
||||||
|
ENT_mix_left_data FLOAT,
|
||||||
|
FOREIGN KEY (config_id) REFERENCES configs(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS filters (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
config_id INTEGER,
|
||||||
|
filter_type TEXT NOT NULL,
|
||||||
|
freq FLOAT,
|
||||||
|
q FLOAT,
|
||||||
|
gain FLOAT,
|
||||||
|
slope FLOAT,
|
||||||
|
enabled BOOLEAN DEFAULT TRUE,
|
||||||
|
position INTEGER,
|
||||||
|
FOREIGN KEY (config_id) REFERENCES configs(id)
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
|
||||||
|
def get_connection(self):
|
||||||
|
return sqlite3.connect(self.db_path)
|
||||||
|
|
||||||
|
def get_all_configs(self) -> List[ConfigData]:
|
||||||
|
with self.get_connection() as conn:
|
||||||
|
cursor = conn.execute("SELECT id, name, created_at FROM configs")
|
||||||
|
return [ConfigData(*row) for row in cursor.fetchall()]
|
||||||
|
|
||||||
|
def load_config(self, config_id: int) -> Tuple[Optional[ParamData], List[FilterData]]:
|
||||||
|
with self.get_connection() as conn:
|
||||||
|
cursor = conn.execute(
|
||||||
|
"SELECT * FROM params WHERE config_id = ?",
|
||||||
|
(config_id,)
|
||||||
|
)
|
||||||
|
param_row = cursor.fetchone()
|
||||||
|
params = ParamData(*param_row[2:]) if param_row else None
|
||||||
|
|
||||||
|
cursor = conn.execute(
|
||||||
|
"SELECT * FROM filters WHERE config_id = ? ORDER BY position",
|
||||||
|
(config_id,)
|
||||||
|
)
|
||||||
|
filters = [FilterData(*row[2:]) for row in cursor.fetchall()]
|
||||||
|
|
||||||
|
return params, filters
|
||||||
|
|
||||||
|
def save_config(self, name: str, params: ParamData, filters: List[FilterData]) -> int:
|
||||||
|
with self.get_connection() as conn:
|
||||||
|
cursor = conn.execute(
|
||||||
|
"INSERT INTO configs (name) VALUES (?)",
|
||||||
|
(name,)
|
||||||
|
)
|
||||||
|
config_id = cursor.lastrowid
|
||||||
|
|
||||||
|
conn.execute(
|
||||||
|
"""INSERT INTO params
|
||||||
|
(config_id, delay_data1, ENC_volume_data1, ENT_mx_right_data, ENT_mix_left_data)
|
||||||
|
VALUES (?, ?, ?, ?, ?)""",
|
||||||
|
(config_id, params.delay_data1, params.ENC_volume_data1,
|
||||||
|
params.ENT_mx_right_data, params.ENT_mix_left_data)
|
||||||
|
)
|
||||||
|
|
||||||
|
for filter_data in filters:
|
||||||
|
conn.execute(
|
||||||
|
"""INSERT INTO filters
|
||||||
|
(config_id, filter_type, freq, q, gain, slope, enabled, position)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
|
(config_id, filter_data.filter_type, filter_data.freq,
|
||||||
|
filter_data.q, filter_data.gain, filter_data.slope,
|
||||||
|
filter_data.enabled, filter_data.position)
|
||||||
|
)
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
return config_id
|
||||||
|
|
||||||
|
def update_filter(self, filter_id: int, **kwargs):
|
||||||
|
with self.get_connection() as conn:
|
||||||
|
set_clause = ", ".join(f"{k} = ?" for k in kwargs.keys())
|
||||||
|
query = f"UPDATE filters SET {set_clause} WHERE id = ?"
|
||||||
|
conn.execute(query, (*kwargs.values(), filter_id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def update_params(self, config_id: int, params: ParamData):
|
||||||
|
with self.get_connection() as conn:
|
||||||
|
conn.execute(
|
||||||
|
"""UPDATE params SET
|
||||||
|
delay_data1 = ?, ENC_volume_data1 = ?,
|
||||||
|
ENT_mx_right_data = ?, ENT_mix_left_data = ?
|
||||||
|
WHERE config_id = ?""",
|
||||||
|
(params.delay_data1, params.ENC_volume_data1,
|
||||||
|
params.ENT_mx_right_data, params.ENT_mix_left_data,
|
||||||
|
config_id)
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def init_database(self):
|
||||||
|
"""初始化数据库,如果没有数据则创建默认配置"""
|
||||||
|
configs = self.get_all_configs()
|
||||||
|
if not configs:
|
||||||
|
# 创建默认配置
|
||||||
|
default_params = ParamData(
|
||||||
|
delay_data1=0.0,
|
||||||
|
ENC_volume_data1=0.0,
|
||||||
|
ENT_mx_right_data=0.0,
|
||||||
|
ENT_mix_left_data=0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建一些默认滤波器
|
||||||
|
default_filters = [
|
||||||
|
FilterData(
|
||||||
|
filter_type="PEQ",
|
||||||
|
freq=1000.0,
|
||||||
|
q=1.0,
|
||||||
|
gain=0.0,
|
||||||
|
slope=12.0
|
||||||
|
),
|
||||||
|
FilterData(
|
||||||
|
filter_type="HPF",
|
||||||
|
freq=20.0,
|
||||||
|
q=0.707,
|
||||||
|
gain=0.0,
|
||||||
|
slope=12.0
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 保存默认配置
|
||||||
|
self.save_config("Default Config", default_params, default_filters)
|
||||||
27
database/models.py
Normal file
27
database/models.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Optional, Tuple
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FilterData:
|
||||||
|
filter_type: str
|
||||||
|
freq: float
|
||||||
|
q: float
|
||||||
|
gain: float
|
||||||
|
slope: float
|
||||||
|
id: int = None
|
||||||
|
enabled: bool = True
|
||||||
|
position: int = 0
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ParamData:
|
||||||
|
delay_data1: float
|
||||||
|
ENC_volume_data1: float
|
||||||
|
ENT_mx_right_data: float
|
||||||
|
ENT_mix_left_data: float
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ConfigData:
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
created_at: str
|
||||||
BIN
doc/page.png
Normal file
BIN
doc/page.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 139 KiB |
BIN
doc/通道逻辑架构设计.pdf
Normal file
BIN
doc/通道逻辑架构设计.pdf
Normal file
Binary file not shown.
10
main.py
Normal file
10
main.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import sys
|
||||||
|
from PySide6.QtWidgets import QApplication
|
||||||
|
from widgets.avas_widget import AVAS_WIDGET
|
||||||
|
from widgets.audio_filter_widget import AudioFilterWidget
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
main_window = AVAS_WIDGET()
|
||||||
|
main_window.show()
|
||||||
|
sys.exit(app.exec())
|
||||||
2695
widgets/Ui_widget.py
Normal file
2695
widgets/Ui_widget.py
Normal file
File diff suppressed because it is too large
Load Diff
0
widgets/__init__.py
Normal file
0
widgets/__init__.py
Normal file
BIN
widgets/__pycache__/Ui_widget.cpython-313.pyc
Normal file
BIN
widgets/__pycache__/Ui_widget.cpython-313.pyc
Normal file
Binary file not shown.
BIN
widgets/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
widgets/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
widgets/__pycache__/audio_filter_widget.cpython-313.pyc
Normal file
BIN
widgets/__pycache__/audio_filter_widget.cpython-313.pyc
Normal file
Binary file not shown.
BIN
widgets/__pycache__/avas_widget.cpython-313.pyc
Normal file
BIN
widgets/__pycache__/avas_widget.cpython-313.pyc
Normal file
Binary file not shown.
455
widgets/audio_filter_widget.py
Normal file
455
widgets/audio_filter_widget.py
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
from PySide6.QtWidgets import QWidget, QDialog, QVBoxLayout, QComboBox, QPushButton, QLabel, QTableWidgetItem, QAbstractScrollArea
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
from widgets.Ui_widget import Ui_Widget
|
||||||
|
from database.db_manager import DatabaseManager
|
||||||
|
from database.models import FilterData, ParamData
|
||||||
|
|
||||||
|
class AudioFilterModel:
|
||||||
|
def __init__(self, db_manager: DatabaseManager):
|
||||||
|
self.db_manager = db_manager
|
||||||
|
self.current_config_id: Optional[int] = None
|
||||||
|
self.filters: List[FilterData] = []
|
||||||
|
self.params: ParamData = ParamData(
|
||||||
|
delay_data1=0.0,
|
||||||
|
ENC_volume_data1=0.0,
|
||||||
|
ENT_mx_right_data=0.0,
|
||||||
|
ENT_mix_left_data=0.0
|
||||||
|
)
|
||||||
|
self.current_filter_index: int = -1
|
||||||
|
|
||||||
|
def load_config(self, config_id: int) -> bool:
|
||||||
|
params, filters = self.db_manager.load_config(config_id)
|
||||||
|
if params is not None:
|
||||||
|
self.params = params
|
||||||
|
self.filters = filters
|
||||||
|
self.current_config_id = config_id
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def save_config(self, name: str) -> int:
|
||||||
|
return self.db_manager.save_config(name, self.params, self.filters)
|
||||||
|
|
||||||
|
def update_filter(self, index: int, **kwargs):
|
||||||
|
if 0 <= index < len(self.filters):
|
||||||
|
filter_data = self.filters[index]
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(filter_data, key, value)
|
||||||
|
self.db_manager.update_filter(filter_data.id, **kwargs)
|
||||||
|
|
||||||
|
def update_params(self, **kwargs):
|
||||||
|
if self.params is None:
|
||||||
|
self.params = ParamData(
|
||||||
|
delay_data1=0.0,
|
||||||
|
ENC_volume_data1=0.0,
|
||||||
|
ENT_mx_right_data=0.0,
|
||||||
|
ENT_mix_left_data=0.0
|
||||||
|
)
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self.params, key, value)
|
||||||
|
if self.current_config_id:
|
||||||
|
self.db_manager.update_params(self.current_config_id, self.params)
|
||||||
|
|
||||||
|
class FilterTypeDialog(QDialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("选择滤波器类型")
|
||||||
|
self.setModal(True)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# 添加说明标签
|
||||||
|
label = QLabel("请选择要添加的滤波器类型:")
|
||||||
|
layout.addWidget(label)
|
||||||
|
|
||||||
|
# 创建下拉框
|
||||||
|
self.combo = QComboBox()
|
||||||
|
self.combo.addItems(["PEAK", "LOWPASS", "HIGHPASS", "ALLPASS"])
|
||||||
|
layout.addWidget(self.combo)
|
||||||
|
|
||||||
|
# 添加确认按钮
|
||||||
|
button = QPushButton("确定")
|
||||||
|
button.clicked.connect(self.accept)
|
||||||
|
layout.addWidget(button)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def get_selected_type(self):
|
||||||
|
return self.combo.currentText()
|
||||||
|
|
||||||
|
class AudioFilterController:
|
||||||
|
def __init__(self, model: AudioFilterModel, view):
|
||||||
|
print("AudioFilterController initialized")
|
||||||
|
self.model = model
|
||||||
|
self.view = view
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
print("setup_connections")
|
||||||
|
# 表格选择变化
|
||||||
|
self.view.ui.tableWidget.itemSelectionChanged.connect(self.on_selection_changed)
|
||||||
|
# 滑块值变化
|
||||||
|
self.view.ui.verticalSlider.valueChanged.connect(
|
||||||
|
lambda v: self.update_filter_param('freq', v)
|
||||||
|
)
|
||||||
|
self.view.ui.verticalSlider_2.valueChanged.connect(
|
||||||
|
lambda v: self.update_filter_param('q', v)
|
||||||
|
)
|
||||||
|
self.view.ui.verticalSlider_3.valueChanged.connect(
|
||||||
|
lambda v: self.update_filter_param('gain', v)
|
||||||
|
)
|
||||||
|
self.view.ui.verticalSlider_4.valueChanged.connect(
|
||||||
|
lambda v: self.update_filter_param('slope', v)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 参数输入框变化
|
||||||
|
self.view.ui.lineEdit_11.textChanged.connect(
|
||||||
|
lambda v: self.update_param('delay_data1', float(v))
|
||||||
|
)
|
||||||
|
# to do: 其他参数输入框连接
|
||||||
|
|
||||||
|
# 按钮点击
|
||||||
|
self.view.ui.pushButton_7.clicked.connect(self.add_filter)
|
||||||
|
self.view.ui.pushButton_8.clicked.connect(self.delete_filter)
|
||||||
|
self.view.ui.pushButton_9.clicked.connect(self.send_params)
|
||||||
|
self.view.ui.pushButton_10.clicked.connect(self.get_params)
|
||||||
|
|
||||||
|
def on_selection_changed(self):
|
||||||
|
row = self.view.ui.tableWidget.currentRow()
|
||||||
|
print(f"len model:{len(self.model.filters)}")
|
||||||
|
if row >= 0 and row < len(self.model.filters):
|
||||||
|
print(f"on_selection_changed:{row}")
|
||||||
|
self.model.current_filter_index = row
|
||||||
|
filter_name = self.model.filters[row].filter_type
|
||||||
|
self.view.ui.groupBox_4.setTitle(filter_name)
|
||||||
|
self.view.update_sliders()
|
||||||
|
|
||||||
|
def update_filter_param(self, param: str, value: float):
|
||||||
|
if self.model.current_filter_index >= 0:
|
||||||
|
print(f"update_filter_param {self.model.current_filter_index} {value}")
|
||||||
|
# 更新滤波器参数
|
||||||
|
self.model.update_filter(self.model.current_filter_index, **{param: value})
|
||||||
|
# 更新表格显示
|
||||||
|
self.view.update_table_row(self.model.current_filter_index)
|
||||||
|
# 如果有当前配置,保存到数据库
|
||||||
|
if self.model.current_config_id:
|
||||||
|
self.model.save_config(f"Config {self.model.current_config_id}")
|
||||||
|
|
||||||
|
def update_param(self, param: str, value: float):
|
||||||
|
try:
|
||||||
|
self.model.update_params(**{param: value})
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Error updating parameter: {e}")
|
||||||
|
|
||||||
|
def get_next_available_number(self, filter_type: str) -> int:
|
||||||
|
"""
|
||||||
|
获取下一个可用的序号,确保序号连续
|
||||||
|
"""
|
||||||
|
# 获取当前所有同类型的滤波器序号
|
||||||
|
existing_numbers = set()
|
||||||
|
for f in self.model.filters:
|
||||||
|
if f.filter_type.startswith(filter_type + "_"):
|
||||||
|
try:
|
||||||
|
number = int(f.filter_type.split("_")[1])
|
||||||
|
existing_numbers.add(number)
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 从1开始查找第一个可用的序号
|
||||||
|
next_number = 1
|
||||||
|
while next_number in existing_numbers:
|
||||||
|
next_number += 1
|
||||||
|
|
||||||
|
return next_number
|
||||||
|
|
||||||
|
def add_filter(self):
|
||||||
|
# 检查是否达到最大滤波器数量
|
||||||
|
if len(self.model.filters) >= 20:
|
||||||
|
print("已达到最大滤波器数量限制(20个)")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 显示滤波器类型选择对话框
|
||||||
|
dialog = FilterTypeDialog(self.view)
|
||||||
|
if dialog.exec():
|
||||||
|
filter_type = dialog.get_selected_type()
|
||||||
|
|
||||||
|
# 获取下一个可用的序号
|
||||||
|
next_number = self.get_next_available_number(filter_type)
|
||||||
|
|
||||||
|
# 创建新的滤波器名称
|
||||||
|
new_filter_type = f"{filter_type}_{next_number}"
|
||||||
|
|
||||||
|
# 创建新的滤波器,使用默认参数
|
||||||
|
new_filter = FilterData(
|
||||||
|
filter_type=new_filter_type,
|
||||||
|
freq=1000.0,
|
||||||
|
q=1.0,
|
||||||
|
gain=0.0,
|
||||||
|
slope=12.0,
|
||||||
|
position=len(self.model.filters)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加到模型中
|
||||||
|
self.model.filters.append(new_filter)
|
||||||
|
|
||||||
|
# 如果有当前配置,保存到数据库
|
||||||
|
if self.model.current_config_id:
|
||||||
|
self.model.save_config(f"Config {self.model.current_config_id}")
|
||||||
|
|
||||||
|
# 更新视图
|
||||||
|
self.view.update_table()
|
||||||
|
|
||||||
|
def delete_filter(self):
|
||||||
|
# 获取当前选中的行
|
||||||
|
current_row = self.view.ui.tableWidget.currentRow()
|
||||||
|
|
||||||
|
# 检查是否有选中的行
|
||||||
|
if current_row < 0 or current_row >= len(self.model.filters):
|
||||||
|
print("请先选择要删除的滤波器")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 从模型中删除滤波器
|
||||||
|
self.model.filters.pop(current_row)
|
||||||
|
|
||||||
|
# 重置当前选中的滤波器索引
|
||||||
|
self.model.current_filter_index = -1
|
||||||
|
|
||||||
|
# 如果有当前配置,保存到数据库
|
||||||
|
if self.model.current_config_id:
|
||||||
|
self.model.save_config(f"Config {self.model.current_config_id}")
|
||||||
|
|
||||||
|
# 更新视图
|
||||||
|
self.view.update_table()
|
||||||
|
# 清空滤波器组标题
|
||||||
|
self.view.ui.groupBox_4.setTitle("")
|
||||||
|
|
||||||
|
def send_params(self):
|
||||||
|
# 实现发送参数的逻辑
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_params(self):
|
||||||
|
# 实现获取参数的逻辑
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AudioFilterWidget(QWidget):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.ui = Ui_Widget()
|
||||||
|
self.ui.setupUi(self)
|
||||||
|
print("AudioFilterWidget initialized")
|
||||||
|
|
||||||
|
# 初始化表格设置
|
||||||
|
self.setup_table()
|
||||||
|
|
||||||
|
self.db_manager = DatabaseManager()
|
||||||
|
self.model = AudioFilterModel(self.db_manager)
|
||||||
|
|
||||||
|
# 加载配置
|
||||||
|
configs = self.db_manager.get_all_configs()
|
||||||
|
if configs:
|
||||||
|
# 有配置则加载最后一个
|
||||||
|
last_config_id = configs[-1].id
|
||||||
|
self.model.load_config(last_config_id)
|
||||||
|
else:
|
||||||
|
# 没有配置则创建默认配置
|
||||||
|
default_params = ParamData(
|
||||||
|
delay_data1=0.0,
|
||||||
|
ENC_volume_data1=0.0,
|
||||||
|
ENT_mx_right_data=0.0,
|
||||||
|
ENT_mix_left_data=0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建默认滤波器
|
||||||
|
self.model.filters = [
|
||||||
|
FilterData(
|
||||||
|
filter_type="PEQ",
|
||||||
|
freq=1000.0,
|
||||||
|
q=1.0,
|
||||||
|
gain=0.0,
|
||||||
|
slope=12.0
|
||||||
|
)
|
||||||
|
]
|
||||||
|
self.model.params = default_params
|
||||||
|
|
||||||
|
# 保存到数据库
|
||||||
|
config_id = self.model.save_config("Default Config")
|
||||||
|
self.model.current_config_id = config_id
|
||||||
|
|
||||||
|
self.controller = AudioFilterController(self.model, self)
|
||||||
|
self.update_view()
|
||||||
|
|
||||||
|
# 添加表格数据变化的信号连接
|
||||||
|
self.ui.tableWidget.itemChanged.connect(self.on_table_item_changed)
|
||||||
|
|
||||||
|
def update_view(self):
|
||||||
|
self.update_table()
|
||||||
|
self.update_sliders()
|
||||||
|
self.update_params()
|
||||||
|
|
||||||
|
def update_table_row(self, row: int):
|
||||||
|
filter_data = self.model.filters[row]
|
||||||
|
# 创建带复选框的滤波器名称项
|
||||||
|
filter_item = QTableWidgetItem(filter_data.filter_type)
|
||||||
|
filter_item.setFlags(filter_item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
|
||||||
|
filter_item.setCheckState(Qt.CheckState.Checked) # 默认选中
|
||||||
|
|
||||||
|
# 更新各列的值
|
||||||
|
self.ui.tableWidget.setItem(row, 0, filter_item)
|
||||||
|
self.ui.tableWidget.setItem(row, 1, QTableWidgetItem(str(filter_data.freq)))
|
||||||
|
self.ui.tableWidget.setItem(row, 2, QTableWidgetItem(str(filter_data.q)))
|
||||||
|
self.ui.tableWidget.setItem(row, 3, QTableWidgetItem(str(filter_data.gain)))
|
||||||
|
self.ui.tableWidget.setItem(row, 4, QTableWidgetItem(str(filter_data.slope)))
|
||||||
|
|
||||||
|
def update_table(self):
|
||||||
|
self.ui.tableWidget.setRowCount(len(self.model.filters))
|
||||||
|
for i, filter_data in enumerate(self.model.filters):
|
||||||
|
self.update_table_row(i)
|
||||||
|
|
||||||
|
def update_sliders(self):
|
||||||
|
if self.model.current_filter_index >= 0:
|
||||||
|
filter_data = self.model.filters[self.model.current_filter_index]
|
||||||
|
self.ui.verticalSlider.setValue(int(filter_data.freq))
|
||||||
|
self.ui.verticalSlider_2.setValue(int(filter_data.q))
|
||||||
|
self.ui.verticalSlider_3.setValue(int(filter_data.gain))
|
||||||
|
self.ui.verticalSlider_4.setValue(int(filter_data.slope))
|
||||||
|
|
||||||
|
def update_params(self):
|
||||||
|
if self.model.params:
|
||||||
|
self.ui.lineEdit_11.setText(str(self.model.params.delay_data1))
|
||||||
|
self.ui.lineEdit_10.setText(str(self.model.params.ENC_volume_data1))
|
||||||
|
self.ui.lineEdit_13.setText(str(self.model.params.ENT_mx_right_data))
|
||||||
|
self.ui.lineEdit_12.setText(str(self.model.params.ENT_mix_left_data))
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
"""初始化表格设置"""
|
||||||
|
table = self.ui.tableWidget
|
||||||
|
|
||||||
|
# 设置列数和列标题
|
||||||
|
table.setColumnCount(5)
|
||||||
|
headers = ["滤波器", "Freq", "Q", "Gain", "Slop"]
|
||||||
|
table.setHorizontalHeaderLabels(headers)
|
||||||
|
|
||||||
|
# 创建带复选框的表头项
|
||||||
|
header_item = QTableWidgetItem("滤波器")
|
||||||
|
header_item.setFlags(header_item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
|
||||||
|
header_item.setCheckState(Qt.CheckState.Checked) # 默认选中
|
||||||
|
table.setHorizontalHeaderItem(0, header_item)
|
||||||
|
|
||||||
|
# 设置第一列的特殊属性,使其支持复选框
|
||||||
|
table.setItemDelegateForColumn(0, None)
|
||||||
|
|
||||||
|
# 设置表格样式,包括复选框的颜色
|
||||||
|
table.setStyleSheet("""
|
||||||
|
/* 基础样式设置 */
|
||||||
|
QTableWidget, QHeaderView {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 同时设置表格和表头的复选框样式 */
|
||||||
|
QTableWidget::indicator,
|
||||||
|
QHeaderView::down-arrow,
|
||||||
|
QHeaderView::indicator {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #DFE1E2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中状态 */
|
||||||
|
QTableWidget::indicator:checked,
|
||||||
|
QHeaderView::down-arrow:checked,
|
||||||
|
QHeaderView::indicator:checked {
|
||||||
|
image: none;
|
||||||
|
background-color: #FF0000;
|
||||||
|
border: 2px solid #FF0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 未选中状态 */
|
||||||
|
QTableWidget::indicator:unchecked,
|
||||||
|
QHeaderView::down-arrow:unchecked,
|
||||||
|
QHeaderView::indicator:unchecked {
|
||||||
|
image: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid #DFE1E2;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
# 设置表格属性
|
||||||
|
table.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||||
|
table.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||||
|
table.setSizeAdjustPolicy(QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents)
|
||||||
|
table.setAutoScroll(True)
|
||||||
|
table.setGridStyle(Qt.PenStyle.DotLine)
|
||||||
|
|
||||||
|
# 设置表头
|
||||||
|
header = table.horizontalHeader()
|
||||||
|
header.setVisible(True)
|
||||||
|
header.setMinimumSectionSize(18)
|
||||||
|
header.setDefaultSectionSize(67)
|
||||||
|
header.setHighlightSections(False)
|
||||||
|
header.setProperty("showSortIndicator", False)
|
||||||
|
header.setStretchLastSection(False)
|
||||||
|
|
||||||
|
# 隐藏垂直表头
|
||||||
|
v_header = table.verticalHeader()
|
||||||
|
v_header.setVisible(False)
|
||||||
|
v_header.setMinimumSectionSize(17)
|
||||||
|
v_header.setDefaultSectionSize(30)
|
||||||
|
v_header.setHighlightSections(False)
|
||||||
|
|
||||||
|
# 添加表头复选框变化的信号连接
|
||||||
|
table.horizontalHeader().sectionClicked.connect(self.on_header_clicked)
|
||||||
|
|
||||||
|
def on_header_clicked(self, logical_index):
|
||||||
|
"""处理表头点击事件"""
|
||||||
|
if logical_index == 0: # 只处理第一列
|
||||||
|
header_item = self.ui.tableWidget.horizontalHeaderItem(0)
|
||||||
|
new_state = Qt.CheckState.Unchecked if header_item.checkState() == Qt.CheckState.Checked else Qt.CheckState.Checked
|
||||||
|
header_item.setCheckState(new_state)
|
||||||
|
|
||||||
|
# 更新所有行的复选框状态
|
||||||
|
for row in range(self.ui.tableWidget.rowCount()):
|
||||||
|
item = self.ui.tableWidget.item(row, 0)
|
||||||
|
if item:
|
||||||
|
item.setCheckState(new_state)
|
||||||
|
|
||||||
|
def on_table_item_changed(self, item):
|
||||||
|
"""处理表格数据变化"""
|
||||||
|
row = item.row()
|
||||||
|
column = item.column()
|
||||||
|
|
||||||
|
if column == 0: # 处理滤波器名称的修改
|
||||||
|
new_name = item.text()
|
||||||
|
# 检查是否存在重名(不包括当前滤波器)
|
||||||
|
for i, filter_data in enumerate(self.model.filters):
|
||||||
|
if i != row and filter_data.filter_type == new_name:
|
||||||
|
print(f"滤波器名称 '{new_name}' 已存在,请使用其他名称")
|
||||||
|
# 恢复原来的名称
|
||||||
|
item.setText(self.model.filters[row].filter_type)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 更新滤波器名称并保存到数据库
|
||||||
|
self.model.update_filter(row, filter_type=new_name)
|
||||||
|
# 更新组框标题(如果当前选中的是这个滤波器)
|
||||||
|
if row == self.model.current_filter_index:
|
||||||
|
self.ui.groupBox_4.setTitle(new_name)
|
||||||
|
# 保存整个配置到数据库
|
||||||
|
if self.model.current_config_id:
|
||||||
|
self.model.save_config(f"Config {self.model.current_config_id}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 处理其他数值列的修改
|
||||||
|
new_value = float(item.text())
|
||||||
|
|
||||||
|
# 更新对应的滑动条
|
||||||
|
if column == 1: # Freq
|
||||||
|
self.ui.verticalSlider.setValue(int(new_value))
|
||||||
|
elif column == 2: # Q
|
||||||
|
self.ui.verticalSlider_2.setValue(int(new_value))
|
||||||
|
elif column == 3: # Gain
|
||||||
|
self.ui.verticalSlider_3.setValue(int(new_value))
|
||||||
|
elif column == 4: # Slope
|
||||||
|
self.ui.verticalSlider_4.setValue(int(new_value))
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
print("输入的值无效,请输入数字")
|
||||||
204
widgets/avas_widget.py
Normal file
204
widgets/avas_widget.py
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
import sys
|
||||||
|
from PySide6.QtWidgets import (QApplication, QWidget, QPushButton, QGridLayout,
|
||||||
|
QVBoxLayout, QHBoxLayout, QCheckBox, QComboBox,
|
||||||
|
QLabel, QSlider, QFrame, QGroupBox)
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
from widgets.audio_filter_widget import AudioFilterWidget
|
||||||
|
|
||||||
|
class ChannelButton(QWidget):
|
||||||
|
def __init__(self, channel_num):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
# Create GroupBox for the channel
|
||||||
|
self.group = QGroupBox(f"CH{channel_num}")
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.setSpacing(2)
|
||||||
|
|
||||||
|
# Channel button
|
||||||
|
self.channel_btn = QPushButton(f"Channel {channel_num}")
|
||||||
|
self.channel_btn.setFixedHeight(25)
|
||||||
|
|
||||||
|
# Store audio filter window reference
|
||||||
|
self.filter_window = None
|
||||||
|
|
||||||
|
# Controls container with two rows
|
||||||
|
controls_layout = QGridLayout()
|
||||||
|
controls_layout.setSpacing(2)
|
||||||
|
|
||||||
|
# SOLO row
|
||||||
|
self.solo_cb = QCheckBox("S")
|
||||||
|
self.solo_btn = QPushButton("SOLO")
|
||||||
|
self.solo_btn.setFixedHeight(20)
|
||||||
|
controls_layout.addWidget(self.solo_cb, 0, 0)
|
||||||
|
controls_layout.addWidget(self.solo_btn, 0, 1)
|
||||||
|
|
||||||
|
# MUTE row
|
||||||
|
self.mute_cb = QCheckBox("M")
|
||||||
|
self.mute_btn = QPushButton("MUTE")
|
||||||
|
self.mute_btn.setFixedHeight(20)
|
||||||
|
controls_layout.addWidget(self.mute_cb, 1, 0)
|
||||||
|
controls_layout.addWidget(self.mute_btn, 1, 1)
|
||||||
|
|
||||||
|
# Add widgets to main layout
|
||||||
|
layout.addWidget(self.channel_btn)
|
||||||
|
layout.addLayout(controls_layout)
|
||||||
|
|
||||||
|
# Set GroupBox layout
|
||||||
|
self.group.setLayout(layout)
|
||||||
|
|
||||||
|
# Main layout
|
||||||
|
main_layout = QVBoxLayout()
|
||||||
|
main_layout.addWidget(self.group)
|
||||||
|
main_layout.setContentsMargins(2, 2, 2, 2)
|
||||||
|
self.setLayout(main_layout)
|
||||||
|
|
||||||
|
# Set fixed size for the channel widget
|
||||||
|
self.setFixedSize(110, 100)
|
||||||
|
|
||||||
|
# Style
|
||||||
|
self.channel_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #404040;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid #505050;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #505050;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
button_style = """
|
||||||
|
QPushButton {
|
||||||
|
background-color: #303030;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid #404040;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #404040;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.solo_btn.setStyleSheet(button_style)
|
||||||
|
self.mute_btn.setStyleSheet(button_style)
|
||||||
|
|
||||||
|
self.group.setStyleSheet("""
|
||||||
|
QGroupBox {
|
||||||
|
border: 1px solid #505050;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
QCheckBox {
|
||||||
|
spacing: 2px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Connect button click event
|
||||||
|
self.channel_btn.clicked.connect(self.show_filter_window)
|
||||||
|
|
||||||
|
def show_filter_window(self):
|
||||||
|
if not self.filter_window:
|
||||||
|
self.filter_window = AudioFilterWidget()
|
||||||
|
# Set window title to include channel number
|
||||||
|
self.filter_window.setWindowTitle(f"Channel {self.group.title()} Filter Settings")
|
||||||
|
|
||||||
|
# Show the window if it's not visible
|
||||||
|
if not self.filter_window.isVisible():
|
||||||
|
self.filter_window.show()
|
||||||
|
else:
|
||||||
|
# If already visible, bring to front
|
||||||
|
self.filter_window.raise_()
|
||||||
|
self.filter_window.activateWindow()
|
||||||
|
|
||||||
|
class AVAS_WIDGET(QWidget):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("AVAS")
|
||||||
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
main_layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Top section - Channel buttons
|
||||||
|
channels_layout = QGridLayout()
|
||||||
|
for i in range(24):
|
||||||
|
row = i // 8
|
||||||
|
col = i % 8
|
||||||
|
channel = ChannelButton(i + 1)
|
||||||
|
channels_layout.addWidget(channel, row, col)
|
||||||
|
|
||||||
|
# Middle section
|
||||||
|
middle_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
# Left side - Parameter matrix
|
||||||
|
param_matrix = self.create_parameter_matrix()
|
||||||
|
|
||||||
|
# Right side - Filter controls
|
||||||
|
filter_controls = self.create_filter_controls()
|
||||||
|
|
||||||
|
middle_layout.addWidget(param_matrix, stretch=2)
|
||||||
|
middle_layout.addWidget(filter_controls, stretch=1)
|
||||||
|
|
||||||
|
# Bottom section
|
||||||
|
bottom_layout = QHBoxLayout()
|
||||||
|
clean_btn = QPushButton("Clean")
|
||||||
|
send_btn = QPushButton("Send")
|
||||||
|
enable_cb = QCheckBox("Enable")
|
||||||
|
|
||||||
|
bottom_layout.addStretch()
|
||||||
|
bottom_layout.addWidget(clean_btn)
|
||||||
|
bottom_layout.addWidget(send_btn)
|
||||||
|
bottom_layout.addWidget(enable_cb)
|
||||||
|
|
||||||
|
# Add all sections to main layout
|
||||||
|
main_layout.addLayout(channels_layout)
|
||||||
|
main_layout.addLayout(middle_layout)
|
||||||
|
main_layout.addLayout(bottom_layout)
|
||||||
|
|
||||||
|
self.setLayout(main_layout)
|
||||||
|
|
||||||
|
def create_parameter_matrix(self):
|
||||||
|
frame = QFrame()
|
||||||
|
frame.setFrameStyle(QFrame.Box)
|
||||||
|
layout = QGridLayout()
|
||||||
|
|
||||||
|
# Add headers and parameter rows
|
||||||
|
for i in range(24): # 24 channels
|
||||||
|
layout.addWidget(QLabel(f"Ch{i+1}"), i+1, 0)
|
||||||
|
# Add parameter controls for each channel
|
||||||
|
for j in range(5): # Example: 5 parameters per channel
|
||||||
|
layout.addWidget(QSlider(Qt.Horizontal), i+1, j+1)
|
||||||
|
|
||||||
|
frame.setLayout(layout)
|
||||||
|
return frame
|
||||||
|
|
||||||
|
def create_filter_controls(self):
|
||||||
|
frame = QFrame()
|
||||||
|
frame.setFrameStyle(QFrame.Box)
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Filter selection
|
||||||
|
filter_select = QComboBox()
|
||||||
|
filter_select.addItems(["Filter 1", "Filter 2", "Filter 3"])
|
||||||
|
|
||||||
|
# Filter type radio buttons
|
||||||
|
filter_types = QVBoxLayout()
|
||||||
|
types = ["Pink-Pin", "Bass Setting", "Stereo Surround", "None"]
|
||||||
|
for type_name in types:
|
||||||
|
filter_types.addWidget(QCheckBox(type_name))
|
||||||
|
|
||||||
|
# Sliders
|
||||||
|
sliders_layout = QVBoxLayout()
|
||||||
|
slider_labels = ["Input Level", "Freq", "Gain"]
|
||||||
|
for label in slider_labels:
|
||||||
|
sliders_layout.addWidget(QLabel(label))
|
||||||
|
sliders_layout.addWidget(QSlider(Qt.Horizontal))
|
||||||
|
|
||||||
|
layout.addWidget(QLabel("Filter Select"))
|
||||||
|
layout.addWidget(filter_select)
|
||||||
|
layout.addLayout(filter_types)
|
||||||
|
layout.addLayout(sliders_layout)
|
||||||
|
|
||||||
|
frame.setLayout(layout)
|
||||||
|
return frame
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user