[feature] 新增配置选择按钮

This commit is contained in:
Sam 2025-02-16 21:24:51 +08:00
parent bdc2bd9ba7
commit ae09c4f5be
11 changed files with 501 additions and 40 deletions

Binary file not shown.

View File

@ -121,35 +121,69 @@ class DatabaseManager:
def save_config(self, name: str, channel_id: int, params: ParamData, filters: List[FilterData]) -> int: def save_config(self, name: str, channel_id: int, params: ParamData, filters: List[FilterData]) -> int:
with self.get_connection() as conn: with self.get_connection() as conn:
# 检查是否存在相同的配置
cursor = conn.execute( cursor = conn.execute(
"INSERT INTO configs (name, channel_id) VALUES (?, ?)", "SELECT id FROM configs WHERE name = ? AND channel_id = ?",
(name, channel_id) (name, channel_id)
) )
config_id = cursor.lastrowid existing_config = cursor.fetchone()
params.config_id = config_id if existing_config:
conn.execute( # 更新现有配置
"""INSERT INTO params config_id = existing_config[0]
(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 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( cursor = conn.execute(
"""INSERT INTO filters "INSERT INTO configs (name, channel_id) VALUES (?, ?)",
(config_id, channel_id, filter_type, freq, q, gain, slope, enabled, position) (name, channel_id)
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 config_id = cursor.lastrowid
conn.commit() params.config_id = config_id
return 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): def update_filter(self, filter_id: int, **kwargs):
with self.get_connection() as conn: with self.get_connection() as conn:

BIN
widget_file_list.zip Normal file

Binary file not shown.

View File

@ -0,0 +1,303 @@
#include "QtWidgetsApplication4.h"
#include <QPainter>
#include <QApplication>
#include <QVBoxLayout>
#include <QMenu>
#include <QDate>
#include <QProcess>
#include <QDir>
#include <QDebug>
//#include <QTextCodec>
#include <QFileInfo>
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<CardData>();
// 绘制卡片背景
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<CardData>();
data.activated = false;
currentItem->setData(Qt::UserRole, QVariant::fromValue(data));
}
// 设置当前点击项目为激活状态
CardData data = item->data(Qt::UserRole).value<CardData>();
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<CardData>();
qDebug() << "发送到设备:" << data.name;
}
void MainWindow::editItem(QListWidgetItem* item)
{
CardData data = item->data(Qt::UserRole).value<CardData>();
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
}

View File

@ -0,0 +1,59 @@
#ifndef CARD_LIST_WIDGET_H
#define CARD_LIST_WIDGET_H
#include <QWidget>
#include <QListWidget>
#include <QStyledItemDelegate>
#include <QPainterPath>
// 卡片数据结构
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

30
widget_file_list/main.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "QtWidgetsApplication4.h"
#include <QtWidgets/QApplication>
#include <QPainter>
#include <QVBoxLayout>
#include <QMenu>
#include <QDate>
#include <QProcess>
#include <QDir>
#include <QDebug>
//#include <QTextCodec>
#include <QFileInfo>
// 注册自定义数据类型
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();
}

View File

@ -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 PySide6.QtCore import Qt
from widgets.Ui_widget import Ui_Widget from widgets.Ui_widget import Ui_Widget
from database.db_manager import DatabaseManager 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: class AudioFilterModel:
def __init__(self, db_manager: DatabaseManager): def __init__(self, db_manager: DatabaseManager):
@ -253,13 +254,14 @@ class AudioFilterWidget(QWidget):
self.model = AudioFilterModel(self.db_manager) self.model = AudioFilterModel(self.db_manager)
self.model.current_channel_id = channel_id # 设置当前通道ID self.model.current_channel_id = channel_id # 设置当前通道ID
# 加载该通道的默认配置 # 加载该通道的所有配置
configs = self.db_manager.get_all_configs() configs = self.db_manager.get_all_configs()
if configs: if configs:
# 查找当前通道的配置 # 获取当前通道的所有配置
channel_config = next((config for config in configs if config.channel_id == channel_id), None) channel_configs = [config for config in configs if config.channel_id == channel_id]
if channel_config: if channel_configs:
self.model.load_config(channel_config.id) # 可以让用户选择要加载哪个配置
self.show_config_selection_dialog(channel_configs)
self.controller = AudioFilterController(self.model, self) self.controller = AudioFilterController(self.model, self)
self.update_view() self.update_view()
@ -442,3 +444,32 @@ class AudioFilterWidget(QWidget):
except ValueError: except ValueError:
print("输入的值无效,请输入数字") 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)

View File

@ -99,18 +99,22 @@ class ChannelButton(QWidget):
self.channel_btn.clicked.connect(self.show_filter_window) self.channel_btn.clicked.connect(self.show_filter_window)
def show_filter_window(self): def show_filter_window(self):
if not self.filter_window: # 如果窗口已存在且可见,则关闭它
# 创建滤波器窗口时传入对应的通道号 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 = AudioFilterWidget(channel_id=self.channel_num)
self.filter_window.setWindowTitle(f"Channel {self.group.title()} Filter Settings") self.filter_window.setWindowTitle(f"Channel {self.group.title()} Filter Settings")
# Show the window if it's not visible # 监听窗口关闭事件,在窗口关闭时重置 filter_window
if not self.filter_window.isVisible(): self.filter_window.destroyed.connect(self._on_filter_window_closed)
self.filter_window.show() self.filter_window.show()
else:
# If already visible, bring to front def _on_filter_window_closed(self):
self.filter_window.raise_() """当滤波器窗口关闭时调用"""
self.filter_window.activateWindow() self.filter_window = None
class AVAS_WIDGET(QWidget): class AVAS_WIDGET(QWidget):
def __init__(self): def __init__(self):