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>
|
|
|
|
|
|
|
|
|
|
|
|
//// ==================== OrderTypeDelegate ====================
|
|
|
|
|
|
//OrderTypeDelegate::OrderTypeDelegate(QObject *parent)
|
|
|
|
|
|
// : QStyledItemDelegate(parent)
|
|
|
|
|
|
//{
|
|
|
|
|
|
//}
|
|
|
|
|
|
//
|
|
|
|
|
|
//void OrderTypeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
|
|
|
|
|
// const QModelIndex &index) const
|
|
|
|
|
|
//{
|
|
|
|
|
|
// if (!index.isValid()) return;
|
|
|
|
|
|
//
|
|
|
|
|
|
// QString type = index.data(Qt::DisplayRole).toString();
|
|
|
|
|
|
// if (type == "买入") {
|
|
|
|
|
|
// painter->fillRect(option.rect, QColor(255, 230, 230)); // 买入订单浅红背景
|
|
|
|
|
|
// } else if (type == "卖出") {
|
|
|
|
|
|
// painter->fillRect(option.rect, QColor(230, 255, 230)); // 卖出订单浅绿背景
|
|
|
|
|
|
// }
|
|
|
|
|
|
// QStyledItemDelegate::paint(painter, option, index);
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== QBigOrderViewer ====================
|
|
|
|
|
|
QBigOrderViewer::QBigOrderViewer(QWidget *parent)
|
|
|
|
|
|
: QWidget(parent),
|
|
|
|
|
|
m_model(new QStandardItemModel(0, 7, this)),
|
|
|
|
|
|
m_proxyModel(new QSortFilterProxyModel(this)),
|
|
|
|
|
|
m_typeDelegate(new OrderTypeDelegate(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);
|
|
|
|
|
|
|
|
|
|
|
|
// 默认按时间列(第6列,索引5)升序排序
|
|
|
|
|
|
const int timeColumnIndex = 6;
|
|
|
|
|
|
m_proxyModel->sort(timeColumnIndex, Qt::AscendingOrder);
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
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");
|
|
|
|
|
|
}
|
|
|
|
|
|
rowItems << new QStandardItem(str);
|
2025-08-15 15:56:40 +08:00
|
|
|
|
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::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-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");
|
|
|
|
|
|
}
|
|
|
|
|
|
m_model->setData(m_model->index(row, 6), str);
|
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();
|
|
|
|
|
|
}
|