refactor: use tabs for selected instrument tables

This commit is contained in:
王鹏
2026-07-09 11:57:15 +08:00
parent 5d66423895
commit 8b51463387
2 changed files with 42 additions and 43 deletions

View File

@@ -9,7 +9,6 @@ from PySide6.QtWidgets import (
QDialog, QDialog,
QFrame, QFrame,
QGridLayout, QGridLayout,
QGroupBox,
QHBoxLayout, QHBoxLayout,
QHeaderView, QHeaderView,
QLabel, QLabel,
@@ -19,6 +18,7 @@ from PySide6.QtWidgets import (
QPushButton, QPushButton,
QSplitter, QSplitter,
QSpinBox, QSpinBox,
QTabWidget,
QTableWidget, QTableWidget,
QTableWidgetItem, QTableWidgetItem,
QVBoxLayout, QVBoxLayout,
@@ -128,26 +128,10 @@ class MainWindow(QMainWindow):
self.holdings_table.itemSelectionChanged.connect(self._on_holding_selected) self.holdings_table.itemSelectionChanged.connect(self._on_holding_selected)
splitter.addWidget(self.holdings_table) splitter.addWidget(self.holdings_table)
details = QSplitter(Qt.Orientation.Horizontal) self.details_tabs = QTabWidget()
self.detail_box = QGroupBox("选中标的详情")
self.detail_layout = QGridLayout(self.detail_box)
self.detail_labels = {
"price_source": QLabel("-"),
"cost": QLabel("-"),
"breakeven": QLabel("-"),
"profit": QLabel("-"),
}
self.detail_layout.addWidget(QLabel("价格来源"), 0, 0)
self.detail_layout.addWidget(self.detail_labels["price_source"], 0, 1)
self.detail_layout.addWidget(QLabel("剩余成本"), 1, 0)
self.detail_layout.addWidget(self.detail_labels["cost"], 1, 1)
self.detail_layout.addWidget(QLabel("回本价"), 2, 0)
self.detail_layout.addWidget(self.detail_labels["breakeven"], 2, 1)
self.detail_layout.addWidget(QLabel("利润"), 3, 0)
self.detail_layout.addWidget(self.detail_labels["profit"], 3, 1)
grid_group = QGroupBox("网格档位") grid_tab = QWidget()
grid_layout = QVBoxLayout(grid_group) grid_layout = QVBoxLayout(grid_tab)
grid_controls = QHBoxLayout() grid_controls = QHBoxLayout()
self.grid_levels_count_edit = QSpinBox() self.grid_levels_count_edit = QSpinBox()
self.grid_levels_count_edit.setRange(1, 100) self.grid_levels_count_edit.setRange(1, 100)
@@ -166,8 +150,8 @@ class MainWindow(QMainWindow):
grid_layout.addLayout(grid_controls) grid_layout.addLayout(grid_controls)
grid_layout.addWidget(self.grid_levels_table) grid_layout.addWidget(self.grid_levels_table)
open_grid_group = QGroupBox("待卖网格") open_grid_tab = QWidget()
open_grid_layout = QVBoxLayout(open_grid_group) open_grid_layout = QVBoxLayout(open_grid_tab)
self.open_grid_lots_table = QTableWidget(0, len(self.OPEN_GRID_LOT_COLUMNS)) self.open_grid_lots_table = QTableWidget(0, len(self.OPEN_GRID_LOT_COLUMNS))
self.open_grid_lots_table.setHorizontalHeaderLabels(self.OPEN_GRID_LOT_COLUMNS) self.open_grid_lots_table.setHorizontalHeaderLabels(self.OPEN_GRID_LOT_COLUMNS)
self.open_grid_lots_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) self.open_grid_lots_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
@@ -175,8 +159,8 @@ class MainWindow(QMainWindow):
self.open_grid_lots_table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) self.open_grid_lots_table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
open_grid_layout.addWidget(self.open_grid_lots_table) open_grid_layout.addWidget(self.open_grid_lots_table)
trade_group = QGroupBox("最近成交") trade_tab = QWidget()
trade_layout = QVBoxLayout(trade_group) trade_layout = QVBoxLayout(trade_tab)
trade_buttons = QHBoxLayout() trade_buttons = QHBoxLayout()
edit_trade_button = QPushButton("编辑成交") edit_trade_button = QPushButton("编辑成交")
edit_trade_button.clicked.connect(self._edit_selected_trade) edit_trade_button.clicked.connect(self._edit_selected_trade)
@@ -193,11 +177,11 @@ class MainWindow(QMainWindow):
trade_layout.addLayout(trade_buttons) trade_layout.addLayout(trade_buttons)
trade_layout.addWidget(self.trades_table) trade_layout.addWidget(self.trades_table)
details.addWidget(self.detail_box) self.details_tabs.addTab(grid_tab, "网格档位")
details.addWidget(grid_group) self.details_tabs.addTab(open_grid_tab, "待卖网格")
details.addWidget(open_grid_group) self.details_tabs.addTab(trade_tab, "最近成交")
details.addWidget(trade_group) self.details_tabs.setCurrentIndex(1)
splitter.addWidget(details) splitter.addWidget(self.details_tabs)
splitter.setSizes([470, 230]) splitter.setSizes([470, 230])
content_layout.addWidget(splitter) content_layout.addWidget(splitter)
@@ -341,20 +325,10 @@ class MainWindow(QMainWindow):
def _refresh_details(self) -> None: def _refresh_details(self) -> None:
position = self._selected_position() position = self._selected_position()
if position is None: if position is None:
for label in self.detail_labels.values():
label.setText("-")
self._refresh_grid_levels(None) self._refresh_grid_levels(None)
self._refresh_open_grid_lots(None) self._refresh_open_grid_lots(None)
self._fill_trades_table([]) self._fill_trades_table([])
return return
self.detail_labels["price_source"].setText(position.price_source)
self.detail_labels["cost"].setText(format_money(position.remaining_cost))
self.detail_labels["breakeven"].setText(
f"持仓 {format_price(position.position_breakeven_price)} / 账户 {format_price(position.account_breakeven_price)}"
)
self.detail_labels["profit"].setText(
f"已实现 {format_money(position.realized_pnl)} / 网格 {format_money(position.grid_profit)}"
)
self._refresh_grid_levels(position) self._refresh_grid_levels(position)
self._refresh_open_grid_lots(position) self._refresh_open_grid_lots(position)
self._fill_trades_table(self.service.list_trades(instrument_id=position.instrument_id)) self._fill_trades_table(self.service.list_trades(instrument_id=position.instrument_id))

