From ae09c4f5be28f79e6a4a54b1398f066e51b8e91b Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 16 Feb 2025 21:24:51 +0800 Subject: [PATCH] =?UTF-8?q?[feature]=20=E6=96=B0=E5=A2=9E=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=80=89=E6=8B=A9=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/database.db | Bin 20480 -> 20480 bytes .../__pycache__/db_manager.cpython-313.pyc | Bin 10441 -> 11344 bytes database/db_manager.py | 80 +++-- widget_file_list.zip | Bin 0 -> 4321 bytes widget_file_list/QtWidgetsApplication4.cpp | 303 ++++++++++++++++++ widget_file_list/QtWidgetsApplication4.h | 59 ++++ widget_file_list/main.cpp | 30 ++ .../audio_filter_widget.cpython-313.pyc | Bin 28397 -> 29897 bytes .../__pycache__/avas_widget.cpython-313.pyc | Bin 8927 -> 9287 bytes widgets/audio_filter_widget.py | 45 ++- widgets/avas_widget.py | 24 +- 11 files changed, 501 insertions(+), 40 deletions(-) create mode 100644 widget_file_list.zip create mode 100644 widget_file_list/QtWidgetsApplication4.cpp create mode 100644 widget_file_list/QtWidgetsApplication4.h create mode 100644 widget_file_list/main.cpp diff --git a/data/database.db b/data/database.db index c9cae12b1b9c54f008aa7c66e95e9015a3c4b5ed..7d12544ca24a3d24e4957ad2c3e730ca82ea0213 100644 GIT binary patch delta 440 zcmZ9GJxjw-6o&7)xq;k%aMVhmXl+Ivg1tA5gbYFnP6`$}bgT{<1&Kew{sguC0UaE~ zO?2qy;_gy(5}aJ>?CfbxBzUHCULKzJ^heMi!Q~1BSE~^O_Xjsv5*Ral#Yeox_+fFO zix(ThWG;Bo$zhTlojKZx&XeQQnCpi2-G+A6mZQ8->rgcv!(=t-{Q{2uN2pNy4$l)R zPw3EZhALPGsv}2Qrv{((HTr7IaEfDm#~1qQ7Cl?I02%c6lPHUtgsk7lUy2W=n^iJR z7=DxM11Ds9#_fWz7=T41xHM>&v)zz9Te$@mlk>F97&CFm@C)DY3GZ-7jy@)~xoj56 yQUysvqV`T}x2FmfL8(krBMoh17S`veWae_1>%S0JxMwX2UOZeFs`L8kXU)wBbaF~B$0O#au@_Im_*9`oxAwm=6 zCcoB~1PXm);Qs~{(iGufY8nklE~eHp-#b|HBBp| ziUXIVht_DSR`ddg9%!YiiMSy3PwJoCQ#~ruTJ%uVLodB-N~%;nq@A&$D3aA%51p01 z_vU+V-pu=UcE5YBJllL?u{2V6eD?GG#eDbu=7^nsqXB9{0M6N;f{$5O!YCQLNd_MB z_Y@@=)3hG@dDHCXlCDZ)kSLKk^Uv+6o7~?(9IP1lh zn$OtjEs8Qz0wtLWbeba1dDng-MMytu9iY$1yg@REbxIPAi7*kt zqPL2E7;^-5|2qkL>hvoybtKe_LHHNzY#?gFP3z#eF|{5`$EV^~W`x|lkPxp<3v(;^ zh1_C6xR#8IaUm}+XN6(mk}#4Q73N-&^ZD#j2B%_5A@^}TIEt6g#9_|nq1Qiw-#Ejt zY-)f*P6uS#r>p*l*w42&!XNoTZ~$AK!PAV@(|R(?_F7d58N{NbU!WR zX&}Qdy+OT_!2$S(*A2efclgF=Fb`h{tb+1B3iJVXo~$YmAZg0$kPS|NZ{hV|Ztz*4 z_w4F4ir9OtHG7b&*|PY2`I?Nx7f+{)w;opWv{4%V9()hng~jgfmQ@`{gWssCQGB@m E-#K89hyVZp delta 960 zcmZuvO=uHA7~RP>X=1W*H+9V>-KN>BO*fmO7Hf*Ff7l;me*{;nkt&*6Ev-#klZc`% z>OrU=*f65D2&O0NrKIHIK}67l-ZD^_MMU)M!POQ7PtK+YJ!BZ(`@Z+Rc?`pp5}!`E z*BE9CMb@7D%E)_Z#l6!Dp21}{3@>?A=&{u>6V-{#IBiVZc7l{vxac!%m>Nxx@wt@mw>l)#R zD8N)*0+vL9j@&B3Rzgu+ICQp39xn z*>0WVOKfP(N^f`1b{lreSC0h+3CirTZ}rNmOVnNLqrL}yhQ-3|Hf)qfz?_6QDIddf z3zAz3S8yzjV)2h*+>=B-NuBdzE`qqoQVd5sP_)CiLj$f0tUGLsdyS=njdM<2X z9`!!xeb}$ph=1BCw~Uz(*?%+UKh1RpJKO0G?OVDWMW6HofalOEk1-d?C}K-1gwn%s zT26w6;#YaX0xrV-aO2j`%=M|^WcKofsbo5t%D}g>uX>|gEY63ITEGzeu1taDq88Z& z*da6Jnh9AGreRL)0q@`!b+kEPt;^E}MW|3yrq`ooDru)nXOc)8+UQ2c)=iIb7{KZ1 W0+@x~CWSjrgOlJlRi?B|lK%neyz*rL diff --git a/database/db_manager.py b/database/db_manager.py index 3f4d7c3..d4ba4cd 100644 --- a/database/db_manager.py +++ b/database/db_manager.py @@ -121,35 +121,69 @@ class DatabaseManager: def save_config(self, name: str, channel_id: int, params: ParamData, filters: List[FilterData]) -> int: with self.get_connection() as conn: + # 检查是否存在相同的配置 cursor = conn.execute( - "INSERT INTO configs (name, channel_id) VALUES (?, ?)", + "SELECT id FROM configs WHERE name = ? AND channel_id = ?", (name, channel_id) ) - config_id = cursor.lastrowid + existing_config = cursor.fetchone() - params.config_id = config_id - conn.execute( - """INSERT INTO params - (config_id, channel_id, delay_data1, ENC_volume_data1, ENT_mx_right_data, ENT_mix_left_data) - VALUES (?, ?, ?, ?, ?, ?)""", - (params.config_id, params.channel_id, params.delay_data1, params.ENC_volume_data1, - params.ENT_mx_right_data, params.ENT_mix_left_data) - ) - - for filter_data in filters: - filter_data.config_id = config_id + if existing_config: + # 更新现有配置 + config_id = existing_config[0] + + # 更新参数 + params.config_id = config_id + self.update_params(config_id, params) + + # 删除旧的滤波器 + conn.execute("DELETE FROM filters WHERE config_id = ?", (config_id,)) + + # 插入新的滤波器 + for filter_data in filters: + filter_data.config_id = config_id + cursor = conn.execute( + """INSERT INTO filters + (config_id, channel_id, filter_type, freq, q, gain, slope, enabled, position) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", + (filter_data.config_id, filter_data.channel_id, filter_data.filter_type, + filter_data.freq, filter_data.q, filter_data.gain, filter_data.slope, + filter_data.enabled, filter_data.position) + ) + filter_data.id = cursor.lastrowid + + return config_id + else: + # 创建新配置(原有的插入逻辑) cursor = conn.execute( - """INSERT INTO filters - (config_id, channel_id, filter_type, freq, q, gain, slope, enabled, position) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", - (filter_data.config_id, filter_data.channel_id, filter_data.filter_type, - filter_data.freq, filter_data.q, filter_data.gain, filter_data.slope, - filter_data.enabled, filter_data.position) + "INSERT INTO configs (name, channel_id) VALUES (?, ?)", + (name, channel_id) ) - filter_data.id = cursor.lastrowid - - conn.commit() - return config_id + config_id = cursor.lastrowid + + params.config_id = config_id + conn.execute( + """INSERT INTO params + (config_id, channel_id, delay_data1, ENC_volume_data1, ENT_mx_right_data, ENT_mix_left_data) + VALUES (?, ?, ?, ?, ?, ?)""", + (params.config_id, params.channel_id, params.delay_data1, params.ENC_volume_data1, + params.ENT_mx_right_data, params.ENT_mix_left_data) + ) + + for filter_data in filters: + filter_data.config_id = config_id + cursor = conn.execute( + """INSERT INTO filters + (config_id, channel_id, filter_type, freq, q, gain, slope, enabled, position) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", + (filter_data.config_id, filter_data.channel_id, filter_data.filter_type, + filter_data.freq, filter_data.q, filter_data.gain, filter_data.slope, + filter_data.enabled, filter_data.position) + ) + filter_data.id = cursor.lastrowid + + conn.commit() + return config_id def update_filter(self, filter_id: int, **kwargs): with self.get_connection() as conn: diff --git a/widget_file_list.zip b/widget_file_list.zip new file mode 100644 index 0000000000000000000000000000000000000000..7a252b0b7845625004983cfd719d790a5899e2d9 GIT binary patch literal 4321 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 +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +CardData::CardData(const QString& name, const QString& date, const QString& description) + : name(name), date(date), description(description), activated(false) +{ +} + +CardItemDelegate::CardItemDelegate(QObject* parent) + : QStyledItemDelegate(parent) +{ +} + +void CardItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + painter->setRenderHint(QPainter::Antialiasing); + painter->save(); + + // 获取数据 + CardData data = index.data(Qt::UserRole).value(); + + // 绘制卡片背景 + QRect rect = option.rect; + rect.adjust(8, 4, -8, -4); + + // 创建圆角路径 + QPainterPath path; + path.addRoundedRect(rect, 8, 8); + + // 绘制阴影 + QColor shadowColor(0, 0, 0, 30); + for (int i = 0; i < 5; ++i) { + QRect shadowRect = rect.adjusted(0, i, 0, i); + QPainterPath shadowPath; + shadowPath.addRoundedRect(shadowRect, 8, 8); + painter->fillPath(shadowPath, shadowColor); + } + + // 绘制卡片背景 + QLinearGradient gradient(rect.topLeft(), rect.bottomLeft()); + if (data.activated) { + gradient.setColorAt(0, QColor(40, 70, 45)); + gradient.setColorAt(1, QColor(45, 80, 50)); + painter->fillPath(path, gradient); + painter->setPen(QPen(QColor(60, 180, 90), 2)); + } + else if (option.state & QStyle::State_Selected) { + gradient.setColorAt(0, QColor(45, 45, 55)); + gradient.setColorAt(1, QColor(55, 55, 65)); + painter->fillPath(path, gradient); + painter->setPen(QPen(QColor(70, 130, 180), 2)); + } + else { + painter->fillPath(path, QColor(35, 35, 40)); + painter->setPen(QPen(QColor(60, 60, 65))); + } + + painter->drawPath(path); + + // 设置字体 + QFont nameFont("Microsoft YaHei", 10); + nameFont.setBold(true); + + QFont normalFont("Microsoft YaHei", 9); + + // 计算文本区域 + int padding = 15; + QRect nameRect = rect.adjusted(padding, padding, -padding, 0); + nameRect.setHeight(25); + + QRect dateRect = nameRect; + dateRect.translate(0, nameRect.height()); + + QRect descRect = dateRect; + descRect.translate(0, dateRect.height()); + descRect.setHeight(40); + + // 绘制文本 + painter->setFont(nameFont); + painter->setPen(Qt::white); + painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, data.name); + + painter->setFont(normalFont); + painter->setPen(QColor(180, 180, 180)); + painter->drawText(dateRect, Qt::AlignLeft | Qt::AlignVCenter, data.date); + + painter->setPen(QColor(160, 160, 160)); + painter->drawText(descRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, + data.description); + + painter->restore(); +} + +QSize CardItemDelegate::sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + Q_UNUSED(option); + Q_UNUSED(index); + return QSize(380, 130); +} + +MainWindow::MainWindow(QWidget* parent) + : QWidget(parent) +{ + setupUI(); + setupStyle(); + + // 添加示例数据 + for (int i = 1; i <= 10; ++i) { + CardData data( + QString("项目 %1").arg(i), + QDate::currentDate().toString("yyyy-MM-dd"), + QString("这是项目 %1 的详细描述信息,可以包含多行文本内容。这是一个较长的描述,用于测试换行效果。").arg(i) + ); + addCardItem(data); + } +} + +void MainWindow::setupUI() +{ + setWindowTitle(tr("卡片列表示例")); + resize(400, 600); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(10, 10, 10, 10); + layout->setSpacing(0); + + listWidget = new QListWidget(this); + listWidget->setSpacing(10); + listWidget->setResizeMode(QListWidget::Adjust); + listWidget->setUniformItemSizes(false); + listWidget->setViewMode(QListWidget::ListMode); + listWidget->setVerticalScrollMode(QListWidget::ScrollPerPixel); + listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + CardItemDelegate* delegate = new CardItemDelegate(listWidget); + listWidget->setItemDelegate(delegate); + + // 连接信号 + connect(listWidget, &QListWidget::itemDoubleClicked, + this, &MainWindow::onItemDoubleClicked); + listWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(listWidget, &QListWidget::customContextMenuRequested, + this, &MainWindow::showContextMenu); + + layout->addWidget(listWidget); +} + +void MainWindow::setupStyle() +{ + setStyleSheet("QWidget {" + " background-color: #1e1e1e;" + "}" + "QListWidget {" + " background-color: #1e1e1e;" + " border: none;" + " outline: none;" + "}" + "QListWidget::item {" + " background-color: transparent;" + " padding: 4px;" + "}" + "QListWidget::item:selected {" + " background-color: transparent;" + "}" + "QScrollBar:vertical {" + " border: none;" + " background: #1e1e1e;" + " width: 8px;" + " margin: 0px;" + "}" + "QScrollBar::handle:vertical {" + " background: #404040;" + " min-height: 20px;" + " border-radius: 4px;" + "}" + "QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {" + " height: 0px;" + "}" + "QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {" + " background: none;" + "}" + "QMenu {" + " background-color: #2d2d2d;" + " border: 1px solid #404040;" + " padding: 5px;" + "}" + "QMenu::item {" + " background-color: transparent;" + " padding: 6px 25px;" + " border-radius: 4px;" + " color: #ffffff;" + "}" + "QMenu::item:selected {" + " background-color: #404040;" + "}" + "QMenu::separator {" + " height: 1px;" + " background: #404040;" + " margin: 5px 0px;" + "}" + ); +} + +void MainWindow::addCardItem(const CardData& data) +{ + QListWidgetItem* item = new QListWidgetItem(listWidget); + item->setData(Qt::UserRole, QVariant::fromValue(data)); + item->setSizeHint(QSize(380, 120)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); +} + +void MainWindow::onItemDoubleClicked(QListWidgetItem* item) +{ + // 先将所有项目设置为未激活状态 + for (int row = 0; row < listWidget->count(); ++row) { + QListWidgetItem* currentItem = listWidget->item(row); + CardData data = currentItem->data(Qt::UserRole).value(); + data.activated = false; + currentItem->setData(Qt::UserRole, QVariant::fromValue(data)); + } + + // 设置当前点击项目为激活状态 + CardData data = item->data(Qt::UserRole).value(); + data.activated = true; + item->setData(Qt::UserRole, QVariant::fromValue(data)); + + listWidget->viewport()->update(); +} + +void MainWindow::showContextMenu(const QPoint& pos) +{ + QListWidgetItem* item = listWidget->itemAt(pos); + if (!item) return; + + QMenu menu(this); + + QAction* sendAction = menu.addAction(tr("发送到设备")); + menu.addSeparator(); + QAction* editAction = menu.addAction(tr("修改")); + QAction* deleteAction = menu.addAction(tr("删除")); + menu.addSeparator(); + QAction* showInExplorerAction = menu.addAction(tr("在资源管理器中显示")); + + // 设置图标 + sendAction->setIcon(QIcon::fromTheme("document-send")); + editAction->setIcon(QIcon::fromTheme("document-edit")); + deleteAction->setIcon(QIcon::fromTheme("document-delete")); + showInExplorerAction->setIcon(QIcon::fromTheme("folder")); + + QAction* action = menu.exec(listWidget->viewport()->mapToGlobal(pos)); + if (!action) return; + + if (action == sendAction) + sendToDevice(item); + else if (action == editAction) + editItem(item); + else if (action == deleteAction) + deleteItem(item); + else if (action == showInExplorerAction) + showInExplorer(item); +} + +void MainWindow::sendToDevice(QListWidgetItem* item) +{ + CardData data = item->data(Qt::UserRole).value(); + qDebug() << "发送到设备:" << data.name; +} + +void MainWindow::editItem(QListWidgetItem* item) +{ + CardData data = item->data(Qt::UserRole).value(); + qDebug() << "编辑项目:" << data.name; +} + +void MainWindow::deleteItem(QListWidgetItem* item) +{ + int row = listWidget->row(item); + delete listWidget->takeItem(row); +} + +void MainWindow::showInExplorer(QListWidgetItem* item) +{ + QString path = QDir::currentPath(); +#ifdef Q_OS_WIN + QProcess::startDetached("explorer.exe", { "/select,", QDir::toNativeSeparators(path) }); +#elif defined(Q_OS_MAC) + QProcess::startDetached("open", { "-R", path }); +#else + QProcess::startDetached("xdg-open", { QFileInfo(path).path() }); +#endif +} + diff --git a/widget_file_list/QtWidgetsApplication4.h b/widget_file_list/QtWidgetsApplication4.h new file mode 100644 index 0000000..2fec10f --- /dev/null +++ b/widget_file_list/QtWidgetsApplication4.h @@ -0,0 +1,59 @@ +#ifndef CARD_LIST_WIDGET_H +#define CARD_LIST_WIDGET_H + +#include +#include +#include +#include + +// 卡片数据结构 +class CardData { +public: + // 添加默认构造函数 + CardData() : activated(false) {} + + // 保留原有构造函数 + CardData(const QString& name, const QString& date, const QString& description); + + QString name; + QString date; + QString description; + bool activated; +}; + +// 自定义委托类来绘制卡片样式 +class CardItemDelegate : public QStyledItemDelegate { + Q_OBJECT +public: + explicit CardItemDelegate(QObject* parent = nullptr); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const override; +}; + +// 主窗口类 +class MainWindow : public QWidget { + Q_OBJECT +public: + explicit MainWindow(QWidget* parent = nullptr); + +private slots: + void onItemDoubleClicked(QListWidgetItem* item); + void showContextMenu(const QPoint& pos); + void sendToDevice(QListWidgetItem* item); + void editItem(QListWidgetItem* item); + void deleteItem(QListWidgetItem* item); + void showInExplorer(QListWidgetItem* item); + +private: + void addCardItem(const CardData& data); + void setupUI(); + void setupStyle(); + + QListWidget* listWidget; +}; + + +#endif // CARD_LIST_WIDGET_H \ No newline at end of file diff --git a/widget_file_list/main.cpp b/widget_file_list/main.cpp new file mode 100644 index 0000000..1cf9de4 --- /dev/null +++ b/widget_file_list/main.cpp @@ -0,0 +1,30 @@ +#include "QtWidgetsApplication4.h" +#include + +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +// 注册自定义数据类型 +Q_DECLARE_METATYPE(CardData) + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + + // 设置编码和字体 + //QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); + QFont font("Microsoft YaHei", 9); + app.setFont(font); + + MainWindow window; + window.show(); + + return app.exec(); +} diff --git a/widgets/__pycache__/audio_filter_widget.cpython-313.pyc b/widgets/__pycache__/audio_filter_widget.cpython-313.pyc index 4993e0d2df02bbba590eba8ac731b85ad4111e9d..67e11559c9e95e1052c4be57aca7d284ab846f0b 100644 GIT binary patch delta 5925 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 delta 4887 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 delta 625 zcmXAmO=uHQ5Xawax|uRi~#y+Wsw-n~_;kwU6m!1I^p3uATj@};*h@pS)v0Z5j z`78AfP`gO4Joc}50@VmbUma9hsC!36MVQBbs`P|s-TVf2mO_5^U6BDHz3U?8(8s;$3>_BGC2?SN7Usp}bJ+vhL${tRq?i X$gKA7Fv>f5#}q(dHNMLz@45Z~*ioHN diff --git a/widgets/audio_filter_widget.py b/widgets/audio_filter_widget.py index 5b8b5ab..0abd8f6 100644 --- a/widgets/audio_filter_widget.py +++ b/widgets/audio_filter_widget.py @@ -1,8 +1,9 @@ -from PySide6.QtWidgets import QWidget, QDialog, QVBoxLayout, QComboBox, QPushButton, QLabel, QTableWidgetItem, QAbstractScrollArea +from PySide6.QtWidgets import QWidget, QDialog, QVBoxLayout, QComboBox, QPushButton, QLabel, QTableWidgetItem, QAbstractScrollArea, QHBoxLayout from PySide6.QtCore import Qt from widgets.Ui_widget import Ui_Widget from database.db_manager import DatabaseManager -from database.models import FilterData, ParamData +from database.models import FilterData, ParamData, ConfigData +from typing import List class AudioFilterModel: def __init__(self, db_manager: DatabaseManager): @@ -253,13 +254,14 @@ class AudioFilterWidget(QWidget): self.model = AudioFilterModel(self.db_manager) self.model.current_channel_id = channel_id # 设置当前通道ID - # 加载该通道的默认配置 + # 加载该通道的所有配置 configs = self.db_manager.get_all_configs() if configs: - # 查找当前通道的配置 - channel_config = next((config for config in configs if config.channel_id == channel_id), None) - if channel_config: - self.model.load_config(channel_config.id) + # 获取当前通道的所有配置 + channel_configs = [config for config in configs if config.channel_id == channel_id] + if channel_configs: + # 可以让用户选择要加载哪个配置 + self.show_config_selection_dialog(channel_configs) self.controller = AudioFilterController(self.model, self) self.update_view() @@ -442,3 +444,32 @@ class AudioFilterWidget(QWidget): except ValueError: print("输入的值无效,请输入数字") + + def show_config_selection_dialog(self, configs: List[ConfigData]): + dialog = QDialog(self) + dialog.setWindowTitle("选择配置") + layout = QVBoxLayout() + + combo = QComboBox() + for config in configs: + combo.addItem(f"{config.name} (创建于: {config.created_at})", config.id) + + layout.addWidget(QLabel("请选择要加载的配置:")) + layout.addWidget(combo) + + buttons = QHBoxLayout() + ok_button = QPushButton("确定") + cancel_button = QPushButton("取消") + + ok_button.clicked.connect(dialog.accept) + cancel_button.clicked.connect(dialog.reject) + + buttons.addWidget(ok_button) + buttons.addWidget(cancel_button) + layout.addLayout(buttons) + + dialog.setLayout(layout) + + if dialog.exec() == QDialog.Accepted: + selected_config_id = combo.currentData() + self.model.load_config(selected_config_id) diff --git a/widgets/avas_widget.py b/widgets/avas_widget.py index a0d867e..ab6b78c 100644 --- a/widgets/avas_widget.py +++ b/widgets/avas_widget.py @@ -99,18 +99,22 @@ class ChannelButton(QWidget): self.channel_btn.clicked.connect(self.show_filter_window) def show_filter_window(self): - if not self.filter_window: - # 创建滤波器窗口时传入对应的通道号 - self.filter_window = AudioFilterWidget(channel_id=self.channel_num) - 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 + # 如果窗口已存在且可见,则关闭它 + if self.filter_window and self.filter_window.isVisible(): self.filter_window.raise_() self.filter_window.activateWindow() + else: + # 如果窗口不存在或已关闭,创建新窗口 + self.filter_window = AudioFilterWidget(channel_id=self.channel_num) + self.filter_window.setWindowTitle(f"Channel {self.group.title()} Filter Settings") + + # 监听窗口关闭事件,在窗口关闭时重置 filter_window + self.filter_window.destroyed.connect(self._on_filter_window_closed) + self.filter_window.show() + + def _on_filter_window_closed(self): + """当滤波器窗口关闭时调用""" + self.filter_window = None class AVAS_WIDGET(QWidget): def __init__(self):