[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