[feature] 初始化仓库

This commit is contained in:
Sam 2025-02-15 21:20:54 +08:00
commit 9145e72263
23 changed files with 6362 additions and 0 deletions

5
.cursorrules Normal file
View 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.

View File

@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}

Binary file not shown.

Binary file not shown.

BIN
data/database.db Normal file

Binary file not shown.

0
database/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

165
database/db_manager.py Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

10
main.py Normal file
View 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())

2793
widget.ui Normal file

File diff suppressed because it is too large Load Diff

2695
widgets/Ui_widget.py Normal file

File diff suppressed because it is too large Load Diff

0
widgets/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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
View 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