From f9986dde14353feaedd5e7a9fdb02536a73a5a54 Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 08:58:42 +0800 Subject: [PATCH 01/17] =?UTF-8?q?code=5Feditor:=20=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E4=BA=86=E7=BF=BB=E8=AF=91=E9=97=AE=E9=A2=98=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E6=97=A0=E5=9B=BE=E7=9A=84=E8=8F=9C=E5=8D=95=E9=A1=B9=E4=B8=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code_editor/assets/languages/en.ts | 38 ++++++------ .../code_editor/assets/languages/zh_CN.qm | Bin 227 -> 345 bytes .../code_editor/assets/languages/zh_CN.ts | 56 +++++++++--------- .../code_editor/assets/languages/zh_TW.ts | 38 ++++++------ packages/code_editor/main.py | 6 +- packages/code_editor/utils/base_object.py | 20 ++++++- packages/code_editor/utils/operation.py | 2 +- .../widgets/editors/base_editor.py | 4 +- .../widgets/editors/python_editor.py | 2 +- 9 files changed, 88 insertions(+), 78 deletions(-) diff --git a/packages/code_editor/assets/languages/en.ts b/packages/code_editor/assets/languages/en.ts index 0a874eae..6b5c4d75 100644 --- a/packages/code_editor/assets/languages/en.ts +++ b/packages/code_editor/assets/languages/en.ts @@ -46,65 +46,65 @@ - + save.svg - - Find - - - - + Replace - + Find In Path - + AutoComp - + Goto Line - + Add Breakpoint - + Remove Breakpoint - + View BreakPoints - + Save - + Save file "{0}"? - + Save file + + + Find Code + + PMCodeEditTabWidget @@ -345,12 +345,12 @@ - + Help - + Cannot get name. Maybe There is: 1、Syntax error in your code. @@ -358,7 +358,7 @@ Maybe There is: - + Error diff --git a/packages/code_editor/assets/languages/zh_CN.qm b/packages/code_editor/assets/languages/zh_CN.qm index 8f65a357a42b5cb76a1288a6d457a2400fc285d4..6633d5f95aa2a7bbaa7311e909ba6162caf13554 100644 GIT binary patch delta 159 zcmaFNc#~;@oSp^)%Nqj*28KsW^Z$ne>2%hFXYYXYat2&kTT0;U|{5( zXtO+x8OY=4&j~V!p5^Ds0aC&S!~*X53MKgpE~#mmd6^}d`FZR>LB0UrfXb4L{5;o` a%#!>fMvzvPIK7~iaBVCesW}DMlmY Format Code - 代码格式化 + 代码格式化 Run Code - 运行代码 + 运行代码 Run Selected Code - 运行选中的代码 + 运行选中的代码 - + save.svg - - Find - - - - + Replace - + Find In Path - + AutoComp - + Goto Line - + Add Breakpoint - + Remove Breakpoint - + View BreakPoints - + Save - + Save file "{0}"? - + Save file + + + Find Code + + PMCodeEditTabWidget @@ -327,7 +327,7 @@ - + PMPythonEditor @@ -342,25 +342,25 @@ Go to Definition - + 转到定义 - + Help + 帮助 + + + + Error - + Cannot get name. Maybe There is: 1、Syntax error in your code. 2、No word under text cursor. - - - Error - - diff --git a/packages/code_editor/assets/languages/zh_TW.ts b/packages/code_editor/assets/languages/zh_TW.ts index 0a874eae..6b5c4d75 100644 --- a/packages/code_editor/assets/languages/zh_TW.ts +++ b/packages/code_editor/assets/languages/zh_TW.ts @@ -46,65 +46,65 @@ - + save.svg - - Find - - - - + Replace - + Find In Path - + AutoComp - + Goto Line - + Add Breakpoint - + Remove Breakpoint - + View BreakPoints - + Save - + Save file "{0}"? - + Save file + + + Find Code + + PMCodeEditTabWidget @@ -345,12 +345,12 @@ - + Help - + Cannot get name. Maybe There is: 1、Syntax error in your code. @@ -358,7 +358,7 @@ Maybe There is: - + Error diff --git a/packages/code_editor/main.py b/packages/code_editor/main.py index 985b0ccb..5733b46f 100644 --- a/packages/code_editor/main.py +++ b/packages/code_editor/main.py @@ -1,5 +1,6 @@ import logging import os +import sys from pathlib import Path from typing import Dict, Union, TYPE_CHECKING @@ -18,11 +19,6 @@ from pmgwidgets import PMGPanel, load_json, dump_json __prevent_from_ide_optimization = PMEditorToolbar # 这一行的目的是防止导入被编辑器自动优化。 logger = logging.getLogger('code_editor') -# 翻译文件只需要加载一次 -__translator = QTranslator() -__translator.load(str(Path(__file__).parent / 'assets' / 'translations' / f'qt_{QLocale.system().name()}.qm')) -QApplication.instance().installTranslator(__translator) - class Extension(BaseExtension): editor_widget: 'PMCodeEditTabWidget' diff --git a/packages/code_editor/utils/base_object.py b/packages/code_editor/utils/base_object.py index 4bfe0169..51fd868a 100644 --- a/packages/code_editor/utils/base_object.py +++ b/packages/code_editor/utils/base_object.py @@ -1,4 +1,7 @@ -from typing import TYPE_CHECKING, Callable +from pathlib import Path + +from PySide2.QtCore import QTranslator, QLocale +from PySide2.QtWidgets import QApplication from ..settings import Settings @@ -6,5 +9,16 @@ from ..settings import Settings class CodeEditorBaseObject: settings = Settings() - if TYPE_CHECKING: - tr: Callable[[str], str] + # 翻译文件只需要加载一次 + __app = QApplication.instance() + __translator = QTranslator(__app) + __translator.load(str(Path(__file__).parent.parent / 'assets' / 'languages' / f'{QLocale.system().name()}.qm')) + __app.installTranslator(__translator) + + def tr(self, source: str): + translations = [self.__translator.translate(klass.__name__, source) for klass in self.__class__.mro()] + translations = [t for t in translations if t] + if translations: + return translations[0] + else: + return source diff --git a/packages/code_editor/utils/operation.py b/packages/code_editor/utils/operation.py index 13c26cae..cc4792d9 100644 --- a/packages/code_editor/utils/operation.py +++ b/packages/code_editor/utils/operation.py @@ -36,7 +36,7 @@ class Operation(CodeEditorBaseObject): icon = None else: icon = QIcon(self.settings.get_icon(icon_name)) - self.__action: QAction = QAction(icon, label, widget) # 用于进行缓存,以免每次都要重新创建一个action对象 + self.__action: QAction = QAction(label, widget) # 用于进行缓存,以免每次都要重新创建一个action对象 # noinspection PyUnresolvedReferences has_slot and self.__action.triggered.connect(slot) diff --git a/packages/code_editor/widgets/editors/base_editor.py b/packages/code_editor/widgets/editors/base_editor.py index 926f8a04..2aa00eba 100644 --- a/packages/code_editor/widgets/editors/base_editor.py +++ b/packages/code_editor/widgets/editors/base_editor.py @@ -186,13 +186,13 @@ class PMBaseEditor(CodeEditorBaseObject, QWidget): # 保存代码 self.__operation_save_code = Operation( - widget=self.text_edit, name='save code', label=self.tr('save.svg'), + widget=self.text_edit, name='save code', label=self.tr('Save Code'), slot=self.slot_save, key='Ctrl+S', icon_name='save.svg', ) # 查找代码 self.__operation_find = Operation( - widget=self.text_edit, name='find code', label=self.tr('Find'), + widget=self.text_edit, name='find code', label=self.tr('Find Code'), slot=self.slot_find, key='Ctrl+F', ) diff --git a/packages/code_editor/widgets/editors/python_editor.py b/packages/code_editor/widgets/editors/python_editor.py index 87625d8b..97ce608c 100644 --- a/packages/code_editor/widgets/editors/python_editor.py +++ b/packages/code_editor/widgets/editors/python_editor.py @@ -38,7 +38,7 @@ from typing import List, Tuple, Optional, TYPE_CHECKING, Callable from PySide2.QtCore import SignalInstance, Signal, Qt, QDir from PySide2.QtGui import QKeySequence, QCloseEvent -from PySide2.QtWidgets import QAction, QShortcut, QMessageBox +from PySide2.QtWidgets import QAction, QShortcut, QMessageBox, QApplication from yapf.yapflib import py3compat, yapf_api from pmgwidgets import in_unit_test, PMGOneShotThreadRunner, run_python_file_in_terminal, parse_simplified_pmgjson, \ -- Gitee From c522f8e48abbe197bf3bdb3607df143ff9fb5651 Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 09:02:51 +0800 Subject: [PATCH 02/17] =?UTF-8?q?code=5Feditor:=20=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E4=BA=86=E8=8F=9C=E5=8D=95=E9=A1=B9=E5=9B=BE=E6=A0=87=E4=B8=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code_editor/utils/operation.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/code_editor/utils/operation.py b/packages/code_editor/utils/operation.py index cc4792d9..996de192 100644 --- a/packages/code_editor/utils/operation.py +++ b/packages/code_editor/utils/operation.py @@ -33,11 +33,12 @@ class Operation(CodeEditorBaseObject): # 设置Action的图标 if icon_name is None: - icon = None + # 用于进行缓存,以免每次都要重新创建一个action对象 + self.__action: QAction = QAction(label, widget) else: icon = QIcon(self.settings.get_icon(icon_name)) - self.__action: QAction = QAction(label, widget) # 用于进行缓存,以免每次都要重新创建一个action对象 - # noinspection PyUnresolvedReferences + self.__action: QAction = QAction(icon, label, widget) + # noinspection PyUnresolvedReferences has_slot and self.__action.triggered.connect(slot) # 设置QAction和QShortcut快捷键 -- Gitee From d8ae623e7d0507ab613c8c725d3cd17f585e73d0 Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 12:08:54 +0800 Subject: [PATCH 03/17] =?UTF-8?q?code=5Feditor:=20=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E7=94=A8=E5=88=B0=E7=9A=84=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E8=AF=B4=E6=98=8E=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=BF=BB=E8=AF=91=E6=97=B6=E7=9A=84=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E5=AE=9E=E7=8E=B0=E8=BF=90=E8=A1=8C=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E7=AE=97=E5=8A=9B=E8=8A=82=E7=9C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code_editor/assets/languages/en.ts | 40 +++++------ .../code_editor/assets/languages/zh_CN.ts | 70 +++++++++---------- .../code_editor/assets/languages/zh_TW.ts | 40 +++++------ .../code_editor/scripts/translation/README.md | 15 ++-- .../scripts/translation/code_editor.pro | 5 +- .../scripts/translation/code_editor.pro_t | 7 ++ .../scripts/translation/create_ts_files.py | 30 +++++++- .../translation/get_all_python_files.py | 22 ------ .../scripts/translation/release.py | 17 +---- packages/code_editor/utils/base_object.py | 49 +++++++++---- utils/dev/__init__.py | 3 + utils/dev/system.py | 18 +++++ 12 files changed, 175 insertions(+), 141 deletions(-) create mode 100644 packages/code_editor/scripts/translation/code_editor.pro_t delete mode 100644 packages/code_editor/scripts/translation/get_all_python_files.py create mode 100644 utils/dev/__init__.py create mode 100644 utils/dev/system.py diff --git a/packages/code_editor/assets/languages/en.ts b/packages/code_editor/assets/languages/en.ts index 6b5c4d75..cec13cfc 100644 --- a/packages/code_editor/assets/languages/en.ts +++ b/packages/code_editor/assets/languages/en.ts @@ -46,65 +46,65 @@ - - save.svg + + Save Code - + + Find Code + + + + Replace - + Find In Path - + AutoComp - + Goto Line - + Add Breakpoint - + Remove Breakpoint - + View BreakPoints - + Save - + Save file "{0}"? - + Save file - - - Find Code - - PMCodeEditTabWidget @@ -345,12 +345,12 @@ - + Help - + Cannot get name. Maybe There is: 1、Syntax error in your code. @@ -358,7 +358,7 @@ Maybe There is: - + Error diff --git a/packages/code_editor/assets/languages/zh_CN.ts b/packages/code_editor/assets/languages/zh_CN.ts index b59e678f..141a46cd 100644 --- a/packages/code_editor/assets/languages/zh_CN.ts +++ b/packages/code_editor/assets/languages/zh_CN.ts @@ -33,78 +33,78 @@ Format Code - 代码格式化 + 代码格式化 Run Code - 运行代码 + 运行代码 Run Selected Code - 运行选中的代码 + 运行选中的代码 - - save.svg + + Save Code - + + Find Code + + + + Replace - + Find In Path - + AutoComp - + Goto Line - + Add Breakpoint - + Remove Breakpoint - + View BreakPoints - + Save - + Save file "{0}"? - + Save file - - - Find Code - - PMCodeEditTabWidget @@ -327,40 +327,40 @@ - + PMPythonEditor - - - Function Help - - - - - Help In Console - - Go to Definition 转到定义 - + Help 帮助 - - Error + + Function Help + + + + + Help In Console - + Cannot get name. Maybe There is: 1、Syntax error in your code. 2、No word under text cursor. + + + Error + + diff --git a/packages/code_editor/assets/languages/zh_TW.ts b/packages/code_editor/assets/languages/zh_TW.ts index 6b5c4d75..cec13cfc 100644 --- a/packages/code_editor/assets/languages/zh_TW.ts +++ b/packages/code_editor/assets/languages/zh_TW.ts @@ -46,65 +46,65 @@ - - save.svg + + Save Code - + + Find Code + + + + Replace - + Find In Path - + AutoComp - + Goto Line - + Add Breakpoint - + Remove Breakpoint - + View BreakPoints - + Save - + Save file "{0}"? - + Save file - - - Find Code - - PMCodeEditTabWidget @@ -345,12 +345,12 @@ - + Help - + Cannot get name. Maybe There is: 1、Syntax error in your code. @@ -358,7 +358,7 @@ Maybe There is: - + Error diff --git a/packages/code_editor/scripts/translation/README.md b/packages/code_editor/scripts/translation/README.md index e05b9815..e3097817 100644 --- a/packages/code_editor/scripts/translation/README.md +++ b/packages/code_editor/scripts/translation/README.md @@ -2,15 +2,8 @@ 出于开发时的简洁性,使用英文进行开发,而后需要译回中文。 -主要包括以下脚本: +翻译生成工具包括两个脚本,其主要工作流程如下: -`get_all_python_files.py`,可以获取所有的code_editor中的python文件, -将其输出结果复制到`code_editor.pro`里面,就可以选择需要的python文件了。 - -目前没有`ui`文件,因此其相关内容暂时没做。 - -执行create_ts_files.bat,可以创建(或更新?)ts文件。 - -使用linguist编辑ts文件,我是借助了PyCharm的External Tools功能实现的。 - -最后编译ts文件至qm文件,使用`release.py`。 \ No newline at end of file +1. 运行`create_ts_files.py`,增量生成各个语言的ts文件,以用于翻译; +1. 使用Qt语言家进行翻译,翻译结果直接保存在生成的ts文件内; +1. 执行`release.py`,将翻译文件生成可以用Qt读取的文件。 \ No newline at end of file diff --git a/packages/code_editor/scripts/translation/code_editor.pro b/packages/code_editor/scripts/translation/code_editor.pro index ee4728f4..07719d69 100644 --- a/packages/code_editor/scripts/translation/code_editor.pro +++ b/packages/code_editor/scripts/translation/code_editor.pro @@ -45,7 +45,4 @@ TRANSLATIONS = ..\..\assets\languages\en.ts \ ..\..\assets\languages\zh_TW.ts CODECFORTR = UTF-8 -CODECFORSRC = UTF-8 - -# pylupdate5.exe pyminer.pro -# linguist.exe languages\en\en.ts languages\zh_CN\zh_CN.ts languages\zh_TW\zh_TW.ts \ No newline at end of file +CODECFORSRC = UTF-8 \ No newline at end of file diff --git a/packages/code_editor/scripts/translation/code_editor.pro_t b/packages/code_editor/scripts/translation/code_editor.pro_t new file mode 100644 index 00000000..f65f1d8e --- /dev/null +++ b/packages/code_editor/scripts/translation/code_editor.pro_t @@ -0,0 +1,7 @@ +SOURCES = {{ python_files }} +TRANSLATIONS = ..\..\assets\languages\en.ts \ + ..\..\assets\languages\zh_CN.ts \ + ..\..\assets\languages\zh_TW.ts + +CODECFORTR = UTF-8 +CODECFORSRC = UTF-8 \ No newline at end of file diff --git a/packages/code_editor/scripts/translation/create_ts_files.py b/packages/code_editor/scripts/translation/create_ts_files.py index 2a76e6a4..ffab5a32 100644 --- a/packages/code_editor/scripts/translation/create_ts_files.py +++ b/packages/code_editor/scripts/translation/create_ts_files.py @@ -2,13 +2,39 @@ import os from pathlib import Path import PySide2 +import jinja2 + +from utils.dev.system import system + + +def get_python_files(): + base_path = Path(__file__).parent.parent.parent.absolute() + python_files = [] + for root, sub_dirs, files in os.walk(base_path): + root = Path(root) + if any(p in root.parts for p in ('__pycache__', 'assets', 'scripts')): + continue + for file in files: + file = root / file + if file.suffix != '.py': + continue + file = os.path.join('..', '..', file.relative_to(base_path)) + python_files.append(file) + return python_files def main(): exe = Path(PySide2.__file__).parent / 'pyside2-lupdate.exe' + template = Path(__file__).absolute().parent / 'code_editor.pro_t' + with open(template, 'r', encoding='utf-8') as f: + template_content = f.read() + python_files = get_python_files() + python_files = '\\\n '.join(python_files) + pro_content = jinja2.Template(template_content).render(python_files=python_files) pro = Path(__file__).absolute().parent / 'code_editor.pro' - content = os.popen(f'"{exe}" "{pro}"').read() - print(content) + with open(pro, 'w', encoding='utf-8') as f: + f.write(pro_content) + system(exe, pro) if __name__ == '__main__': diff --git a/packages/code_editor/scripts/translation/get_all_python_files.py b/packages/code_editor/scripts/translation/get_all_python_files.py deleted file mode 100644 index f292ef82..00000000 --- a/packages/code_editor/scripts/translation/get_all_python_files.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -from pathlib import Path - - -def main(): - base_path = Path(__file__).parent.parent.parent.absolute() - python_files = [] - for root, sub_dirs, files in os.walk(base_path): - root = Path(root) - if any(p in root.parts for p in ('__pycache__', 'assets', 'scripts')): - continue - for file in files: - file = root / file - if file.suffix != '.py': - continue - file = os.path.join('..', '..', file.relative_to(base_path)) - python_files.append(file) - print('\\\n '.join(python_files)) - - -if __name__ == '__main__': - main() diff --git a/packages/code_editor/scripts/translation/release.py b/packages/code_editor/scripts/translation/release.py index d92c5f14..694adada 100644 --- a/packages/code_editor/scripts/translation/release.py +++ b/packages/code_editor/scripts/translation/release.py @@ -1,11 +1,8 @@ -import io -import os -import subprocess -from io import BytesIO from pathlib import Path import PySide2 -import chardet + +from utils.dev.system import system def main(): @@ -15,15 +12,7 @@ def main(): ts_dir = base_path / 'assets' / 'languages' for file in ts_dir.glob('*.ts'): output = file.parent / f'{file.stem}.qm' - result = subprocess.run(args=[exe, f'{file}', '-qm', output], capture_output=True) - if stdout := result.stdout: - print('Out: ') - stdout = stdout.decode(chardet.detect(result.stdout)['encoding']) - print(stdout) - if stderr := result.stderr: - print('Error: ') - stderr = stderr.decode(chardet.detect(result.stderr)['encoding']) - print(stderr) + system(exe, f'{file}', '-qm', output) if __name__ == '__main__': diff --git a/packages/code_editor/utils/base_object.py b/packages/code_editor/utils/base_object.py index 51fd868a..b400b32e 100644 --- a/packages/code_editor/utils/base_object.py +++ b/packages/code_editor/utils/base_object.py @@ -1,24 +1,47 @@ +from functools import cached_property from pathlib import Path +from typing import Optional, Callable from PySide2.QtCore import QTranslator, QLocale -from PySide2.QtWidgets import QApplication from ..settings import Settings +def _get_translator() -> Optional[QTranslator]: + result = QTranslator() + language = QLocale.system().name() + file = Path(__file__).parent.parent / 'assets' / 'languages' / f'{language}.qm' + if file.exists(): + result.load(str(file)) + return result + else: + return None + + class CodeEditorBaseObject: settings = Settings() - # 翻译文件只需要加载一次 - __app = QApplication.instance() - __translator = QTranslator(__app) - __translator.load(str(Path(__file__).parent.parent / 'assets' / 'languages' / f'{QLocale.system().name()}.qm')) - __app.installTranslator(__translator) - - def tr(self, source: str): - translations = [self.__translator.translate(klass.__name__, source) for klass in self.__class__.mro()] - translations = [t for t in translations if t] - if translations: - return translations[0] + __translator = _get_translator() + + @cached_property + def tr(self) -> Callable[[str], str]: + """使用属性的方式返回一个运行时生成的函数。 + + 这个函数可以实现自动判断Context以查找对应的翻译。 + 这个函数旨在解决的问题是,PySide2默认情况下采用静态编码的方式生成翻译,而后Python在运行时由于继承,类名不再是self.tr所在的类名, + 因此translator无法找到相对应的翻译。 + + 使用cached property以实现MRO查找等性能的节省。 + """ + klass_names = [klass.__name__ for klass in self.__class__.mro()] + if self.__translator is None: + return lambda source: source else: - return source + translate: Callable[[str, str], str] = self.__translator.translate + + def wrapper(source: str): + translations = [translate(klass, source) for klass in klass_names] + translations = [t for t in translations if t] + return translations[0] if translations else source + + return wrapper diff --git a/utils/dev/__init__.py b/utils/dev/__init__.py new file mode 100644 index 00000000..8f507ff9 --- /dev/null +++ b/utils/dev/__init__.py @@ -0,0 +1,3 @@ +""" +用于存储开发时用到的一些工具 +""" diff --git a/utils/dev/system.py b/utils/dev/system.py new file mode 100644 index 00000000..9f866a55 --- /dev/null +++ b/utils/dev/system.py @@ -0,0 +1,18 @@ +import subprocess +from pathlib import Path +from typing import Union + +import chardet + + +def system(*args: Union[str, Path]): + args = [str(arg) for arg in args] + result = subprocess.run(args=args, capture_output=True) + if stdout := result.stdout: + print('Out: ') + stdout = stdout.decode(chardet.detect(result.stdout)['encoding']) + print(stdout) + if stderr := result.stderr: + print('Error: ') + stderr = stderr.decode(chardet.detect(result.stderr)['encoding']) + print(stderr) -- Gitee From 7db116e50c5c5394e2d64e36201c13063af36272 Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 12:17:41 +0800 Subject: [PATCH 04/17] =?UTF-8?q?code=5Feditor:=20=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E7=AE=80=E4=B8=AD=E7=9A=84=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code_editor/assets/languages/zh_CN.qm | Bin 345 -> 4671 bytes .../code_editor/assets/languages/zh_CN.ts | 145 +++++++++--------- 2 files changed, 75 insertions(+), 70 deletions(-) diff --git a/packages/code_editor/assets/languages/zh_CN.qm b/packages/code_editor/assets/languages/zh_CN.qm index 6633d5f95aa2a7bbaa7311e909ba6162caf13554..5a274e0d16c4d593c2ce8531a6196faf34cd3e49 100644 GIT binary patch literal 4671 zcma)94Qw0b8UCD{?X!Kh)3Ws^E%a+q)_{`KP<1L%+1mUuqr~+eH5Facv;D=s<$ULz z&q>@YZ7kB!F00atW~60>TOIJURX^DxVm|O8Uj0Fhr!M|=gEB# zhkKjr^YI42IrWo0>lz+94SvR5Me%8nf5r9aJK(qdR*pH-3EUsynCWdG|0L&P#^Bt+ zsh0s?yMr5e1mtTa?g-lgJo>m(mdoJRU%1E4{TK9Y;$C?2JovkvyVPR#jiH}D^Fe?4>&aDQ+g3$6x#ue%?se+s@|?Pn07u#?C?)rmmnqmQjk~EF(H{Es)a(B z)YbT*1z{>ZCO#u|OMy;mCcUHA8(C!-b9Ko|hF7t< zIrjKR6JpqRwA;Wq?vYg)chAwc)DBztQ;nFMjjx`vd8qzCgO%2dzfTJK4C%w zJMq@h?lR)oa9*ksh=&4BI0DTBV`Z=v6c!;ONvI@Q1<&lh^1%Iub4-LEF!PKGmYHx+ z;VPo6>HZFxad1qnAcc24@8=tL5P8yIA4%T(yq)c*Hl2x3`XS1@RV#;l+vq9s=G{nzXsit@~9_dBpX|X#? zB-}NZ7N)@Uu|^t3pqZvT3Zep1Q5wleD$B~XH1UH4H7)C9g9RxkBne(7DkoJ6OqKFv z2UV@h0hwZbs>z_%7D*^VR%@fCajZ!-mA`7YPfal8@?sJflbAxm)0+qfi>+uoNqi)pKqKR3r|msd~IVa zmLfDYsj9%$@O1CrY1N(Bu>bodmDfYNM^39@JSeDXoRBja=p-3AnTNTPRfs6_NtmdR zpSd*jOlVv%O^A|(5By;$O-0RTn^hr0wBoE7lgZSrEE~3k6@zDIgk*UIib5=yvdkQM z{oss&VCM=zG?iz=_C#E~cJd7aQ0us;!AYY1AyBt?cyB&)4ii?p&-mhK3KX4l+WhIfe? zQA<7Hjuq)vOXq4y!{?%8NTu08EmU?4fYjU~pm!cvz7H;PlmVWchLbh-&F|Zb#pvC^ z5L&YL*OZ#lFlEG}mH&2L>Re@Hbl59!YY-r~tx=18s zX#nZ~Z*ItKrlio)fZ3rHLS3+juGn<*A;##n^*B3Ud$8RsVK5G zdMcyq>E#DjMG*-|LIjN*KN9-*!P-6lnq9bgXgfU)jTR#XN#%zyp`inV zbS5gmY!bxpgr5U49)ueq&O?h-xhd-P7P4=cUc%J{V!oK}*xg+3+O z{VhgfxV!RFLKUC|euaoyhEzha^fL9pnSkHG)!hRFT*e)wPmrJw$Xa<b3EsnA*m^im+bWY6s;^rbE~_kYAkpt%45 delta 66 zcmdn5a+7I-tUiMV1Irr&1_p*lO!NPT0_k+tglF%7^l}a@o~b~Zfrn**DUdSDWME+A Roh-m&J^3+9!Q`2ONdQM=5YYet diff --git a/packages/code_editor/assets/languages/zh_CN.ts b/packages/code_editor/assets/languages/zh_CN.ts index 141a46cd..f49b7e09 100644 --- a/packages/code_editor/assets/languages/zh_CN.ts +++ b/packages/code_editor/assets/languages/zh_CN.ts @@ -1,31 +1,32 @@ - + + FindInPathWidget Case - + 匹配大小写 Whole Word - + 全词匹配 Find - + 查找 Find In Path - + 在路径中查找 line - + 行号 @@ -33,77 +34,77 @@ Format Code - 代码格式化 + 代码格式化 Run Code - 运行代码 + 运行代码 Run Selected Code - 运行选中的代码 + 运行选中的代码 Save Code - + 保存代码 Find Code - + 查找代码 Replace - + 替换 Find In Path - + 在路径中查找 AutoComp - + 自动补全 Goto Line - + 跳转到行 Add Breakpoint - + 添加断点 Remove Breakpoint - + 移除断点 View BreakPoints - + 查看所有断点 Save - + 保存 Save file "{0}"? - + 保存文件“{0}”? Save file - + 保存文件 @@ -111,58 +112,59 @@ Warning - + 警告 Editor does not support file: %s - + 编辑器不支持的文件类型: +%s Open File - + 打开文件 Run: %s - + 运行:%s Run Python Code inside %s - + 在%s中运行Python代码 Script - + 脚本 Builtin (3.8.5) - + 内置(3.8.5) This Editor does not support instant boot. - + 本编辑器不支持立即启动。 Builtin (%s) - + 内置(%s) Editor - + 编辑器 Find In Path - + 在路径中查找 @@ -170,17 +172,17 @@ Debugger - + 调试器 Terminate - + 中止 Process is running, Would you like to terminate this process? - + 进程正在运行,是否想要结束进程? @@ -188,72 +190,72 @@ New Script - + 新脚本 Open Script - + 打开脚本 Save - + 保存 Find - + 查找 Toggle Comment - + 切换注释 Goto Line - + 跳转到行 Indent - + 缩进 Dedent - + 撤销缩进 IPython - + IPython Separately - + 分割 Terminal - + 终端 Instant Boot - + 立即启动 Run script with common module preloaded to shorten interpterter startup-time. - + 使用通用预加载模块来运行脚本以加快启动速度。 Editor - + 编辑器 @@ -261,52 +263,52 @@ Text to Find - + 要查找的文本 Text to Replace - + 要替换的文本 Wrap - + Wrap Regex - + 正则 Case Sensitive - + 匹配大小写 Whole Word - + 全词 Up - + 向上 Down - + 向下 Replace - + 替换 Replace All - + 替换所有 @@ -314,53 +316,56 @@ Input Value Error - + 入参错误 Cannot convert '%s' to integer. - + 无法将“%s”转化为整数。 Line Number {line} out of range! - + 行号{line}超出范围! - + PMPythonEditor Go to Definition - 转到定义 + 转到定义 Help - 帮助 + 帮助 Function Help - + 函数帮助 Help In Console - + 在控制台中显示帮助 - + Cannot get name. Maybe There is: 1、Syntax error in your code. 2、No word under text cursor. - + 无法获取名称。 +可能出现的问题: +1、语法错误; +2、光标下没有文本。 Error - + 错误 -- Gitee From 3d8585f8ebdf3b59e0fc8f6262d67ea0def9bdfe Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 13:48:27 +0800 Subject: [PATCH 05/17] =?UTF-8?q?code=5Feditor:=20=E6=95=B4=E7=90=86?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E7=9A=84=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code_editor/assets/languages/zh_CN.qm | Bin 4671 -> 4466 bytes .../code_editor/assets/languages/zh_CN.ts | 17 +++++++++++----- .../code_editor/scripts/translation/README.md | 10 ++++++--- .../scripts/translation/release.py | 19 ------------------ .../{create_ts_files.py => translate.py} | 17 +++++++++++++++- requirements_dev.txt | 1 + 6 files changed, 36 insertions(+), 28 deletions(-) delete mode 100644 packages/code_editor/scripts/translation/release.py rename packages/code_editor/scripts/translation/{create_ts_files.py => translate.py} (69%) diff --git a/packages/code_editor/assets/languages/zh_CN.qm b/packages/code_editor/assets/languages/zh_CN.qm index 5a274e0d16c4d593c2ce8531a6196faf34cd3e49..5a2e6092c28c17a62a0d5bbfd12dcd058669a000 100644 GIT binary patch delta 76 zcmdn5@=0le9HYQQc|9h+^Ar8`84V`Zmof28nfN`8i7#eyJR?y2*W`Xi!^tNYikJ - - + FindInPathWidget @@ -355,9 +354,9 @@ Cannot get name. Maybe There is: -1、Syntax error in your code. -2、No word under text cursor. - 无法获取名称。 +1、Syntax error in your code. +2、No word under text cursor. + 无法获取名称。 可能出现的问题: 1、语法错误; 2、光标下没有文本。 @@ -367,5 +366,13 @@ Maybe There is: Error 错误 + + + Cannot get name. +Maybe There is: +1、Syntax error in your code. +2、No word under text cursor. + + diff --git a/packages/code_editor/scripts/translation/README.md b/packages/code_editor/scripts/translation/README.md index e3097817..d77c261f 100644 --- a/packages/code_editor/scripts/translation/README.md +++ b/packages/code_editor/scripts/translation/README.md @@ -2,8 +2,12 @@ 出于开发时的简洁性,使用英文进行开发,而后需要译回中文。 -翻译生成工具包括两个脚本,其主要工作流程如下: +翻译生成工具只有一个脚本,其主要工作流程如下: -1. 运行`create_ts_files.py`,增量生成各个语言的ts文件,以用于翻译; +1. 运行`translate.py`,增量生成各个语言的ts文件,以用于翻译; 1. 使用Qt语言家进行翻译,翻译结果直接保存在生成的ts文件内; -1. 执行`release.py`,将翻译文件生成可以用Qt读取的文件。 \ No newline at end of file +1. 执行`translate.py`,将翻译文件生成可以用Qt读取的文件。 + +这个脚本同时执行了两项功能,即创建ts文件,再将ts文件转换为qm文件。 因为这两个步骤对性能的要求都不高,因此没有进行分别处理。 + +这个脚本仅在Windows下进行过测试,其他平台麻烦其他人协助测试。 \ No newline at end of file diff --git a/packages/code_editor/scripts/translation/release.py b/packages/code_editor/scripts/translation/release.py deleted file mode 100644 index 694adada..00000000 --- a/packages/code_editor/scripts/translation/release.py +++ /dev/null @@ -1,19 +0,0 @@ -from pathlib import Path - -import PySide2 - -from utils.dev.system import system - - -def main(): - base_path = Path(__file__).absolute().parent.parent.parent - print(base_path) - exe = Path(PySide2.__file__).parent / 'lrelease.exe' - ts_dir = base_path / 'assets' / 'languages' - for file in ts_dir.glob('*.ts'): - output = file.parent / f'{file.stem}.qm' - system(exe, f'{file}', '-qm', output) - - -if __name__ == '__main__': - main() diff --git a/packages/code_editor/scripts/translation/create_ts_files.py b/packages/code_editor/scripts/translation/translate.py similarity index 69% rename from packages/code_editor/scripts/translation/create_ts_files.py rename to packages/code_editor/scripts/translation/translate.py index ffab5a32..57591bb0 100644 --- a/packages/code_editor/scripts/translation/create_ts_files.py +++ b/packages/code_editor/scripts/translation/translate.py @@ -1,9 +1,12 @@ import os +import sys from pathlib import Path import PySide2 import jinja2 +sys.path.insert(1, str(Path(__file__).absolute().parent.parent.parent.parent.parent)) + from utils.dev.system import system @@ -24,7 +27,8 @@ def get_python_files(): def main(): - exe = Path(PySide2.__file__).parent / 'pyside2-lupdate.exe' + # 生成pro文件 + # TODO 由于目前没有ui文件,因此没有考虑ui文件 template = Path(__file__).absolute().parent / 'code_editor.pro_t' with open(template, 'r', encoding='utf-8') as f: template_content = f.read() @@ -34,8 +38,19 @@ def main(): pro = Path(__file__).absolute().parent / 'code_editor.pro' with open(pro, 'w', encoding='utf-8') as f: f.write(pro_content) + + # 使用lupdate生成ts文件 + exe = Path(PySide2.__file__).parent / 'pyside2-lupdate.exe' system(exe, pro) + # 将ts文件转换为qm文件 + base_path = Path(__file__).absolute().parent.parent.parent + exe = Path(PySide2.__file__).parent / 'lrelease.exe' + ts_dir = base_path / 'assets' / 'languages' + for file in ts_dir.glob('*.ts'): + output = file.parent / f'{file.stem}.qm' + system(exe, f'{file}', '-qm', output) + if __name__ == '__main__': main() diff --git a/requirements_dev.txt b/requirements_dev.txt index ca98cd0d..f1f2bf03 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -6,3 +6,4 @@ flake8 pytest numpydoc pytest-qt +jinja2 \ No newline at end of file -- Gitee From 24278a01cf095bab25252691c5c272bc30fe6e09 Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 13:50:11 +0800 Subject: [PATCH 06/17] =?UTF-8?q?code=5Feditor:=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=E7=9A=84=E7=BF=BB=E8=AF=91=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/translations/qt_zh_CN.qm | Bin 5815 -> 0 bytes .../assets/translations/qt_zh_CN.ts | 459 ------------------ 2 files changed, 459 deletions(-) delete mode 100644 packages/code_editor/assets/translations/qt_zh_CN.qm delete mode 100644 packages/code_editor/assets/translations/qt_zh_CN.ts diff --git a/packages/code_editor/assets/translations/qt_zh_CN.qm b/packages/code_editor/assets/translations/qt_zh_CN.qm deleted file mode 100644 index 9c2a84bec977655d28e061a6a60479065c3bfd3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5815 zcmbVQ4{Q_H8GlL6@!3AdNoWZ$I<9DG5=IgSN(B~GOB~W>vW;Uq7?wgf+b{Ml_MLS; zhlGDg)Jz1WG(iOhtmV%JYu6QJQl%AEwWW+|z}C^NN~Nu3l(m`I)-G)8pRHY$eeW*0 z*vmN}qFB%8d%y4dec$_j-+Onf&qU^39)J44oA)k!ZOe&Qu0D$pGHi>rIX(3VOhKl>xhco9)=HNf>CqTbnyP=Os$AK4L_a}P2cdIKTqNp++x>R239 z$C4^!xX}x^YUDZ`La4M0ZFuD(*!u(e_8uB=Rfv1|LxgMwH1gpW@Vf(@aa;iZG4z+N zzawOMCU1G$*Wv#2`CqHQj!@yB@?(d9&zvXolk)+W_iuH~|1|%|C72JMj!m>6WPG#W zAASL$l2cUWu1g3p1JqAm2q0vAgL>HlJj`}=w0>@&$nQmO8_c6kz$a+f@@KG9`o7`F zunhh?49DIh{2ny?`oC~(OBp^ljKjQ_)sb#j$CCAhuWmUFdAY8R^q6sEsR8&MGhXRq zV0^%M?OFov`_-}VAiacv{1z^xzsYhi`slqYHzHL0IDPu(_rn@U^e-zQuX&7V)$1vQ zOtq$mOEK_&!gThvkHO9}>S$bOI#+)a?#I-z^rY$a^G^ZqWtO``uJV9^}`rq%C?Mx|1otes$e?52RQnEW`Ev$ z!0#+`bmTu^?*#MW56;27HOy$u4aoW#bu_%7j^>EfbfFgb7Fg$5KZRx4Vcp{`1HW^u zM;Dv~JL5&p3;O|YF6teD^({JR>u7WWpR2atRrG?t5!+}9@M0QygzhRR01IZHyRCP? zg=&3eP&kk>_>?>%va-m!cmdZs#dta)7|F~Ao{NjoM?_i7Bgj(J3b;*(MXku;D}!&2 zX$!3cyoqgfgcbZIhn@xV#)`*m&4=+iZjpv8a&jrm`mvDWWxf+Dutr{psLmC~RG`FT z(EwC~xK-u%f76QmtM<7&!oK+fv7uMnY#PAe!%^I&Yq)(P9Ma|)eK;BC!nzb_GP5~0 zd_$XUnVik8iO2P9Gkf}?W1(H0{djkDsfJ<#xJxFvCfxO4g~X9(I#Z$C)|QYdCgC;- zUQUh$`A8JY>bhoIo=S=SWF}a-cf76NQ=J8Z!WJTIlfV+wxjo%h@H&1c;BPwgMo~(r zsf90VBfKn1N*r?!^lbhSwA}5;j&>pzM6w;#p`9A=ZWk7!a?G*azLQl%mU3*b+o@}5 zz%{X8%8sv0`JQRX<`PsZpwe7677m*{)qY3t+O#FK14Nt6$?C^o0SN72b8s=E78F2@ zs2Z)*A~CcC8mm|8F)xPM0^(5@szfe8RcRQyMc}(CU5!fyr8`{a32BS-JZ`NqQ#S$T8tjjOcWCnY&#D{y%@q<{1EY~Z;noU={t^3 zC(ml&qBW5STQ6a*BPsHNOrVU{T@OW^!GiAltM9o1+#J(j^qRCRI>m&di%Vj-V~|SX0#CE2``^$rothy@k;e-ffdCSrodTYRym3`Wm7G_C3LjXkYS)dXE z0)6AKj3cw4bBIqTqAAg7>uSk4(IYlcLp6xR-9&u|aj z%MQKEuJ8U%d*Dp}baZU1C2KK@*YkL*x|CkhB~tnnn8#o8ztj8`PHwL8{a7PzZA=Sc znHL52Q5;wLg~jW3dc7I_g=zk7j{se?9Hlb4C{v&2<1#O>mE|d=lg-A$Y$}~hijqu%lQ}_VL!v0x%(`6EV0++O znpI^ z93-U8Pif9e$u>$hM&)rNBym!AE(3*66bz1L6eit(L+NM~O9W8nbt{)nQAoi7H(Md> z(7W3#9pPZ5vak$kReSn&UF-;TM97_tCiNf$5yq($3wf5(0!hnKI4q~*5w=@QvvIyd zVJc%O!3&%W7i2|}Do8nc%u0kJPPN!gfhjY$jDT*w@>oev)zG^dxUd07uzF;%dEIKB z1ELrYaq1a$78&c#zpWF5E|T8+uc5flKH$>0naCrga_}XA>4Hj86Y`>NB%>G-kw6wk zgBzPv&E^Et8;<&pXJ?~Hm^B+T(N&fcrfE_x^&pqrp72jMf03nXvj;YVX=UpMr?j&y zltpQ0w}7qoj2-It9Ut2HoqK~pc#b^VW+RkIiy+1(7|(BdnT-`2D|)J$uMjOZzeHLE z{_Maf8BM}XnqEq2J?vIqj$Q+;vgrT^F~Fd#bZ=^rU2z zCvaU9?K6f$@f-UtYvoiF5Tj9u96C*6HpS$K2%aU}oYWD4mk=4`5k^;X3q;7 z_YZh1@e@%KdDPD+g@}_A$PNL|->ksFYMR|#cL=kA7?xl&OgYSTzxd(#-t&F_Zb9a{ zSS(4R1jRslodk6g0mL$OhDT(#!aD;t4LXxpnY`2pr=^rADHQ_!UX^S{hA2W%TLi;e zVw>npZ|?1$o%FC~wf(dn@ib}Piq-d94Y-{b;4PD=ygt$c(d z0sl=;^$eJez{q=v$qgu1r1woJy0f2X+TYUbghN|7iVV1Xc6FREWh~)RP&3(uSZ7+2 bpn4};59`G7I9o~H!Lj9vl*=Pkx=ZmtKV!jF diff --git a/packages/code_editor/assets/translations/qt_zh_CN.ts b/packages/code_editor/assets/translations/qt_zh_CN.ts deleted file mode 100644 index 42d01c2d..00000000 --- a/packages/code_editor/assets/translations/qt_zh_CN.ts +++ /dev/null @@ -1,459 +0,0 @@ - - - - - PMGotoLineDialog - - - Go to Line/Column - 前往行/列 - - - - [Line] [:column]: - [行] [:列]: - - - - PMFindDialog - - - Text to Find - 要查找的文本 - - - - Text to Replace - 替换为 - - - - Wrap - 循环查找 - - - - Regex - 匹配正则表达式 - - - - Case Sensitive - 大小写敏感 - - - - Whole Word - 匹配整个文字 - - - - Up - 向上 - - - - Down - 向下 - - - - Replace - 替换 - - - - Replace All - 替换全部 - - - - FindInPathWidget - - - Case - 大小写敏感 - - - - Whole Word - 匹配整个文字 - - - - Find - 查找 - - - - Find In Path - 在路径中查找 - - - - FormEditor - - - Length:{0} Lines:{1} - 长度:{0} 行{1} - - - - UTF-8 - UTF-8 - - - - Sel:{0} | {1} - 选中区域:{0} | {1} - - - - Ln:{0} Col:{1} - 行:{0} 列:{1} - - - - Unix(LF) - Unix(LF) - - - - Form - - - - - PMBaseEditor - - - Save - 保存 - - - - Save file "{0}"? - 是否保存文件 "{0}"? - - - - PMBaseEditor - - - Ln:{0} Col:{1} - 行:{0} 列:{1} - - - - Ln:1 Col:1 - 行:1 列:1 - - - - Length:0 Lines:1 - 长度:0 行数:1 - - - - Sel:0 | 0 - 选中:0|0 - - - - Format Code - 格式化代码 - - - - Run Code - 运行代码 - - - - Run Selected Code - 运行选中代码 - - - - Save - 保存 - - - - Find/Replace - 查找/替换 - - - - Find In Path - 在路径中查找 - - - - AutoComp - 自动补全 - - - - Add Breakpoint - 添加断点 - - - - Remove Breakpoint - 移除断点 - - - - View BreakPoints - 查看断点 - - - - Save file "{0}"? - 是否保存文件“{0}”? - - - - Length:{0} Lines:{1} - 长度:{0} 行{1} - - - - Save file - 保存文件 - - - - Sel:{0} | {1} - 选中区域:{0} | {1} - - - - File Modified - 文件已经更改 - - - - PMCPPEditor - - - Function Help - 获取函数帮助 - - - - PMCodeEditTabWidget - - - Open File - 打开文件 - - - - Run: %s - 运行:%s - - - - Run Python Code inside %s - 运行 %s 中的代码 - - - - Script - 脚本 - - - - Editor - 编辑器 - - - - Builtin (3.8.5) - 内置解释器(3.8.5) - - - - Warning - 警告 - - - - This Editor does not support instant boot. - 当前编辑器不支持快速启动脚本的运行。 - - - - Builtin (%s) - 内置(%s) - - - - Find In Path - 在路径中查找 - - - - PMCythonEditor - - - Compile to Library - 编译为运行库 - - - - Analyse Profile - 生成代码分析报告 - - - - PMDebugConsoleTabWidget - - - Terminate - 终止 - - - - Process is running, Would you like to terminate this process? - 进程正在运行,是否要终止此进程? - - - - Debugger - 调试器 - - - - PMEditorToolbar - - - New Script - 新建脚本 - - - - Open Script - 打开脚本 - - - - Save - 保存 - - - - Find/Replace - 查找/替换 - - - - Toggle Comment - 切换注释 - - - - Goto Line - 跳转到行 - - - - Indent - 增加缩进 - - - - Dedent - 减少缩进 - - - - IPython - IPython运行 - - - - Separately - 独立运行 - - - - Terminal - 终端中运行 - - - - Editor - 编辑器 - - - - Instant Boot - 快速运行 - - - - Run script with common module preloaded to shorten interpterter startup-time. - 以预加载模块的方式运行当前脚本,从而大大缩减代码冷启动时间。 - - - - PMMarkdownEditor - - - Save - 保存 - - - - Save file "{0}"? - 是否保存文件 "{0}"? - - - - PMPythonEditor - - - Function Help - 获取函数帮助 - - - - Help In Console - 控制台中获取帮助 - - - - Go to Definition - 跳转到定义 - - - - Help - 获取帮助 - - - - Cannot get name. -Maybe There is: -1、Syntax error in your code. -2、No word under text cursor. - 无法获取名称。 -可能有以下错误: -1、代码中有语法错误 -2、指针下没有文字。 - - - - Error - 错误 - - - - Running Current Script Cell (Line %d to %d). - 运行当前的脚本单元(%d行到%d行)。 - - - -- Gitee From 1c352856d9c7cb69bf3e9158852dfd9a7388a171 Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 14:02:27 +0800 Subject: [PATCH 07/17] =?UTF-8?q?code=5Feditor:=20=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_code_editor/test_gui/test_python_edit.py | 4 ++-- tests/test_code_editor/test_gui/test_python_editor.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_code_editor/test_gui/test_python_edit.py b/tests/test_code_editor/test_gui/test_python_edit.py index 5a0af330..a2783b2d 100644 --- a/tests/test_code_editor/test_gui/test_python_edit.py +++ b/tests/test_code_editor/test_gui/test_python_edit.py @@ -6,5 +6,5 @@ def test_myapp(qtbot): qtbot.addWidget(window) window.show() qtbot.waitForWindowShown(window) - window.setPlainText('a = 123') - qtbot.wait(1000) + qtbot.keyClicks(window, 'a=123') + assert window.code == 'a=123' diff --git a/tests/test_code_editor/test_gui/test_python_editor.py b/tests/test_code_editor/test_gui/test_python_editor.py index 5d10fcff..d831ff10 100644 --- a/tests/test_code_editor/test_gui/test_python_editor.py +++ b/tests/test_code_editor/test_gui/test_python_editor.py @@ -8,8 +8,9 @@ def test_format_code(qtbot): qtbot.addWidget(window) window.show() qtbot.waitForWindowShown(window) + qtbot.wait(100) qtbot.keyClicks(window.text_edit, 'a = 123') - qtbot.keyPress(window.text_edit, 'F', modifier=Qt.ControlModifier | Qt.AltModifier) + qtbot.keySequence(window.text_edit, 'Ctrl+Alt+F') assert window.text() == 'a = 123\n' -- Gitee From ce080294da59af0aec9d708bd499a6a6b8dbc04e Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 14:55:04 +0800 Subject: [PATCH 08/17] =?UTF-8?q?code=5Feditor:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E5=9B=9E=E8=BD=A6=E5=90=8E?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=B0=83=E6=95=B4=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_code_editor/test_gui/test_python_editor.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_code_editor/test_gui/test_python_editor.py b/tests/test_code_editor/test_gui/test_python_editor.py index d831ff10..f38165bf 100644 --- a/tests/test_code_editor/test_gui/test_python_editor.py +++ b/tests/test_code_editor/test_gui/test_python_editor.py @@ -25,3 +25,16 @@ def test_auto_completion(qtbot): qtbot.keyClick(window.text_edit.autocompletion_dropdown, Qt.Key_Return) qtbot.wait(1000) assert window.text() == 'import numbers' + + +def test_format_when_editing(qtbot): + """回车后会自动调整格式""" + window = PMPythonEditor() + qtbot.addWidget(window) + window.show() + qtbot.waitForWindowShown(window) + qtbot.wait(100) + qtbot.keyClicks(window.text_edit, 'def a():') + qtbot.keyClick(window.text_edit, Qt.Key_Return) + qtbot.keyClicks(window.text_edit, 'print(123)') + assert window.text_edit.code == 'def a():\n print(123)' -- Gitee From c576d0feed7b88409df59cbfca528d664158fa4f Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 15:05:14 +0800 Subject: [PATCH 09/17] =?UTF-8?q?code=5Feditor:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=86=E6=B3=A8=E9=87=8A=E5=92=8C=E5=8F=8D=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_gui/test_python_editor.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_code_editor/test_gui/test_python_editor.py b/tests/test_code_editor/test_gui/test_python_editor.py index f38165bf..b5e8d548 100644 --- a/tests/test_code_editor/test_gui/test_python_editor.py +++ b/tests/test_code_editor/test_gui/test_python_editor.py @@ -38,3 +38,23 @@ def test_format_when_editing(qtbot): qtbot.keyClick(window.text_edit, Qt.Key_Return) qtbot.keyClicks(window.text_edit, 'print(123)') assert window.text_edit.code == 'def a():\n print(123)' + + +def test_comment(qtbot): + """注释功能和反注释功能""" + window = PMPythonEditor() + qtbot.addWidget(window) + window.show() + qtbot.waitForWindowShown(window) + qtbot.wait(100) + window.text_edit.setPlainText('def a():\n print(123)\n') + qtbot.keySequence(window.text_edit, 'Ctrl+A') + qtbot.keySequence(window.text_edit, 'Ctrl+/') + assert window.text_edit.code == '#def a():\n# print(123)\n#' + qtbot.keySequence(window.text_edit, 'Ctrl+/') + assert window.text_edit.code == 'def a():\n print(123)\n' + qtbot.keyClick(window.text_edit, Qt.Key_Left) + qtbot.keySequence(window.text_edit, 'Ctrl+/') + assert window.text_edit.code == '#def a():\n print(123)\n' + qtbot.keySequence(window.text_edit, 'Ctrl+/') + assert window.text_edit.code == 'def a():\n print(123)\n' -- Gitee From 0e41f25aa67bf2e909781f47911ca221d80d637b Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 18:55:36 +0800 Subject: [PATCH 10/17] =?UTF-8?q?code=5Feditor:=20=E5=B0=86=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E7=9A=84=E5=88=9B=E5=BB=BA=E8=BF=81=E7=A7=BB=E8=87=B3?= =?UTF-8?q?text=5Fedit=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/editors/base_editor.py | 128 +----------------- .../widgets/editors/python_editor.py | 5 +- .../widgets/text_edit/base_text_edit.py | 115 +++++++++++++++- 3 files changed, 114 insertions(+), 134 deletions(-) diff --git a/packages/code_editor/widgets/editors/base_editor.py b/packages/code_editor/widgets/editors/base_editor.py index 2aa00eba..e915ce50 100644 --- a/packages/code_editor/widgets/editors/base_editor.py +++ b/packages/code_editor/widgets/editors/base_editor.py @@ -31,17 +31,15 @@ import os import time from typing import Dict, Callable, TYPE_CHECKING, Type -from PySide2.QtCore import SignalInstance, Signal, QDir, QCoreApplication, QPoint +from PySide2.QtCore import SignalInstance, Signal, QDir from PySide2.QtGui import QTextDocument, QTextCursor -from PySide2.QtWidgets import QWidget, QMessageBox, QLabel, QVBoxLayout, QFileDialog, \ - QMenu +from PySide2.QtWidgets import QWidget, QMessageBox, QLabel, QVBoxLayout, QFileDialog from features.extensions.extensionlib.extension_lib import ExtensionLib from ..dialogs.find_dialog import PMFindDialog from ..dialogs.goto_line_dialog import PMGotoLineDialog from ..text_edit.base_text_edit import PMBaseCodeEdit from ...utils.base_object import CodeEditorBaseObject -from ...utils.operation import Operation from ...utils.utils import decode logger = logging.getLogger(__name__) @@ -49,10 +47,8 @@ logger = logging.getLogger(__name__) class PMBaseEditor(CodeEditorBaseObject, QWidget): """ - 编辑器的各种操件应当由这个类及其子类进行管理,包括代码重构、代码缩进等内容。 - 快捷键也应当定义在这个类中。 - 这个类需要调用其text_edit属性提供的各种方法来实现对编辑器的操作。 - 关于具体的代码缩进调整等仅与代码文本有关的内容应当定义在TextEdit里面。 + 这个类仅作为布局管理一些辅助内容,例如显示行号、列号等内容。 + 所有实际的代码操作都应写在TextEdit里面。 """ # 用于子类继承时的配置项 @@ -152,90 +148,6 @@ class PMBaseEditor(CodeEditorBaseObject, QWidget): self.text_edit.setTextCursor(cursor) return ret - def _init_signals(self) -> None: - """初始化信号绑定""" - - # 绑定右键菜单信号 - self.text_edit.customContextMenuRequested.connect(self.slot_custom_context_menu_requested) - - def _init_actions(self) -> None: - """初始化快捷键和菜单项""" - - def text_exists(): - """判断是否有文本,如果有文本才允许使用自动排版等功能""" - return len(self.text().strip()) > 0 - - # 格式化代码 - self.__operation_format = Operation( - widget=self.text_edit, name='format code', label=self.tr('Format Code'), - slot=self.slot_code_format, key='Ctrl+Alt+F', icon_name='format.svg', conditions=[text_exists], - ) - - # 运行代码 - self.__operation_run_code = Operation( - widget=self.text_edit, name='run code', label=self.tr('Run Code'), - slot=self.slot_code_run, key='Ctrl+R', icon_name='run.svg', conditions=[text_exists], - ) - - # 运行选中代码 - self.__operation_run_selected_code = Operation( - widget=self.text_edit, name='run code', label=self.tr('Run Selected Code'), - slot=self.slot_code_sel_run, key='F9', icon_name='python.svg', conditions=[text_exists], - # TODO 添加判别条件:仅当有文本选中时才可用 - ) - - # 保存代码 - self.__operation_save_code = Operation( - widget=self.text_edit, name='save code', label=self.tr('Save Code'), - slot=self.slot_save, key='Ctrl+S', icon_name='save.svg', - ) - - # 查找代码 - self.__operation_find = Operation( - widget=self.text_edit, name='find code', label=self.tr('Find Code'), - slot=self.slot_find, key='Ctrl+F', - ) - - # 替换代码 - self.__operation_replace = Operation( - widget=self.text_edit, name='replace code', label=self.tr('Replace'), - slot=self.slot_replace, key='Ctrl+H', - ) - - # 在路径中查找,暂不理解这个功能的含义 - self.__operation_find_in_path = Operation( - widget=self.text_edit, name='find in path', label=self.tr('Find In Path'), - slot=self.slot_find_in_path, key='Ctrl+Shift+F', - ) - - # 自动补全功能是每隔一段时间自动显示的,使用快捷键可以立刻显示 - self.__operation_auto_completion = Operation( - widget=self.text_edit, name='auto completion', label=self.tr('AutoComp'), - slot=self.auto_completion, key='Ctrl+P', - ) - - # 跳转到行 - self.__operation_goto_line = Operation( - widget=self.text_edit, name='goto line', label=self.tr('Goto Line'), - slot=self.slot_goto_line, key='Ctrl+G', - ) - - # 添加断点 - self.__operation_add_breakpoint = Operation( - widget=self.text_edit, name='add breakpoint', label=self.tr('Add Breakpoint'), - icon_name='breakpoint.svg', - ) - - # 移除断点 - self.__operation_remove_breakpoint = Operation( - widget=self.text_edit, name='remove breakpoint', label=self.tr('Remove Breakpoint'), - ) - - # 查看所有断点 - self.__operation_view_breakpoints = Operation( - widget=self.text_edit, name='view breakpoints', label=self.tr('View BreakPoints'), - ) - def auto_completion(self): pass @@ -447,38 +359,6 @@ class PMBaseEditor(CodeEditorBaseObject, QWidget): def slot_code_sel_run(self): """运行选中的代码""" - def create_context_menu(self) -> 'QMenu': - """创建上下文菜单。""" - menu = self.text_edit.createStandardContextMenu() - - # 遍历本身已有的菜单项做翻译处理 - # 前提是要加载了Qt自带的翻译文件 - for action in menu.actions(): - action.setText(QCoreApplication.translate('QTextControl', action.text())) - # 添加额外菜单 - menu.addSeparator() - menu.addAction(self.__operation_format.action) - menu.addAction(self.__operation_run_code.action) - menu.addAction(self.__operation_run_selected_code.action) - menu.addAction(self.__operation_save_code.action) - menu.addAction(self.__operation_find.action) - menu.addAction(self.__operation_replace.action) - menu.addAction(self.__operation_find_in_path.action) - menu.addAction(self.__operation_add_breakpoint.action) - menu.addAction(self.__operation_remove_breakpoint.action) - menu.addAction(self.__operation_view_breakpoints.action) - # menu.addAction(self) - return menu - - def slot_custom_context_menu_requested(self, pos: QPoint) -> None: - """打开右键菜单""" - menu = self.create_context_menu() - # 根据条件决定菜单是否可用 - logger.setLevel(logging.DEBUG) - logger.debug('menu craeted') - menu.exec_(self.text_edit.mapToGlobal(pos)) - logger.debug('menu deleted') - def slot_find_in_path(self): selected_text = self.text_edit.get_selected_text() self.signal_request_find_in_path.emit(selected_text) diff --git a/packages/code_editor/widgets/editors/python_editor.py b/packages/code_editor/widgets/editors/python_editor.py index 97ce608c..e9ed81bb 100644 --- a/packages/code_editor/widgets/editors/python_editor.py +++ b/packages/code_editor/widgets/editors/python_editor.py @@ -38,7 +38,7 @@ from typing import List, Tuple, Optional, TYPE_CHECKING, Callable from PySide2.QtCore import SignalInstance, Signal, Qt, QDir from PySide2.QtGui import QKeySequence, QCloseEvent -from PySide2.QtWidgets import QAction, QShortcut, QMessageBox, QApplication +from PySide2.QtWidgets import QAction, QShortcut, QMessageBox from yapf.yapflib import py3compat, yapf_api from pmgwidgets import in_unit_test, PMGOneShotThreadRunner, run_python_file_in_terminal, parse_simplified_pmgjson, \ @@ -90,7 +90,6 @@ class PMPythonEditor(PMBaseEditor): def _init_actions(self) -> None: """初始化事件""" - super(PMPythonEditor, self)._init_actions() self._action_help = QAction(self.tr('Function Help'), self.text_edit) self._shortcut_help = QShortcut(QKeySequence('F1'), self.text_edit, context=Qt.WidgetShortcut) self._action_help.setShortcut(QKeySequence('F1')) @@ -108,7 +107,6 @@ class PMPythonEditor(PMBaseEditor): # noinspection PyUnresolvedReferences def _init_signals(self) -> None: """初始化信号绑定。""" - super(PMPythonEditor, self)._init_signals() self._shortcut_help.activated.connect(self.get_help) self._action_help.triggered.connect(self.get_help) @@ -455,6 +453,7 @@ class PMPythonEditor(PMBaseEditor): def create_context_menu(self) -> 'QMenu': logger.info('create_context_menu') + # TODO 迁移这里 menu = super().create_context_menu() menu.addAction(self._action_help) menu.addAction(self._action_help_in_console) diff --git a/packages/code_editor/widgets/text_edit/base_text_edit.py b/packages/code_editor/widgets/text_edit/base_text_edit.py index 688bdc8c..597160cf 100644 --- a/packages/code_editor/widgets/text_edit/base_text_edit.py +++ b/packages/code_editor/widgets/text_edit/base_text_edit.py @@ -8,7 +8,7 @@ from itertools import groupby from queue import Queue from typing import Callable, Tuple, Dict, List, TYPE_CHECKING, Type, Any -from PySide2.QtCore import SignalInstance, Signal, Qt, QTimer, QModelIndex, QUrl, QRect +from PySide2.QtCore import SignalInstance, Signal, Qt, QTimer, QModelIndex, QUrl, QRect, QPoint, QCoreApplication from PySide2.QtGui import QFocusEvent, QTextCursor, QMouseEvent, QKeyEvent, QDragEnterEvent, QDropEvent, QPainter, \ QColor, QTextFormat, QFontDatabase, QFont, QTextDocument from PySide2.QtWidgets import QPlainTextEdit, QWidget, QApplication, QTextEdit, QLabel @@ -17,9 +17,11 @@ from jedi.api.classes import Completion as CompletionResult import utils from .line_number_area import QLineNumberArea from ..auto_complete_dropdown.base_auto_complete_dropdown import BaseAutoCompleteDropdownWidget +from ...utils.base_object import CodeEditorBaseObject from ...utils.grammar_analyzer.get_indent import get_indent from ...utils.grammar_analyzer.grammar_analyzer import GrammarAnalyzer from ...utils.highlighter.python_highlighter import PythonHighlighter +from ...utils.operation import Operation if TYPE_CHECKING: from ...utils.highlighter.base_highlighter import BaseHighlighter @@ -30,11 +32,9 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) -class PMBaseCodeEdit(QPlainTextEdit): +class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): """ - 与语言无关的编辑器相关操作应该定义在这里。 - 所有与TextEdit相关的操作都应该定义在这里,而对代码相关的操作应当定义在Editor中。 - 应当尽可能地避免子控件对父控件的调用,而是通过信号与槽的方式进行解耦。 + 所有与代码相关的编辑功能都应该定义在这里,包括排版、高亮等功能。 """ # 各个子类的配置项 highlighter_class: 'Type[BaseHighlighter]' = None # 语法高亮类 @@ -120,10 +120,11 @@ class PMBaseCodeEdit(QPlainTextEdit): self.ui_update_timer.start(300) # 绑定各个信号 - self._bind_signals() + self.__bind_signals() + self.__create_operations() # noinspection PyUnresolvedReferences - def _bind_signals(self): + def __bind_signals(self): # 定时触发的事件 self.ui_update_timer.timeout.connect(self.update_ui) @@ -142,6 +143,106 @@ class PMBaseCodeEdit(QPlainTextEdit): # 在代码提示框里面双击后,将自动补全的内容添加至代码 self.autocompletion_dropdown.doubleClicked.connect(self._insert_autocomp) + # 绑定右键菜单信号 + self.customContextMenuRequested.connect(self.slot_custom_context_menu_requested) + + def __create_operations(self): + def text_exists(): + """判断是否有文本,如果有文本才允许使用自动排版等功能""" + return len(self.code) > 0 + + # 格式化代码 + self.__operation_format = Operation( + widget=self, name='format code', label=self.tr('Format Code'), + slot=self.parent().slot_code_format, key='Ctrl+Alt+F', icon_name='format.svg', conditions=[text_exists], + ) + + # 运行代码 + self.__operation_run_code = Operation( + widget=self, name='run code', label=self.tr('Run Code'), + slot=self.parent().slot_code_run, key='Ctrl+R', icon_name='run.svg', conditions=[text_exists], + ) + + # 运行选中代码 + self.__operation_run_selected_code = Operation( + widget=self, name='run code', label=self.tr('Run Selected Code'), + slot=self.parent().slot_code_sel_run, key='F9', icon_name='python.svg', conditions=[text_exists], + # TODO 添加判别条件:仅当有文本选中时才可用 + ) + + # 保存代码 + self.__operation_save_code = Operation( + widget=self, name='save code', label=self.tr('Save Code'), + slot=self.parent().slot_save, key='Ctrl+S', icon_name='save.svg', + ) + + # 查找代码 + self.__operation_find = Operation( + widget=self, name='find code', label=self.tr('Find Code'), + slot=self.parent().slot_find, key='Ctrl+F', + ) + + # 替换代码 + self.__operation_replace = Operation( + widget=self, name='replace code', label=self.tr('Replace'), + slot=self.parent().slot_replace, key='Ctrl+H', + ) + + # 在路径中查找,暂不理解这个功能的含义 + self.__operation_find_in_path = Operation( + widget=self, name='find in path', label=self.tr('Find In Path'), + slot=self.parent().slot_find_in_path, key='Ctrl+Shift+F', + ) + + # 自动补全功能是每隔一段时间自动显示的,使用快捷键可以立刻显示 + self.__operation_auto_completion = Operation( + widget=self, name='auto completion', label=self.tr('AutoComp'), + slot=self.parent().auto_completion, key='Ctrl+P', + ) + + # 跳转到行 + self.__operation_goto_line = Operation( + widget=self, name='goto line', label=self.tr('Goto Line'), + slot=self.parent().slot_goto_line, key='Ctrl+G', + ) + + # 添加断点 + self.__operation_add_breakpoint = Operation( + widget=self, name='add breakpoint', label=self.tr('Add Breakpoint'), icon_name='breakpoint.svg', + ) + + # 移除断点 + self.__operation_remove_breakpoint = Operation( + widget=self, name='remove breakpoint', label=self.tr('Remove Breakpoint'), + ) + + # 查看所有断点 + self.__operation_view_breakpoints = Operation( + widget=self, name='view breakpoints', label=self.tr('View BreakPoints'), + ) + + def slot_custom_context_menu_requested(self, pos: QPoint): + """打开右键菜单""" + menu = self.createStandardContextMenu() + + # 遍历本身已有的菜单项做翻译处理,前提是要加载了Qt自带的翻译文件 + for action in menu.actions(): + action.setText(QCoreApplication.translate('QTextControl', action.text())) + + # 添加额外菜单 + menu.addSeparator() + menu.addAction(self.__operation_format.action) + menu.addAction(self.__operation_run_code.action) + menu.addAction(self.__operation_run_selected_code.action) + menu.addAction(self.__operation_save_code.action) + menu.addAction(self.__operation_find.action) + menu.addAction(self.__operation_replace.action) + menu.addAction(self.__operation_find_in_path.action) + menu.addAction(self.__operation_add_breakpoint.action) + menu.addAction(self.__operation_remove_breakpoint.action) + menu.addAction(self.__operation_view_breakpoints.action) + menu.exec_(self.mapToGlobal(pos)) + @property def line_number_area_width(self): return 30 + self.fontMetrics().width('9') * len(str(max(1, self.blockCount()))) -- Gitee From 6f306c124f3467647e2c6a7f40db231de7729e1c Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 19:13:33 +0800 Subject: [PATCH 11/17] =?UTF-8?q?code=5Feditor:=20=E6=95=B4=E7=90=86operat?= =?UTF-8?q?ion=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E4=BD=BF=E4=B9=8B?= =?UTF-8?q?=E6=9B=B4=E4=B8=BA=E7=B4=A7=E5=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/text_edit/base_text_edit.py | 97 ++++++------------- 1 file changed, 28 insertions(+), 69 deletions(-) diff --git a/packages/code_editor/widgets/text_edit/base_text_edit.py b/packages/code_editor/widgets/text_edit/base_text_edit.py index 597160cf..733383af 100644 --- a/packages/code_editor/widgets/text_edit/base_text_edit.py +++ b/packages/code_editor/widgets/text_edit/base_text_edit.py @@ -11,7 +11,7 @@ from typing import Callable, Tuple, Dict, List, TYPE_CHECKING, Type, Any from PySide2.QtCore import SignalInstance, Signal, Qt, QTimer, QModelIndex, QUrl, QRect, QPoint, QCoreApplication from PySide2.QtGui import QFocusEvent, QTextCursor, QMouseEvent, QKeyEvent, QDragEnterEvent, QDropEvent, QPainter, \ QColor, QTextFormat, QFontDatabase, QFont, QTextDocument -from PySide2.QtWidgets import QPlainTextEdit, QWidget, QApplication, QTextEdit, QLabel +from PySide2.QtWidgets import QPlainTextEdit, QWidget, QApplication, QTextEdit, QLabel, QMenu from jedi.api.classes import Completion as CompletionResult import utils @@ -151,97 +151,56 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): """判断是否有文本,如果有文本才允许使用自动排版等功能""" return len(self.code) > 0 - # 格式化代码 - self.__operation_format = Operation( + # 这里代码比较紧凑,以节省行数 + self.__menu_operations = [Operation( # 格式化代码 widget=self, name='format code', label=self.tr('Format Code'), - slot=self.parent().slot_code_format, key='Ctrl+Alt+F', icon_name='format.svg', conditions=[text_exists], - ) - - # 运行代码 - self.__operation_run_code = Operation( + slot=self.parent().slot_code_format, key='Ctrl+Alt+F', icon_name='format.svg', + conditions=[text_exists], + ), Operation( # 运行代码 widget=self, name='run code', label=self.tr('Run Code'), - slot=self.parent().slot_code_run, key='Ctrl+R', icon_name='run.svg', conditions=[text_exists], - ) - - # 运行选中代码 - self.__operation_run_selected_code = Operation( + slot=self.parent().slot_code_run, key='Ctrl+R', icon_name='run.svg', + conditions=[text_exists], + ), Operation( # 运行选中代码 widget=self, name='run code', label=self.tr('Run Selected Code'), slot=self.parent().slot_code_sel_run, key='F9', icon_name='python.svg', conditions=[text_exists], # TODO 添加判别条件:仅当有文本选中时才可用 - ) - - # 保存代码 - self.__operation_save_code = Operation( + ), Operation( # 保存代码 widget=self, name='save code', label=self.tr('Save Code'), slot=self.parent().slot_save, key='Ctrl+S', icon_name='save.svg', - ) - - # 查找代码 - self.__operation_find = Operation( + ), Operation( # 查找代码 widget=self, name='find code', label=self.tr('Find Code'), slot=self.parent().slot_find, key='Ctrl+F', - ) - - # 替换代码 - self.__operation_replace = Operation( + ), Operation( # 替换代码 widget=self, name='replace code', label=self.tr('Replace'), slot=self.parent().slot_replace, key='Ctrl+H', - ) - - # 在路径中查找,暂不理解这个功能的含义 - self.__operation_find_in_path = Operation( + ), Operation( # 在路径中查找,暂不理解这个功能的含义 widget=self, name='find in path', label=self.tr('Find In Path'), slot=self.parent().slot_find_in_path, key='Ctrl+Shift+F', - ) - - # 自动补全功能是每隔一段时间自动显示的,使用快捷键可以立刻显示 - self.__operation_auto_completion = Operation( + ), Operation( # 自动补全功能是每隔一段时间自动显示的,使用快捷键可以立刻显示 widget=self, name='auto completion', label=self.tr('AutoComp'), slot=self.parent().auto_completion, key='Ctrl+P', - ) - - # 跳转到行 - self.__operation_goto_line = Operation( + ), Operation( # 跳转到行 widget=self, name='goto line', label=self.tr('Goto Line'), slot=self.parent().slot_goto_line, key='Ctrl+G', - ) - - # 添加断点 - self.__operation_add_breakpoint = Operation( + ), Operation( # 添加断点 widget=self, name='add breakpoint', label=self.tr('Add Breakpoint'), icon_name='breakpoint.svg', - ) - - # 移除断点 - self.__operation_remove_breakpoint = Operation( + ), Operation( # 移除断点 widget=self, name='remove breakpoint', label=self.tr('Remove Breakpoint'), - ) - - # 查看所有断点 - self.__operation_view_breakpoints = Operation( + ), Operation( # 查看所有断点 widget=self, name='view breakpoints', label=self.tr('View BreakPoints'), - ) - - def slot_custom_context_menu_requested(self, pos: QPoint): - """打开右键菜单""" - menu = self.createStandardContextMenu() + )] + def createStandardContextMenu(self) -> QMenu: + menu = super().createStandardContextMenu() # 遍历本身已有的菜单项做翻译处理,前提是要加载了Qt自带的翻译文件 - for action in menu.actions(): - action.setText(QCoreApplication.translate('QTextControl', action.text())) - + [action.setText(QCoreApplication.translate('QTextControl', action.text())) for action in menu.actions()] # 添加额外菜单 - menu.addSeparator() - menu.addAction(self.__operation_format.action) - menu.addAction(self.__operation_run_code.action) - menu.addAction(self.__operation_run_selected_code.action) - menu.addAction(self.__operation_save_code.action) - menu.addAction(self.__operation_find.action) - menu.addAction(self.__operation_replace.action) - menu.addAction(self.__operation_find_in_path.action) - menu.addAction(self.__operation_add_breakpoint.action) - menu.addAction(self.__operation_remove_breakpoint.action) - menu.addAction(self.__operation_view_breakpoints.action) - menu.exec_(self.mapToGlobal(pos)) + menu.addSeparator(), [menu.addAction(operation.action) for operation in self.__menu_operations] + return menu + + def slot_custom_context_menu_requested(self, pos: QPoint): + """打开右键菜单""" + self.createStandardContextMenu().exec_(self.mapToGlobal(pos)) @property def line_number_area_width(self): -- Gitee From 3d6b3275be35109aecd5f3fcb4087905d0e9f42c Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 19:30:10 +0800 Subject: [PATCH 12/17] =?UTF-8?q?code=5Feditor:=20=E5=B0=86PythonEditor?= =?UTF-8?q?=E5=86=85=E7=9A=84=E4=B8=80=E4=BA=9Baction=E7=A7=BB=E8=87=B3Bas?= =?UTF-8?q?eTextEdit=E9=87=8C=E9=9D=A2=E7=9A=84Operation=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/editors/python_editor.py | 48 ++----------------- .../widgets/text_edit/base_text_edit.py | 19 ++++++++ 2 files changed, 22 insertions(+), 45 deletions(-) diff --git a/packages/code_editor/widgets/editors/python_editor.py b/packages/code_editor/widgets/editors/python_editor.py index e9ed81bb..fd915d26 100644 --- a/packages/code_editor/widgets/editors/python_editor.py +++ b/packages/code_editor/widgets/editors/python_editor.py @@ -36,9 +36,9 @@ from functools import cached_property from pathlib import Path from typing import List, Tuple, Optional, TYPE_CHECKING, Callable -from PySide2.QtCore import SignalInstance, Signal, Qt, QDir -from PySide2.QtGui import QKeySequence, QCloseEvent -from PySide2.QtWidgets import QAction, QShortcut, QMessageBox +from PySide2.QtCore import SignalInstance, Signal, QDir +from PySide2.QtGui import QCloseEvent +from PySide2.QtWidgets import QMessageBox from yapf.yapflib import py3compat, yapf_api from pmgwidgets import in_unit_test, PMGOneShotThreadRunner, run_python_file_in_terminal, parse_simplified_pmgjson, \ @@ -71,7 +71,6 @@ class PMPythonEditor(PMBaseEditor): self.browser_id = None self._parent = parent self.last_hint = '' - self.prepare_actions() def stop_auto_complete_thread(self): logger.info('autocomp stopped') @@ -84,38 +83,6 @@ class PMPythonEditor(PMBaseEditor): result = json.load(f) return result - def prepare_actions(self): - self._init_actions() - self._init_signals() - - def _init_actions(self) -> None: - """初始化事件""" - self._action_help = QAction(self.tr('Function Help'), self.text_edit) - self._shortcut_help = QShortcut(QKeySequence('F1'), self.text_edit, context=Qt.WidgetShortcut) - self._action_help.setShortcut(QKeySequence('F1')) - - self._action_help_in_console = QAction(self.tr('Help In Console'), self.text_edit) - self._shortcut_help_in_console = QShortcut(QKeySequence('F2'), self.text_edit, context=Qt.WidgetShortcut) - self._action_help_in_console.setShortcut(QKeySequence('F2')) - - self._action_goto_definition = QAction(self.tr('Go to Definition'), self.text_edit) - self._shortcut_goto_definition = QShortcut(QKeySequence('Ctrl+B'), self.text_edit, context=Qt.WidgetShortcut) - self._action_goto_definition.setShortcut(QKeySequence('Ctrl+B')) - - self._action_help_in_console.setVisible(False) - - # noinspection PyUnresolvedReferences - def _init_signals(self) -> None: - """初始化信号绑定。""" - self._shortcut_help.activated.connect(self.get_help) - self._action_help.triggered.connect(self.get_help) - - self._shortcut_help_in_console.activated.connect(self.get_help_in_console) - self._action_help_in_console.triggered.connect(self.get_help_in_console) - - self._shortcut_goto_definition.activated.connect(self.slot_goto_definition) - self._action_goto_definition.triggered.connect(self.slot_goto_definition) - def set_indicators(self, msgs: List[Tuple[int, int, str, str]], clear=True): """ 设置 error warning info 指示器 @@ -451,15 +418,6 @@ class PMPythonEditor(PMBaseEditor): traceback.print_exc() pass - def create_context_menu(self) -> 'QMenu': - logger.info('create_context_menu') - # TODO 迁移这里 - menu = super().create_context_menu() - menu.addAction(self._action_help) - menu.addAction(self._action_help_in_console) - menu.addAction(self._action_goto_definition) - return menu - def check_mkval_expr(self, code: str) -> Optional[Tuple[str, object, List[object]]]: """ 判断一行是否满足mkval的需求。 diff --git a/packages/code_editor/widgets/text_edit/base_text_edit.py b/packages/code_editor/widgets/text_edit/base_text_edit.py index 733383af..646d54a8 100644 --- a/packages/code_editor/widgets/text_edit/base_text_edit.py +++ b/packages/code_editor/widgets/text_edit/base_text_edit.py @@ -151,6 +151,9 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): """判断是否有文本,如果有文本才允许使用自动排版等功能""" return len(self.code) > 0 + def always_false(): + return False + # 这里代码比较紧凑,以节省行数 self.__menu_operations = [Operation( # 格式化代码 widget=self, name='format code', label=self.tr('Format Code'), @@ -182,6 +185,16 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): ), Operation( # 跳转到行 widget=self, name='goto line', label=self.tr('Goto Line'), slot=self.parent().slot_goto_line, key='Ctrl+G', + ), Operation( + widget=self, name='goto definition', label=self.tr('Goto Definition'), + slot=self.parent().slot_goto_definition, key='Ctrl+B', + ), Operation( # 函数帮助 + widget=self, name='function help', label=self.tr('Function Help'), + slot=self.slot_function_help, key='F1', + ), Operation( + widget=self, name='help in console', label=self.tr('Help in Console'), + slot=self.slot_help_in_console, key='F2', + conditions=[always_false], ), Operation( # 添加断点 widget=self, name='add breakpoint', label=self.tr('Add Breakpoint'), icon_name='breakpoint.svg', ), Operation( # 移除断点 @@ -853,3 +866,9 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): self.hint_widget.setText(text.strip()) self.hint_widget.setVisible(flag) event.ignore() + + def slot_function_help(self): + return self.parent().get_help() + + def slot_help_in_console(self): + return self.parent().get_help_in_console() -- Gitee From 11c8feff1fc636d8b627fafc90be41a54b39cafb Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 19:35:07 +0800 Subject: [PATCH 13/17] =?UTF-8?q?code=5Feditor:=20=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E7=94=B1=E4=BA=8E=E4=BB=A3=E7=A0=81=E9=87=8D=E6=9E=84=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=9A=84=E7=BF=BB=E8=AF=91=E4=B8=A2=E5=A4=B1=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code_editor/assets/languages/en.ts | 71 ++++----- .../code_editor/assets/languages/zh_CN.qm | Bin 4466 -> 4708 bytes .../code_editor/assets/languages/zh_CN.ts | 135 ++++++++++++++---- .../code_editor/assets/languages/zh_TW.ts | 71 ++++----- 4 files changed, 182 insertions(+), 95 deletions(-) diff --git a/packages/code_editor/assets/languages/en.ts b/packages/code_editor/assets/languages/en.ts index cec13cfc..b34577a5 100644 --- a/packages/code_editor/assets/languages/en.ts +++ b/packages/code_editor/assets/languages/en.ts @@ -29,79 +29,97 @@ - PMBaseEditor + PMBaseCodeEdit - + Format Code - + Run Code - + Run Selected Code - + Save Code - + Find Code - + Replace - + Find In Path - + AutoComp - + Goto Line - + + Goto Definition + + + + + Function Help + + + + + Help in Console + + + + Add Breakpoint - + Remove Breakpoint - + View BreakPoints + + + PMBaseEditor - + Save - + Save file "{0}"? - + Save file @@ -330,27 +348,12 @@ PMPythonEditor - - Function Help - - - - - Help In Console - - - - - Go to Definition - - - - + Help - + Cannot get name. Maybe There is: 1、Syntax error in your code. @@ -358,7 +361,7 @@ Maybe There is: - + Error diff --git a/packages/code_editor/assets/languages/zh_CN.qm b/packages/code_editor/assets/languages/zh_CN.qm index 5a2e6092c28c17a62a0d5bbfd12dcd058669a000..7f5950d5ffa96412ee63e191c5f4cd0ba7624149 100644 GIT binary patch delta 1121 zcmZvZdq@;<5XWb4cW-ZBcc&F;FBd7aQrohiq(7w7BC^vuBg3FhZ_A@|xAxY$qd)G% z2*RS`lFUkbS$|j<*o$7M+2fC%lto|>RG?6)K}1m6$U;Qh!7|ME_nn!~%zaJ&F#W)^ zSPB5$2_VLSYHWb9A9dv|fKUe1(*n$L&Bzu5^+^VB{Q|o2E}BZr$gc&uKaTY;fsqT* z{{md`VE|_f6dkER|1&7N^#J?Tf^3#{jC5*HPv>$6u|MBy20ZhI%-BXji$zQc7P77 z7y+;q(M<;o04AMo=^FzmrXT*qoU5b1(}NHLs*P#SqLHY8c~UDN=_|)90WU%HIrc4F z2f)`k?q}e}WS$v?Hg-{07%z~-<|iu%aEa|)GK7Q1vt8W@0Q&%Ua6K;MB>4#yGf>}c zMs_E^Gjaq$Wxg)diu!f_=op?4*pTK~?f-YFG;?|EtodYzvgznP$;?DeK~PI@jXUXe&jE zUAFjc3R4pI=;Xv%I?_>Y7r9uG@-Ea{ch&c@RE!SEHsE}N@n73_HGFAo9GwIl%#{i6 z`v~F9E-zb~S^qiI+mKp6I64_$=E;P*u!`~Wp_7|g-kbk{m}|VDy>pSk(dk#0xw0JdX iho%Rl8a1HklA%Nlsj@~7YkE#%(4CqHx@RYtq>SHMm_u#= delta 817 zcmZ8dZAepL6h3!*-|c?9ce7=zHZob7WUB^4(6A8Kib2UHMny(C^oKNDr46AU+n@r4 zNO_HPlA09-CPKpqqL{I$AAyAy2&*8>45TcmD1tiU{*im(<>5T{Jm-1NSt$AL>Me>F z0MHBoy9Z?83gka>{~dtR1LWmith^eENg&G}0Lu)}fu{h*wHnIZK$j8#st9I}AN%LP zn)rtGD>yJ1#(obRdGs9TrJ=cI0YD1CgI!tl+YO()z5v8c7X|N$BK)x|)J)h=$PK49 zU?W`9kj)!L{v84^%#xO+TL3~E8Nb^AAZ{WL6*M5@8j2xG^1S4vO^Z0#(oa*podBi= zI+Dv9C+Um7SW8p%7yXJ0jG8Ci6ym^Q=9whJM3Pv}@B+e|c&YLzfIKBWE5l6MXKx^OA#WDUg$1dh z7_^eT-m!$2YSK^`(vbR9DZ3L5j;rg{A9(tZdO9?Vy7y{#(2hY@)M3X>z;JApC z)iKjVud1Tkbv=s~m%PgQ2Gv`?p行号 + + PMBaseCodeEdit + + + Format Code + 代码格式化 + + + + Run Code + 运行代码 + + + + Run Selected Code + 运行选中的代码 + + + + Save Code + 保存代码 + + + + Find Code + 查找代码 + + + + Replace + 替换 + + + + Find In Path + 在路径中查找 + + + + AutoComp + 自动补全 + + + + Goto Line + 跳转到行 + + + + Goto Definition + 转到定义 + + + + Function Help + 函数帮助 + + + + Help in Console + 在控制台中显示帮助 + + + + Add Breakpoint + 添加断点 + + + + Remove Breakpoint + 移除断点 + + + + View BreakPoints + 查看所有断点 + + PMBaseEditor Format Code - 代码格式化 + 代码格式化 Run Code - 运行代码 + 运行代码 Run Selected Code - 运行选中的代码 + 运行选中的代码 Save Code - 保存代码 + 保存代码 Find Code - 查找代码 + 查找代码 Replace - 替换 + 替换 Find In Path - 在路径中查找 + 在路径中查找 AutoComp - 自动补全 + 自动补全 Goto Line - 跳转到行 + 跳转到行 Add Breakpoint - 添加断点 + 添加断点 Remove Breakpoint - 移除断点 + 移除断点 View BreakPoints - 查看所有断点 + 查看所有断点 - + Save 保存 - + Save file "{0}"? 保存文件“{0}”? - + Save file 保存文件 @@ -333,46 +411,49 @@ Go to Definition - 转到定义 + 转到定义 - + Help 帮助 Function Help - 函数帮助 + 函数帮助 Help In Console - 在控制台中显示帮助 + 在控制台中显示帮助 Cannot get name. Maybe There is: -1、Syntax error in your code. -2、No word under text cursor. +1、Syntax error in your code. +2、No word under text cursor. 无法获取名称。 可能出现的问题: 1、语法错误; 2、光标下没有文本。 - + Error 错误 - - + + Cannot get name. Maybe There is: -1、Syntax error in your code. -2、No word under text cursor. - +1、Syntax error in your code. +2、No word under text cursor. + 无法获取名称。 +可能的问题: +1、代码中存在语法错误; +2、游标下没有单词。 diff --git a/packages/code_editor/assets/languages/zh_TW.ts b/packages/code_editor/assets/languages/zh_TW.ts index cec13cfc..b34577a5 100644 --- a/packages/code_editor/assets/languages/zh_TW.ts +++ b/packages/code_editor/assets/languages/zh_TW.ts @@ -29,79 +29,97 @@ - PMBaseEditor + PMBaseCodeEdit - + Format Code - + Run Code - + Run Selected Code - + Save Code - + Find Code - + Replace - + Find In Path - + AutoComp - + Goto Line - + + Goto Definition + + + + + Function Help + + + + + Help in Console + + + + Add Breakpoint - + Remove Breakpoint - + View BreakPoints + + + PMBaseEditor - + Save - + Save file "{0}"? - + Save file @@ -330,27 +348,12 @@ PMPythonEditor - - Function Help - - - - - Help In Console - - - - - Go to Definition - - - - + Help - + Cannot get name. Maybe There is: 1、Syntax error in your code. @@ -358,7 +361,7 @@ Maybe There is: - + Error -- Gitee From 7d73d3cf387655b8b00f2be956be00b60ce495cd Mon Sep 17 00:00:00 2001 From: wolfpan Date: Tue, 10 Aug 2021 19:41:15 +0800 Subject: [PATCH 14/17] =?UTF-8?q?code=5Feditor:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=B8=80=E4=B8=AAtodo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code_editor/widgets/text_edit/base_text_edit.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/code_editor/widgets/text_edit/base_text_edit.py b/packages/code_editor/widgets/text_edit/base_text_edit.py index 646d54a8..a23b13f7 100644 --- a/packages/code_editor/widgets/text_edit/base_text_edit.py +++ b/packages/code_editor/widgets/text_edit/base_text_edit.py @@ -154,6 +154,8 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): def always_false(): return False + # TODO 将这些操作全部迁移至这个类下 + # 这里代码比较紧凑,以节省行数 self.__menu_operations = [Operation( # 格式化代码 widget=self, name='format code', label=self.tr('Format Code'), -- Gitee From 78f4de9699e914424c421df0f9ab4027c3b1283d Mon Sep 17 00:00:00 2001 From: wolfpan Date: Wed, 11 Aug 2021 02:08:14 +0800 Subject: [PATCH 15/17] =?UTF-8?q?code=5Feditor:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=85=A8=E5=B1=80extension=5Flib=EF=BC=8C=E5=B9=B6=E5=B0=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=9A=84=E8=BF=90=E8=A1=8C=E7=AD=89=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E7=8B=AC=E7=AB=8B=E4=B8=BA=E4=B8=80=E4=B8=AA=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E5=AE=9E=E7=8E=B0=E8=A7=A3=E8=80=A6=EF=BC=88?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=AE=8C=E6=88=90=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extensions_manager/ExtensionLoader.py | 1 + .../code_editor/code_handlers/__init__.py | 0 .../code_editor/code_handlers/base_handler.py | 23 +++++++++++++++++++ .../code_handlers/python_handler.py | 18 +++++++++++++++ packages/code_editor/main.py | 5 ---- packages/code_editor/utils/base_object.py | 8 ++++++- .../widgets/editors/base_editor.py | 16 +++++++++++-- .../widgets/editors/python_editor.py | 18 --------------- packages/code_editor/widgets/tab_widget.py | 10 ++++---- .../widgets/text_edit/base_text_edit.py | 14 +++++++++-- .../widgets/text_edit/python_text_edit.py | 2 ++ 11 files changed, 82 insertions(+), 33 deletions(-) create mode 100644 packages/code_editor/code_handlers/__init__.py create mode 100644 packages/code_editor/code_handlers/base_handler.py create mode 100644 packages/code_editor/code_handlers/python_handler.py diff --git a/features/extensions/extensions_manager/ExtensionLoader.py b/features/extensions/extensions_manager/ExtensionLoader.py index d4ab4655..d7c9aa50 100644 --- a/features/extensions/extensions_manager/ExtensionLoader.py +++ b/features/extensions/extensions_manager/ExtensionLoader.py @@ -164,6 +164,7 @@ class ExtensionLoader: def load_class(self, file, class_name): path = os.path.join(self.path, file) + # TODO 这里的设置将导致奇怪的错误,即模块不再为单例模式,而这是Python中的一个重要特性 module = self.import_module(path) if module: if hasattr(module, class_name): diff --git a/packages/code_editor/code_handlers/__init__.py b/packages/code_editor/code_handlers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/packages/code_editor/code_handlers/base_handler.py b/packages/code_editor/code_handlers/base_handler.py new file mode 100644 index 00000000..388c7aa3 --- /dev/null +++ b/packages/code_editor/code_handlers/base_handler.py @@ -0,0 +1,23 @@ +from PySide2.QtWidgets import QMessageBox + +from packages.code_editor.utils.base_object import CodeEditorBaseObject + + +class BaseHandler(CodeEditorBaseObject): + """ + 代码的执行、格式化、分析等所有工作都应写在这个类及其子类下。 + """ + + def __not_implemented_error(self, name): + title = self.tr('Not Implemented Error') + message = self.tr('{} is not implemented').format(name) + QMessageBox.critical(None, title, message) + + def run_code(self, code: str, hint: str = 'run code'): + """运行一段代码 + + Args: + code: 代码 + hint: 代码的标题 + """ + self.__not_implemented_error(self.tr('run code')) diff --git a/packages/code_editor/code_handlers/python_handler.py b/packages/code_editor/code_handlers/python_handler.py new file mode 100644 index 00000000..9401f03f --- /dev/null +++ b/packages/code_editor/code_handlers/python_handler.py @@ -0,0 +1,18 @@ +from functools import cached_property +from typing import TYPE_CHECKING + +from packages.code_editor.code_handlers.base_handler import BaseHandler + +if TYPE_CHECKING: + from packages.ipython_console.main import ConsoleInterface + + +class PythonHandler(BaseHandler): + @cached_property + def ipython_console(self) -> 'ConsoleInterface': + return self.extension_lib.get_interface('ipython_console') + + def run_code(self, code: str, hint: str = ''): + if hint == '': + hint = self.tr('Run code') + self.ipython_console.run_command(command=code, hint_text=hint, hidden=False) diff --git a/packages/code_editor/main.py b/packages/code_editor/main.py index 5733b46f..2458886c 100644 --- a/packages/code_editor/main.py +++ b/packages/code_editor/main.py @@ -1,12 +1,7 @@ import logging import os -import sys -from pathlib import Path from typing import Dict, Union, TYPE_CHECKING -from PySide2.QtCore import QLocale, QTranslator -from PySide2.QtWidgets import QApplication - if TYPE_CHECKING: from packages.file_tree.main import Interface as FileTreeInterface diff --git a/packages/code_editor/utils/base_object.py b/packages/code_editor/utils/base_object.py index b400b32e..b026790e 100644 --- a/packages/code_editor/utils/base_object.py +++ b/packages/code_editor/utils/base_object.py @@ -1,11 +1,15 @@ from functools import cached_property from pathlib import Path -from typing import Optional, Callable +from typing import Optional, Callable, TYPE_CHECKING from PySide2.QtCore import QTranslator, QLocale +from features.extensions.extensionlib import extension_lib from ..settings import Settings +if TYPE_CHECKING: + from features.extensions.extensionlib.extension_lib import ExtensionLib + def _get_translator() -> Optional[QTranslator]: result = QTranslator() @@ -19,6 +23,8 @@ def _get_translator() -> Optional[QTranslator]: class CodeEditorBaseObject: + # 由于插件内为独立的模块加载,其命名空间不共享,因此暂时只能采用这种方式实现全局的extension_lib读取 + extension_lib: 'ExtensionLib' = extension_lib settings = Settings() __translator = _get_translator() diff --git a/packages/code_editor/widgets/editors/base_editor.py b/packages/code_editor/widgets/editors/base_editor.py index e915ce50..5e27cfdd 100644 --- a/packages/code_editor/widgets/editors/base_editor.py +++ b/packages/code_editor/widgets/editors/base_editor.py @@ -354,10 +354,22 @@ class PMBaseEditor(CodeEditorBaseObject, QWidget): """格式化代码""" def slot_code_run(self): - """运行全部代码""" + """运行代码""" + logger.warning('run code') + text = self.text().strip() + if not text: + return + + self._parent.slot_run_script(text) def slot_code_sel_run(self): - """运行选中的代码""" + """运行选中代码""" + # TODO 存在问题,当选中了多行时,会报错 + text = self.text(selected=True).strip() + if not text: + text = self.current_line_text().strip() + + self._parent.slot_run_sel(text) def slot_find_in_path(self): selected_text = self.text_edit.get_selected_text() diff --git a/packages/code_editor/widgets/editors/python_editor.py b/packages/code_editor/widgets/editors/python_editor.py index fd915d26..5c1efe40 100644 --- a/packages/code_editor/widgets/editors/python_editor.py +++ b/packages/code_editor/widgets/editors/python_editor.py @@ -336,24 +336,6 @@ class PMPythonEditor(PMBaseEditor): # 清除被标记波浪线 self.text_edit.clearIndicatorRange(row, 0, row, col, self._indicator_error2) - def slot_code_sel_run(self): - """运行选中代码""" - # TODO 存在问题,当选中了多行时,会报错 - text = self.text(selected=True).strip() - if not text: - text = self.current_line_text().strip() - - self._parent.slot_run_sel(text) - - def slot_code_run(self): - """运行代码""" - logger.warning('run code') - text = self.text().strip() - if not text: - return - - self._parent.slot_run_script(text) - def slot_run_in_terminal(self): """在终端中运行代码 diff --git a/packages/code_editor/widgets/tab_widget.py b/packages/code_editor/widgets/tab_widget.py index 90fc7fe3..1a5664b6 100644 --- a/packages/code_editor/widgets/tab_widget.py +++ b/packages/code_editor/widgets/tab_widget.py @@ -13,6 +13,7 @@ from pmgwidgets import PMDockObject, UndoManager, PMGFileSystemWatchdog, in_unit from .editors.markdown_editor import PMMarkdownEditorPM from .editors.python_editor import PMPythonEditor from .ui.findinpath import FindInPathWidget +from ..utils.base_object import CodeEditorBaseObject from ..utils.code_checker.base_code_checker import CodeCheckWorker from ..utils.highlighter.python_highlighter import PythonHighlighter @@ -50,6 +51,7 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): def set_extension_lib(self, extension_lib): self.extension_lib = extension_lib + CodeEditorBaseObject.extension_lib = extension_lib self.extension_lib.Data.add_data_changed_callback(lambda name, var, source: self.slot_check_code(True)) self.extension_lib.Data.add_data_deleted_callback(lambda name, provider: self.slot_check_code(True)) @@ -518,14 +520,12 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): code = code.strip() if hint == '': - hint = self.tr( - 'Run: %s') % self.get_current_filename() + hint = self.tr('Run: %s') % self.get_current_filename() elif isinstance(self.currentWidget(), PMMarkdownEditorPM): code = self.currentWidget().get_code() code = code.strip() if hint == '': - hint = self.tr( - 'Run Python Code inside %s') % self.get_current_filename() + hint = self.tr('Run Python Code inside %s') % self.get_current_filename() else: return if not code: @@ -533,7 +533,7 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): if not in_unit_test(): self.extension_lib.get_interface('ipython_console').run_command(command=code, hint_text=hint, hidden=False) else: - logger.info('In Unit test at method \'slot_run_script\'.code is :\n%s,\nhint is :%s' % (code, hint)) + logger.info("In Unit test at method 'slot_run_script'.code is :\n%s,\nhint is :%s" % (code, hint)) def slot_run_sel(self, sel_text): """ diff --git a/packages/code_editor/widgets/text_edit/base_text_edit.py b/packages/code_editor/widgets/text_edit/base_text_edit.py index a23b13f7..07743a85 100644 --- a/packages/code_editor/widgets/text_edit/base_text_edit.py +++ b/packages/code_editor/widgets/text_edit/base_text_edit.py @@ -17,6 +17,7 @@ from jedi.api.classes import Completion as CompletionResult import utils from .line_number_area import QLineNumberArea from ..auto_complete_dropdown.base_auto_complete_dropdown import BaseAutoCompleteDropdownWidget +from ...code_handlers.base_handler import BaseHandler from ...utils.base_object import CodeEditorBaseObject from ...utils.grammar_analyzer.get_indent import get_indent from ...utils.grammar_analyzer.grammar_analyzer import GrammarAnalyzer @@ -43,6 +44,9 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): highlighter: 'BaseHighlighter' = None auto_complete_thread: 'BaseAutoCompleteThread' = None + handler_class: 'Type[BaseHandler]' = BaseHandler # 代码核心操作类 + handler: 'BaseHandler' = None + # cursorPositionChanged = Signal() signal_save: SignalInstance = Signal() # 触发保存的事件,具体的保存操作交由给editor控件进行操作 signal_focused_in: SignalInstance = Signal(QFocusEvent) # 使用click代替focus,因为focus in信号触发过于频繁 @@ -74,6 +78,7 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): self.auto_complete_thread = self.auto_complete_thread_class() self.auto_complete_thread.trigger.connect(self.on_autocomp_signal_received) self.auto_complete_thread.start() + self.handler = self.handler_class() self.setTabChangesFocus(False) # 不允许Tab切换焦点,因Tab有更重要的切换缩进的作用 self.setMouseTracking(True) # 启用鼠标跟踪,这允许在鼠标滑过该控件时捕捉到事件 @@ -103,7 +108,6 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): self.setLineWrapMode(QPlainTextEdit.NoWrap) self.doc_tab_widget = parent - self.filename = '*' self.path = '' self.modified = False self._last_text = '' @@ -147,6 +151,8 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): self.customContextMenuRequested.connect(self.slot_custom_context_menu_requested) def __create_operations(self): + """创建操作,绑定快捷键,生成菜单项。""" + def text_exists(): """判断是否有文本,如果有文本才允许使用自动排版等功能""" return len(self.code) > 0 @@ -163,7 +169,7 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): conditions=[text_exists], ), Operation( # 运行代码 widget=self, name='run code', label=self.tr('Run Code'), - slot=self.parent().slot_code_run, key='Ctrl+R', icon_name='run.svg', + slot=self.slot_code_run, key='Ctrl+R', icon_name='run.svg', conditions=[text_exists], ), Operation( # 运行选中代码 widget=self, name='run code', label=self.tr('Run Selected Code'), @@ -874,3 +880,7 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): def slot_help_in_console(self): return self.parent().get_help_in_console() + + def slot_code_run(self): + # TODO 这里的path应该是本对象的属性,而非父对象的属性 + self.handler.run_code(self.code, self.tr('Running {}').format(self.parent()._path)) diff --git a/packages/code_editor/widgets/text_edit/python_text_edit.py b/packages/code_editor/widgets/text_edit/python_text_edit.py index fed28e12..5a19c68d 100644 --- a/packages/code_editor/widgets/text_edit/python_text_edit.py +++ b/packages/code_editor/widgets/text_edit/python_text_edit.py @@ -5,6 +5,7 @@ from PySide2.QtCore import QPoint from jedi.api.classes import Completion as CompletionResult from .base_text_edit import PMBaseCodeEdit +from ...code_handlers.python_handler import PythonHandler from ...utils.auto_complete_thread.python_auto_complete import PythonAutoCompleteThread from ...utils.highlighter.python_highlighter import PythonHighlighter @@ -17,6 +18,7 @@ class PMPythonCodeEdit(PMBaseCodeEdit): auto_complete_thread_class = PythonAutoCompleteThread highlighter_class = PythonHighlighter + handler_class = PythonHandler def on_autocomp_signal_received(self, text_cursor_content: tuple, completions: List[CompletionResult]): """ -- Gitee From 185a5fda8348ebd9de7ccb4b4f562df5f074fcd5 Mon Sep 17 00:00:00 2001 From: wolfpan Date: Wed, 11 Aug 2021 12:41:58 +0800 Subject: [PATCH 16/17] =?UTF-8?q?code=5Feditor:=20=E5=B0=86=E4=B8=80?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD=E4=BB=8Eui=E9=87=8C?= =?UTF-8?q?=E9=9D=A2=E6=8A=BD=E8=B1=A1=E5=88=B0handler=E9=87=8C=E9=9D=A2?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E5=AE=9E=E7=8E=B0=E8=A7=A3=E8=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code_editor/code_handlers/base_handler.py | 54 +++++++++++++++++++ .../widgets/dialogs/find_dialog.py | 4 +- .../widgets/editors/base_editor.py | 4 +- .../widgets/text_edit/base_text_edit.py | 24 ++++++--- .../test_gui/test_python_code_edit.py | 36 ++++++++----- .../test_code_editor/test_handler/__init__.py | 0 .../test_handler/test_base_handler.py | 27 ++++++++++ 7 files changed, 126 insertions(+), 23 deletions(-) create mode 100644 tests/test_code_editor/test_handler/__init__.py create mode 100644 tests/test_code_editor/test_handler/test_base_handler.py diff --git a/packages/code_editor/code_handlers/base_handler.py b/packages/code_editor/code_handlers/base_handler.py index 388c7aa3..3521a9de 100644 --- a/packages/code_editor/code_handlers/base_handler.py +++ b/packages/code_editor/code_handlers/base_handler.py @@ -1,12 +1,66 @@ +from functools import cached_property +from typing import Tuple, Type, List, Optional + from PySide2.QtWidgets import QMessageBox from packages.code_editor.utils.base_object import CodeEditorBaseObject +class BaseAnalyzer(CodeEditorBaseObject): + """ + 在每次进行代码分析的时候都创建一遍这个对象。 + 这个对象的好处是所有的属性计算都是惰性的,按需计算,降低性能损耗。 + """ + + def __init__(self, code: str, cursor: int, selection_range: Optional[Tuple[int, int]] = None): + self.code: str = code + self.cursor: int = cursor + self.selection_range: Tuple[int, int] = selection_range if selection_range is not None else (cursor, cursor) + + @cached_property + def has_selection(self): + return self.selection_range[0] != self.selection_range[1] + + @cached_property + def lines(self) -> List[str]: + return self.code.split('\n') + + @cached_property + def current_line_index(self) -> int: + """行的位置,用于进行索引,从0开始""" + return self.code[:self.cursor].count('\n') + + @cached_property + def current_line_number(self) -> int: + """行号,用于进行显示,从1开始""" + return self.current_line_index + 1 + + @cached_property + def selected_code(self): + if self.has_selection: + return self.code[self.selection_range[0]:self.selection_range[1]] + else: + return self.lines[self.current_line_index] + + class BaseHandler(CodeEditorBaseObject): """ 代码的执行、格式化、分析等所有工作都应写在这个类及其子类下。 + + 这里相当于是界面的后端,所有的对代码的操作都应该放在这里。 """ + analyzer_class: Type[BaseAnalyzer] = BaseAnalyzer + analyzer: BaseAnalyzer + + def feed(self, code: str, position: int, selection_range: Tuple[int, int]): + """输入代码,以用于分析等操作 + + Args: + code: 代码,应该是plainText + position: 游标的位置,是一个整数,而不是行列号 + selection_range: 选区的位置,是一对整数,表示起止位置,而不是行列号 + """ + self.analyzer = self.analyzer_class(code, position, selection_range) def __not_implemented_error(self, name): title = self.tr('Not Implemented Error') diff --git a/packages/code_editor/widgets/dialogs/find_dialog.py b/packages/code_editor/widgets/dialogs/find_dialog.py index d688be98..79685eb4 100644 --- a/packages/code_editor/widgets/dialogs/find_dialog.py +++ b/packages/code_editor/widgets/dialogs/find_dialog.py @@ -80,8 +80,8 @@ class PMFindDialog(QDialog): def show(self) -> None: super().show() - if self.text_edit.get_selected_text() != '': - self.settings_panel.set_value({'text_to_find': self.text_edit.get_selected_text()}) + if self.text_edit.selected_code != '': + self.settings_panel.set_value({'text_to_find': self.text_edit.selected_code}) def show_replace_actions(self, replace_on: bool = False): self.settings_panel.get_ctrl('text_to_replace').setVisible(replace_on) diff --git a/packages/code_editor/widgets/editors/base_editor.py b/packages/code_editor/widgets/editors/base_editor.py index 5e27cfdd..858d3da5 100644 --- a/packages/code_editor/widgets/editors/base_editor.py +++ b/packages/code_editor/widgets/editors/base_editor.py @@ -335,7 +335,7 @@ class PMBaseEditor(CodeEditorBaseObject, QWidget): str, 选中的或全部的代码 """ if selected: - return self.text_edit.get_selected_text() + return self.text_edit.selected_code else: return self.text_edit.toPlainText() @@ -372,7 +372,7 @@ class PMBaseEditor(CodeEditorBaseObject, QWidget): self._parent.slot_run_sel(text) def slot_find_in_path(self): - selected_text = self.text_edit.get_selected_text() + selected_text = self.text_edit.selected_code self.signal_request_find_in_path.emit(selected_text) def slot_find(self): diff --git a/packages/code_editor/widgets/text_edit/base_text_edit.py b/packages/code_editor/widgets/text_edit/base_text_edit.py index 07743a85..259fdb47 100644 --- a/packages/code_editor/widgets/text_edit/base_text_edit.py +++ b/packages/code_editor/widgets/text_edit/base_text_edit.py @@ -160,8 +160,11 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): def always_false(): return False - # TODO 将这些操作全部迁移至这个类下 + # 如果没有定义父对象,则直接返回,因为目前这个体系之下,大量的操作定义在了父对象中,导致单元测试跑不起来 + if not self.parent(): + return + # TODO 将这些操作全部迁移至这个类下 # 这里代码比较紧凑,以节省行数 self.__menu_operations = [Operation( # 格式化代码 widget=self, name='format code', label=self.tr('Format Code'), @@ -710,11 +713,20 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): text_cursor.setPosition(pos) self.setTextCursor(text_cursor) - def get_selected_text(self) -> str: - if self.textCursor().hasSelection(): - return self.textCursor().selectedText() - else: - return '' + @property + def selected_code(self): + """获取光标选中的代码,或当前行""" + # 这里使用selection而不是getSelectedText,是因为plainText会自动处理一些特殊字符,比如\u2029 + cursor = self.textCursor() + self.handler.feed(self.code, self.get_cursor_position(), (cursor.selectionStart(), cursor.selectionEnd())) + return self.handler.analyzer.selected_code + + @property + def current_line_code(self): + """当前行的代码,包括尾换行符""" + lines = self.code.splitlines(keepends=True) + row = self.textCursor().blockNumber() + return '' if row >= len(lines) else lines[row] def get_selected_row_numbers(self) -> Tuple[int, int]: """返回选中的行号范围""" diff --git a/tests/test_code_editor/test_gui/test_python_code_edit.py b/tests/test_code_editor/test_gui/test_python_code_edit.py index 8ab8ddb9..b30825d4 100644 --- a/tests/test_code_editor/test_gui/test_python_code_edit.py +++ b/tests/test_code_editor/test_gui/test_python_code_edit.py @@ -1,16 +1,26 @@ -from packages.code_editor.widgets.text_edit.python_text_edit import PMPythonCodeEdit - +from PySide2.QtCore import Qt -def test_myapp(qtbot): - # TODO 用例错误 - editor = PMPythonCodeEdit() - qtbot.addWidget(editor) - editor.show() - qtbot.waitForWindowShown(editor) +from packages.code_editor.widgets.text_edit.python_text_edit import PMPythonCodeEdit - editor.setPlainText("abcdefg = 123\n" * 100) - editor.highlighter.registerHighlight(5, 3, 7, editor.highlighter.DEHIGHLIGHT, 'This is an Dehighlight') - editor.highlighter.registerHighlight(3, 1, 7, editor.highlighter.WARNING, 'This is an warning') - editor.highlighter.rehighlight() - qtbot.wait(1000) +def test_get_selected_text(qtbot): + window = PMPythonCodeEdit() + qtbot.addWidget(window) + window.show() + qtbot.waitForWindowShown(window) + qtbot.wait(100) + qtbot.keyClicks(window, 'a = 123') + qtbot.keySequence(window, 'Ctrl+A') + assert window.selected_code == 'a = 123' + qtbot.keyClick(window, Qt.Key_Right) + qtbot.keyClick(window, Qt.Key_Return) + qtbot.keyClicks(window, 'print(123)') + qtbot.keyClick(window, Qt.Key_Return) + qtbot.keySequence(window, 'Shift+Up') + assert window.selected_code == 'print(123)\n' + qtbot.keyClick(window, Qt.Key_Right) + qtbot.keyClick(window, Qt.Key_Right) + qtbot.keyClick(window, Qt.Key_Right) + assert window.selected_code == '' + qtbot.keyClick(window, Qt.Key_Up) + assert window.selected_code == 'print(123)' diff --git a/tests/test_code_editor/test_handler/__init__.py b/tests/test_code_editor/test_handler/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_code_editor/test_handler/test_base_handler.py b/tests/test_code_editor/test_handler/test_base_handler.py new file mode 100644 index 00000000..32d38e7c --- /dev/null +++ b/tests/test_code_editor/test_handler/test_base_handler.py @@ -0,0 +1,27 @@ +from packages.code_editor.code_handlers.base_handler import BaseAnalyzer + +code = ''' +a = 1 +b = 2 +c = 3 +print(a, b, c) +''' + + +# 2的结尾是位置12,3的前面是位置13 + + +def test_current_line_number(): + analyzer = BaseAnalyzer(code, 12) + assert analyzer.current_line_index == 2 + analyzer = BaseAnalyzer(code, 13) + assert analyzer.current_line_index == 3 + + +def test_selection(): + analyzer = BaseAnalyzer(code, 12, (12, 12)) + assert not analyzer.has_selection + assert analyzer.selected_code == 'b = 2' + analyzer = BaseAnalyzer(code, 12, (12, 17)) + assert analyzer.has_selection + assert analyzer.selected_code == '\nc = ' -- Gitee From 3f17ef888c74c4093462c62de4228cbfb05e6d1a Mon Sep 17 00:00:00 2001 From: wolfpan Date: Wed, 11 Aug 2021 14:50:47 +0800 Subject: [PATCH 17/17] =?UTF-8?q?code=5Feditor:=20=E5=B0=86handler?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=E6=9B=B4=E6=96=B0=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E5=88=B0=E5=90=84=E4=B8=AA=E4=BA=8B=E4=BB=B6=E4=B8=AD=EF=BC=8C?= =?UTF-8?q?=E4=BB=8E=E8=80=8C=E8=87=AA=E5=8A=A8=E8=A7=A6=E5=8F=91=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code_editor/code_handlers/base_handler.py | 7 ++++++- .../widgets/text_edit/base_text_edit.py | 17 +++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/code_editor/code_handlers/base_handler.py b/packages/code_editor/code_handlers/base_handler.py index 3521a9de..6326d3e0 100644 --- a/packages/code_editor/code_handlers/base_handler.py +++ b/packages/code_editor/code_handlers/base_handler.py @@ -50,16 +50,21 @@ class BaseHandler(CodeEditorBaseObject): 这里相当于是界面的后端,所有的对代码的操作都应该放在这里。 """ analyzer_class: Type[BaseAnalyzer] = BaseAnalyzer - analyzer: BaseAnalyzer + analyzer: BaseAnalyzer = None def feed(self, code: str, position: int, selection_range: Tuple[int, int]): """输入代码,以用于分析等操作 + 这个更新的方式为增量更新,仅当参数与上一次的参数不一致时,才创建新的analyzer对象。 + Args: code: 代码,应该是plainText position: 游标的位置,是一个整数,而不是行列号 selection_range: 选区的位置,是一对整数,表示起止位置,而不是行列号 """ + if (a := self.analyzer) is not None: + if (code, position, selection_range) == (a.code, a.cursor, a.selection_range): + return self.analyzer = self.analyzer_class(code, position, selection_range) def __not_implemented_error(self, name): diff --git a/packages/code_editor/widgets/text_edit/base_text_edit.py b/packages/code_editor/widgets/text_edit/base_text_edit.py index 259fdb47..bef78ce9 100644 --- a/packages/code_editor/widgets/text_edit/base_text_edit.py +++ b/packages/code_editor/widgets/text_edit/base_text_edit.py @@ -150,6 +150,9 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): # 绑定右键菜单信号 self.customContextMenuRequested.connect(self.slot_custom_context_menu_requested) + self.textChanged.connect(self.update_handler_code) + self.selectionChanged.connect(self.update_handler_code) + def __create_operations(self): """创建操作,绑定快捷键,生成菜单项。""" @@ -331,6 +334,10 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): def hide_autocomp(self): self.autocompletion_dropdown.hide_autocomp() + def update_handler_code(self): + cursor = self.textCursor() + self.handler.feed(self.code, cursor.position(), (cursor.selectionStart(), cursor.selectionEnd())) + def on_text_changed(self): """文字发生改变时的方法""" if not self.modified: @@ -454,6 +461,7 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): 具体的性能没有进行过查证,不过直观上看,使用keyPressEvent在性能上会存在优势。 """ + self.update_handler_code() # TODO 按键处理逻辑仍存在bug,应当分为字符映射和键盘映射两种情况进行处理 # 即分别通过event.text()和event.key()+event.modifier()进行处理 text, key, modifiers = event.text(), event.key(), int(event.modifiers()) @@ -466,6 +474,7 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): callback(event) else: super().keyPressEvent(event) + self.update_handler_code() def on_left_parenthesis(self, event: QKeyEvent): cursor = self.textCursor() @@ -482,7 +491,6 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): return self.toPlainText() def on_right_parenthesis(self, event: QKeyEvent): - print(f'received {event.text()}') left, right = { Qt.Key_ParenRight: ('(', ')'), Qt.Key_BracketRight: ('[', ']'), @@ -494,12 +502,9 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): analyzer = GrammarAnalyzer() analyzer.feed(code) length = len(code) - print(f'{position == length}, {analyzer.is_not_matched(position, left)}, {code[position]}') if position == length or analyzer.is_not_matched(position, left) or code[position] != right: - print('first') cursor.insertText(right) else: - print('second') cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.MoveAnchor, 1) event.accept() @@ -612,7 +617,6 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): if not cursor.selectedText().endswith(' '): cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor, 1) break - # print('cursor.selected',cursor.selectedText()) cursor.removeSelectedText() def on_tab(self, _: QKeyEvent): @@ -716,9 +720,6 @@ class PMBaseCodeEdit(CodeEditorBaseObject, QPlainTextEdit): @property def selected_code(self): """获取光标选中的代码,或当前行""" - # 这里使用selection而不是getSelectedText,是因为plainText会自动处理一些特殊字符,比如\u2029 - cursor = self.textCursor() - self.handler.feed(self.code, self.get_cursor_position(), (cursor.selectionStart(), cursor.selectionEnd())) return self.handler.analyzer.selected_code @property -- Gitee