refactor: use tabs for selected instrument tables
This commit is contained in:
@@ -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))
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user