Files
QTradeProgram/Sqbase/qbigorderviewer.cpp

398 lines
13 KiB
C++
Raw Normal View History

2025-08-15 15:56:40 +08:00
#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, 8, this)),
2025-08-15 15:56:40 +08:00
m_proxyModel(new QSortFilterProxyModel(this)),
2025-08-31 23:07:06 +08:00
m_typeDelegate(new OrderTypeDelegate(this)),
m_lastOrderDelegate(new HighlightDelegate(this))
2025-08-15 15:56:40 +08:00
{
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);
// 设置表头(股票代码、名称、类型、数量、价格、订单数、时间、时间戳)
QStringList headers;
headers << "股票代码" << "股票名称" << "订单类型"
<< "股票数量" << "股票价格" << "订单档位"
<< "时间"<< "Timestamp";
m_model->setHorizontalHeaderLabels(headers);
m_tableView->setColumnHidden(7, true);
2025-08-15 15:56:40 +08:00
2025-09-01 11:08:46 +08:00
// 设置表头文字居中
for (int col = 0; col < m_model->columnCount(); ++col) {
m_model->setHeaderData(col, Qt::Horizontal, Qt::AlignCenter, Qt::TextAlignmentRole);
}
2025-08-15 15:56:40 +08:00
// 表格属性
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);
2025-08-31 23:07:06 +08:00
// 设置高亮委托到所有列
m_tableView->setItemDelegate(m_lastOrderDelegate);
// 默认按时间列第7列索引6升序排序 -> 新插入的行放在最顶上,故修改为降序排序 @2025.08.30 stone
2025-08-15 15:56:40 +08:00
const int timeColumnIndex = 6;
2025-08-31 23:07:06 +08:00
m_proxyModel->sort(timeColumnIndex, Qt::DescendingOrder);
2025-08-15 15:56:40 +08:00
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()
{
2025-09-01 11:08:46 +08:00
m_model->removeRows(0, m_model->rowCount());
for (const auto& order : m_currentOrders) {
QList<QStandardItem*> rowItems;
// 创建项并设置文本居中
QStandardItem* codeItem = new QStandardItem(order->code);
codeItem->setTextAlignment(Qt::AlignCenter);
rowItems << codeItem;
QStandardItem* nameItem = new QStandardItem(order->name);
nameItem->setTextAlignment(Qt::AlignCenter);
rowItems << nameItem;
2025-08-15 15:56:40 +08:00
int nType = order->nBigOrderType;
2025-09-01 11:08:46 +08:00
QStandardItem* typeItem = new QStandardItem(nType ? "多单" : "空单");
typeItem->setTextAlignment(Qt::AlignCenter);
rowItems << typeItem;
QStandardItem* volumeItem = new QStandardItem(QString::number(std::fabs(order->volume / 1000)) + "K");
volumeItem->setTextAlignment(Qt::AlignCenter);
rowItems << volumeItem;
QStandardItem* priceItem = new QStandardItem(QString::number(order->price, 'f', 2));
priceItem->setTextAlignment(Qt::AlignCenter);
rowItems << priceItem;
QStandardItem* levelItem = new QStandardItem(QString::number(order->level));
levelItem->setTextAlignment(Qt::AlignCenter);
rowItems << levelItem;
2025-08-22 10:59:38 +08:00
QString str = order->svrRecvTime.mid(11);
if (str == nullptr)
{
QDateTime dateTime = QDateTime::currentDateTime();
str = dateTime.toString("hh:mm:ss");
}
2025-09-01 11:08:46 +08:00
QStandardItem* timeItem = new QStandardItem(str);
timeItem->setTextAlignment(Qt::AlignCenter);
rowItems << timeItem;
// 添加时间戳列
QStandardItem* timestampItem = new QStandardItem(order->svrRecvTime);
timestampItem->setTextAlignment(Qt::AlignCenter);
rowItems << timestampItem;
2025-09-01 11:08:46 +08:00
m_model->appendRow(rowItems);
}
2025-08-15 15:56:40 +08:00
}
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);
}
}
2025-08-31 23:07:06 +08:00
//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);
// }
//}
2025-08-15 15:56:40 +08:00
void QBigOrderViewer::onBigOrderAdded(const BigOrderInfo &order)
{
auto newOrder = QSharedPointer<BigOrderInfo>::create(order);
m_allOrders.append(newOrder);
2025-08-31 23:07:06 +08:00
// 应用过滤条件
2025-08-15 15:56:40 +08:00
if (matchesFilter(newOrder)) {
m_currentOrders.append(newOrder);
2025-08-31 23:07:06 +08:00
// 在源模型的首行插入
int row = 0 ;
2025-08-15 15:56:40 +08:00
m_model->insertRow(row);
setRowData(row, newOrder);
2025-08-31 23:07:06 +08:00
2025-09-01 10:02:14 +08:00
// 为订单生成唯一标识
QString orderId = QString("%1_%2_%3")
.arg(order.code)
.arg(QString::number(order.price, 'f', 2))
.arg(QDateTime::currentMSecsSinceEpoch());
2025-08-31 23:07:06 +08:00
// 设置高亮标记
for (int col = 0; col < m_model->columnCount(); ++col) {
QModelIndex index = m_model->index(row, col);
2025-09-01 10:02:14 +08:00
m_model->setData(index, true, IsHighlightedRole);
m_model->setData(index, orderId, Qt::UserRole + 102); // 存储唯一标识
2025-08-31 23:07:06 +08:00
}
// 通知视图更新
QModelIndex topLeft = m_model->index(row, 0);
QModelIndex bottomRight = m_model->index(row, m_model->columnCount() - 1);
emit m_model->dataChanged(topLeft, bottomRight);
// 2秒后取消高亮
2025-09-01 10:02:14 +08:00
QTimer::singleShot(2000, this, [this, orderId]() {
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex index = m_model->index(row, 0);
2025-08-31 23:07:06 +08:00
m_model->setData(index, false, IsHighlightedRole);
2025-09-01 10:02:14 +08:00
QString currentOrderId = index.data(Qt::UserRole + 102).toString();
if (currentOrderId == orderId) {
for (int col = 0; col < m_model->columnCount(); ++col) {
QModelIndex cellIndex = m_model->index(row, col);
m_model->setData(cellIndex, 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);
break; // 找到后退出循环
}
2025-08-31 23:07:06 +08:00
}
});
// 确保代理模型按时间降序排序,新数据在顶部
m_proxyModel->sort(6, Qt::DescendingOrder);
2025-08-15 15:56:40 +08:00
}
}
// 新增辅助函数
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));
2025-08-31 23:07:06 +08:00
// 确保时间格式正确,用于排序
QString timeStr;
if (order->svrRecvTime.length() >= 11) {
timeStr = order->svrRecvTime.mid(11);
}
else {
2025-08-22 10:59:38 +08:00
QDateTime dateTime = QDateTime::currentDateTime();
2025-08-31 23:07:06 +08:00
timeStr = dateTime.toString("hh:mm:ss");
}
m_model->setData(m_model->index(row, 6), timeStr);
// 添加时间戳列第7列
m_model->setData(m_model->index(row, 7), order->svrRecvTime);
2025-08-31 23:07:06 +08:00
// 添加一个隐藏的时间戳列用于精确排序
QDateTime fullDateTime;
if (order->svrRecvTime.length() >= 19) {
fullDateTime = QDateTime::fromString(order->svrRecvTime.left(19), "yyyy-MM-dd hh:mm:ss");
}
else {
fullDateTime = QDateTime::currentDateTime();
2025-08-22 10:59:38 +08:00
}
2025-08-31 23:07:06 +08:00
m_model->setData(m_model->index(row, 6), fullDateTime, Qt::UserRole + 101); // 使用自定义角色存储完整时间戳
2025-08-15 15:56:40 +08:00
}
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();
}