[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:
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:

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

View File

@ -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):