View File

@@ -38,7 +38,7 @@ def test_main_window_can_be_constructed_offscreen(tmp_path, monkeypatch):
def test_main_window_contains_grid_level_table(tmp_path, monkeypatch): def test_main_window_contains_grid_level_table(tmp_path, monkeypatch):
monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen") monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen")
from PySide6.QtWidgets import QApplication, QGroupBox from PySide6.QtWidgets import QApplication, QTabWidget
from grid_trading.services.trading_service import TradingService from grid_trading.services.trading_service import TradingService
from grid_trading.ui.main_window import MainWindow from grid_trading.ui.main_window import MainWindow
@@ -48,7 +48,9 @@ def test_main_window_contains_grid_level_table(tmp_path, monkeypatch):
window = MainWindow(service) window = MainWindow(service)
assert window.grid_levels_table.columnCount() == 7 assert window.grid_levels_table.columnCount() == 7
assert any(group.title() == "网格档位" for group in window.findChildren(QGroupBox)) tab_titles = _tab_titles(window.details_tabs)
assert tab_titles == ["网格档位", "待卖网格", "最近成交"]
assert isinstance(window.details_tabs, QTabWidget)
window.close() window.close()
service.close() service.close()
@@ -58,6 +60,26 @@ def test_main_window_contains_grid_level_table(tmp_path, monkeypatch):
def test_main_window_contains_open_grid_lots_table(tmp_path, monkeypatch): def test_main_window_contains_open_grid_lots_table(tmp_path, monkeypatch):
monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen") monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen")
from PySide6.QtWidgets import QApplication
from grid_trading.services.trading_service import TradingService
from grid_trading.ui.main_window import MainWindow
app = QApplication.instance() or QApplication([])
service = TradingService(tmp_path / "grid.db")
window = MainWindow(service)
assert window.open_grid_lots_table.columnCount() == 8
assert "待卖网格" in _tab_titles(window.details_tabs)
window.close()
service.close()
app.processEvents()
def test_main_window_removes_selected_instrument_detail_group(tmp_path, monkeypatch):
monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen")
from PySide6.QtWidgets import QApplication, QGroupBox from PySide6.QtWidgets import QApplication, QGroupBox
from grid_trading.services.trading_service import TradingService from grid_trading.services.trading_service import TradingService
@@ -67,8 +89,7 @@ def test_main_window_contains_open_grid_lots_table(tmp_path, monkeypatch):
service = TradingService(tmp_path / "grid.db") service = TradingService(tmp_path / "grid.db")
window = MainWindow(service) window = MainWindow(service)
assert window.open_grid_lots_table.columnCount() == 8 assert not any(group.title() == "选中标的详情" for group in window.findChildren(QGroupBox))
assert any(group.title() == "待卖网格" for group in window.findChildren(QGroupBox))
window.close() window.close()
service.close() service.close()
@@ -116,6 +137,10 @@ def test_trade_dialog_labels_price_as_trade_price(tmp_path, monkeypatch):
app.processEvents() app.processEvents()
def _tab_titles(tabs):
return [tabs.tabText(index) for index in range(tabs.count())]
def test_quote_refresh_does_not_block_main_window(tmp_path, monkeypatch): def test_quote_refresh_does_not_block_main_window(tmp_path, monkeypatch):
monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen") monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen")