diff --git a/.gitignore b/.gitignore index cb4f4db5756b51e1bf9b02ef68954153d47676dc..036221f64e92d970a7fca4aa1462ed4eef0bfd5c 100644 --- a/.gitignore +++ b/.gitignore @@ -79,7 +79,7 @@ __pycache__/ .env .venv env/ -venv/ +venv*/ ENV/ env.bak/ venv.bak/ diff --git a/app2.py b/app2.py index 902f4197cc8d59ceaca0321bd648f6a4a6427314..bb3d125845a9406568aa3699d106fc12a3743303 100644 --- a/app2.py +++ b/app2.py @@ -59,7 +59,7 @@ os.environ['QT_API'] = 'pyside2' os.environ['PYQTGRAPH_QT_LIB'] = 'PySide2' os.environ['FORCE_QT_API'] = "1" -from typing import List, Callable, Tuple, ClassVar +from typing import List, Callable, Tuple, ClassVar, Optional from PySide2.QtCore import Signal, QTimer, Qt, QCoreApplication from PySide2.QtGui import QCloseEvent, QTextCursor, QResizeEvent, QMoveEvent, QFont, QIcon, QPixmap from PySide2.QtWidgets import QApplication, QMessageBox, QSplashScreen, QStatusBar, QDialog, QVBoxLayout, QProgressBar @@ -107,7 +107,8 @@ def updateSplashMsg(ext_load_status: dict): def try_hide_terminal(): - if not '-d' in sys.argv: + # TODO 上面有一个地方判断的入参为 ``debug`` ,和这里的 ``d`` 应该是一样的吧? + if '-d' not in sys.argv: if platform.system().lower() == 'windows': whnd = ctypes.windll.kernel32.GetConsoleWindow() if whnd != 0: @@ -128,14 +129,21 @@ class MainWindow(BaseMainWindow): settings_changed_signal = Signal() @classmethod - def __new__(cls, *args): + def __new__(cls, *args, **kwargs): if not hasattr(cls, 'instance'): - instance = super().__new__(cls) + instance = super().__new__(cls, *args, **kwargs) cls.instance = instance return cls.instance - def __init__(self, parent=None): + def __init__(self, parent=None, extensions: Optional[List[str]] = None): + """主程序 + + Args: + parent: 父窗口,由于是主程序,始终是None + extensions: 如果指定,则仅加载指定的插件,否则加载全部插件 + """ super().__init__(parent) + self.extension_names = extensions # 需要加载的插件名称 # settings = Settings() t00 = time.time() self.main_option_form = base.OptionForm() @@ -175,7 +183,11 @@ class MainWindow(BaseMainWindow): # 加载插件 self.extensions_manager = extensions_manager - self.extensions_manager.load_from_extension_folder(updateSplashMsg) + if self.extension_names is None: + self.extensions_manager.load_from_extension_folder(updateSplashMsg) + else: + for name in self.extension_names: + self.extensions_manager.load(name) self.ext_manager_widget = PMPageExt(self) dw = self.add_widget_on_dock( @@ -225,11 +237,7 @@ class MainWindow(BaseMainWindow): self.window_geometry_changed_signal.emit() def start_pmlocalserver(self): - """ - 启动本地flask服务器pmlocalserver - Returns:None - - """ + """启动本地flask服务器pmlocalserver""" server.server_thread.start() def clear_workspace(self): @@ -429,7 +437,8 @@ class MainWindow(BaseMainWindow): utils.open_url("http://www.pyminer.com") def main_markdown_display(self): - print("TODO 添加markdown编辑器代码") + pass + # TODO 添加markdown编辑器代码 def main_new_script_display(self): from features.extensions.extensionlib.extension_lib import extension_lib @@ -476,7 +485,18 @@ class MainWindow(BaseMainWindow): console.append(html) -def main(test_function: Callable[[MainWindow], None] = None): +def main( + test_function: Callable[[MainWindow], None] = None, + extensions: Optional[List[str]] = None, + show_welcome=True, +): + """启动主程序 + + Args: + test_function: 测试函数 + extensions: 需要加载的插件名 + show_welcome: 是否显示欢迎屏 + """ global t0 PMExceptions.get_instance().signal_exception_occured.connect(on_exception) @@ -504,7 +524,7 @@ def main(test_function: Callable[[MainWindow], None] = None): load_translator(app) # 启动主程序 - window = MainWindow() + window = MainWindow(extensions=extensions) t1 = time.time() @@ -513,8 +533,9 @@ def main(test_function: Callable[[MainWindow], None] = None): if test_function is None: # 展示快速启动窗口。只有在设置为True的情况下才显示起始页。 - if utils.get_settings_item_from_file("config.ini", "MAIN/SHOW_START_PAGE"): - window.first_form_display() + if show_welcome: + if utils.get_settings_item_from_file("config.ini", "MAIN/SHOW_START_PAGE"): + window.first_form_display() else: test_function(window) logging.info('boot time elapsed:%f s' % (t1 - t0)) diff --git a/check_dependency.py b/check_dependency.py index 6544eae96f24a8daf69bfca874aaa7032e09923b..833df7a49db786cf5218d73917e96b6bf86e2374 100644 --- a/check_dependency.py +++ b/check_dependency.py @@ -223,19 +223,22 @@ def reinstall_requirements_with_gui(): text = tk.Text(root) text.pack() - class A(): - def read(self): - return - - def write(self, ch: str): - text.insert("end", ch) - return - - a = A() + def gui_print(line: str, end='\n'): + text.insert('end', f'{line}{end}') + text.see('end') def th_f(): + try: + import chardet + except: + gui_print('chardet not found, installing ...') + with subprocess.Popen(f'{sys.executable} -m pip install chardet', stdout=subprocess.PIPE) as p: + p.stdout.read() + gui_print('chardet installed') + import chardet + if platform.system().lower() == "windows": - req_name = 'requirements_dev.txt' + req_name = 'requirements.txt' elif platform.system().lower().startswith("linux"): req_name = 'requirements_linux.txt' elif platform.system().lower().startswith("darwin"): @@ -243,24 +246,14 @@ def reinstall_requirements_with_gui(): else: raise SystemError('Platform not supported!') req_path = os.path.join(os.path.dirname(__file__), req_name) - with open(req_path, 'r') as f: - requirements = [line.strip() for line in f.readlines()] - print(requirements) - - for i, req in enumerate(requirements): - req = req.strip() - if req == "": - continue - text.insert("end", f"正在安装{i + 1}/{len(requirements)}:%s\n" % req) - if req.find('>') != -1 or req.find('<') != -1 or req.find('=') != -1: - ret = os.system( - f'{sys.executable} -m pip install {req} -i https://mirrors.cloud.tencent.com/pypi/simple') - else: - ret = os.system( - f'{sys.executable} -m pip install --upgrade {req} -i https://mirrors.cloud.tencent.com/pypi/simple') - - text.insert("end", "已安装:%s\n" % req) - text.see("end") + mirror = 'https://mirrors.cloud.tencent.com/pypi/simple' + cmd = f'{sys.executable} -m pip install -i {mirror} -r {req_path}' + + with subprocess.Popen(cmd, stdout=subprocess.PIPE) as p: + for line in p.stdout: + encoding = chardet.detect(line)['encoding'] + line = line.decode(encoding) + gui_print(line, end='') root.destroy() th = threading.Thread(target=th_f) diff --git a/core/algorithms/calculation/digits.py b/core/algorithms/calculation/digits.py index fb0273fb9642a36a81e69b3665abb334311b6eb5..b459e3342dfbd092c895eb8b8542c5be50a36d1f 100644 --- a/core/algorithms/calculation/digits.py +++ b/core/algorithms/calculation/digits.py @@ -59,5 +59,4 @@ def ConvertToFloat(floatingPoint): mantissa = floatingPointString[floatingPointString.find('1') + 1:] # 获得尾数 mantissa = mantissa[:23] + '0' * (23 - len(mantissa)) floatingPointString = '0b' + sign + exponet + mantissa - print(floatingPointString) return hex(int(floatingPointString, 2)) diff --git a/dist.py b/dist.py index 58f030d7a1b30ae7110995449c62986931937901..14c67b357e6309b1dc782201129eddfb42b99aef 100644 --- a/dist.py +++ b/dist.py @@ -4,25 +4,31 @@ import os import shutil -DIST_PATH = r"C:\Users\12957\Desktop\pyminer_dist\dist2" -CONDA_PATH = r"C:\Users\12957\Desktop\pyminer_dist\Miniconda3-latest-Windows-x86_64.exe" -CODE_FOLDER = os.path.join(DIST_PATH, "bin") -ADDR = "https://gitee.com/py2cn/pyminer.git" -BRANCH = "master" -PACKUP_COMPONENTS_FOLDER = r"C:\Users\12957\Desktop\pyminer_dist\PyMiner打包组件" -# os.system("start /wait "f" {CONDA_PATH} /InstallationType=JustMe /RegisterPython=0 /S /D={DIST_PATH}") -if not os.path.exists(CODE_FOLDER): - pass -else: - shutil.rmtree(CODE_FOLDER) -# os.mkdir(CODE_FOLDER) -os.system(f"git clone {ADDR} -b {BRANCH} {CODE_FOLDER}") -for d in os.listdir(PACKUP_COMPONENTS_FOLDER): - src = os.path.join(PACKUP_COMPONENTS_FOLDER, d) - dest = os.path.join(DIST_PATH, d) - shutil.copy(src, dest) +def make_pyminer_installer(): + DIST_PATH = r"C:\Users\12957\Desktop\pyminer_dist\dist2" + CONDA_PATH = r"C:\Users\12957\Desktop\pyminer_dist\Miniconda3-latest-Windows-x86_64.exe" + CODE_FOLDER = os.path.join(DIST_PATH, "bin") + ADDR = "https://gitee.com/py2cn/pyminer.git" + BRANCH = "master" + PACKUP_COMPONENTS_FOLDER = r"C:\Users\12957\Desktop\pyminer_dist\PyMiner打包组件" + # os.system("start /wait "f" {CONDA_PATH} /InstallationType=JustMe /RegisterPython=0 /S /D={DIST_PATH}") + if not os.path.exists(CODE_FOLDER): + pass + else: + shutil.rmtree(CODE_FOLDER) -PYTHON_PATH = os.path.join(DIST_PATH, "python.exe") -os.system(f"{PYTHON_PATH} -m pip install -r {os.path.join(CODE_FOLDER, 'requirements.txt')} -i " - f"https://mirrors.cloud.tencent.com/pypi/simple") + # os.mkdir(CODE_FOLDER) + os.system(f"git clone {ADDR} -b {BRANCH} {CODE_FOLDER}") + for d in os.listdir(PACKUP_COMPONENTS_FOLDER): + src = os.path.join(PACKUP_COMPONENTS_FOLDER, d) + dest = os.path.join(DIST_PATH, d) + shutil.copy(src, dest) + + PYTHON_PATH = os.path.join(DIST_PATH, "python.exe") + os.system(f"{PYTHON_PATH} -m pip install -r {os.path.join(CODE_FOLDER, 'requirements.txt')} -i " + f"https://mirrors.cloud.tencent.com/pypi/simple") + + +if __name__ == '__main__': + make_pyminer_installer() diff --git a/features/extensions/extensionlib/baseext.py b/features/extensions/extensionlib/baseext.py index 0e3c1bbd5fad74b029dd7af164d788057fa976b7..8b3f359844ff09fe4116e49f98b1fb2b53c28a21 100644 --- a/features/extensions/extensionlib/baseext.py +++ b/features/extensions/extensionlib/baseext.py @@ -1,17 +1,17 @@ -from typing import Dict, TYPE_CHECKING import logging - -logger = logging.getLogger(__name__) +from typing import Dict, TYPE_CHECKING if TYPE_CHECKING: from PySide2.QtWidgets import QWidget - from features.extensions.extensionlib import extension_lib + from features.extensions.extensionlib.extension_lib import ExtensionLib + +logger = logging.getLogger(__name__) -class BaseExtension(): +class BaseExtension: widget_classes: Dict[str, 'QWidget'] = None widgets: Dict[str, 'QWidget'] = None - extension_lib: 'extension_lib' = None + extension_lib: 'ExtensionLib' = None public_interface: 'BaseInterface' = None settings: Dict[str, object] = None interface: 'BaseInterface' @@ -56,6 +56,6 @@ class BaseExtension(): pass -class BaseInterface(): +class BaseInterface: def _set_extension(self, extension): self.extension = extension diff --git a/features/extensions/extensionlib/extension_lib.py b/features/extensions/extensionlib/extension_lib.py index 90126d73bbf9236929e8d36101e8b8fcbb14d5ec..15663e751baa430cd0a955c6509734ce19d4e9a7 100644 --- a/features/extensions/extensionlib/extension_lib.py +++ b/features/extensions/extensionlib/extension_lib.py @@ -6,553 +6,548 @@ from PySide2.QtCore import QRect, Signal from PySide2.QtWidgets import QWidget, QDialog import utils +from features.extensions.extensionlib import baseext +from features.extensions.extensions_manager.manager import extensions_manager +from features.io.exceptions import PMExceptions +from features.ui import pmwidgets from features.workspace.data_adapter import UniversalAdapter +from features.workspace.data_manager import data_manager +from features.workspace_old.datamanager.datamanager import data_manager as data_manager_old +from pyminer_comm.base import DataDesc +from utils import get_main_window if TYPE_CHECKING: from pmgwidgets import PMGToolBar -def wrapper(): - from features.extensions.extensions_manager.manager import extensions_manager - from features.workspace.data_manager import data_manager +class ExtensionLib: + pm_widgets = pmwidgets + BaseInterface = baseext.BaseInterface + BaseExtension = baseext.BaseExtension + + @staticmethod + def get_interface(name: str) -> BaseInterface: + """获取名为 ``name`` 的插件的公共接口(由interface定义) + + Args: + name: 插件名称 + """ + return extensions_manager.get_extension(name).public_interface + + @staticmethod + def insert_widget(widget, insert_mode, config=None): + """ + 在主界面上插入一个控件。 + Args: + widget: + insert_mode: + config: + + Returns: + + """ + return extensions_manager.ui_inserters[insert_mode]( + widget, config) + + @staticmethod + def get_main_program_dir(): + """ + 获取主程序的根目录 + Returns: + + """ + return utils.get_root_dir() + + class UI: + @staticmethod + def widget_exists(widget_name: str) -> bool: + if widget_name in get_main_window().dock_widgets.keys(): + return True + else: + return False + + @staticmethod + def get_toolbar(toolbar_name: str) -> 'PMGToolBar': + """ + 获取工具栏 - from features.workspace_old.datamanager.datamanager import data_manager as data_manager_old - from utils import get_main_window - from features.ui import pmwidgets - from features.extensions.extensionlib import baseext - from features.io.exceptions import PMExceptions - from pyminer_comm.base import DataDesc + Args: + toolbar_name:工具栏名称 - class ExtensionLib: - pm_widgets = pmwidgets - BaseInterface = baseext.BaseInterface - BaseExtension = baseext.BaseExtension + Returns: - def get_interface(self, name: str) -> BaseInterface: """ - 获取名为`name`的插件的公共接口(由interface定义) + tb = get_main_window().toolbars.get(toolbar_name) + + return tb + + @staticmethod + def get_toolbar_widget(toolbar_name: str, widget_name: str) -> Optional['QWidget']: + """ + 获取工具栏上的控件(按钮等) + Args: - name: + toolbar_name:工具栏名称 + widget_name:工具栏上的控件名称 + + Returns: + + """ + toolbar = ExtensionLib.UI.get_toolbar(toolbar_name) + if toolbar is not None: + widget = toolbar.get_control_widget(widget_name) + return widget + return None + + @staticmethod + def get_main_window_geometry() -> 'QRect': + """ + 获取主界面的尺寸 Returns: """ - return extensions_manager.get_extension(name).public_interface + return get_main_window().geometry() - def insert_widget(self, widget, insert_mode, config=None): + @staticmethod + def raise_dock_into_view(dock_widget_name: str) -> None: """ - 在主界面上插入一个控件。 + 将界面上的控件提升到可视的位置 + Args: - widget: - insert_mode: - config: + dock_widget_name: Returns: """ - return extensions_manager.ui_inserters[insert_mode]( - widget, config) + return get_main_window().raise_dock_into_view(dock_widget_name) + + @staticmethod + def get_default_font() -> str: + """ + 获取默认字体文件 + + Returns: Filepath of font. + + """ + app = get_main_window() + return os.path.join(app.font_dir, app.default_font) - def get_main_program_dir(self): + @staticmethod + def switch_toolbar(toolbar_name: str, switch_only: bool = True): """ - 获取主程序的根目录 + 切换工具栏 + + Args: + toolbar_name: + switch_only: + Returns: """ - return utils.get_root_dir() + app = get_main_window() + app.switch_toolbar(toolbar_name, switch_only) + + @staticmethod + def exec_dialog(dlg: QDialog): + assert isinstance(dlg, QDialog) + win = get_main_window() + dlg.setParent(win) + dlg.exec_() + + class Signal: + @staticmethod + def get_close_signal() -> Signal: + """ + 获取关闭信号 - class UI(): - @staticmethod - def widget_exists(widget_name: str) -> bool: - if widget_name in get_main_window().dock_widgets.keys(): - return True - else: - return False + Returns: - @staticmethod - def get_toolbar(toolbar_name: str) -> 'PMGToolBar': - """ - 获取工具栏 + """ + return get_main_window().close_signal - Args: - toolbar_name:工具栏名称 + @staticmethod + def get_window_geometry_changed_signal(): + """ + 获取窗口位置和尺寸变化的事件 - Returns: + Returns: - """ - tb = get_main_window().toolbars.get(toolbar_name) + """ + return get_main_window().window_geometry_changed_signal - return tb + @staticmethod + def get_layouts_ready_signal(): + """ + 获取布局加载完毕的事件 - @staticmethod - def get_toolbar_widget(toolbar_name: str, widget_name: str) -> Optional['QWidget']: - """ - 获取工具栏上的控件(按钮等) + Returns: - Args: - toolbar_name:工具栏名称 - widget_name:工具栏上的控件名称 + """ + return get_main_window().layouts_ready_signal - Returns: + @staticmethod + def get_widgets_ready_signal(): + """ + 获取控件加载完毕的事件 - """ - toolbar = ExtensionLib.UI.get_toolbar(toolbar_name) - if toolbar is not None: - widget = toolbar.get_control_widget(widget_name) - return widget - return None + Returns: - @staticmethod - def get_main_window_geometry() -> 'QRect': - """ - 获取主界面的尺寸 + """ + return get_main_window().widgets_ready_signal - Returns: + @staticmethod + def get_events_ready_signal(): + """ + 获取界面信号和事件绑定完毕的事件。 - """ - return get_main_window().geometry() + Returns: - @staticmethod - def raise_dock_into_view(dock_widget_name: str) -> None: - """ - 将界面上的控件提升到可视的位置 + """ + return get_main_window().events_ready_signal - Args: - dock_widget_name: + @staticmethod + def get_settings_changed_signal() -> 'Signal': + """ + 获取设置发生变化时的事件。 - Returns: + Returns: - """ - return get_main_window().raise_dock_into_view(dock_widget_name) + """ + return get_main_window().settings_changed_signal - @staticmethod - def get_default_font() -> str: - """ - 获取默认字体文件 + class Program: + @staticmethod + def add_settings_panel(text: str, panel_content: List[Tuple[str, str]]): + """ + 添加设置面板 - Returns: Filepath of font. + Args: + text: + panel_content: - """ - app = get_main_window() - return os.path.join(app.font_dir, app.default_font) + Returns: - @staticmethod - def switch_toolbar(toolbar_name: str, switch_only: bool = True): - """ - 切换工具栏 + """ - Args: - toolbar_name: - switch_only: + return get_main_window().main_option_form.add_settings_panel(text, panel_content) - Returns: + @staticmethod + def show_log(level: str, module: str, content: str) -> None: + """ + 调用——PluginInterface.show_log('info','CodeEditor','新建文件') + 输出——2020-08-29 23:43:10 hzy INFO [CodeEditor]:新建文件 + Args: + level: 类型,比如‘info’ + module: 模块。比如'Jupyter' + content: 内容。自定义的字符串 - """ - app = get_main_window() - app.switch_toolbar(toolbar_name, switch_only) + Returns: + """ + get_main_window().slot_flush_console(level, module, content) - @staticmethod - def exec_dialog(dlg: QDialog): - assert isinstance(dlg, QDialog) - win = get_main_window() - dlg.setParent(win) - dlg.exec_() + @staticmethod + def get_main_program_dir(): + """ + 获取主程序路径 - class Signal(): - @staticmethod - def get_close_signal() -> Signal: - """ - 获取关闭信号 + Returns: - Returns: + """ + return utils.get_root_dir() - """ - return get_main_window().close_signal + @staticmethod + def get_settings_item_from_file(file: str, item: str, mode: str = "user"): + """ + 从设置文件中获取设置项 + Args: + file: + item: + mode: - @staticmethod - def get_window_geometry_changed_signal(): - """ - 获取窗口位置和尺寸变化的事件 + Returns: - Returns: + """ + return utils.get_settings_item_from_file(file, item, mode) - """ - return get_main_window().window_geometry_changed_signal + @staticmethod + def write_settings_item_to_file(file: str, item: str, value: Any): + """ + 将配置项写入设置文件中 + Args: + file: + item: + value: - @staticmethod - def get_layouts_ready_signal(): - """ - 获取布局加载完毕的事件 + Returns: - Returns: + """ + utils.write_settings_item_to_file(file, item, value) + get_main_window().settings_changed_signal.emit() - """ - return get_main_window().layouts_ready_signal + @staticmethod + def set_work_dir(work_dir: str) -> None: + """ + 设置当前工作路径 - @staticmethod - def get_widgets_ready_signal(): - """ - 获取控件加载完毕的事件 + Args: + work_dir: - Returns: + Returns: - """ - return get_main_window().widgets_ready_signal + """ + utils.write_settings_item_to_file("config.ini", "MAIN/PATH_WORKDIR", work_dir) + get_main_window().settings_changed_signal.emit() - @staticmethod - def get_events_ready_signal(): - """ - 获取界面信号和事件绑定完毕的事件。 + @staticmethod + def get_work_dir() -> str: + """ + 获取当前工作路径 - Returns: + Returns: - """ - return get_main_window().events_ready_signal + """ - @staticmethod - def get_settings_changed_signal() -> 'Signal': - """ - 获取设置发生变化时的事件。 + dir = utils.get_settings_item_from_file("config.ini", "MAIN/PATH_WORKDIR") + if (not isinstance(dir, str)) or (not os.path.exists(dir)): + dir = os.path.join(os.path.expanduser("~"), "Desktop") + utils.write_settings_item_to_file("config.ini", "MAIN/PATH_WORKDIR", dir) + return dir - Returns: + @staticmethod + def get_theme() -> str: + """ + 获取主题 + Returns: - """ - return get_main_window().settings_changed_signal + """ + return utils.get_settings_item_from_file("config.ini", "MAIN/THEME") + + @staticmethod + def run_python_file(file_path: str, interpreter_path: str): + """ + 运行Python文件命令 + TODO: Write a shell console into pyminer. + + Args: + file_path: + + Returns: + + """ + raise DeprecationWarning + + @staticmethod + def get_plugin_data_path(plugins_name: str) -> str: + """ + 获取插件的数据文件路径 + Args: + plugins_name: + + Returns:str,文件夹路径。 + + """ + ext = extensions_manager.get_extension(plugins_name) + # assert ext is not None, 'Extension named %s isn\'t exist!' % plugins_name + path = os.path.join(os.path.expanduser('~'), '.pyminer', 'packages') + if not os.path.exists(path): + os.mkdir(path) + plugin_data_path = os.path.join(path, plugins_name) + if not os.path.exists(plugin_data_path): + os.mkdir(plugin_data_path) + return plugin_data_path + + @staticmethod + def show_exception_occured_panel(error: BaseException, solution: str, solution_command: str = ''): + """ + + :param error: + :param solution: + :param solution_command: + :return: + """ + PMExceptions.get_instance().emit_exception_occured_signal(error, solution, solution_command) - class Program(): - @staticmethod - def add_settings_panel(text: str, panel_content: List[Tuple[str, str]]): - """ - 添加设置面板 + class Data: + @staticmethod + def get_adapter(key: str) -> UniversalAdapter: + return data_manager[key] - Args: - text: - panel_content: + @staticmethod + def clear(): + data_manager_old.clear() - Returns: + @staticmethod + def delete_variable(var_name: str, provider: str = 'unknown'): + """ + 删除变量 + Args: + var_name: + provider: - """ + Returns: - return get_main_window().main_option_form.add_settings_panel(text, panel_content) + """ + data_manager_old.delete_data(var_name, provider) - @staticmethod - def show_log(level: str, module: str, content: str) -> None: - """ - 调用——PluginInterface.show_log('info','CodeEditor','新建文件') - 输出——2020-08-29 23:43:10 hzy INFO [CodeEditor]:新建文件 - Args: - level: 类型,比如‘info’ - module: 模块。比如'Jupyter' - content: 内容。自定义的字符串 + @staticmethod + def get_all_variable_names() -> List[str]: + """ + 获取所有的变量名 - Returns: - """ - get_main_window().slot_flush_console(level, module, content) + Returns: - @staticmethod - def get_main_program_dir(): - """ - 获取主程序路径 + """ + return list(data_manager.container.keys()) - Returns: + @staticmethod + def get_all_public_variable_names() -> List[str]: + """ + 获取所有非保留的变量的名称 + + Returns: + + """ + return list(data_manager_old.get_all_public_var().keys()) + + @staticmethod + def get_all_variables() -> Dict[str, object]: + """ + 获取全部变量(包含保留类型,可能返回结果比较乱,需要审慎使用) + + Returns: + """ + return data_manager_old.get_all_var() - """ - return utils.get_root_dir() + @staticmethod + def get_all_public_variables() -> Dict[str, object]: + """ + 获取所有的外部可访问变量。 - @staticmethod - def get_settings_item_from_file(file: str, item: str, mode: str = "user"): - """ - 从设置文件中获取设置项 - Args: - file: - item: - mode: + Returns: + """ + return data_manager_old.get_all_public_var() - Returns: - - """ - return utils.get_settings_item_from_file(file, item, mode) + @staticmethod + def add_data_changed_callback(callback: Callable[[str, Any, str], None]) -> None: + """ + 添加数据改变时触发的回调函数 - @staticmethod - def write_settings_item_to_file(file: str, item: str, value: Any): - """ - 将配置项写入设置文件中 - Args: - file: - item: - value: - - Returns: - - """ - utils.write_settings_item_to_file(file, item, value) - get_main_window().settings_changed_signal.emit() - - @staticmethod - def set_work_dir(work_dir: str) -> None: - """ - 设置当前工作路径 - - Args: - work_dir: - - Returns: - - """ - utils.write_settings_item_to_file("config.ini", "MAIN/PATH_WORKDIR", work_dir) - get_main_window().settings_changed_signal.emit() - - @staticmethod - def get_work_dir() -> str: - """ - 获取当前工作路径 - - Returns: - - """ - - dir = utils.get_settings_item_from_file("config.ini", "MAIN/PATH_WORKDIR") - if (not isinstance(dir, str)) or (not os.path.exists(dir)): - dir = os.path.join(os.path.expanduser("~"), "Desktop") - utils.write_settings_item_to_file("config.ini", "MAIN/PATH_WORKDIR", dir) - return dir - - @staticmethod - def get_theme() -> str: - """ - 获取主题 - Returns: - - """ - return utils.get_settings_item_from_file("config.ini", "MAIN/THEME") - - @staticmethod - def run_python_file(file_path: str, interpreter_path: str): - """ - 运行Python文件命令 - TODO: Write a shell console into pyminer. - - Args: - file_path: - - Returns: - - """ - raise DeprecationWarning - - @staticmethod - def get_plugin_data_path(plugins_name: str) -> str: - """ - 获取插件的数据文件路径 - Args: - plugins_name: - - Returns:str,文件夹路径。 - - """ - ext = extensions_manager.get_extension(plugins_name) - # assert ext is not None, 'Extension named %s isn\'t exist!' % plugins_name - path = os.path.join(os.path.expanduser('~'), '.pyminer', 'packages') - if not os.path.exists(path): - os.mkdir(path) - plugin_data_path = os.path.join(path, plugins_name) - if not os.path.exists(plugin_data_path): - os.mkdir(plugin_data_path) - return plugin_data_path - - @staticmethod - def show_exception_occured_panel(error: BaseException, solution: str, solution_command: str = ''): - """ - - :param error: - :param solution: - :param solution_command: - :return: - """ - PMExceptions.get_instance().emit_exception_occured_signal(error, solution, solution_command) - - class Data: - @staticmethod - def get_adapter(key: str) -> UniversalAdapter: - return data_manager[key] - - @staticmethod - def clear(): - data_manager_old.clear() - - @staticmethod - def delete_variable(var_name: str, provider: str = 'unknown'): - """ - 删除变量 - Args: - var_name: - provider: - - Returns: - - """ - data_manager_old.delete_data(var_name, provider) - - @staticmethod - def get_all_variable_names() -> List[str]: - """ - 获取所有的变量名 - - Returns: - - """ - return list(data_manager.container.keys()) - - @staticmethod - def get_all_public_variable_names() -> List[str]: - """ - 获取所有非保留的变量的名称 - - Returns: - - """ - return list(data_manager_old.get_all_public_var().keys()) - - @staticmethod - def get_all_variables() -> Dict[str, object]: - """ - 获取全部变量(包含保留类型,可能返回结果比较乱,需要审慎使用) - - Returns: - """ - return data_manager_old.get_all_var() - - @staticmethod - def get_all_public_variables() -> Dict[str, object]: - """ - 获取所有的外部可访问变量。 - - Returns: - """ - return data_manager_old.get_all_public_var() - - @staticmethod - def add_data_changed_callback(callback: Callable[[str, Any, str], None]) -> None: - """ - 添加数据改变时触发的回调函数 - - Args: - callback: - - Returns:None - """ - assert callable(callback) - assert len( - list(inspect.signature(callback).parameters.keys())) == 3, 'Function args should be 3' - data_manager_old.on_modification(callback) - - @staticmethod - def remove_data_changed_callback(callback: Callable): - if callback in data_manager_old.modification_callback_actions: - data_manager_old.modification_callback_actions.remove(callback) - print('removed callback!') - - @staticmethod - def add_data_deleted_callback(deletion_callback: Callable[[str, str], None]): - """ - 绑定的函数,要求其输入的函数参数为两个。 + Args: + callback: - Args: - deletion_callback: + Returns:None + """ + assert callable(callback) + assert len( + list(inspect.signature(callback).parameters.keys())) == 3, 'Function args should be 3' + data_manager_old.on_modification(callback) + + @staticmethod + def remove_data_changed_callback(callback: Callable): + if callback in data_manager_old.modification_callback_actions: + data_manager_old.modification_callback_actions.remove(callback) + print('removed callback!') + + @staticmethod + def add_data_deleted_callback(deletion_callback: Callable[[str, str], None]): + """ + 绑定的函数,要求其输入的函数参数为两个。 - Returns: + Args: + deletion_callback: - """ - assert callable(deletion_callback) - assert len( - list(inspect.signature(deletion_callback).parameters.keys())) == 2, 'Function args should be 2' - data_manager_old.on_deletion(deletion_callback) + Returns: - @staticmethod - def var_exists(var_name: str): - """ - 判断var_name对应的变量是否存在 - Args: - var_name: + """ + assert callable(deletion_callback) + assert len( + list(inspect.signature(deletion_callback).parameters.keys())) == 2, 'Function args should be 2' + data_manager_old.on_deletion(deletion_callback) - Returns: + @staticmethod + def var_exists(var_name: str): + """ + 判断var_name对应的变量是否存在 + Args: + var_name: - """ - return var_name in data_manager + Returns: - @staticmethod - def set_var(varname: str, variable, provider='unknown', **info): - """ + """ + return var_name in data_manager - Args: - varname: - variable: - provider: - **info: + @staticmethod + def set_var(varname: str, variable, provider='unknown', **info): + """ - Returns: + Args: + varname: + variable: + provider: + **info: - """ - assert isinstance(variable, DataDesc), \ - 'variable name %s ,%s is not type DataDesc' % (varname, variable) - data_manager_old.set_var(varname, variable, provider) + Returns: - @staticmethod - def get_var(var_name: str) -> object: - """ + """ + assert isinstance(variable, DataDesc), \ + 'variable name %s ,%s is not type DataDesc' % (varname, variable) + data_manager_old.set_var(varname, variable, provider) - Args: - var_name: + @staticmethod + def get_var(var_name: str) -> object: + """ - Returns: + Args: + var_name: - """ - raise DeprecationWarning - # return get_var(var_name) + Returns: - @staticmethod - def get_data_desc(var_name) -> DataDesc: - desc = data_manager_old.get_var(var_name) - assert isinstance(desc, DataDesc), repr(desc) - return desc + """ + raise DeprecationWarning + # return get_var(var_name) - @staticmethod - def update_var_dic(var_dic: dict, provider: str, metadata_dic: dict = None): - """ + @staticmethod + def get_data_desc(var_name) -> DataDesc: + desc = data_manager_old.get_var(var_name) + assert isinstance(desc, DataDesc), repr(desc) + return desc - Args: - var_dic: - provider: - metadata_dic: + @staticmethod + def update_var_dic(var_dic: dict, provider: str, metadata_dic: dict = None): + """ - Returns: + Args: + var_dic: + provider: + metadata_dic: - """ - raise DeprecationWarning + Returns: - @staticmethod - def get_metadata(varname: str) -> dict: - """ + """ + raise DeprecationWarning - Args: - varname: + @staticmethod + def get_metadata(varname: str) -> dict: + """ - Returns: + Args: + varname: - """ - return data_manager_old.get_data_info(varname) + Returns: - @staticmethod - def get_all_metadata() -> dict: - """ + """ + return data_manager_old.get_data_info(varname) - Returns: + @staticmethod + def get_all_metadata() -> dict: + """ - """ - d = {k: v for k, v in data_manager_old.metadataset.items()} - return d + Returns: - return ExtensionLib() + """ + d = {k: v for k, v in data_manager_old.metadataset.items()} + return d -extension_lib = wrapper() +extension_lib = ExtensionLib() diff --git a/features/extensions/extensions_manager/ExtensionLoader.py b/features/extensions/extensions_manager/ExtensionLoader.py index 59b7fab5cee5f8751ae7672997373caaa322f8aa..d4ab465599ac83043fb67e4954ea5adc537774db 100644 --- a/features/extensions/extensions_manager/ExtensionLoader.py +++ b/features/extensions/extensions_manager/ExtensionLoader.py @@ -1,21 +1,17 @@ -from features.io.exceptions import pyminer_exc_mgr - -import os -import sys import importlib import json -from collections import namedtuple -import copy -import traceback import logging +import os +import sys +from collections import namedtuple import utils +from features.io.exceptions import pyminer_exc_mgr logger = logging.getLogger('extensionmanager.extensionloader') # from features.extensions.extensions_manager import log - Info = namedtuple('Info', ['icon', 'name', @@ -38,7 +34,7 @@ class ExtensionLoader: self.import_path = os.path.join(utils.get_root_dir(), 'packages') sys.path.append(self.import_path) # 将模块导入路径设置为扩展文件夹,这样可以直接导入入口文件 self.extension_lib_path = os.path.join( - utils.get_root_dir(),'pyminer2', 'extensions', 'extensionlib') + utils.get_root_dir(), 'pyminer2', 'extensions', 'extensionlib') sys.path.append(self.extension_lib_path) def load(self, file, ui_inserters): @@ -54,7 +50,7 @@ class ExtensionLoader: 'group', '未知类型/未知分组').split('/') self.path = os.path.join( - utils.get_root_dir(),'packages', + utils.get_root_dir(), 'packages', self.name) # 扩展文件夹路径 main_config = self.package.get( @@ -159,7 +155,7 @@ class ExtensionLoader: module_path = f'{package_name}.{module_name}' module = None module = importlib.import_module(module_path) - + except Exception as e: logger.error(e, exc_info=True) module = None diff --git a/features/extensions/extensions_manager/manager.py b/features/extensions/extensions_manager/manager.py index 24c2e8bbb134c9956ccadaadfeaa92ee6798914a..14fb3bf117f13e28beebdb2c2ad341284dd4a21d 100644 --- a/features/extensions/extensions_manager/manager.py +++ b/features/extensions/extensions_manager/manager.py @@ -1,17 +1,17 @@ -from features.extensions.extensions_manager.UIInserter import ui_inserters -from features.extensions.extensions_manager.ExtensionLoader import ExtensionLoader -from .vermanager import VersionsManager, Module +import importlib +import json +import json.decoder +import logging import os import shutil import sys import time -import json -import json.decoder import zipfile -import importlib -import traceback -import logging + import utils +from features.extensions.extensions_manager.ExtensionLoader import ExtensionLoader +from features.extensions.extensions_manager.UIInserter import ui_inserters +from .vermanager import VersionsManager logger = logging.getLogger('extensionmanager') logger.setLevel(logging.DEBUG) @@ -27,7 +27,7 @@ class ExtensionsManager: self.ui_inserters = ui_inserters self.id_ = 0 # 扩展id,动态分配的 self.setting_path = os.path.join( - utils.get_root_dir(), 'configuration','extensions.json') # 扩展配置文件位置 + utils.get_root_dir(), 'configuration', 'extensions.json') # 扩展配置文件位置 if not os.path.exists(self.setting_path): raise FileNotFoundError(self.setting_path) try: @@ -103,8 +103,7 @@ class ExtensionsManager: if callable(callback): callback(ext_load_status) t1 = time.time() - logger.info('boot time elapsed for %s : %f s' % - (str(package), t1 - t0)) + logger.info(f'boot time elapsed for {str(package)} : {t1 - t0:f} s') def get_extension(self, name): """通过名称获取扩展""" diff --git a/features/ui/pmwidgets/dockwidget.py b/features/ui/pmwidgets/dockwidget.py index 2ebaf7dc72bb15928e5ce6cb658f27c6fec65e44..052ffdf191767f267ee20c82ac4be5e712dec27f 100644 --- a/features/ui/pmwidgets/dockwidget.py +++ b/features/ui/pmwidgets/dockwidget.py @@ -15,3 +15,7 @@ class PMDockWidget(PMGDockWidget): self.hide() event.accept() main_window.refresh_view_configs() + + def bind_events(self): + """绑定该控件的所有事件,会在主程序加载结束后自动执行,不需要在__init__里面手动执行""" + pass diff --git a/features/ui/pmwidgets/pmmainwindow.py b/features/ui/pmwidgets/pmmainwindow.py index f74385011a4f01ee39ee5adf2e8e15e1e2fe2b15..431baa7cd5391d3b8c26740c16b544618cfc6ba4 100644 --- a/features/ui/pmwidgets/pmmainwindow.py +++ b/features/ui/pmwidgets/pmmainwindow.py @@ -39,12 +39,17 @@ class BaseMainWindow(QMainWindow): dialog_classes: Dict[str, 'QDialog'] = {} toolbars: Dict[str, QToolBar] = {} _current_toolbar_name: str = '' # 当前的窗口标题栏选项卡 - dock_widgets: Dict[str, 'PMDockWidget'] = {} + __dock_widgets: Dict[str, 'PMDockWidget'] dock_places = {'left': Qt.LeftDockWidgetArea, 'right': Qt.RightDockWidgetArea, 'top': Qt.TopDockWidgetArea, 'bottom': Qt.BottomDockWidgetArea} + @property + def dock_widgets(self): + return self.__dock_widgets + def __init__(self, parent=None): super().__init__(parent) + self.__dock_widgets = {} self.setContextMenuPolicy(Qt.NoContextMenu) # 隐藏主界面的菜单栏。 def set_dock_titlebar_visible(self, show: bool): diff --git a/packages/code_editor/codeeditor/autocomplete.py b/packages/code_editor/codeeditor/autocomplete.py index 2d4a2eb6334bd0f6e63ac9548495cc795d28ef2a..e9079c5117edbf513da50ba5e82b59835b7fdc04 100644 --- a/packages/code_editor/codeeditor/autocomplete.py +++ b/packages/code_editor/codeeditor/autocomplete.py @@ -3,7 +3,7 @@ import sys import time import logging from PySide2.QtWidgets import QApplication, QMainWindow -from PySide2.QtCore import QObject, Signal, QThread +from PySide2.QtCore import QObject, Signal, QThread, SignalInstance from pmgwidgets import PMQThreadObject logger = logging.getLogger(__name__) @@ -30,7 +30,7 @@ a class PMAutoCompWorker(QObject): - signal_autocomp_parsed = Signal(list, int, int) + signal_autocomp_parsed: SignalInstance = Signal(list, int, int) def __init__(self): super(PMAutoCompWorker, self).__init__() diff --git a/packages/code_editor/codeeditor/errors_translation/translate.py b/packages/code_editor/codeeditor/errors_translation/translate.py index 154e6bf36ee61bf65c5a600dfaa58bc90201f730..2d57d4a2118449e04bf466799fe7a812f40cea91 100644 --- a/packages/code_editor/codeeditor/errors_translation/translate.py +++ b/packages/code_editor/codeeditor/errors_translation/translate.py @@ -2,7 +2,7 @@ import json import os d = {} -with open('translations.txt', 'r',encoding='utf-8') as f: +with open('translations.txt', 'r', encoding='utf-8') as f: for l in f.read().split('\n'): if l.startswith(('E', 'W', 'C', 'F')): try: diff --git a/packages/code_editor/codeeditor/grammar_analyzer.py b/packages/code_editor/codeeditor/grammar_analyzer.py new file mode 100644 index 0000000000000000000000000000000000000000..3e3802646c04194062e6c29184028af7ad987f6c --- /dev/null +++ b/packages/code_editor/codeeditor/grammar_analyzer.py @@ -0,0 +1,66 @@ +from typing import Tuple, List, Union + +import parso +from parso.tree import Leaf, Node + + +class GrammarAnalyzer: + def __init__(self): + """ + 代码静态分析工具,用于识别括号匹配等内容 + """ + self.__code = '' + self.__parsed_node_cache = None + + def feed(self, code: str) -> None: + """使用当前编辑器里面的代码替换之前分析器中的代码 + + Args: + code: 当前编辑器中的代码 + """ + self.__code = code + self.__parsed_node_cache = None + + @property + def __parsed_node(self) -> Node: + """使用parso进行解析后的代码""" + if self.__parsed_node_cache is None: + self.__parsed_node_cache = parso.parse(self.__code) + return self.__parsed_node_cache + + def _convert_position_to_row_col(self, position: int) -> Tuple[int, int]: + left_part = self.__code[:position] + row = left_part.count('\n') + 1 + col = position - left_part.rfind('\n') - 1 + return row, col + + __PAIRS = {'(': ')', '[': ']', '{': '}'} + + def is_not_matched(self, position: Union[Tuple[int, int], int], left='('): + """是否需要添加另一个配对项 + + 对于如下代码:``print(a, b, c#)`` ,在光标位于#处时,按下右括号键,是不需要再添加一个括号的。 + 本方法即是为了解决这个问题,识别是否需要添加额外的括号。 + + Args: + position: 匹配位置 + left: 配对的左侧一项 + + Returns: + bool, 是否需要添加 + """ + if isinstance(position, int): + position = self._convert_position_to_row_col(position) + position: Tuple[int, int] + right = self.__PAIRS[left] + leaf: Leaf = self.__parsed_node.get_leaf_for_position(position) + node: Node = leaf.parent + child: Node + leaves: List[Node] = [leaf] + while node is not None: + # print(node.children) + leaves.extend([child for child in node.children if child is not leaf and child.type == 'operator']) + node, leaf = node.parent, leaf.parent + characters: List[str] = [leaf.get_code().strip() for leaf in leaves] + # print(characters, characters.count(left), characters.count(right)) + return characters.count(left) - characters.count(right) > 0 diff --git a/packages/code_editor/codeeditor/pythoneditor.py b/packages/code_editor/codeeditor/pythoneditor.py index 3fcf3e6dc798311457c9a6b4a89bbdd5f086feb9..df3bcd3e1379f891fa5e2b4690d5856d852ad775 100644 --- a/packages/code_editor/codeeditor/pythoneditor.py +++ b/packages/code_editor/codeeditor/pythoneditor.py @@ -19,30 +19,32 @@ import os import re from typing import List, Tuple, Optional -from PySide2.QtCore import QDir, Qt, Signal +from PySide2.QtCore import QDir, Qt, Signal, SignalInstance from PySide2.QtGui import QCloseEvent, QKeySequence from PySide2.QtWidgets import QMessageBox, QMenu, QAction, QShortcut from yapf.yapflib import py3compat, yapf_api -# from .baseeditor import PMBaseEditor, PMAPI from packages.code_editor.codeeditor.qtpyeditor import PMGPythonEditor, PythonHighlighter +from pmgwidgets import in_unit_test, PMGPanelDialog, parse_simplified_pmgjson, run_python_file_in_terminal, \ + PMGOneShotThreadRunner with open(os.path.join(os.path.dirname(__file__), 'flake8_trans.json'), 'rb') as f: # 打开flake8提示信息的文件翻译。 flake8_translations = json.load(f) + show_as_errors = {'E999'} show_as_warnings = set() show_as_infos = {'F841', 'F401', 'F403', 'F405', 'E303'} # 分别是变量导入未使用、定义未使用、使用*导入以及无法推断可能由*导入的类型。 + logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) -from pmgwidgets import in_unit_test, PMGPanelDialog, parse_simplified_pmgjson, \ - run_python_file_in_terminal, PMGOneShotThreadRunner class PMPythonEditor(PMGPythonEditor): """ 自定义编辑器控件 """ - signal_goto_definition = Signal(str, int, int) + signal_goto_definition: SignalInstance = Signal(str, int, int) + help_runner: 'PMGOneShotThreadRunner' def __init__(self, parent=None): super(PMPythonEditor, self).__init__(parent) # , comment_string='# ') @@ -56,7 +58,6 @@ class PMPythonEditor(PMGPythonEditor): # self.slot_set_theme(self._theme) self.extension_lib = None self.last_hint = '' - self.help_runner: 'PMGOneShotThreadRunner' = None self.prepare_actions() # self.marker_update_timer = QTimer() @@ -74,11 +75,7 @@ class PMPythonEditor(PMGPythonEditor): self.extension_lib = lib def _init_actions(self) -> None: - """ - 初始化事件 - - :return: - """ + """初始化事件""" 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) @@ -97,10 +94,9 @@ class PMPythonEditor(PMGPythonEditor): self._action_remove_breakpoint.setVisible(False) def _init_signals(self) -> None: - """ - 初始化信号绑定 + """初始化信号绑定。 + 无需super,因为基类已经初始化过了。 - :return: None """ super(PMPythonEditor, self)._init_signals() self._shortcut_help.activated.connect(self.get_help) @@ -372,6 +368,7 @@ class PMPythonEditor(PMGPythonEditor): style_config=os.path.join(os.path.dirname(__file__), 'config', '.style.yapf') ) self.set_text(reformatted_source) + # TODO 重构代码后需要保证光标的位置不动,可以考虑使用parso实现,这里目前的bug有些多 self.goto_line(first_line + 1) # 跳转回开始时的行 # self.text_edit.goToLine(first_line) except Exception as e: @@ -486,7 +483,7 @@ class PMPythonEditor(PMGPythonEditor): pass def create_context_menu(self) -> 'QMenu': - print('create_context_menu') + logger.info('create_context_menu') menu = super().create_context_menu() menu.addAction(self._action_help) menu.addAction(self._action_help_in_console) diff --git a/packages/code_editor/codeeditor/qtpyeditor/Utilities/autocomp.py b/packages/code_editor/codeeditor/qtpyeditor/Utilities/autocomp.py index 19459e431ae102c677fbd4b96122f1e576b9527f..76e411532861ec44a22cd545a4068292033d0a59 100644 --- a/packages/code_editor/codeeditor/qtpyeditor/Utilities/autocomp.py +++ b/packages/code_editor/codeeditor/qtpyeditor/Utilities/autocomp.py @@ -13,9 +13,9 @@ logger.setLevel(logging.DEBUG) class AutoCompThread(QThread): - ''' + """ 当一段时间没有补全需求之后,后台自动补全线程会进入休眠模式。下一次补全请求则会唤醒该后台线程。 - ''' + """ trigger = Signal(tuple, list) def __init__(self): @@ -33,7 +33,7 @@ class AutoCompThread(QThread): except ImportError: print('Jedi not installed.install jedi for better auto-completion!') return - while (1): + while 1: if self.stop_flag: return diff --git a/packages/code_editor/codeeditor/qtpyeditor/codeedit/basecodeedit.py b/packages/code_editor/codeeditor/qtpyeditor/codeedit/basecodeedit.py index f477177bbafb9db2e3291a426e52046643201768..85ccb4ac4f811a422e15e8ecd93e89002475c0f5 100644 --- a/packages/code_editor/codeeditor/qtpyeditor/codeedit/basecodeedit.py +++ b/packages/code_editor/codeeditor/qtpyeditor/codeedit/basecodeedit.py @@ -9,21 +9,17 @@ import re import time from itertools import groupby from queue import Queue +from typing import List, Tuple, Dict, TYPE_CHECKING +from PySide2.QtCore import Qt, QModelIndex, Signal, QTimer, QUrl from PySide2.QtGui import QDropEvent, QPixmap +from PySide2.QtGui import QTextCursor, QKeyEvent, QMouseEvent, QIcon, QFocusEvent +from PySide2.QtWidgets import QApplication, QWidget, QPlainTextEdit, QTableWidget, QTableWidgetItem, QHeaderView -from PySide2.QtWidgets import QAction -from PySide2.QtCore import QRegExp, Qt, QModelIndex, Signal, QThread, QCoreApplication, QTimer, QUrl, QSize -from PySide2.QtWidgets import QApplication, QFileDialog, QTextEdit, QTabWidget, \ - QMessageBox, QListWidget, QListWidgetItem, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPlainTextEdit, QShortcut, \ - QTableWidget, QTableWidgetItem, QHeaderView -from PySide2.QtGui import QTextCursor, QKeyEvent, QMouseEvent, QIcon, QKeySequence, QFocusEvent, QColor, QTextFormat, \ - QPainter, QTextDocument, QTextBlock -from typing import List, Tuple, Dict, TYPE_CHECKING - +from packages.code_editor.codeeditor.grammar_analyzer import GrammarAnalyzer from packages.code_editor.codeeditor.qtpyeditor.highlighters.python import PythonHighlighter -from packages.code_editor.codeeditor.qtpyeditor.syntaxana import getIndent from packages.code_editor.codeeditor.qtpyeditor.linenumber import QCodeEditor +from packages.code_editor.codeeditor.qtpyeditor.syntaxana import getIndent # from pmgwidgets import create_icon @@ -31,7 +27,6 @@ if TYPE_CHECKING: from jedi.api import Completion logger = logging.getLogger(__name__) -logging.basicConfig(level=logging.DEBUG) logger.setLevel(logging.DEBUG) @@ -69,7 +64,6 @@ class AutoCompList(QTableWidget): # self.horizontalHeader().setMinimumWidth(300) # self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) - def verticalHeader(self) -> QHeaderView: return super(AutoCompList, self).verticalHeader() @@ -330,32 +324,32 @@ class PMBaseCodeEdit(QCodeEditor): self.on_backspace(event) event.accept() return - elif k == Qt.Key_ParenLeft: + elif k in (Qt.Key_ParenLeft, Qt.Key_BracketLeft, Qt.Key_BraceLeft): cursor = self.textCursor() cursor.beginEditBlock() - cursor.insertText('()') + string = {Qt.Key_ParenLeft: '()', Qt.Key_BracketLeft: '[]', Qt.Key_BraceLeft: '{}'}[k] + cursor.insertText(string) cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.MoveAnchor, 1) cursor.endEditBlock() self.setTextCursor(cursor) event.accept() return - elif k == Qt.Key_BracketLeft: + elif k in (Qt.Key_ParenRight, Qt.Key_BracketRight, Qt.Key_BraceRight): cursor = self.textCursor() + code = self.toPlainText() cursor.beginEditBlock() - cursor.insertText('[]') - cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.MoveAnchor, 1) - cursor.endEditBlock() - self.setTextCursor(cursor) - event.accept() - return - elif k == Qt.Key_BraceLeft: - cursor = self.textCursor() - cursor.beginEditBlock() - cursor.insertText('{}') - cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.MoveAnchor, 1) + position = cursor.position() + left, right = { + Qt.Key_ParenRight: ('(', ')'), Qt.Key_BracketRight: ('[', ']'), Qt.Key_BraceRight: ('{', '}')}[k] + analyzer = GrammarAnalyzer() + analyzer.feed(code) + length = len(code) + if position == length or analyzer.is_not_matched(position, left) or code[position] != right: + cursor.insertText(right) + else: + cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.MoveAnchor, 1) cursor.endEditBlock() self.setTextCursor(cursor) - event.accept() return super().keyPressEvent(event) @@ -379,10 +373,10 @@ class PMBaseCodeEdit(QCodeEditor): cursor.endEditBlock() def on_return_pressed(self): - ''' + """ 按回车换行的方法 :return: - ''' + """ cursor = self.textCursor() cursor.beginEditBlock() text = cursor.block().text() @@ -598,19 +592,17 @@ class PMBaseCodeEdit(QCodeEditor): """ eols = re.findall(r'\r\n|\r|\n', self.toPlainText()) if not eols: - print('\\n') # self.label_status_eol.setText('Unix(LF)') # self.textEdit.setEolMode(QsciScintilla.EolUnix) # \n换行 return grouped = [(len(list(group)), key) for key, group in groupby(sorted(eols))] eol = sorted(grouped, reverse=True)[0][1] if eol == '\r\n': - print('\\r\\n') + return # self.label_status_eol.setText('Windows(CR LF)') # self.textEdit.setEolMode(QsciScintilla.EolWindows) # \r\n换行 # return QsciScintilla.EolWindows if eol == '\r': - print('\\r') # self.label_status_eol.setText('Mac(CR)') # self.textEdit.setEolMode(QsciScintilla.EolMac) # \r换行 return diff --git a/packages/code_editor/codeeditor/qtpyeditor/codeedit/pythonedit.py b/packages/code_editor/codeeditor/qtpyeditor/codeedit/pythonedit.py index 61a4347eadb9ca5e1a80a87d664ab1bbc0080378..78976dde67470ef0978ff615780646c573e87a1e 100644 --- a/packages/code_editor/codeeditor/qtpyeditor/codeedit/pythonedit.py +++ b/packages/code_editor/codeeditor/qtpyeditor/codeedit/pythonedit.py @@ -45,12 +45,12 @@ class PMPythonCodeEdit(PMBaseCodeEdit): self.hint_widget.setStyleSheet("background-color:#d8d8d8") def on_autocomp_signal_received(self, text_cursor_content: tuple, completions: List['jedi.api.Completion']): - ''' + """ 当收到自动补全提示信号时,执行的函数。 :param text_cursor_content:(row,col,hint_when_completion_triggered) :param completions: :return: - ''' + """ hint = self._get_hint() logger.debug('hint_when_completion_triggered:{0},current_hint:{1}'.format(text_cursor_content[2], hint)) diff --git a/packages/code_editor/codeeditor/qtpyeditor/codeeditor/baseeditor.py b/packages/code_editor/codeeditor/qtpyeditor/codeeditor/baseeditor.py index bbf58f66ef1d2cdb85d19456e604dc01ead3e6ac..4305e25cd2877edd0124bc826a32962e19491cf1 100644 --- a/packages/code_editor/codeeditor/qtpyeditor/codeeditor/baseeditor.py +++ b/packages/code_editor/codeeditor/qtpyeditor/codeeditor/baseeditor.py @@ -42,7 +42,7 @@ import time from itertools import groupby from typing import TYPE_CHECKING, List, Iterable, Dict, Set, Tuple, Any from PySide2.QtGui import QIcon, QKeySequence, QTextDocument, QTextCursor, QTextBlock, QDropEvent -from PySide2.QtCore import QDir, QCoreApplication, Qt, QPoint, Signal, QTranslator, QLocale, QUrl +from PySide2.QtCore import QDir, QCoreApplication, Qt, QPoint, Signal, QTranslator, QLocale, QUrl, SignalInstance from PySide2.QtWidgets import QWidget, QMessageBox, QFileDialog, QAction, QShortcut, QDialog, QVBoxLayout, QPushButton, \ QHBoxLayout, QApplication, QLabel @@ -189,10 +189,13 @@ class FindDialog(QDialog): class PMGBaseEditor(PMAbstractEditor): - signal_focused_in: Signal = None - signal_new_requested: Signal = Signal(str, int) # 文件路径;文件的打开模式(目前都是0) - signal_save_requested: Signal = Signal() - signal_request_find_in_path: Signal = Signal(str) + signal_focused_in: SignalInstance = None + signal_new_requested: SignalInstance = Signal(str, int) # 文件路径;文件的打开模式(目前都是0) + signal_save_requested: SignalInstance = Signal() + signal_request_find_in_path: SignalInstance = Signal(str) + text_edit: 'PMBaseCodeEdit' + find_dialog: 'FindDialog' + goto_line_dialog: 'GotoLineDialog' def __init__(self, parent): app = QApplication.instance() @@ -203,12 +206,9 @@ class PMGBaseEditor(PMAbstractEditor): app.installTranslator(trans_editor_tb) super().__init__(parent) - self.find_dialog: 'FindDialog' = None - self.goto_line_dialog: 'GotoLineDialog' = None self.last_save_time = 0 self._path = '' self._modified = False - self.text_edit: 'PMBaseCodeEdit' = None self._extension_names: List[str] = [] # 该编辑器支持的文件名 self._indicator_dict: Dict[str, str] = {} @@ -245,8 +245,7 @@ class PMGBaseEditor(PMAbstractEditor): def show_cursor_pos(self): row = self.text_edit.textCursor().block().blockNumber() col = self.text_edit.textCursor().columnNumber() - self.status_label.setText( - '行:{row},列:{col}'.format(row=row + 1, col=col + 1)) + self.status_label.setText(f'行:{row + 1},列:{col + 1}') def set_shortcut(self): pass @@ -258,10 +257,7 @@ class PMGBaseEditor(PMAbstractEditor): pass def goto_line(self, line_no: int): - """ - 跳转到对应行列 - :return: - """ + """跳转到对应行列""" doc: QTextDocument = self.text_edit.document() lines = doc.blockCount() assert 1 <= line_no <= lines diff --git a/packages/code_editor/codeeditor/qtpyeditor/codeeditor/pythoneditor.py b/packages/code_editor/codeeditor/qtpyeditor/codeeditor/pythoneditor.py index 140c6f695b5198a9cb11ab1e6176b10a9ca48e4d..b9ac422aaf3644375d41a1de4be674ec645d7a01 100644 --- a/packages/code_editor/codeeditor/qtpyeditor/codeeditor/pythoneditor.py +++ b/packages/code_editor/codeeditor/qtpyeditor/codeeditor/pythoneditor.py @@ -13,11 +13,15 @@ # @Author: Zhanyi Hou # @Email: 1295752786@qq.com # @File: editor.py - -from PySide2.QtWidgets import QApplication, QListWidgetItem, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QTextBrowser +import logging from typing import Dict + +from PySide2.QtWidgets import QApplication + +from packages.code_editor.codeeditor.qtpyeditor.codeedit import PMPythonCodeEdit from packages.code_editor.codeeditor.qtpyeditor.codeeditor import PMGBaseEditor -from packages.code_editor.codeeditor.qtpyeditor.codeedit import PMBaseCodeEdit, PMPythonCodeEdit + +logger = logging.getLogger('code_editor.pythoneditor') class PMGPythonEditor(PMGBaseEditor): @@ -33,7 +37,7 @@ class PMGPythonEditor(PMGBaseEditor): pass def autocomp_stop(self): - print('autocomp stopped') + logger.info('autocomp stopped') self.text_edit.autocomp_thread.on_exit() diff --git a/packages/code_editor/codeeditor/qtpyeditor/highlighters/python.py b/packages/code_editor/codeeditor/qtpyeditor/highlighters/python.py index ba91ee8c15c4e6ea5214e3663680c1e48e78fb03..232e7035fe281fbf532bdd23bebcfd52251c4bc1 100644 --- a/packages/code_editor/codeeditor/qtpyeditor/highlighters/python.py +++ b/packages/code_editor/codeeditor/qtpyeditor/highlighters/python.py @@ -4,6 +4,7 @@ # @Email: 1295752786@qq.com # @File: python.py import builtins +import logging import sys import time from typing import Dict, Tuple, List, Set @@ -12,6 +13,7 @@ from PySide2.QtCore import QRegExp, Qt from PySide2.QtWidgets import QApplication from PySide2.QtGui import QSyntaxHighlighter, QTextCharFormat, QColor, QFont, QCursor, QBrush, QTextBlock +logger = logging.getLogger('code_editor.highlighters.python') color_scheme_intellij = {'keyword': '#101e96'} color_scheme_dark = {'keyword': '#b7602f'} @@ -93,10 +95,10 @@ class PythonHighlighter(BaseHighlighter): self._rehighlight_hint = True self._rehighlight_syntax = True self.KEYWORDS = ["and", "as", "assert", 'async', 'await', "break", "class", - "continue", "def", "del", "elif", "else", "except", - "exec", "finally", "for", "from", "global", "if", - "import", "in", "is", "lambda", "not", "or", "pass", - "raise", "return", "try", "while", "with", "yield"] + "continue", "def", "del", "elif", "else", "except", + "exec", "finally", "for", "from", "global", "if", + "import", "in", "is", "lambda", "not", "or", "pass", + "raise", "return", "try", "while", "with", "yield"] BUILTINS = list(builtins.__dict__.keys()) CONSTANTS = ["False", "True", "None", "NotImplemented", "Ellipsis"] @@ -250,7 +252,7 @@ class PythonHighlighter(BaseHighlighter): QSyntaxHighlighter.rehighlight(self) QApplication.restoreOverrideCursor() t1 = time.time() - print(t1 - t0, 'time.time,elapsed for rendering code', self.counter) + logger.debug(t1 - t0, 'time.time,elapsed for rendering code', self.counter) def registerHighlight(self, line_no: int, start: int, length: int, marker: int, hint: str): """ diff --git a/packages/code_editor/codeeditor/qtpyeditor/syntaxana.py b/packages/code_editor/codeeditor/qtpyeditor/syntaxana.py index d000b8eee4a0f3779306f23216c06c6de0a80f77..1fb4b9226b62725529b7e207123c443461e34cae 100644 --- a/packages/code_editor/codeeditor/qtpyeditor/syntaxana.py +++ b/packages/code_editor/codeeditor/qtpyeditor/syntaxana.py @@ -4,16 +4,16 @@ # @Email: 1295752786@qq.com # @File: syntaxana.py # -*- coding: utf-8 -*- -''' +""" powered by NovalIDE 来自NovalIDE的词法分析模块 作者:侯展意 词法分析模块的重要组成单元、 依靠各种正则表达式进行特征的提取。 -''' -from typing import List, Tuple, Dict +""" import re +from typing import List, Tuple, Dict def getReplacingDic() -> Dict: @@ -22,10 +22,10 @@ def getReplacingDic() -> Dict: dic['。'] = '.' dic[';'] = ';' dic[':'] = ':' - dic['‘'] = '\'' - dic['’'] = '\'' - dic['“'] = '\"' - dic['”'] = '\"' + dic['‘'] = "'" + dic['’'] = "'" + dic['“'] = '"' + dic['”'] = '"' dic['【'] = '[' dic['】'] = ']' dic['('] = '(' @@ -36,9 +36,9 @@ def getReplacingDic() -> Dict: def getIndent(s: str) -> Tuple[str, int]: s = s.replace('\t', ' ') # tab替换成四个空格 s = s.rstrip() - if (len(s) > 0): + if len(s) > 0: for i, ch in enumerate(s): - if (ch != ' '): + if ch != ' ': return s[i:], i return "", i + 1 else: @@ -47,7 +47,7 @@ def getIndent(s: str) -> Tuple[str, int]: def removeComment(s: str) -> str: pos = s.find('#') - if (pos != -1): + if pos != -1: return s[:pos] else: return s @@ -59,7 +59,7 @@ def getStringContent(row): def removeStringContent(row: str) -> str: row = row.replace('\"', '\'') - if (row.count('\'') >= 2): + if row.count('\'') >= 2: s = getAllFromRegex(regex=r'[\'](.*?)[\']', st=row) for item in s: row = row.replace('\'%s\'' % item, '\'\'') # 带着分号一起换掉。 @@ -81,13 +81,13 @@ def getAllFromRegex(regex: str, st: str) -> List[str]: def getInfoFromRegex(regex: str, st: str) -> str: # 从正则表达式中获取信息的函数。如果没有任何结果则返回0。 foundList = re.findall(re.compile(regex, re.S), st) item = '' - if (foundList != []): + if foundList != []: item = foundList[0] return item def getWordsFromString(s: str) -> list: - if (s != ''): + if s != '': syms = s.split(',') # 用逗号分隔开。 for i in range(len(syms)): syms[i] = syms[i].strip() @@ -126,10 +126,10 @@ def getBracketedContent(row: str) -> Tuple[str, str, str]: # 获取任何类型 length = len(row) for i in range(len(lst)): lst[i] = row.find(symList[i]) - if (lst[i] == -1): + if lst[i] == -1: lst[i] = length minVal = min(lst) - if (minVal == length): # 说明根本没括号 + if minVal == length: # 说明根本没括号 return '', '', row[:minVal] # 所以返回值不仅没有括号,还没有括号中的内容(废话),只是返回括号前面的东西。 else: pos = lst.index(minVal) # 获取最小值的索引 @@ -143,13 +143,13 @@ def getFuncArgs(row: str) -> List[str]: # 获取函数的输入参数。 li = getWordsFromString(s) - if (len(li) > 0): - if (li[0] == 'self'): # 不允许函数的第一个参数名字叫self。 + if len(li) > 0: + if li[0] == 'self': # 不允许函数的第一个参数名字叫self。 li.pop(0) for i in range(len(li)): eqSymPos = li[i].find('=') - if (eqSymPos != -1): # 如果eqSymPos中有一个等号 + if eqSymPos != -1: # 如果eqSymPos中有一个等号 li[i] = li[i][:eqSymPos] # 那么将等号去除 colonSymPos = li[i].find(':') if colonSymPos != -1: @@ -168,7 +168,7 @@ def getLocalVarNames(row: str) -> List[str]: # 获取局部变量的名称。 words = getWordsFromString(li) result = [] for w in words: # 如果是函数的方法,则不可。 - if (w.find('.') == -1): + if w.find('.') == -1: result.append(w) return result @@ -182,36 +182,36 @@ def is_number(str_number: str) -> bool: def getForVariables(row: str) -> List[int]: - ''' + """ 获取for循环中定义的变量。 - ''' + """ s = getInfoFromRegex(r'for(.*?)in', row) s = s.strip() return getWordsFromString(s) def getVarType(row: str) -> str: - ''' + """ 获取变量的类型,比如集合,数字等等。 - ''' + """ bracket, content, outer = getBracketedContent(row) li = outer.split('=') - if (len(li) >= 1): + if len(li) >= 1: - if (li[1].strip() == ''): # 这种情况下为直接赋值的语句, - if (bracket == '('): + if li[1].strip() == '': # 这种情况下为直接赋值的语句, + if bracket == '(': return ':tuple' - elif (bracket == '['): + elif bracket == '[': return ':list' else: st = li[1].split(',')[0] - if (is_number(st)): + if is_number(st): return ':number' return '' -class Row(): +class Row: def __init__(self, pos: int, text: str, indent: int) -> None: self.pos = pos self.text = text @@ -239,7 +239,7 @@ def regularize(rawText: List[str]) -> List[Row]: l = removeStringContent(l) l = removeComment(l) - if (skipLine == False): + if skipLine == False: row, currentIndent = getIndent(l) # 获取当前的行名和缩进,同时修剪掉行首的空格 currentRow = Row(i, row, currentIndent) rowList.append(currentRow) @@ -250,12 +250,12 @@ def regularize(rawText: List[str]) -> List[Row]: cp = checkPar(currentRow.text) - if (cp == 0): # 如果括号不匹配,那么就再继续进行,直至寻找到符合要求的行为止。 + if cp == 0: # 如果括号不匹配,那么就再继续进行,直至寻找到符合要求的行为止。 skipLine = True - if (len(currentRow.text) >= 200): # 长度超出,强行退出。 + if len(currentRow.text) >= 200: # 长度超出,强行退出。 skipLine = False continue - elif (cp == -1): # 如果右边括号反倒更多,就跳出这种情况。 + elif cp == -1: # 如果右边括号反倒更多,就跳出这种情况。 skipLine = False continue else: diff --git a/packages/code_editor/codeeditor/tabwidget.py b/packages/code_editor/codeeditor/tabwidget.py index fa0bbcc75b2371666da449048c9bfd1041b17c74..d909d4a46c03c75c8ae58d72a049d0430d21e1ae 100644 --- a/packages/code_editor/codeeditor/tabwidget.py +++ b/packages/code_editor/codeeditor/tabwidget.py @@ -54,10 +54,10 @@ if TYPE_CHECKING or in_unit_test(): # from packages.code_editor.codeeditor.ui.findinpath import FindInPathWidget else: # from codeeditor.baseeditor import PMBaseEditor - from codeeditor.pythoneditor import PMPythonEditor + from .pythoneditor import PMPythonEditor # from codeeditor.cppeditor import PMCPPEditor # from codeeditor.cythoneditor import PMCythonEditor - from codeeditor.markdowneditor import PMMarkdownEditor + from .markdowneditor import PMMarkdownEditor EDITOR_TYPE = Optional[Union['PMBaseEditor', 'PMCythonEditor', 'PMPythonEditor', 'PMCPPEditor', 'PMMarkdownEditor']] logger = logging.getLogger(__name__) @@ -576,7 +576,7 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): current_widget.goto_line(row) logger.warning('goto file %s ,line %d' % (path, row)) t1 = time.time() - print('time elapsed:', t1 - t0) + logger.debug('time elapsed:', t1 - t0) def slot_goto(self): """ @@ -840,7 +840,8 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): self._current_executable = sys.executable else: self._current_executable = \ - self.extension_lib.Program.get_settings_item_from_file("config.ini", 'RUN/EXTERNAL_INTERPRETERS')[interpreter_index - 1]['path'] + self.extension_lib.Program.get_settings_item_from_file("config.ini", 'RUN/EXTERNAL_INTERPRETERS')[ + interpreter_index - 1]['path'] def on_tab_switched(self, index: int) -> None: for i in range(self.count()): @@ -955,11 +956,7 @@ class PMCodeEditTabWidget(QTabWidget, PMDockObject): if __name__ == '__main__': - - cgitb.enable(format='text') - logging.basicConfig(level=logging.INFO) - app = QApplication(sys.argv) # app.setStyleSheet(""" # PMBaseEditor { diff --git a/packages/code_editor/main.py b/packages/code_editor/main.py index d5e1fb5fc46ffa090b7d412400671c233dec7dc6..5346fd4c81cf824127b4fa4288291e5084980449 100644 --- a/packages/code_editor/main.py +++ b/packages/code_editor/main.py @@ -1,44 +1,46 @@ +import logging import os -import sys -from typing import Dict, Union +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 + app = QApplication.instance() trans_editor_tb = QTranslator() app.trans_editor_tb = trans_editor_tb -trans_editor_tb.load(os.path.join(os.path.dirname(__file__), 'translations', 'qt_%s.qm' % QLocale.system().name())) +trans_editor_tb.load(os.path.join(os.path.dirname(__file__), 'translations', f'qt_{QLocale.system().name()}.qm')) app.installTranslator(trans_editor_tb) -sys.path.append(os.path.dirname(__file__)) - from features.extensions.extensionlib import BaseInterface, BaseExtension from .codeeditor.tabwidget import PMCodeEditTabWidget from .debugger import PMDebugConsoleTabWidget from .toolbar import PMEditorToolbar from pmgwidgets import PMGPanel, load_json, dump_json -_PMEditorToolbar = PMEditorToolbar # 这一行的目的是防止导入被编辑器自动优化。 +__prevent_from_ide_optimization = PMEditorToolbar # 这一行的目的是防止导入被编辑器自动优化。 +logger = logging.getLogger('code_editor') class Extension(BaseExtension): + editor_widget: 'PMCodeEditTabWidget' + debuggers_widget: 'PMDebugConsoleTabWidget' + def __init__(self): super(Extension, self).__init__() - self.settings: Dict[str, Union[int, str]] = {} - self.editor_widget: 'PMCodeEditTabWidget' = None def on_loading(self): self.load_settings() def on_load(self): self.widgets["PMCodeEditTabWidget"].set_extension_lib(self.extension_lib) - self.editor_widget: 'PMCodeEditTabWidget' = self.widgets["PMCodeEditTabWidget"] - + self.editor_widget = self.widgets["PMCodeEditTabWidget"] self.interface.editor_tab_widget = self.editor_widget self.editor_widget.settings = self.settings - self.debuggers_widget: 'PMDebugConsoleTabWidget' = self.widgets['PMDebugConsoleTabWidget'] + self.debuggers_widget = self.widgets['PMDebugConsoleTabWidget'] self.debuggers_widget.signal_goto_file.connect(self.on_gotoline_requested) self.debuggers_widget.extension_lib = self.extension_lib self.editor_widget.set_debug_widget(self.debuggers_widget) @@ -77,17 +79,13 @@ class Extension(BaseExtension): self.editor_widget.set_color_scheme('light') else: self.editor_widget.set_color_scheme('dark') - print(work_dir) + logger.debug(f'Working directory: {work_dir}') self.editor_widget.on_work_dir_changed(work_dir) def bind_event(self): - - self.extension_lib.get_interface('file_tree').add_open_file_callback('.py', self.new_script) - self.extension_lib.get_interface('file_tree').add_open_file_callback('.c', self.new_script) - self.extension_lib.get_interface('file_tree').add_open_file_callback('.cpp', self.new_script) - self.extension_lib.get_interface('file_tree').add_open_file_callback('.h', self.new_script) - self.extension_lib.get_interface('file_tree').add_open_file_callback('.pyx', self.new_script) - self.extension_lib.get_interface('file_tree').add_open_file_callback('.md', self.new_script) + extensions = ('.py', '.c', '.cpp', '.h', '.pyx', '.md') + file_tree: 'FileTreeInterface' = self.extension_lib.get_interface('file_tree') + [file_tree.add_open_file_callback(ext, self.new_script) for ext in extensions] def load_settings(self): settings = {'encoding_declaration_text': '# coding = utf-8', @@ -108,10 +106,7 @@ class Extension(BaseExtension): self.settings = settings def add_settings_panel(self): - """ - 向主界面的设置面板插入一个设置页面,并且按照设置数据来更新设置。 - :return: - """ + """向主界面的设置面板插入一个设置页面,并且按照设置数据来更新设置。""" settings = self.settings new_settings = [ @@ -150,8 +145,7 @@ class Extension(BaseExtension): class Interface(BaseInterface): - def __init__(self): - self.editor_tab_widget: 'PMCodeEditTabWidget' = None + editor_tab_widget: 'PMCodeEditTabWidget' def open_script(self, path: str): self.editor_tab_widget.slot_new_script(path) diff --git a/packages/dataio/sample.py b/packages/dataio/sample.py index 4f207de6d1a9cc5bbf011b6f564d583620a2f942..8af5aed28a75ebb7d2bcdde886be99dab1929ca9 100644 --- a/packages/dataio/sample.py +++ b/packages/dataio/sample.py @@ -18,9 +18,6 @@ from pyminer_comm import set_var, run_command # 导入matlab加载模块 # 定义日志输出格式 -logging.basicConfig(format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%Y-%m-%d %H:%M:%S", - level=logging.INFO) - logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) diff --git a/packages/file_tree/file_tree.py b/packages/file_tree/file_tree.py index d9f7e6edaa603b1ec130d8ab74d7cabb7270d2ff..9b37739d10f09236221627089fb3d9196a82f055 100644 --- a/packages/file_tree/file_tree.py +++ b/packages/file_tree/file_tree.py @@ -1,10 +1,12 @@ import os import platform from typing import Dict, Callable, List -from PySide2.QtCore import Qt, QModelIndex, QMimeData, QUrl -from PySide2.QtGui import QPixmap, QIcon, QCloseEvent, QKeyEvent + +from PySide2.QtCore import Qt, QModelIndex +from PySide2.QtGui import QPixmap, QIcon, QCloseEvent from PySide2.QtWidgets import QVBoxLayout, QWidget, QFileDialog, QPushButton, QHBoxLayout, QLineEdit, QToolButton, \ QApplication, QMessageBox + from pmgwidgets import PMGFilesTreeview, PMDockObject, in_unit_test, UndoManager @@ -259,11 +261,8 @@ class PMFilesTree(QWidget, PMDockObject): if __name__ == '__main__': import sys import cgitb - import logging cgitb.enable(format='text') - logging.basicConfig(level=logging.INFO) - app = QApplication(sys.argv) w = PMFilesTree() diff --git a/packages/jupyter_notebook_support/scripts/pyminer_ipython_node.py b/packages/jupyter_notebook_support/scripts/pyminer_ipython_node.py index a1e03fab6053a35ac1c7f7552847720055b5a184..1f20d9dc1586c229957576444b7492dabc650e7e 100644 --- a/packages/jupyter_notebook_support/scripts/pyminer_ipython_node.py +++ b/packages/jupyter_notebook_support/scripts/pyminer_ipython_node.py @@ -14,10 +14,6 @@ import sys, os import typing from IPython.core.magic import register_line_magic, register_cell_magic, register_line_cell_magic -# logging.basicConfig(format="%(asctime)s %(name)s:%(levelname)s:%(message)s", -# datefmt="%Y-%m-%d %H:%M:%S", -# level=logging.INFO, -# filename=r"c:\users\12957\Desktop\123.txt") __logger = logging.getLogger() __logger.info("Env variable-- IPYTHON_AS_PYMINER_NODE: %s" % os.environ.get('IPYTHON_AS_PYMINER_NODE')) if (os.environ.get('IPYTHON_AS_PYMINER_NODE') is not None) and (int(os.environ.get('IPYTHON_AS_PYMINER_NODE')) >= 1): diff --git a/packages/pm_calc/package.json b/packages/pm_calc/package.json index e232b79651906eaddab36e9731aa0233f1e9ea1f..61e8bb7891a27a7c27ba1a59db3820f0e20bc31b 100644 --- a/packages/pm_calc/package.json +++ b/packages/pm_calc/package.json @@ -1,26 +1,27 @@ { - "name":"pm_calc", - "display_name":"计算", - "version":"0.1.0", - "description":"内置计算的菜单", - "icon":"ipython_console.jpg", - "interface":{ - "file":"main.py", - "interface":"CalcInterface" + "name": "pm_calc", + "display_name": "计算", + "version": "0.1.0", + "description": "内置计算的菜单", + "icon": "ipython_console.jpg", + "interface": { + "file": "main.py", + "interface": "CalcInterface" }, - "widgets":[ + "widgets": [ { - "file":"main.py", - "widget":"PMCalcToolBar", - "position":"new_toolbar", - "config":{ + "file": "main.py", + "widget": "PMCalcToolBar", + "position": "new_toolbar", + "config": { "message": "no", "name": "pm_calc", "text": "数据" } } ], - "requirements":[ - "workspace_inspector" + "requirements": [ + "workspace_inspector", + "applications_toolbar" ] } \ No newline at end of file diff --git a/packages/pm_calc/preprocess.py b/packages/pm_calc/preprocess.py index abdb9e44d4f0228b5414c2a600b017f217c1122b..5a39a1ebcb0c654437b8682d430868859267a09c 100644 --- a/packages/pm_calc/preprocess.py +++ b/packages/pm_calc/preprocess.py @@ -1,40 +1,36 @@ -import os import sys -import logging import datetime +import logging +import sys import webbrowser + import numpy as np import pandas as pd -from pandas.api.types import is_numeric_dtype +# # 导入Qt模块 +from PySide2.QtCore import Qt, Signal +from PySide2.QtWidgets import QWidget, QMessageBox, \ + QFileDialog, QDesktopWidget, QDialog, QTableWidgetItem, QInputDialog, QAbstractItemView, QLineEdit, \ + QApplication from pandas.api.types import is_float_dtype +from pandas.api.types import is_numeric_dtype from pandas.api.types import is_string_dtype -# # 导入Qt模块 -from PySide2.QtCore import QSize, Qt, Signal -from PySide2.QtGui import QPixmap -from PySide2.QtWidgets import QHBoxLayout, QWidget, QSpacerItem, QToolButton, QSizePolicy, QWizard, QMessageBox, \ - QFileDialog, QFrame, QDesktopWidget, QDialog, QTableWidgetItem, QInputDialog, QAbstractItemView, QLineEdit, \ - QApplication -# 导入数据相关操作模块 -from packages.pm_preprocess.ui.data_role import Ui_Form as DataRole_Ui_Form # 数据角色 -from packages.pm_preprocess.ui.data_info import Ui_Form as DataInfo_Ui_Form # 数据信息 from packages.pm_preprocess.ui.data_column_desc import Ui_Form as Columns_desc_Ui_Form # 数据列描述 -from packages.pm_preprocess.ui.data_delete_row import Ui_Form as DataDeleteRow_Ui_Form # 数据删除行 +from packages.pm_preprocess.ui.data_column_encode import Ui_Form as DataColumnEncode_Ui_Form +from packages.pm_preprocess.ui.data_column_name import Ui_Form as DataColumnName_Ui_Form from packages.pm_preprocess.ui.data_delete_column import Ui_Form as DataDeleteColumn_Ui_Form -from packages.pm_preprocess.ui.data_merge_vertical import Ui_Form as DataMergeVertical_Ui_Form +from packages.pm_preprocess.ui.data_delete_row import Ui_Form as DataDeleteRow_Ui_Form # 数据删除行 +from packages.pm_preprocess.ui.data_info import Ui_Form as DataInfo_Ui_Form # 数据信息 from packages.pm_preprocess.ui.data_merge_horizontal import Ui_Form as DataMergeHorizontal_Ui_Form -from packages.pm_preprocess.ui.data_partition import Ui_Form as DataPartition_Ui_Form +from packages.pm_preprocess.ui.data_merge_vertical import Ui_Form as DataMergeVertical_Ui_Form from packages.pm_preprocess.ui.data_new_column import Ui_Form as DataNewColumn_Ui_Form -from packages.pm_preprocess.ui.data_sort import Ui_Form as DataSort_Ui_Form -from packages.pm_preprocess.ui.data_transpose import Ui_Form as DataTranspose_Ui_Form +from packages.pm_preprocess.ui.data_partition import Ui_Form as DataPartition_Ui_Form +# 导入数据相关操作模块 +from packages.pm_preprocess.ui.data_role import Ui_Form as DataRole_Ui_Form # 数据角色 from packages.pm_preprocess.ui.data_sample import Ui_Form as DataSample_Ui_Form +from packages.pm_preprocess.ui.data_sort import Ui_Form as DataSort_Ui_Form from packages.pm_preprocess.ui.data_standard import Ui_Form as DataStandard_Ui_Form -from packages.pm_preprocess.ui.data_column_encode import Ui_Form as DataColumnEncode_Ui_Form -from packages.pm_preprocess.ui.data_column_name import Ui_Form as DataColumnName_Ui_Form - -# 定义日志输出格式 -logging.basicConfig(format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%Y-%m-%d %H:%M:%S", - level=logging.INFO) +from packages.pm_preprocess.ui.data_transpose import Ui_Form as DataTranspose_Ui_Form class DataInfoForm(QDialog, DataInfo_Ui_Form): @@ -1087,9 +1083,6 @@ class DataNewColumnForm(QWidget, DataNewColumn_Ui_Form): self.move(int((screen.width() - size.width()) / 2), int((screen.height() - size.height()) / 2)) - - - class DataSortForm(QWidget, DataSort_Ui_Form): """ 打开"从sas导入"窗口 diff --git a/packages/pm_preprocess/preprocess.py b/packages/pm_preprocess/preprocess.py index abdb9e44d4f0228b5414c2a600b017f217c1122b..ef1a21aedd685fcd423d3ed3bd5afe6e02015600 100644 --- a/packages/pm_preprocess/preprocess.py +++ b/packages/pm_preprocess/preprocess.py @@ -32,10 +32,6 @@ from packages.pm_preprocess.ui.data_standard import Ui_Form as DataStandard_Ui_F from packages.pm_preprocess.ui.data_column_encode import Ui_Form as DataColumnEncode_Ui_Form from packages.pm_preprocess.ui.data_column_name import Ui_Form as DataColumnName_Ui_Form -# 定义日志输出格式 -logging.basicConfig(format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%Y-%m-%d %H:%M:%S", - level=logging.INFO) - class DataInfoForm(QDialog, DataInfo_Ui_Form): """ @@ -1087,9 +1083,6 @@ class DataNewColumnForm(QWidget, DataNewColumn_Ui_Form): self.move(int((screen.width() - size.width()) / 2), int((screen.height() - size.height()) / 2)) - - - class DataSortForm(QWidget, DataSort_Ui_Form): """ 打开"从sas导入"窗口 diff --git a/packages/socket_server/main.py b/packages/socket_server/main.py index 1915fb61076f3fc4feb0736a3c725571263a4e18..bad80c712b212272c5af1cd7a2775a206d832c57 100644 --- a/packages/socket_server/main.py +++ b/packages/socket_server/main.py @@ -2,11 +2,14 @@ import inspect import logging from typing import Callable +import flask.cli from flask import Flask from features.extensions.extensionlib import BaseExtension, BaseInterface from .server_by_socket import run, app +# 不显示flask的启动日志 +flask.cli.show_server_banner = lambda *_, **__: None logger = logging.getLogger(__name__) @@ -20,9 +23,7 @@ class Extension(BaseExtension): class Interface(BaseInterface): extension_lib = None - - def __init__(self): - self.app: Flask = None + app: Flask def add_handler(self, rule, callback: Callable[[], str]): """ @@ -41,8 +42,6 @@ class Interface(BaseInterface): self.app.add_url_rule(rule, callback.__name__, callback) except AssertionError as e: if "overwriting an existing endpoint function" in repr(e): - desc = 'Problem happened when trying register function {func} with rule {rule}'.format( - func=callback, rule=rule) - logger.error(desc) + logger.error(f'Problem happened when trying register function {callback} with rule {rule}') raise e # self.extension_lib.Program.show_exception_occured_panel(e, solution='') diff --git a/packages/socket_server/server_by_socket.py b/packages/socket_server/server_by_socket.py index 9975e4f22edfc9bfc896ea785ef72710900b80e1..d4a9949158331092dd02a4800ce98a0dc8f2d23b 100644 --- a/packages/socket_server/server_by_socket.py +++ b/packages/socket_server/server_by_socket.py @@ -48,7 +48,9 @@ def get_settings(): :return: """ global message_queue - return "['Warning this method was deprecated']" + msg = "['Warning this method was deprecated']" + logger.warning(msg) + return msg @app.route('/get_stylesheet') @@ -64,11 +66,7 @@ def get_stylesheet(): @app.route('/set_data') def modify_data_descs(): - """ - 将工作空间中设置为data_desc型变量 - Returns: - - """ + """将工作空间中设置为data_desc型变量""" global message_queue try: datadesc_dict = b64_to_dict(request.args.get('msg')) @@ -85,11 +83,7 @@ def modify_data_descs(): @app.route('/interface_call') def run_command(): - """ - 运行命令,调用接口函数。 - Returns: - - """ + """运行命令,调用接口函数。""" global message_queue try: settings = request.args.get('msg') @@ -232,7 +226,10 @@ def run_server(port: int = None, ext_lib=None): extension_lib = ext_lib server = PMGServer() - server_thread = threading.Thread(target=app.run, kwargs={'port': port}) + def wrapper(): + app.run(port=port) + + server_thread = threading.Thread(target=wrapper) server_thread.setDaemon(True) server_thread.start() diff --git a/pmgwidgets/display/dynamicgraph/base/basetimeseries.py b/pmgwidgets/display/dynamicgraph/base/basetimeseries.py index d00a7e044fca6a1371c8b9fc27c58178d75b773f..d50643aadee51e607c7384382716397417408992 100644 --- a/pmgwidgets/display/dynamicgraph/base/basetimeseries.py +++ b/pmgwidgets/display/dynamicgraph/base/basetimeseries.py @@ -1,3 +1,4 @@ +import logging import sys import time @@ -7,6 +8,8 @@ import numpy as np from typing import List, Dict, Tuple from pmgwidgets.display.matplotlib.qt5agg import PMMatplotlibQt5Widget +logger = logging.getLogger('display.basetimeseries') + class PMBaseTimeSeriesWidget(QWidget): def __init__(self, parent: 'QWidget' = None, recent_samples: int = 100, show_mode: str = '', @@ -137,7 +140,7 @@ class PMTimeSeriesPGWidget(PMBaseTimeSeriesWidget): def __init__(self, max_samples: int = 10000, recent_samples: int = 100): super(PMTimeSeriesPGWidget, self).__init__(max_samples=max_samples, recent_samples=recent_samples) import pyqtgraph - self.curves:Dict ={} + self.curves: Dict = {} self.setLayout(QVBoxLayout()) self.plot_widget = pyqtgraph.plot() # PMMatplotlibQt5Widget(self) self.layout().addWidget(self.plot_widget) @@ -146,7 +149,7 @@ class PMTimeSeriesPGWidget(PMBaseTimeSeriesWidget): self.ax = None def add_data(self, data_name, data_type): - super().add_data(data_name,data_type) + super().add_data(data_name, data_type) self.curves[data_name] = self.plot_widget.plot() # def plot(self, t, y): @@ -154,7 +157,8 @@ class PMTimeSeriesPGWidget(PMBaseTimeSeriesWidget): def refresh(self): for k in self.time.keys(): t, y = self.get_recent_data(k) - self.curves[k].setData(t,y) + self.curves[k].setData(t, y) + def clear(self): pass @@ -165,7 +169,7 @@ def time_out(): i += 1 graphics.add_sample('a', value=np.random.randint(100)) t1 = time.time() - print(i, 'time elapsed :', t1 - t0) + logger.debug(i, 'time elapsed :', t1 - t0) if __name__ == '__main__': @@ -193,6 +197,7 @@ if __name__ == '__main__': graphics.add_data('a', np.float) + timer = QTimer() timer.timeout.connect(time_out) diff --git a/pmgwidgets/elements/toolbar.py b/pmgwidgets/elements/toolbar.py index 27f6863cf75df0852adcf692301a5c223800e290..2f91a75849ea5d06ed49b32f9180c5453e062b31 100644 --- a/pmgwidgets/elements/toolbar.py +++ b/pmgwidgets/elements/toolbar.py @@ -40,7 +40,7 @@ class TopToolBar(QToolBar): pbtn.setObjectName('pmtopToolbarButton') pbtn.setProperty('stat', 'unselected') - assert insert_after in self.button_names + assert insert_after in self.button_names, f'{insert_after} do not in buttons: {self.button_names}' index = self.button_names.index(insert_after) self.button_names.insert(index + 1, name) self.buttonbar_widget.layout().insertWidget(index + 1, pbtn) @@ -93,7 +93,7 @@ class PMGToolBar(QToolBar): tb.setToolTip(tooltip) tb.setProperty('qssp', 'tooliconbtn') if icon is not None: - pixmap = icon.pixmap(QSize(40,40)) + pixmap = icon.pixmap(QSize(40, 40)) icon = QIcon(pixmap) tb.setIcon(icon) diff --git a/pmgwidgets/flowchart/core/flowchart_scene.py b/pmgwidgets/flowchart/core/flowchart_scene.py index 6c1137d2f58052938ad91acd59ef7bdf37cc03e8..fe408083e32e126a63b849049121fc7ebe752578 100644 --- a/pmgwidgets/flowchart/core/flowchart_scene.py +++ b/pmgwidgets/flowchart/core/flowchart_scene.py @@ -12,8 +12,8 @@ type:str params:List[str] """ import json +import logging import os -import sys import time from typing import List, Dict, TYPE_CHECKING @@ -22,14 +22,13 @@ if TYPE_CHECKING: from pmgwidgets.flowchart.core.flow_content import FlowContentForFunction, flowcontent_types from pmgwidgets.flowchart.core.flow_items import CustomPort, CustomLine, CustomMidPoint, CustomRect from pmgwidgets.flowchart.core.flow_node import Node -from pmgwidgets.flowchart.core.nodemanager import NodeManagerWidget from pmgwidgets.utilities.uilogics.undomanager import UndoManager -from PySide2.QtCore import QSize, QCoreApplication, QLineF, Qt, QPointF, QThread, Signal -from PySide2.QtGui import QPen, QColor, QKeyEvent, QWheelEvent, QCloseEvent -from PySide2.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QToolButton, QSpacerItem, QSizePolicy, QGraphicsView, \ - QFrame, QApplication, QGraphicsScene, QGraphicsSceneMouseEvent, QMenu, QGraphicsSceneContextMenuEvent, QFileDialog, \ - QMessageBox +from PySide2.QtCore import QLineF, Qt, QPointF, Signal +from PySide2.QtGui import QPen, QColor, QKeyEvent +from PySide2.QtWidgets import QGraphicsView, \ + QGraphicsScene, QGraphicsSceneMouseEvent, QMenu, QGraphicsSceneContextMenuEvent +logger = logging.getLogger(__name__) COLOR_NORMAL = QColor(212, 227, 242) COLOR_HOVER = QColor(255, 200, 00) COLOR_HOVER_PORT = QColor(0, 0, 50) diff --git a/pmgwidgets/utilities/network/generalclient.py b/pmgwidgets/utilities/network/generalclient.py index d19ab5fe6d04beea002513dc4491ae5644152266..d9655f3a71ea50ab62ba6ffa02c029df015b4a67 100644 --- a/pmgwidgets/utilities/network/generalclient.py +++ b/pmgwidgets/utilities/network/generalclient.py @@ -1,14 +1,13 @@ # coding=utf-8 -''' +""" client端 长连接,短连接,心跳 -''' +""" import json import threading import zlib from typing import List - import time from .util import timeit, receive diff --git a/pmgwidgets/utilities/network/qtclient.py b/pmgwidgets/utilities/network/qtclient.py index 35047ed361f2147a437cac95b4446bc6234e6b55..c177b3da36cd1ab06d47494fbaebf22a6afe59c9 100644 --- a/pmgwidgets/utilities/network/qtclient.py +++ b/pmgwidgets/utilities/network/qtclient.py @@ -1,24 +1,21 @@ # coding=utf-8 __author__ = '侯展意' -import base64 import json +import logging +import socket +import sys import warnings import zlib from typing import List -import logging -import sys -import cloudpickle -import time -import socket - -logger = logging.getLogger(__name__) -from PySide2.QtWidgets import QApplication from PySide2.QtCore import QObject, QThread, Signal, QTimer +from PySide2.QtWidgets import QApplication -from .util import timeit, receive from .baseclient import BaseClient +from .util import timeit, receive + +logger = logging.getLogger(__name__) def parse_splicing_packets(packet_bytes: bytes) -> List[bytes]: diff --git a/pmgwidgets/utilities/network/server.py b/pmgwidgets/utilities/network/server.py index d6ebf377aa6c2e854a4be9e04ad7b8f3b651795b..f69a0dc0e1196a7721abb277131890492b07594f 100644 --- a/pmgwidgets/utilities/network/server.py +++ b/pmgwidgets/utilities/network/server.py @@ -5,14 +5,14 @@ """ import json -import time +import logging import socket import sys -import logging +import time from typing import List, Tuple, Dict, Union -from PySide2.QtWidgets import QApplication from PySide2.QtCore import QObject, QThread, QTimer +from PySide2.QtWidgets import QApplication from .util import receive, strip_byte_end @@ -93,8 +93,7 @@ class LoopWork(QObject): t0 = time.time() self.handle_packet(sock, b) t1 = time.time() - logger.info( - "Message handled!\nThe message was:%s" % (message) + '\nTime elapsed %f s\n' % (t1 - t0)) + logger.info(f"Message handled!\nThe message was:{message}\nTime elapsed {t1 - t0:f} s\n") except: import traceback diff --git a/pmgwidgets/utilities/network/util.py b/pmgwidgets/utilities/network/util.py index bf1e8ab0fd0b361068140d6e2728b7d930feebde..7c9fface370a526d6482c121e26de4a91fc9fa74 100644 --- a/pmgwidgets/utilities/network/util.py +++ b/pmgwidgets/utilities/network/util.py @@ -89,7 +89,7 @@ def timeit(func): def wrapper(*args, **kwargs): t0 = time.time() r = func(*args, **kwargs) - print('time_elapsed', time.time() - t0) + logger.debug('time_elapsed', time.time() - t0) return r return wrapper diff --git a/pmgwidgets/widgets/basic/others/console.py b/pmgwidgets/widgets/basic/others/console.py index 591f5e908683aadd7407b4d60942c49119787636..15d074ac454cf8a97b918f1ea9ab5f697296a836 100644 --- a/pmgwidgets/widgets/basic/others/console.py +++ b/pmgwidgets/widgets/basic/others/console.py @@ -8,10 +8,10 @@ Created on 2020/8/24 @file: console.py @description: Console Widget """ +import json import logging import os import sys -import json from typing import Tuple, Dict, Callable from PySide2.QtCore import QObject, Signal, QThread, QWaitCondition, QMutex, QPoint, QCoreApplication, QTranslator, \ @@ -22,6 +22,7 @@ from qtconsole import styles from qtconsole.manager import QtKernelManager from qtconsole.rich_jupyter_widget import RichJupyterWidget from qtconsole.styles import default_light_syntax_style, default_light_style_sheet + from features.io import settings default_dark_style_template = styles.default_template + """\ @@ -32,7 +33,6 @@ default_dark_style_sheet = default_dark_style_template % dict( bgcolor='#19232d', fgcolor='white', select="#ccc") default_dark_syntax_style = 'monokai' # 'default' -logging.basicConfig(stream=sys.stderr) logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -359,8 +359,8 @@ class PMGIpythonConsole(RichJupyterWidget): with open(self.history_path, 'w') as f: json.dump(history, f) + if __name__ == '__main__': - import sys import cgitb cgitb.enable(format='text') diff --git a/pmlocalserver/server.py b/pmlocalserver/server.py index 745325d7dc6f4aecde3bd60b6d987d2883f7dd47..876e0f65a1c3a67a28ce4fd7ba85df336b866664 100644 --- a/pmlocalserver/server.py +++ b/pmlocalserver/server.py @@ -1,11 +1,9 @@ import threading +from multiprocessing import shared_memory from flask import Flask from flask_cors import CORS -import threading -from multiprocessing import shared_memory - server = Flask(__name__, static_folder='static', static_url_path='/static') CORS(server, supports_credentials=True) # 解决跨域 diff --git a/pyminer_comm/__init__.py b/pyminer_comm/__init__.py index 5b0fd311c464d9f93b8a3739a14fdda8e43f743e..a611c6a401be4bb1e301433fdbb23ba14ca87770 100644 --- a/pyminer_comm/__init__.py +++ b/pyminer_comm/__init__.py @@ -3,7 +3,13 @@ # @Author: Zhanyi Hou # @Email: 1295752786@qq.com # @File: __init__.py.py +import logging -from pyminer_comm.pyminer_client import * from pyminer_comm.data_client import * +from pyminer_comm.pyminer_client import * +logging.basicConfig( + format="%(asctime)-15s %(name)-40s %(levelname)-8s %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + level=logging.INFO, +) diff --git a/requirements.txt b/requirements.txt index 45dd7f3790da85cc71eaf037ff5fb6c641db9c19..078381d7a90031cf388153ee66ae697d7f2b3ade 100644 --- a/requirements.txt +++ b/requirements.txt @@ -58,5 +58,5 @@ pyminer_comm>=0.7.1 ipyparams pathspec codegen - - +chardet +parso \ No newline at end of file diff --git a/tests/test_code_editor/__init__.py b/tests/test_code_editor/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/test_code_editor/run_main.py b/tests/test_code_editor/run_main.py new file mode 100644 index 0000000000000000000000000000000000000000..dce1ce118e7c2d0c08e4a12dbc4446bb6a1d5cef --- /dev/null +++ b/tests/test_code_editor/run_main.py @@ -0,0 +1,4 @@ +from app2 import main + +if __name__ == '__main__': + main(extensions=['code_editor'], show_welcome=False) diff --git a/tests/test_code_editor/test_grammer/__init__.py b/tests/test_code_editor/test_grammer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/test_code_editor/test_grammer/test_python/__init__.py b/tests/test_code_editor/test_grammer/test_python/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/test_code_editor/test_grammer/test_python/test_bracket.py b/tests/test_code_editor/test_grammer/test_python/test_bracket.py new file mode 100644 index 0000000000000000000000000000000000000000..2d5af0536dd1a7038bb0fa6ec690960798eb5665 --- /dev/null +++ b/tests/test_code_editor/test_grammer/test_python/test_bracket.py @@ -0,0 +1,91 @@ +from unittest import TestCase + +from packages.code_editor.codeeditor.grammar_analyzer import GrammarAnalyzer + +func_def_1 = """ +def add(x, y): + return x + y +print(add(3, 5) +""" + + +def test_convert_position_to_row_col(): + """ + PyQt得到的position是一个整数,需要将其进行转化,方可被parso识别。 + """ + analyzer = GrammarAnalyzer() + analyzer.feed(func_def_1) + assert analyzer._convert_position_to_row_col(22) == (3, 6) + assert analyzer._convert_position_to_row_col(46) == (4, 13) + assert analyzer._convert_position_to_row_col(47) == (4, 14) + + +class TestBracket(TestCase): + """ + 测试JEDI的括号识别功能 + """ + + def setUp(self) -> None: + self.analyzer = GrammarAnalyzer() + + def test_normal(self): + self.analyzer.feed(func_def_1) + assert self.analyzer.is_not_matched((4, 15)) + assert self.analyzer.is_not_matched((4, 14)) + assert self.analyzer.is_not_matched(47) + + def test_line_1(self): + line_1 = """print(a, b, c)""" + self.analyzer.feed(line_1) + assert not self.analyzer.is_not_matched((1, 13)) + + def test_line_2(self): + line_2 = """print(a, b, c""" + self.analyzer.feed(line_2) + assert self.analyzer.is_not_matched((1, 13)) + + def test_line_3(self): + # 这个用例有问题,parso无法识别,暂不处理 + line_3 = """[][]""" + self.analyzer.feed(line_3) + # assert not self.analyzer.is_not_matched((1, 3), left='[') + + def test_line_4(self): + line_4 = """[][""" + self.analyzer.feed(line_4) + assert self.analyzer.is_not_matched((1, 3), left='[') + + def test_code_5(self): + code = """ +a = [[1, 2, 3], [5, 6, 7]] +b = a[1][2] +""" + self.analyzer.feed(code) + assert not self.analyzer.is_not_matched((3, 8), left='[') + + def test_code_6(self): + code = """ +a = [[1, 2, 3], [5, 6, 7]] +b = a[1[2] +""" + self.analyzer.feed(code) + assert self.analyzer.is_not_matched((3, 8), left='[') + + def test_code_7(self): + code = """ +print(1, 2, +abc(), +""" + self.analyzer.feed(code) + assert self.analyzer.is_not_matched((3, 6)) + + def test_code_8(self): + code = """ +a = [1, 2, [ +3, 4, 5, [ +6, 7, [8, 9], +], +10, +""" + self.analyzer.feed(code) + assert self.analyzer.is_not_matched((6, 2), left='[') diff --git a/tests/test_jupyter_notebook_support/__init__.py b/tests/test_jupyter_notebook_support/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/test_jupyter_notebook_support/run_jupyter_notebook_support.py b/tests/test_jupyter_notebook_support/run_jupyter_notebook_support.py new file mode 100644 index 0000000000000000000000000000000000000000..50ec664335f149a4db41412ea030253c97ee7789 --- /dev/null +++ b/tests/test_jupyter_notebook_support/run_jupyter_notebook_support.py @@ -0,0 +1,4 @@ +from app2 import main + +if __name__ == '__main__': + main(extensions=['embedded_browser', 'code_editor'], show_welcome=False)