commit
523ff044c5
@ -19,3 +19,4 @@ ibapi
|
|||||||
deap
|
deap
|
||||||
pyzmq
|
pyzmq
|
||||||
wmi
|
wmi
|
||||||
|
QScintilla
|
@ -4,6 +4,7 @@ import traceback
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from inspect import getfile
|
||||||
|
|
||||||
from vnpy.event import Event, EventEngine
|
from vnpy.event import Event, EventEngine
|
||||||
from vnpy.trader.engine import BaseEngine, MainEngine
|
from vnpy.trader.engine import BaseEngine, MainEngine
|
||||||
@ -116,6 +117,12 @@ class BacktesterEngine(BaseEngine):
|
|||||||
msg = f"策略文件{module_name}加载失败,触发异常:\n{traceback.format_exc()}"
|
msg = f"策略文件{module_name}加载失败,触发异常:\n{traceback.format_exc()}"
|
||||||
self.write_log(msg)
|
self.write_log(msg)
|
||||||
|
|
||||||
|
def reload_strategy_class(self):
|
||||||
|
""""""
|
||||||
|
self.classes.clear()
|
||||||
|
self.load_strategy_class()
|
||||||
|
self.write_log("策略文件重载刷新完成")
|
||||||
|
|
||||||
def get_strategy_class_names(self):
|
def get_strategy_class_names(self):
|
||||||
""""""
|
""""""
|
||||||
return list(self.classes.keys())
|
return list(self.classes.keys())
|
||||||
@ -425,3 +432,9 @@ class BacktesterEngine(BaseEngine):
|
|||||||
def get_history_data(self):
|
def get_history_data(self):
|
||||||
""""""
|
""""""
|
||||||
return self.backtesting_engine.history_data
|
return self.backtesting_engine.history_data
|
||||||
|
|
||||||
|
def get_strategy_class_file(self, class_name: str):
|
||||||
|
""""""
|
||||||
|
strategy_class = self.classes[class_name]
|
||||||
|
file_path = getfile(strategy_class)
|
||||||
|
return file_path
|
||||||
|
@ -13,6 +13,7 @@ from vnpy.trader.constant import Interval, Direction
|
|||||||
from vnpy.trader.engine import MainEngine
|
from vnpy.trader.engine import MainEngine
|
||||||
from vnpy.trader.ui import QtCore, QtWidgets, QtGui
|
from vnpy.trader.ui import QtCore, QtWidgets, QtGui
|
||||||
from vnpy.trader.ui.widget import BaseMonitor, BaseCell, DirectionCell, EnumCell
|
from vnpy.trader.ui.widget import BaseMonitor, BaseCell, DirectionCell, EnumCell
|
||||||
|
from vnpy.trader.ui.editor import CodeEditor
|
||||||
from vnpy.event import Event, EventEngine
|
from vnpy.event import Event, EventEngine
|
||||||
from vnpy.chart import ChartWidget, CandleItem, VolumeItem
|
from vnpy.chart import ChartWidget, CandleItem, VolumeItem
|
||||||
|
|
||||||
@ -117,6 +118,12 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
self.candle_button.clicked.connect(self.show_candle_chart)
|
self.candle_button.clicked.connect(self.show_candle_chart)
|
||||||
self.candle_button.setEnabled(False)
|
self.candle_button.setEnabled(False)
|
||||||
|
|
||||||
|
edit_button = QtWidgets.QPushButton("代码编辑")
|
||||||
|
edit_button.clicked.connect(self.edit_strategy_code)
|
||||||
|
|
||||||
|
reload_button = QtWidgets.QPushButton("策略重载")
|
||||||
|
reload_button.clicked.connect(self.reload_strategy_class)
|
||||||
|
|
||||||
for button in [
|
for button in [
|
||||||
backtesting_button,
|
backtesting_button,
|
||||||
optimization_button,
|
optimization_button,
|
||||||
@ -125,7 +132,9 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
self.order_button,
|
self.order_button,
|
||||||
self.trade_button,
|
self.trade_button,
|
||||||
self.daily_button,
|
self.daily_button,
|
||||||
self.candle_button
|
self.candle_button,
|
||||||
|
edit_button,
|
||||||
|
reload_button
|
||||||
]:
|
]:
|
||||||
button.setFixedHeight(button.sizeHint().height() * 2)
|
button.setFixedHeight(button.sizeHint().height() * 2)
|
||||||
|
|
||||||
@ -142,18 +151,24 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
form.addRow("回测资金", self.capital_line)
|
form.addRow("回测资金", self.capital_line)
|
||||||
form.addRow("合约模式", self.inverse_combo)
|
form.addRow("合约模式", self.inverse_combo)
|
||||||
|
|
||||||
|
result_grid = QtWidgets.QGridLayout()
|
||||||
|
result_grid.addWidget(self.trade_button, 0, 0)
|
||||||
|
result_grid.addWidget(self.order_button, 0, 1)
|
||||||
|
result_grid.addWidget(self.daily_button, 1, 0)
|
||||||
|
result_grid.addWidget(self.candle_button, 1, 1)
|
||||||
|
|
||||||
left_vbox = QtWidgets.QVBoxLayout()
|
left_vbox = QtWidgets.QVBoxLayout()
|
||||||
left_vbox.addLayout(form)
|
left_vbox.addLayout(form)
|
||||||
left_vbox.addWidget(backtesting_button)
|
left_vbox.addWidget(backtesting_button)
|
||||||
left_vbox.addWidget(downloading_button)
|
left_vbox.addWidget(downloading_button)
|
||||||
left_vbox.addStretch()
|
left_vbox.addStretch()
|
||||||
left_vbox.addWidget(self.trade_button)
|
left_vbox.addLayout(result_grid)
|
||||||
left_vbox.addWidget(self.order_button)
|
|
||||||
left_vbox.addWidget(self.daily_button)
|
|
||||||
left_vbox.addWidget(self.candle_button)
|
|
||||||
left_vbox.addStretch()
|
left_vbox.addStretch()
|
||||||
left_vbox.addWidget(optimization_button)
|
left_vbox.addWidget(optimization_button)
|
||||||
left_vbox.addWidget(self.result_button)
|
left_vbox.addWidget(self.result_button)
|
||||||
|
left_vbox.addStretch()
|
||||||
|
left_vbox.addWidget(edit_button)
|
||||||
|
left_vbox.addWidget(reload_button)
|
||||||
|
|
||||||
# Result part
|
# Result part
|
||||||
self.statistics_monitor = StatisticsMonitor()
|
self.statistics_monitor = StatisticsMonitor()
|
||||||
@ -197,6 +212,9 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
hbox.addWidget(self.chart)
|
hbox.addWidget(self.chart)
|
||||||
self.setLayout(hbox)
|
self.setLayout(hbox)
|
||||||
|
|
||||||
|
# Code Editor
|
||||||
|
self.editor = CodeEditor(self.main_engine, self.event_engine)
|
||||||
|
|
||||||
def register_event(self):
|
def register_event(self):
|
||||||
""""""
|
""""""
|
||||||
self.signal_log.connect(self.process_log_event)
|
self.signal_log.connect(self.process_log_event)
|
||||||
@ -403,6 +421,21 @@ class BacktesterManager(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self.candle_dialog.exec_()
|
self.candle_dialog.exec_()
|
||||||
|
|
||||||
|
def edit_strategy_code(self):
|
||||||
|
""""""
|
||||||
|
class_name = self.class_combo.currentText()
|
||||||
|
file_path = self.backtester_engine.get_strategy_class_file(class_name)
|
||||||
|
|
||||||
|
self.editor.open_editor(file_path)
|
||||||
|
self.editor.show()
|
||||||
|
|
||||||
|
def reload_strategy_class(self):
|
||||||
|
""""""
|
||||||
|
self.backtester_engine.reload_strategy_class()
|
||||||
|
|
||||||
|
self.class_combo.clear()
|
||||||
|
self.init_strategy_settings()
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
""""""
|
""""""
|
||||||
self.showMaximized()
|
self.showMaximized()
|
||||||
|
454
vnpy/trader/ui/editor.py
Normal file
454
vnpy/trader/ui/editor.py
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
from typing import Callable, Dict
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PyQt5 import QtWidgets, Qsci, QtGui
|
||||||
|
|
||||||
|
|
||||||
|
class CodeEditor(QtWidgets.QMainWindow):
|
||||||
|
""""""
|
||||||
|
NEW_FILE_NAME = "Untitled"
|
||||||
|
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
""""""
|
||||||
|
if not cls._instance:
|
||||||
|
cls._instance = QtWidgets.QMainWindow.__new__(cls, *args, **kwargs)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self, main_engine=None, event_engine=None):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.new_file_count = 0
|
||||||
|
self.editor_path_map: Dict[Qsci.QsciScintilla, str] = {}
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setWindowTitle("策略编辑器")
|
||||||
|
|
||||||
|
self.init_menu()
|
||||||
|
self.init_central()
|
||||||
|
|
||||||
|
def init_central(self):
|
||||||
|
""""""
|
||||||
|
self.tab = QtWidgets.QTabWidget()
|
||||||
|
self.tab.currentChanged.connect(self.update_path_label)
|
||||||
|
|
||||||
|
self.path_label = QtWidgets.QLabel()
|
||||||
|
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addWidget(self.tab)
|
||||||
|
vbox.addWidget(self.path_label)
|
||||||
|
|
||||||
|
widget = QtWidgets.QWidget()
|
||||||
|
widget.setLayout(vbox)
|
||||||
|
|
||||||
|
self.setCentralWidget(widget)
|
||||||
|
|
||||||
|
def init_menu(self):
|
||||||
|
""""""
|
||||||
|
bar = self.menuBar()
|
||||||
|
|
||||||
|
file_menu = bar.addMenu("文件")
|
||||||
|
self.add_menu_action(file_menu, "新建文件", self.new_file, "Ctrl+N")
|
||||||
|
self.add_menu_action(file_menu, "打开文件", self.open_file, "Ctrl+O")
|
||||||
|
self.add_menu_action(file_menu, "关闭文件", self.close_editor, "Ctrl+W")
|
||||||
|
file_menu.addSeparator()
|
||||||
|
self.add_menu_action(file_menu, "保存", self.save_file, "Ctrl+S")
|
||||||
|
self.add_menu_action(
|
||||||
|
file_menu,
|
||||||
|
"另存为",
|
||||||
|
self.save_file_as,
|
||||||
|
"Ctrl+Shift+S"
|
||||||
|
)
|
||||||
|
file_menu.addSeparator()
|
||||||
|
self.add_menu_action(file_menu, "退出", self.close)
|
||||||
|
|
||||||
|
edit_menu = bar.addMenu("编辑")
|
||||||
|
self.add_menu_action(edit_menu, "撤销", self.undo, "Ctrl+Z")
|
||||||
|
self.add_menu_action(edit_menu, "恢复", self.redo, "Ctrl+Y")
|
||||||
|
edit_menu.addSeparator()
|
||||||
|
self.add_menu_action(edit_menu, "复制", self.copy, "Ctrl+C")
|
||||||
|
self.add_menu_action(edit_menu, "粘贴", self.paste, "Ctrl+P")
|
||||||
|
self.add_menu_action(edit_menu, "剪切", self.cut, "Ctrl+X")
|
||||||
|
edit_menu.addSeparator()
|
||||||
|
self.add_menu_action(edit_menu, "查找", self.find, "Ctrl+F")
|
||||||
|
self.add_menu_action(edit_menu, "替换", self.replace, "Ctrl+H")
|
||||||
|
|
||||||
|
def add_menu_action(
|
||||||
|
self,
|
||||||
|
menu: QtWidgets.QMenu,
|
||||||
|
action_name: str,
|
||||||
|
func: Callable,
|
||||||
|
shortcut: str = "",
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
action = QtWidgets.QAction(action_name, self)
|
||||||
|
action.triggered.connect(func)
|
||||||
|
menu.addAction(action)
|
||||||
|
|
||||||
|
if shortcut:
|
||||||
|
sequence = QtGui.QKeySequence(shortcut)
|
||||||
|
action.setShortcut(sequence)
|
||||||
|
|
||||||
|
def new_editor(self):
|
||||||
|
""""""
|
||||||
|
# Create editor object
|
||||||
|
editor = Qsci.QsciScintilla()
|
||||||
|
|
||||||
|
# Set editor font
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setFamily('Consolas')
|
||||||
|
font.setFixedPitch(True)
|
||||||
|
font.setPointSize(10)
|
||||||
|
|
||||||
|
editor.setFont(font)
|
||||||
|
editor.setMarginsFont(font)
|
||||||
|
|
||||||
|
# Set margin for line numbers
|
||||||
|
font_metrics = QtGui.QFontMetrics(font)
|
||||||
|
editor.setMarginWidth(0, font_metrics.width("00000") + 6)
|
||||||
|
editor.setMarginLineNumbers(0, True)
|
||||||
|
editor.setMarginsBackgroundColor(QtGui.QColor("#cccccc"))
|
||||||
|
|
||||||
|
# Set brace matching
|
||||||
|
editor.setBraceMatching(Qsci.QsciScintilla.SloppyBraceMatch)
|
||||||
|
|
||||||
|
# Hide horizontal scroll bar
|
||||||
|
editor.SendScintilla(Qsci.QsciScintilla.SCI_SETHSCROLLBAR, 0)
|
||||||
|
|
||||||
|
# Set current line color
|
||||||
|
editor.setCaretLineVisible(True)
|
||||||
|
editor.setCaretLineBackgroundColor(QtGui.QColor("#ffe4e4"))
|
||||||
|
|
||||||
|
# Set Python lexer
|
||||||
|
lexer = Qsci.QsciLexerPython()
|
||||||
|
lexer.setDefaultFont(font)
|
||||||
|
editor.setLexer(lexer)
|
||||||
|
|
||||||
|
# Add minimum editor size
|
||||||
|
editor.setMinimumSize(600, 450)
|
||||||
|
|
||||||
|
# Enable auto complete
|
||||||
|
editor.setAutoCompletionSource(Qsci.QsciScintilla.AcsAll)
|
||||||
|
editor.setAutoCompletionThreshold(2)
|
||||||
|
editor.setAutoCompletionCaseSensitivity(False)
|
||||||
|
editor.setAutoCompletionReplaceWord(False)
|
||||||
|
|
||||||
|
# Use space indentation
|
||||||
|
editor.setIndentationsUseTabs(False)
|
||||||
|
editor.setTabWidth(4)
|
||||||
|
editor.setIndentationGuides(True)
|
||||||
|
|
||||||
|
# Enable folding
|
||||||
|
editor.setFolding(True)
|
||||||
|
|
||||||
|
return editor
|
||||||
|
|
||||||
|
def open_editor(self, file_path: str = ""):
|
||||||
|
""""""
|
||||||
|
# Show editor tab if file already opened
|
||||||
|
if file_path:
|
||||||
|
file_path = str(Path(file_path))
|
||||||
|
|
||||||
|
for editor, path in self.editor_path_map.items():
|
||||||
|
if file_path == path:
|
||||||
|
editor.show()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Otherwise create new editor
|
||||||
|
editor = self.new_editor()
|
||||||
|
|
||||||
|
if file_path:
|
||||||
|
buf = open(file_path, encoding="UTF8").read()
|
||||||
|
editor.setText(buf)
|
||||||
|
file_name = Path(file_path).name
|
||||||
|
|
||||||
|
self.editor_path_map[editor] = file_path
|
||||||
|
else:
|
||||||
|
self.new_file_count += 1
|
||||||
|
file_name = f"{self.NEW_FILE_NAME}-{self.new_file_count}"
|
||||||
|
|
||||||
|
self.editor_path_map[editor] = file_name
|
||||||
|
|
||||||
|
i = self.tab.addTab(editor, file_name)
|
||||||
|
self.tab.setCurrentIndex(i)
|
||||||
|
|
||||||
|
self.path_label.setText(file_path)
|
||||||
|
|
||||||
|
def close_editor(self):
|
||||||
|
""""""
|
||||||
|
i = self.tab.currentIndex()
|
||||||
|
|
||||||
|
# Close editor if last file closed
|
||||||
|
if not i:
|
||||||
|
self.close()
|
||||||
|
# Otherwise only close current tab
|
||||||
|
else:
|
||||||
|
self.save_file()
|
||||||
|
|
||||||
|
editor = self.get_active_editor()
|
||||||
|
self.editor_path_map.pop(editor)
|
||||||
|
|
||||||
|
self.tab.removeTab(i)
|
||||||
|
|
||||||
|
def open_file(self):
|
||||||
|
""""""
|
||||||
|
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(
|
||||||
|
self, "打开文件", "", "Python(*.py)")
|
||||||
|
|
||||||
|
if file_path:
|
||||||
|
self.open_editor(file_path)
|
||||||
|
|
||||||
|
def new_file(self):
|
||||||
|
""""""
|
||||||
|
self.open_editor("")
|
||||||
|
|
||||||
|
def save_file(self):
|
||||||
|
""""""
|
||||||
|
editor = self.get_active_editor()
|
||||||
|
file_path = self.editor_path_map[editor]
|
||||||
|
|
||||||
|
if self.NEW_FILE_NAME in file_path:
|
||||||
|
file_path, _ = QtWidgets.QFileDialog.getSaveFileName(
|
||||||
|
self, "保存", "", "Python(*.py)")
|
||||||
|
|
||||||
|
self.save_editor_text(editor, file_path)
|
||||||
|
|
||||||
|
def save_file_as(self):
|
||||||
|
""""""
|
||||||
|
editor = self.get_active_editor()
|
||||||
|
|
||||||
|
file_path, _ = QtWidgets.QFileDialog.getSaveFileName(
|
||||||
|
self, "保存", "", "Python(*.py)")
|
||||||
|
|
||||||
|
self.save_editor_text(editor, file_path)
|
||||||
|
|
||||||
|
def save_editor_text(self, editor: Qsci.QsciScintilla, file_path: str):
|
||||||
|
""""""
|
||||||
|
if file_path:
|
||||||
|
self.editor_path_map[editor] = file_path
|
||||||
|
|
||||||
|
i = self.tab.currentIndex()
|
||||||
|
file_name = Path(file_path).name
|
||||||
|
self.tab.setTabText(i, file_name)
|
||||||
|
|
||||||
|
with open(file_path, "w", encoding="UTF8") as f:
|
||||||
|
f.write(editor.text())
|
||||||
|
|
||||||
|
self.update_path_label()
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
""""""
|
||||||
|
self.get_active_editor().copy()
|
||||||
|
|
||||||
|
def paste(self):
|
||||||
|
""""""
|
||||||
|
self.get_active_editor().paste()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
""""""
|
||||||
|
self.get_active_editor().undo()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
""""""
|
||||||
|
self.get_active_editor().redo()
|
||||||
|
|
||||||
|
def cut(self):
|
||||||
|
""""""
|
||||||
|
self.get_active_editor().cut()
|
||||||
|
|
||||||
|
def find(self):
|
||||||
|
""""""
|
||||||
|
dialog = FindDialog(
|
||||||
|
self.get_active_editor(),
|
||||||
|
False
|
||||||
|
)
|
||||||
|
dialog.exec_()
|
||||||
|
|
||||||
|
def replace(self):
|
||||||
|
""""""
|
||||||
|
dialog = FindDialog(
|
||||||
|
self.get_active_editor(),
|
||||||
|
True
|
||||||
|
)
|
||||||
|
dialog.exec_()
|
||||||
|
|
||||||
|
def get_active_editor(self):
|
||||||
|
""""""
|
||||||
|
return self.tab.currentWidget()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
""""""
|
||||||
|
for editor, path in self.editor_path_map.items():
|
||||||
|
i = QtWidgets.QMessageBox.question(
|
||||||
|
self,
|
||||||
|
"退出保存",
|
||||||
|
f"是否要保存{path}?",
|
||||||
|
QtWidgets.QMessageBox.Save | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel,
|
||||||
|
QtWidgets.QMessageBox.Save
|
||||||
|
)
|
||||||
|
|
||||||
|
if i == QtWidgets.QMessageBox.Save:
|
||||||
|
if self.NEW_FILE_NAME in path:
|
||||||
|
path, _ = QtWidgets.QFileDialog.getSaveFileName(
|
||||||
|
self, "保存", "", "Python(*.py)")
|
||||||
|
|
||||||
|
if path:
|
||||||
|
self.save_editor_text(editor, path)
|
||||||
|
elif i == QtWidgets.QMessageBox.Cancel:
|
||||||
|
break
|
||||||
|
|
||||||
|
event.accept()
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
""""""
|
||||||
|
if not self.tab.count():
|
||||||
|
self.open_editor()
|
||||||
|
|
||||||
|
self.showMaximized()
|
||||||
|
|
||||||
|
def update_path_label(self):
|
||||||
|
""""""
|
||||||
|
editor = self.get_active_editor()
|
||||||
|
path = self.editor_path_map[editor]
|
||||||
|
self.path_label.setText(path)
|
||||||
|
|
||||||
|
|
||||||
|
class FindDialog(QtWidgets.QDialog):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
editor: Qsci.QsciScintilla,
|
||||||
|
show_replace: bool = False
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.editor: Qsci.QsciScintilla = editor
|
||||||
|
self.show_replace: bool = show_replace
|
||||||
|
self.new_task: bool = True
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
find_label = QtWidgets.QLabel("查找")
|
||||||
|
replace_label = QtWidgets.QLabel("替换")
|
||||||
|
|
||||||
|
selected_text = self.editor.selectedText()
|
||||||
|
self.find_line = QtWidgets.QLineEdit(selected_text)
|
||||||
|
self.find_line.textChanged.connect(self.reset_task)
|
||||||
|
|
||||||
|
self.replace_line = QtWidgets.QLineEdit()
|
||||||
|
|
||||||
|
self.case_check = QtWidgets.QCheckBox("大小写")
|
||||||
|
self.case_check.setChecked(True)
|
||||||
|
self.case_check.stateChanged.connect(self.reset_task)
|
||||||
|
|
||||||
|
self.whole_check = QtWidgets.QCheckBox("全词匹配")
|
||||||
|
self.whole_check.stateChanged.connect(self.reset_task)
|
||||||
|
|
||||||
|
self.selection_check = QtWidgets.QCheckBox("选中区域")
|
||||||
|
self.selection_check.stateChanged.connect(self.reset_task)
|
||||||
|
|
||||||
|
find_button = QtWidgets.QPushButton("查找")
|
||||||
|
find_button.clicked.connect(self.find_text)
|
||||||
|
|
||||||
|
self.replace_button = QtWidgets.QPushButton("替换")
|
||||||
|
self.replace_button.clicked.connect(self.replace_text)
|
||||||
|
self.replace_button.setEnabled(False)
|
||||||
|
|
||||||
|
check_hbox = QtWidgets.QHBoxLayout()
|
||||||
|
check_hbox.addWidget(self.case_check)
|
||||||
|
check_hbox.addStretch()
|
||||||
|
check_hbox.addWidget(self.whole_check)
|
||||||
|
check_hbox.addStretch()
|
||||||
|
check_hbox.addWidget(self.selection_check)
|
||||||
|
check_hbox.addStretch()
|
||||||
|
|
||||||
|
button_hbox = QtWidgets.QHBoxLayout()
|
||||||
|
button_hbox.addWidget(find_button)
|
||||||
|
button_hbox.addWidget(self.replace_button)
|
||||||
|
|
||||||
|
form = QtWidgets.QFormLayout()
|
||||||
|
form.addRow(find_label, self.find_line)
|
||||||
|
form.addRow(replace_label, self.replace_line)
|
||||||
|
form.addRow(check_hbox)
|
||||||
|
form.addRow(button_hbox)
|
||||||
|
|
||||||
|
self.setLayout(form)
|
||||||
|
|
||||||
|
if self.show_replace:
|
||||||
|
self.setWindowTitle("替换")
|
||||||
|
else:
|
||||||
|
self.setWindowTitle("查找")
|
||||||
|
|
||||||
|
replace_label.setVisible(False)
|
||||||
|
self.replace_line.setVisible(False)
|
||||||
|
self.replace_button.setVisible(False)
|
||||||
|
|
||||||
|
def find_text(self):
|
||||||
|
""""""
|
||||||
|
if not self.new_task:
|
||||||
|
result = self.editor.findNext()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
self.new_task = False
|
||||||
|
self.replace_button.setEnabled(True)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.new_task = True
|
||||||
|
|
||||||
|
self.editor.cancelFind()
|
||||||
|
|
||||||
|
if not self.selection_check.isChecked():
|
||||||
|
result = self.editor.findFirst(
|
||||||
|
self.find_line.text(),
|
||||||
|
False,
|
||||||
|
self.case_check.isChecked(),
|
||||||
|
self.whole_check.isChecked(),
|
||||||
|
False,
|
||||||
|
line=1,
|
||||||
|
index=1
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
result = self.editor.findFirstInSelection(
|
||||||
|
self.find_line.text(),
|
||||||
|
False,
|
||||||
|
self.case_check.isChecked(),
|
||||||
|
self.whole_check.isChecked(),
|
||||||
|
False
|
||||||
|
)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
self.new_task = False
|
||||||
|
self.replace_button.setEnabled(True)
|
||||||
|
else:
|
||||||
|
self.new_task = True
|
||||||
|
|
||||||
|
def replace_text(self):
|
||||||
|
""""""
|
||||||
|
new_text = self.replace_line.text()
|
||||||
|
|
||||||
|
self.editor.replace(new_text)
|
||||||
|
self.editor.findNext()
|
||||||
|
|
||||||
|
def reset_task(self):
|
||||||
|
""""""
|
||||||
|
self.new_task = True
|
||||||
|
self.replace_button.setEnabled(False)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from vnpy.trader.ui import create_qapp
|
||||||
|
|
||||||
|
app = create_qapp()
|
||||||
|
editor = CodeEditor()
|
||||||
|
editor.show()
|
||||||
|
app.exec_()
|
Binary file not shown.
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
@ -24,6 +24,7 @@ from .widget import (
|
|||||||
AboutDialog,
|
AboutDialog,
|
||||||
GlobalDialog
|
GlobalDialog
|
||||||
)
|
)
|
||||||
|
from .editor import CodeEditor
|
||||||
from ..engine import MainEngine
|
from ..engine import MainEngine
|
||||||
from ..utility import get_icon_path, TRADER_DIR
|
from ..utility import get_icon_path, TRADER_DIR
|
||||||
|
|
||||||
@ -138,6 +139,18 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
partial(self.open_widget, ContractManager, "contract")
|
partial(self.open_widget, ContractManager, "contract")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.add_menu_action(
|
||||||
|
help_menu,
|
||||||
|
"代码编辑",
|
||||||
|
"editor.ico",
|
||||||
|
partial(self.open_widget, CodeEditor, "editor")
|
||||||
|
)
|
||||||
|
self.add_toolbar_action(
|
||||||
|
"代码编辑",
|
||||||
|
"editor.ico",
|
||||||
|
partial(self.open_widget, CodeEditor, "editor")
|
||||||
|
)
|
||||||
|
|
||||||
self.add_menu_action(
|
self.add_menu_action(
|
||||||
help_menu, "还原窗口", "restore.ico", self.restore_window_setting
|
help_menu, "还原窗口", "restore.ico", self.restore_window_setting
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user