diff --git a/data/database.db b/data/database.db index c9cae12..7d12544 100644 Binary files a/data/database.db and b/data/database.db differ diff --git a/database/__pycache__/db_manager.cpython-313.pyc b/database/__pycache__/db_manager.cpython-313.pyc index d458632..d41ab2b 100644 Binary files a/database/__pycache__/db_manager.cpython-313.pyc and b/database/__pycache__/db_manager.cpython-313.pyc differ 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 0000000..7a252b0 Binary files /dev/null and b/widget_file_list.zip differ diff --git a/widget_file_list/QtWidgetsApplication4.cpp b/widget_file_list/QtWidgetsApplication4.cpp new file mode 100644 index 0000000..1efb17a --- /dev/null +++ b/widget_file_list/QtWidgetsApplication4.cpp @@ -0,0 +1,303 @@ +#include "QtWidgetsApplication4.h" +#include +#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 4993e0d..67e1155 100644 Binary files a/widgets/__pycache__/audio_filter_widget.cpython-313.pyc and b/widgets/__pycache__/audio_filter_widget.cpython-313.pyc differ diff --git a/widgets/__pycache__/avas_widget.cpython-313.pyc b/widgets/__pycache__/avas_widget.cpython-313.pyc index 8df9d26..3af0af5 100644 Binary files a/widgets/__pycache__/avas_widget.cpython-313.pyc and b/widgets/__pycache__/avas_widget.cpython-313.pyc differ 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):