Files
QTradeProgram/Sqbase/qbigorderviewer.cpp
2025-08-31 23:07:06 +08:00

350 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "qbigorderviewer.h"
#include "qbigordermanager.h"
#include "qhistoryorderdialog.h"
#include <QHeaderView>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QFileDialog>
#include <QTextStream>
#include <QLineEdit>
#include <QComboBox>
#include <QTableView>
#include <QPushButton>
#include <QWidget>
#include <QCheckBox>
// ==================== QBigOrderViewer ====================
QBigOrderViewer::QBigOrderViewer(QWidget *parent)
: QWidget(parent),
m_model(new QStandardItemModel(0, 7, this)),
m_proxyModel(new QSortFilterProxyModel(this)),
m_typeDelegate(new OrderTypeDelegate(this)),
m_lastOrderDelegate(new HighlightDelegate(this))
{
initUI();
initConnections();
QBigOrderManager* manager = QBigOrderManager::instance();
connect(manager, &QBigOrderManager::bigOrderAdded,
this, &QBigOrderViewer::onBigOrderAdded);
connect(manager, &QBigOrderManager::saveOverRefreshView,
this, [this]() {
m_model->removeRows(0, m_model->rowCount());
}, Qt::QueuedConnection // 必须使用跨线程连接
);
}
QBigOrderViewer::~QBigOrderViewer()
{
delete m_model;
delete m_proxyModel;
delete m_typeDelegate;
}
void QBigOrderViewer::initUI()
{
QVBoxLayout *mainLayout = new QVBoxLayout(this);
// 过滤控件区域
QHBoxLayout *filterLayout = new QHBoxLayout();
QCheckBox *cboAutoSave = new QCheckBox("自动保存数据");
cboAutoSave->setChecked(true);
filterLayout->addWidget(cboAutoSave);
filterLayout->addWidget(new QLabel("股票代码:"));
m_stockCodeFilter = new QLineEdit();
m_stockCodeFilter->setPlaceholderText("支持模糊搜索");
filterLayout->addWidget(m_stockCodeFilter);
filterLayout->addWidget(new QLabel("订单类型:"));
m_orderTypeFilter = new QComboBox();
m_orderTypeFilter->addItem("全部", "");
m_orderTypeFilter->addItem("买入", "BID");
m_orderTypeFilter->addItem("卖出", "ASK");
filterLayout->addWidget(m_orderTypeFilter);
filterLayout->addStretch();
// 添加历史查询按钮
QPushButton *historyButton = new QPushButton("历史查询");
filterLayout->addWidget(historyButton);
m_exportButton = new QPushButton("导出数据");
filterLayout->addWidget(m_exportButton);
mainLayout->addLayout(filterLayout);
// 表格视图配置
m_tableView = new QTableView();
m_proxyModel->setSourceModel(m_model);
m_tableView->setModel(m_proxyModel);
m_tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
// 设置表头(股票代码、名称、类型、数量、价格、订单数、时间)
m_model->setHorizontalHeaderLabels(
QStringList()
<< "股票代码"
<< "股票名称"
<< "订单类型"
<< "股票数量"
<< "股票价格"
<< "订单档位"
<< "时间"
);
// 表格属性
m_tableView->setItemDelegateForColumn(2, m_typeDelegate);
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_tableView->setSortingEnabled(true);
// m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 设置高亮委托到所有列
m_tableView->setItemDelegate(m_lastOrderDelegate);
// 默认按时间列第6列索引5升序排序 -> 新插入的行放在最顶上,故修改为降序排序 @2025.08.30 stone
const int timeColumnIndex = 6;
m_proxyModel->sort(timeColumnIndex, Qt::DescendingOrder);
mainLayout->addWidget(m_tableView);
// 连接历史查询按钮
connect(historyButton, &QPushButton::clicked, this, [this] {
QHistoryOrderDialog *dialog = new QHistoryOrderDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
});
connect(cboAutoSave, &QCheckBox::stateChanged, [](int state) {
if (state == Qt::Checked) {
QBigOrderManager::instance()->setAutoSave(true);
// 复选框被选中
}
else if (state == Qt::Unchecked) {
QBigOrderManager::instance()->setAutoSave(false);
// 复选框被取消选中
}
});
}
void QBigOrderViewer::initConnections()
{
connect(m_stockCodeFilter, &QLineEdit::textChanged,
this, &QBigOrderViewer::onFilterChanged);
connect(m_orderTypeFilter, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &QBigOrderViewer::onFilterChanged);
connect(m_exportButton, &QPushButton::clicked,
this, &QBigOrderViewer::onExportClicked);
// connect(m_tableView->horizontalHeader(), &QHeaderView::sectionClicked,
// m_proxyModel, &QSortFilterProxyModel::sort);
// // 表头点击排序使用lambda适配参数
connect(m_tableView->horizontalHeader(), &QHeaderView::sectionClicked,
[this](int logicalIndex) {
Qt::SortOrder currentOrder = m_proxyModel->sortOrder();
m_proxyModel->sort(logicalIndex,
currentOrder == Qt::AscendingOrder
? Qt::AscendingOrder
: Qt::DescendingOrder);
});
QObject::connect(m_tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
[&](const QItemSelection &selected, const QItemSelection &deselected) {
Q_UNUSED(deselected); // 未使用deselected避免警告
// 获取当前选中的所有行
QModelIndexList selectedIndexes = m_tableView->selectionModel()->selectedIndexes();
// 如果没有选中任何项
if (selectedIndexes.isEmpty()) {
return;
}
emit sendStockCodeToMainForm(m_proxyModel->index(selectedIndexes.at(0).row(), selectedIndexes.at(0).column()).data().toString());
});
}
void QBigOrderViewer::updateView()
{
m_model->removeRows(0, m_model->rowCount());
for (const auto& order : m_currentOrders) {
QList<QStandardItem*> rowItems;
rowItems << new QStandardItem(order->code);
rowItems << new QStandardItem(order->name);
int nType = order->nBigOrderType;
if(nType)
rowItems << new QStandardItem("多单");
else
rowItems << new QStandardItem("空单");
rowItems << new QStandardItem(QString::number(std::fabs(order->volume/1000)) + "K");
rowItems << new QStandardItem(QString::number(order->price, 'f', 2));
rowItems << new QStandardItem(QString::number(order->level));
QString str = order->svrRecvTime.mid(11);
if (str == nullptr)
{
QDateTime dateTime = QDateTime::currentDateTime();
str = dateTime.toString("hh:mm:ss");
}
rowItems << new QStandardItem(str);
m_model->appendRow(rowItems);
}
}
void QBigOrderViewer::applyFilters()
{
QString stockCode = m_stockCodeFilter->text().trimmed();
QString orderType = m_orderTypeFilter->currentData().toString();
m_currentOrders.clear();
for (const auto& order : m_allOrders) {
bool match = true;
if (!stockCode.isEmpty() && !order->code.contains(stockCode, Qt::CaseInsensitive)) {
match = false;
}
if (!orderType.isEmpty() && order->nBigOrderType != orderType) {
match = false;
}
if (match) m_currentOrders.append(order);
}
}
//void QBigOrderViewer::onBigOrderAdded(const BigOrderInfo &order)
//{
// auto newOrder = QSharedPointer<BigOrderInfo>::create(order);
// m_allOrders.append(newOrder);
// if (matchesFilter(newOrder)) {
// m_currentOrders.append(newOrder);
//
// // 高效插入单行
// int row = m_model->rowCount();
// m_model->insertRow(row);
// setRowData(row, newOrder);
// }
//}
void QBigOrderViewer::onBigOrderAdded(const BigOrderInfo &order)
{
auto newOrder = QSharedPointer<BigOrderInfo>::create(order);
m_allOrders.append(newOrder);
// 应用过滤条件
if (matchesFilter(newOrder)) {
m_currentOrders.append(newOrder);
// 在源模型的首行插入
int row = 0 ;
m_model->insertRow(row);
setRowData(row, newOrder);
// 设置高亮标记
for (int col = 0; col < m_model->columnCount(); ++col) {
QModelIndex index = m_model->index(row, col);
m_model->setData(index, true, IsHighlightedRole);
}
// 通知视图更新
QModelIndex topLeft = m_model->index(row, 0);
QModelIndex bottomRight = m_model->index(row, m_model->columnCount() - 1);
emit m_model->dataChanged(topLeft, bottomRight);
// 2秒后取消高亮
QTimer::singleShot(2000, this, [this, row]() {
for (int col = 0; col < m_model->columnCount(); ++col) {
QModelIndex index = m_model->index(row, col);
m_model->setData(index, false, IsHighlightedRole);
}
// 通知视图更新
QModelIndex topLeft = m_model->index(row, 0);
QModelIndex bottomRight = m_model->index(row, m_model->columnCount() - 1);
emit m_model->dataChanged(topLeft, bottomRight);
});
// 确保代理模型按时间降序排序,新数据在顶部
m_proxyModel->sort(6, Qt::DescendingOrder);
}
}
// 新增辅助函数
void QBigOrderViewer::setRowData(int row, QSharedPointer<BigOrderInfo> order)
{
m_model->setData(m_model->index(row, 0), order->code);
m_model->setData(m_model->index(row, 1), order->name);
m_model->setData(m_model->index(row, 2),
order->nBigOrderType == 0 ? "卖出" : "买入");
m_model->setData(m_model->index(row, 3),
QString::number(std::fabs(order->volume / 1000)) + "K");
m_model->setData(m_model->index(row, 4),
QString::number(order->price, 'f', 2));
m_model->setData(m_model->index(row, 5),
QString::number(order->level));
// 确保时间格式正确,用于排序
QString timeStr;
if (order->svrRecvTime.length() >= 11) {
timeStr = order->svrRecvTime.mid(11);
}
else {
QDateTime dateTime = QDateTime::currentDateTime();
timeStr = dateTime.toString("hh:mm:ss");
}
m_model->setData(m_model->index(row, 6), timeStr);
// 添加一个隐藏的时间戳列用于精确排序
QDateTime fullDateTime;
if (order->svrRecvTime.length() >= 19) {
fullDateTime = QDateTime::fromString(order->svrRecvTime.left(19), "yyyy-MM-dd hh:mm:ss");
}
else {
fullDateTime = QDateTime::currentDateTime();
}
m_model->setData(m_model->index(row, 6), fullDateTime, Qt::UserRole + 101); // 使用自定义角色存储完整时间戳
}
bool QBigOrderViewer::matchesFilter(QSharedPointer<BigOrderInfo> order)
{
QString stockCode = m_stockCodeFilter->text().trimmed();
QString orderType = m_orderTypeFilter->currentData().toString();
return (stockCode.isEmpty() || order->code.contains(stockCode, Qt::CaseInsensitive))
&& (orderType.isEmpty() || order->nBigOrderType == orderType);
}
void QBigOrderViewer::onFilterChanged()
{
applyFilters();
updateView();
}
void QBigOrderViewer::onExportClicked()
{
QString fileName = QFileDialog::getSaveFileName(this, "导出CSV", "", "CSV文件 (*.csv)");
if (fileName.isEmpty()) return;
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
QTextStream out(&file);
out.setCodec("GBK");
// 写入表头
for (int col = 0; col < m_model->columnCount(); ++col) {
out << m_model->headerData(col, Qt::Horizontal).toString();
if (col < m_model->columnCount() - 1) out << ",";
}
out << "\n";
// 写入数据
for (int row = 0; row < m_proxyModel->rowCount(); ++row) {
for (int col = 0; col < m_proxyModel->columnCount(); ++col) {
out << m_proxyModel->index(row, col).data().toString();
if (col < m_proxyModel->columnCount() - 1) out << ",";
}
out << "\n";
}
file.close();
}