diff --git a/vnpy/trader/ui/editor.py b/vnpy/trader/ui/editor.py index 91b6d682..112030c3 100644 --- a/vnpy/trader/ui/editor.py +++ b/vnpy/trader/ui/editor.py @@ -12,7 +12,7 @@ class CodeEditor(QtWidgets.QMainWindow): """""" super().__init__() - self.new_file_count = 1 + self.new_file_count = 0 self.editor_path_map: Dict[Qsci.QsciScintilla, str] = {} self.init_ui() @@ -27,6 +27,8 @@ class CodeEditor(QtWidgets.QMainWindow): def init_central(self): """""" self.tab = QtWidgets.QTabWidget() + self.tab.currentChanged.connect(self.update_path_label) + self.path_label = QtWidgets.QLabel() vbox = QtWidgets.QVBoxLayout() @@ -42,34 +44,45 @@ class CodeEditor(QtWidgets.QMainWindow): """""" bar = self.menuBar() - file_bar = bar.addMenu("文件") - self.add_menu_action(file_bar, "新建", self.new_file) - self.add_menu_action(file_bar, "打开", self.open_file) - self.add_menu_action(file_bar, "保存", self.save_file) - self.add_menu_action(file_bar, "另存为", self.save_file) + 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.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_bar = bar.addMenu("编辑") - self.add_menu_action(edit_bar, "复制", self.copy) - self.add_menu_action(edit_bar, "粘贴", self.paste) - self.add_menu_action(edit_bar, "剪切", self.cut) - edit_bar.addSeparator() - self.add_menu_action(edit_bar, "查找", self.find) - self.add_menu_action(edit_bar, "替换", self.replace) + edit_menu = bar.addMenu("编辑") + 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") - help_bar = bar.addMenu("帮助") - self.add_menu_action(help_bar, "快捷键", self.show_shortcut) + help_menu = bar.addMenu("帮助") 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 @@ -128,6 +141,8 @@ class CodeEditor(QtWidgets.QMainWindow): """""" # 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() @@ -135,15 +150,18 @@ class CodeEditor(QtWidgets.QMainWindow): # Otherwise create new editor editor = self.new_editor() - self.editor_path_map[editor] = file_path 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: - file_name = f"{self.NEW_FILE_NAME}-{self.new_file_count}" 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) @@ -174,13 +192,33 @@ class CodeEditor(QtWidgets.QMainWindow): if self.NEW_FILE_NAME in file_path: file_path, _ = QtWidgets.QFileDialog.getSaveFileName( self, "保存", "", "Python(*.py)") + file_path = str(Path(file_path)) + 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") as f: f.write(editor.text()) + self.update_path_label() + def copy(self): """""" self.get_active_editor().copy() @@ -193,22 +231,165 @@ class CodeEditor(QtWidgets.QMainWindow): """""" self.get_active_editor.cut() - def show_shortcut(self): - """""" - pass - def find(self): """""" - pass + dialog = FindDialog( + self.get_active_editor(), + False + ) + dialog.exec_() def replace(self): """""" - pass + 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 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, replace: bool = False): + """""" + super().__init__() + + self.editor = editor + self.replace = replace + + self.text = "" + self.case_sensitive = False + self.whole_word = False + self.selection = False + + self.init_ui() + + def init_ui(self): + """""" + find_label = QtWidgets.QLabel("查找") + replace_label = QtWidgets.QLabel("替换") + + self.find_line = QtWidgets.QLineEdit() + self.replace_line = QtWidgets.QLineEdit() + + self.case_check = QtWidgets.QCheckBox("大小写") + self.whole_check = QtWidgets.QCheckBox("全词匹配") + self.selection_check = QtWidgets.QCheckBox("选中区域") + + find_button = QtWidgets.QPushButton("查找") + find_button.clicked.connect(self.find_text) + + replace_button = QtWidgets.QPushButton("替换") + replace_button.clicked.connect(self.replace_text) + + 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(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.replace: + self.setWindowTitle("替换") + else: + self.setWindowTitle("查找") + replace_label.setVisible(False) + self.replace_line.setVisible(False) + replace_button.setVisible(False) + + def find_text(self): + """""" + new_text = self.find_line.text() + new_case_sensitive = self.case_check.isChecked() + new_whole_word = self.whole_check.isChecked() + new_selection = self.selection_check.isChecked() + + if ( + new_text == self.text + and new_case_sensitive == self.case_sensitive + and new_whole_word == self.whole_word + and new_selection == self.selection + ): + self.editor.findNext() + return + + self.text = new_text + self.case_sensitive = new_case_sensitive + self.whole_word = new_whole_word + self.selection = new_selection + + if not self.selection: + self.editor.findFirst( + self.text, + False, + self.case_sensitive, + self.whole_word, + False, + show=True + ) + else: + self.editor.findFirstInSelection( + self.text, + False, + self.case_sensitive, + self.whole_word, + False, + show=True + ) + + def replace_text(self): + """""" + new_text = self.replace_line.text() + + self.editor.replace(new_text) + if __name__ == "__main__": import sys