From ce72017406b1932e0a2effd3b8f2b97e9e43cdfa Mon Sep 17 00:00:00 2001 From: JE-Chen Date: Wed, 25 Mar 2026 20:44:03 +0800 Subject: [PATCH 1/2] Update both version * Add jupyterlab tab --- dev.toml | 9 +-- exe/auto_py_to_exe_setting.json | 16 ++--- exe/{automation_ide.ico => pybreeze_icon.ico} | Bin ...automation_editor.py => start_pybreeze.py} | 0 pybreeze/__init__.py | 4 +- .../api_testka/api_testka_process.py | 10 +-- .../auto_control/auto_control_process.py | 10 +-- .../file_automation_process.py | 10 +-- .../load_density/load_density_process.py | 10 +-- .../mail_thunder/mail_thunder_process.py | 4 +- .../process_executor_utils.py | 6 +- .../test_pioneer_process_manager.py | 8 +-- .../web_runner/web_runner_process.py | 10 +-- pybreeze/pybreeze_ui/editor_main/main_ui.py | 14 ++--- .../extend_multi_language/extend_english.py | 4 +- .../extend_traditional_chinese.py | 4 +- .../pybreeze_ui/jupyter_lab_gui/__init__.py | 0 .../jupyter_lab_gui/jupyer_lab_thread.py | 54 ++++++++++++++++ .../jupyter_lab_gui/jupyter_lab_widget.py | 58 ++++++++++++++++++ .../api_testka_menu/build_api_testka_menu.py | 6 +- .../build_autocontrol_menu.py | 8 +-- .../build_automation_file_menu.py | 4 +- .../build_load_density_menu.py | 6 +- .../build_mail_thunder_menu.py | 4 +- .../build_test_pioneer_menu.py | 6 +- .../web_runner_menu/build_webrunner_menu.py | 4 +- pybreeze/pybreeze_ui/menu/build_menubar.py | 28 +++++---- .../menu/extend_jeditor_tab_menu/__init__.py | 0 .../jupyter_lab_tab.py | 31 ++++++++++ .../build_automation_install_menu.py | 16 ++--- .../menu/install_menu/install_utils.py | 4 +- .../tools_menu/build_tool_install_menu.py | 6 +- pybreeze/pybreeze_ui/menu/menu_utils.py | 4 +- pybreeze/pybreeze_ui/menu/tools/tools_menu.py | 8 +-- pybreeze/pybreeze_ui/syntax/syntax_extend.py | 4 +- pybreeze/utils/logging/logger.py | 10 +-- stable.toml | 7 ++- 37 files changed, 270 insertions(+), 117 deletions(-) rename exe/{automation_ide.ico => pybreeze_icon.ico} (100%) rename exe/{start_automation_editor.py => start_pybreeze.py} (100%) create mode 100644 pybreeze/pybreeze_ui/jupyter_lab_gui/__init__.py create mode 100644 pybreeze/pybreeze_ui/jupyter_lab_gui/jupyer_lab_thread.py create mode 100644 pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py create mode 100644 pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/__init__.py create mode 100644 pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/jupyter_lab_tab.py diff --git a/dev.toml b/dev.toml index a46a08e..c605106 100644 --- a/dev.toml +++ b/dev.toml @@ -6,17 +6,18 @@ build-backend = "setuptools.build_meta" [project] name = "pybreeze_dev" -version = "1.0.5" +version = "1.0.8" authors = [ { name = "JE-Chen", email = "jechenmailman@gmail.com" }, ] -description = "AutomationEditor for multi automation" +description = "IDE for multi automation" requires-python = ">=3.10" license-files = ["LICENSE"] dependencies = [ - "je_editor_dev", "je_auto_control", "je_web_runner", + "je-editor", "je_auto_control", "je_web_runner", "je_load_density", "je_api_testka", "je-mail-thunder", - "automation-file", "PySide6==6.10.2", "test_pioneer", "paramiko" + "automation-file", "PySide6==6.11.0", "test_pioneer", "paramiko", + "jupyterlab", ] classifiers = [ "Programming Language :: Python :: 3.10", diff --git a/exe/auto_py_to_exe_setting.json b/exe/auto_py_to_exe_setting.json index f1a4bd8..3edc1c9 100644 --- a/exe/auto_py_to_exe_setting.json +++ b/exe/auto_py_to_exe_setting.json @@ -7,7 +7,7 @@ }, { "optionDest": "filenames", - "value": "C:/CodeWorkspace/Python/AutomationIDE/exe/start_automation_editor.py" + "value": "C:/CodeWorkspace/Python/PyBreeze/exe/start_pybreeze.py" }, { "optionDest": "onefile", @@ -19,11 +19,11 @@ }, { "optionDest": "icon_file", - "value": "C:/CodeWorkspace/Python/AutomationIDE/exe/automation_ide.ico" + "value": "C:/CodeWorkspace/Python/PyBreeze/exe/pybreeze_icon.ico" }, { "optionDest": "name", - "value": "AutomationIDE" + "value": "PyBreeze" }, { "optionDest": "clean_build", @@ -59,23 +59,23 @@ }, { "optionDest": "datas", - "value": "C:/CodeWorkspace/Python/AutomationIDE/.venv/Lib/site-packages/ipython-9.8.0.dist-info;ipython-9.8.0.dist-info/" + "value": "C:/CodeWorkspace/Python/PyBreeze/.venv/Lib/site-packages/ipython-9.11.0.dist-info;ipython-9.11.0.dist-info/" }, { "optionDest": "datas", - "value": "C:/CodeWorkspace/Python/AutomationIDE/.venv/Lib/site-packages/IPython;.IPython/" + "value": "C:/CodeWorkspace/Python/PyBreeze/.venv/Lib/site-packages/IPython;.IPython/" }, { "optionDest": "datas", - "value": "C:/CodeWorkspace/Python/AutomationIDE/.venv/Lib/site-packages/ipykernel-7.1.0.dist-info;ipykernel-7.1.0.dist-info/" + "value": "C:/CodeWorkspace/Python/PyBreeze/.venv/Lib/site-packages/ipykernel-7.2.0.dist-info;ipykernel-7.2.0.dist-info/" }, { "optionDest": "datas", - "value": "C:/CodeWorkspace/Python/AutomationIDE/.venv/Lib/site-packages/ipykernel;ipykernel/" + "value": "C:/CodeWorkspace/Python/PyBreeze/.venv/Lib/site-packages/ipykernel;ipykernel/" }, { "optionDest": "pathex", - "value": "C:/CodeWorkspace/Python/AutomationIDE/.venv" + "value": "C:/CodeWorkspace/Python/PyBreeze/.venv" } ], "nonPyinstallerOptions": { diff --git a/exe/automation_ide.ico b/exe/pybreeze_icon.ico similarity index 100% rename from exe/automation_ide.ico rename to exe/pybreeze_icon.ico diff --git a/exe/start_automation_editor.py b/exe/start_pybreeze.py similarity index 100% rename from exe/start_automation_editor.py rename to exe/start_pybreeze.py diff --git a/pybreeze/__init__.py b/pybreeze/__init__.py index f86eb83..80807f9 100644 --- a/pybreeze/__init__.py +++ b/pybreeze/__init__.py @@ -1,7 +1,7 @@ -from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor +from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow from pybreeze.pybreeze_ui.editor_main.main_ui import EDITOR_EXTEND_TAB from pybreeze.pybreeze_ui.editor_main.main_ui import start_editor __all__ = [ - "start_editor", "AutomationEditor", "EDITOR_EXTEND_TAB" + "start_editor", "PyBreezeMainWindow", "EDITOR_EXTEND_TAB" ] diff --git a/pybreeze/extend/process_executor/api_testka/api_testka_process.py b/pybreeze/extend/process_executor/api_testka/api_testka_process.py index fc2710c..4e227b7 100644 --- a/pybreeze/extend/process_executor/api_testka/api_testka_process.py +++ b/pybreeze/extend/process_executor/api_testka/api_testka_process.py @@ -5,14 +5,14 @@ from pybreeze.extend.process_executor.process_executor_utils import build_process if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from pybreeze.utils.file_process.get_dir_file_list import ask_and_get_dir_files_as_list def call_api_testka( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -20,7 +20,7 @@ def call_api_testka( def call_api_testka_with_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -28,7 +28,7 @@ def call_api_testka_with_send( def call_api_testka_multi_file( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: @@ -47,7 +47,7 @@ def call_api_testka_multi_file( def call_api_testka_multi_file_and_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: diff --git a/pybreeze/extend/process_executor/auto_control/auto_control_process.py b/pybreeze/extend/process_executor/auto_control/auto_control_process.py index 257df5a..ef6dcce 100644 --- a/pybreeze/extend/process_executor/auto_control/auto_control_process.py +++ b/pybreeze/extend/process_executor/auto_control/auto_control_process.py @@ -5,14 +5,14 @@ from pybreeze.extend.process_executor.process_executor_utils import build_process if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from pybreeze.utils.file_process.get_dir_file_list import ask_and_get_dir_files_as_list def call_auto_control( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -20,7 +20,7 @@ def call_auto_control( def call_auto_control_with_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -28,7 +28,7 @@ def call_auto_control_with_send( def call_auto_control_multi_file( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): need_to_execute_list: list = ask_and_get_dir_files_as_list(main_window) @@ -44,7 +44,7 @@ def call_auto_control_multi_file( def call_auto_control_multi_file_and_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: diff --git a/pybreeze/extend/process_executor/file_automation/file_automation_process.py b/pybreeze/extend/process_executor/file_automation/file_automation_process.py index 1a4be92..02ca8bf 100644 --- a/pybreeze/extend/process_executor/file_automation/file_automation_process.py +++ b/pybreeze/extend/process_executor/file_automation/file_automation_process.py @@ -5,14 +5,14 @@ from pybreeze.extend.process_executor.process_executor_utils import build_process if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from pybreeze.utils.file_process.get_dir_file_list import ask_and_get_dir_files_as_list def call_file_automation_test( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -20,7 +20,7 @@ def call_file_automation_test( def call_file_automation_test_with_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -28,7 +28,7 @@ def call_file_automation_test_with_send( def call_file_automation_test_multi_file( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: @@ -47,7 +47,7 @@ def call_file_automation_test_multi_file( def call_file_automation_test_multi_file_and_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: diff --git a/pybreeze/extend/process_executor/load_density/load_density_process.py b/pybreeze/extend/process_executor/load_density/load_density_process.py index 553241b..41b6c30 100644 --- a/pybreeze/extend/process_executor/load_density/load_density_process.py +++ b/pybreeze/extend/process_executor/load_density/load_density_process.py @@ -5,14 +5,14 @@ from pybreeze.extend.process_executor.process_executor_utils import build_process if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from pybreeze.utils.file_process.get_dir_file_list import ask_and_get_dir_files_as_list def call_load_density( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -20,7 +20,7 @@ def call_load_density( def call_load_density_with_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -28,7 +28,7 @@ def call_load_density_with_send( def call_load_density_multi_file( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: @@ -47,7 +47,7 @@ def call_load_density_multi_file( def call_load_density_multi_file_and_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: diff --git a/pybreeze/extend/process_executor/mail_thunder/mail_thunder_process.py b/pybreeze/extend/process_executor/mail_thunder/mail_thunder_process.py index dfcd561..0e5f24a 100644 --- a/pybreeze/extend/process_executor/mail_thunder/mail_thunder_process.py +++ b/pybreeze/extend/process_executor/mail_thunder/mail_thunder_process.py @@ -5,11 +5,11 @@ from pybreeze.extend.process_executor.process_executor_utils import build_process if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow def call_mail_thunder( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): diff --git a/pybreeze/extend/process_executor/process_executor_utils.py b/pybreeze/extend/process_executor/process_executor_utils.py index 62c9300..ea7093a 100644 --- a/pybreeze/extend/process_executor/process_executor_utils.py +++ b/pybreeze/extend/process_executor/process_executor_utils.py @@ -13,11 +13,11 @@ from pybreeze.utils.exception.exceptions import ITETestExecutorException if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow def build_process( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, package: str, exec_str: Union[str, None] = None, send_mail: bool = False, @@ -42,7 +42,7 @@ def build_process( def start_process( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, package: str, test_format_code: str, send_mail: bool = False, diff --git a/pybreeze/extend/process_executor/test_pioneer/test_pioneer_process_manager.py b/pybreeze/extend/process_executor/test_pioneer/test_pioneer_process_manager.py index e843c8a..3f79f85 100644 --- a/pybreeze/extend/process_executor/test_pioneer/test_pioneer_process_manager.py +++ b/pybreeze/extend/process_executor/test_pioneer/test_pioneer_process_manager.py @@ -17,19 +17,19 @@ from pybreeze.pybreeze_ui.show_code_window.code_window import CodeWindow if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow class TestPioneerProcess(object): def __init__( self, - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, executable_path: str, program_buffer: int = 1024000, encoding: str = "utf-8", ): - self._main_window: AutomationEditor = main_window + self._main_window: PyBreezeMainWindow = main_window self._widget: QWidget = main_window.tab_widget.currentWidget() # Code window init self._code_window = CodeWindow() @@ -175,7 +175,7 @@ def start_test_pioneer_process(self): self._timer.start() -def init_and_start_test_pioneer_process(ui_we_want_to_set: AutomationEditor, file_path: str): +def init_and_start_test_pioneer_process(ui_we_want_to_set: PyBreezeMainWindow, file_path: str): test_pioneer_process_manager = TestPioneerProcess( main_window=ui_we_want_to_set, executable_path=file_path) test_pioneer_process_manager.start_test_pioneer_process() diff --git a/pybreeze/extend/process_executor/web_runner/web_runner_process.py b/pybreeze/extend/process_executor/web_runner/web_runner_process.py index 4a24d68..7d468d6 100644 --- a/pybreeze/extend/process_executor/web_runner/web_runner_process.py +++ b/pybreeze/extend/process_executor/web_runner/web_runner_process.py @@ -5,14 +5,14 @@ from pybreeze.extend.process_executor.process_executor_utils import build_process if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from pybreeze.utils.file_process.get_dir_file_list import ask_and_get_dir_files_as_list def call_web_runner_test( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -20,7 +20,7 @@ def call_web_runner_test( def call_web_runner_test_with_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, exec_str: Union[str, None] = None, program_buffer: int = 1024000 ): @@ -28,7 +28,7 @@ def call_web_runner_test_with_send( def call_web_runner_test_multi_file( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: @@ -47,7 +47,7 @@ def call_web_runner_test_multi_file( def call_web_runner_test_multi_file_and_send( - main_window: AutomationEditor, + main_window: PyBreezeMainWindow, program_buffer: int = 1024000 ): try: diff --git a/pybreeze/pybreeze_ui/editor_main/main_ui.py b/pybreeze/pybreeze_ui/editor_main/main_ui.py index 7cfdc54..7f80f74 100644 --- a/pybreeze/pybreeze_ui/editor_main/main_ui.py +++ b/pybreeze/pybreeze_ui/editor_main/main_ui.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import List, Dict, Type -from pybreeze.utils.logging.logger import automation_ide_logger +from pybreeze.utils.logging.logger import pybreeze_logger environ["LOCUST_SKIP_MONKEY_PATCH"] = "1" @@ -24,7 +24,7 @@ } -class AutomationEditor(EditorMain): +class PyBreezeMainWindow(EditorMain): def __init__(self, debug_mode: bool = False, show_system_tray_ray: bool = False, extend: bool = False) -> None: super().__init__(debug_mode, show_system_tray_ray, extend=True) @@ -40,20 +40,20 @@ def __init__(self, debug_mode: bool = False, show_system_tray_ray: bool = False, update_language_dict() # Title - self.setWindowTitle(language_wrapper.language_word_dict.get("automation_editor_application_name")) - self.setToolTip(language_wrapper.language_word_dict.get("automation_editor_application_name")) + self.setWindowTitle(language_wrapper.language_word_dict.get("application_name")) + self.setToolTip(language_wrapper.language_word_dict.get("application_name")) # Windows 系統專用:設定應用程式 ID # Windows only: set application ID if not extend: - self.id = language_wrapper.language_word_dict.get("automation_editor_application_name") + self.id = language_wrapper.language_word_dict.get("application_name") if sys.platform in ["win32", "cygwin", "msys"]: from ctypes import windll windll.shell32.SetCurrentProcessExplicitAppUserModelID(self.id) # Icon if not extend: - self.icon_path = Path(os.getcwd() + "/pybreeze.ico") + self.icon_path = Path(os.getcwd() + "/pybreeze_icon.ico") self.icon = QIcon(str(self.icon_path)) if not self.icon.isNull(): self.setWindowIcon(self.icon) @@ -94,7 +94,7 @@ def start_editor(debug_mode: bool = False, **kwargs) -> None: new_ide = QCoreApplication.instance() if new_ide is None: new_ide = QApplication(sys.argv) - window = AutomationEditor(debug_mode=debug_mode, **kwargs) + window = PyBreezeMainWindow(debug_mode=debug_mode, **kwargs) apply_stylesheet(new_ide, theme="dark_amber.xml") window.showMaximized() try: diff --git a/pybreeze/pybreeze_ui/extend_multi_language/extend_english.py b/pybreeze/pybreeze_ui/extend_multi_language/extend_english.py index f0d9fb5..035419b 100644 --- a/pybreeze/pybreeze_ui/extend_multi_language/extend_english.py +++ b/pybreeze/pybreeze_ui/extend_multi_language/extend_english.py @@ -5,7 +5,7 @@ def update_english_word_dict(): english_word_dict.update( { # application name - "automation_editor_application_name": "Automation Editor", + "application_name": "PyBreeze", # Menubar "automation_menu_label": "Automation", "install_menu_label": "Install", @@ -13,6 +13,8 @@ def update_english_word_dict(): "run_label": "Run", "help_label": "HELP", "project_label": "Project", + # Tab tools menu + "tab_menu_jupyterlab_tab_name": "JupyterLab", # APITestka Menu "apitestka_menu_label": "APITestka", "apitestka_run_script_label": "Run APITestka Script", diff --git a/pybreeze/pybreeze_ui/extend_multi_language/extend_traditional_chinese.py b/pybreeze/pybreeze_ui/extend_multi_language/extend_traditional_chinese.py index 820b751..ee01a56 100644 --- a/pybreeze/pybreeze_ui/extend_multi_language/extend_traditional_chinese.py +++ b/pybreeze/pybreeze_ui/extend_multi_language/extend_traditional_chinese.py @@ -5,7 +5,7 @@ def update_traditional_chinese_word_dict(): traditional_chinese_word_dict.update( { # application name - "automation_editor_application_name": "Automation Editor", + "application_name": "PyBreeze", # Menubar "automation_menu_label": "自動化", "install_menu_label": "安裝", @@ -13,6 +13,8 @@ def update_traditional_chinese_word_dict(): "run_label": "運行", "help_label": "幫助", "project_label": "專案", + # Tab tools menu + "tab_menu_jupyterlab_tab_name": "JupyterLab", # APITestka Menu "apitestka_menu_label": "APITestka", "apitestka_run_script_label": "運行 APITestka 腳本", diff --git a/pybreeze/pybreeze_ui/jupyter_lab_gui/__init__.py b/pybreeze/pybreeze_ui/jupyter_lab_gui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyer_lab_thread.py b/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyer_lab_thread.py new file mode 100644 index 0000000..f1940d7 --- /dev/null +++ b/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyer_lab_thread.py @@ -0,0 +1,54 @@ +import socket +import subprocess +import sys +from PySide6.QtCore import QThread, Signal +import time + + +def find_free_port(): + s = socket.socket() + s.bind(("", 0)) + port = s.getsockname()[1] + s.close() + return port + + +class JupyterServerThread(QThread): + server_ready = Signal(str) + + def __init__(self): + super().__init__() + self.process = None + + def run(self): + port = find_free_port() + + cmd = [ + sys.executable, + "-m", + "jupyterlab", + "--no-browser", + f"--ServerApp.port={port}", + "--ServerApp.token=", + "--ServerApp.password=", + "--ServerApp.allow_origin=*", + "--ServerApp.disable_check_xsrf=True", + ] + + self.process = subprocess.Popen(cmd) + + # 輪詢 port,直到可連 + while True: + try: + s = socket.create_connection(("localhost", port), timeout=0.5) + s.close() + break + except OSError: + time.sleep(0.1) + + # Server ready,發射 signal + self.server_ready.emit(f"http://localhost:{port}/lab") + + def stop(self): + if self.process: + self.process.terminate() \ No newline at end of file diff --git a/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py b/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py new file mode 100644 index 0000000..a151cfa --- /dev/null +++ b/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py @@ -0,0 +1,58 @@ +import sys + +from PySide6.QtCore import QUrl +from PySide6.QtWebEngineWidgets import QWebEngineView +from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget + +from pybreeze.pybreeze_ui.jupyter_lab_gui.jupyer_lab_thread import JupyterServerThread + + +class JupyterLabWidget(QWidget): + + def __init__(self): + super().__init__() + + layout = QVBoxLayout(self) + + self.browser = QWebEngineView() + layout.addWidget(self.browser) + + # 啟動 Jupyter server thread + self.thread = JupyterServerThread() + self.thread.server_ready.connect(self.load_lab) + self.thread.start() + + def load_lab(self, url: str): + """Load JupyterLab URL into WebEngine""" + + if not url: + print("Invalid JupyterLab URL") + return + + print("JupyterLab running at:", url) + + qurl = QUrl(url) + + if not qurl.isValid(): + print("Invalid QUrl:", url) + return + + self.browser.setUrl(qurl) + + def closeEvent(self, event): + + if hasattr(self, "thread") and self.thread.isRunning(): + self.thread.stop() + self.thread.quit() + self.thread.wait() + + event.accept() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + + win = JupyterLabWidget() + win.show() + + sys.exit(app.exec()) diff --git a/pybreeze/pybreeze_ui/menu/automation_menu/api_testka_menu/build_api_testka_menu.py b/pybreeze/pybreeze_ui/menu/automation_menu/api_testka_menu/build_api_testka_menu.py index fd63d7b..80895a2 100644 --- a/pybreeze/pybreeze_ui/menu/automation_menu/api_testka_menu/build_api_testka_menu.py +++ b/pybreeze/pybreeze_ui/menu/automation_menu/api_testka_menu/build_api_testka_menu.py @@ -8,7 +8,7 @@ from pybreeze.pybreeze_ui.menu.menu_utils import open_web_browser if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from PySide6.QtGui import QAction @@ -17,7 +17,7 @@ call_api_testka_with_send, call_api_testka_multi_file, call_api_testka_multi_file_and_send -def set_apitestka_menu(ui_we_want_to_set: AutomationEditor): +def set_apitestka_menu(ui_we_want_to_set: PyBreezeMainWindow): """ Build menu include APITestka feature. :param ui_we_want_to_set: main window to add menu. @@ -131,7 +131,7 @@ def create_project() -> None: print(repr(error), file=sys.stderr) -def add_api_testka_gui(ui_we_want_to_set: AutomationEditor) -> None: +def add_api_testka_gui(ui_we_want_to_set: PyBreezeMainWindow) -> None: ui_we_want_to_set.tab_widget.addTab( APITestkaWidget(), "APITestka GUI" ) diff --git a/pybreeze/pybreeze_ui/menu/automation_menu/auto_control_menu/build_autocontrol_menu.py b/pybreeze/pybreeze_ui/menu/automation_menu/auto_control_menu/build_autocontrol_menu.py index 0f7a7f5..20511fa 100644 --- a/pybreeze/pybreeze_ui/menu/automation_menu/auto_control_menu/build_autocontrol_menu.py +++ b/pybreeze/pybreeze_ui/menu/automation_menu/auto_control_menu/build_autocontrol_menu.py @@ -9,7 +9,7 @@ from pybreeze.pybreeze_ui.menu.menu_utils import open_web_browser if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys import je_auto_control @@ -20,7 +20,7 @@ call_auto_control_multi_file_and_send -def set_autocontrol_menu(ui_we_want_to_set: AutomationEditor): +def set_autocontrol_menu(ui_we_want_to_set: PyBreezeMainWindow): """ Build menu include AutoControl feature. :param ui_we_want_to_set: main window to add menu. @@ -153,7 +153,7 @@ def create_project() -> None: print(repr(error), file=sys.stderr) -def stop_record(editor_instance: AutomationEditor): +def stop_record(editor_instance: PyBreezeMainWindow): widget = editor_instance.tab_widget.currentWidget() if isinstance(widget, EditorWidget): text_cursor = widget.code_edit.textCursor() @@ -163,7 +163,7 @@ def stop_record(editor_instance: AutomationEditor): text_cursor.insertBlock() -def add_autocontrol_gui(ui_we_want_to_set: AutomationEditor) -> None: +def add_autocontrol_gui(ui_we_want_to_set: PyBreezeMainWindow) -> None: ui_we_want_to_set.tab_widget.addTab( AutoControlGUIWidget(), "AutoControl GUI" ) diff --git a/pybreeze/pybreeze_ui/menu/automation_menu/automation_file_menu/build_automation_file_menu.py b/pybreeze/pybreeze_ui/menu/automation_menu/automation_file_menu/build_automation_file_menu.py index 26fbfb5..2e64537 100644 --- a/pybreeze/pybreeze_ui/menu/automation_menu/automation_file_menu/build_automation_file_menu.py +++ b/pybreeze/pybreeze_ui/menu/automation_menu/automation_file_menu/build_automation_file_menu.py @@ -7,7 +7,7 @@ from pybreeze.pybreeze_ui.menu.menu_utils import open_web_browser if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from PySide6.QtGui import QAction @@ -17,7 +17,7 @@ call_file_automation_test_multi_file_and_send -def set_automation_file_menu(ui_we_want_to_set: AutomationEditor): +def set_automation_file_menu(ui_we_want_to_set: PyBreezeMainWindow): """ Build menu include WebRunner feature. :param ui_we_want_to_set: main window to add menu. diff --git a/pybreeze/pybreeze_ui/menu/automation_menu/load_density_menu/build_load_density_menu.py b/pybreeze/pybreeze_ui/menu/automation_menu/load_density_menu/build_load_density_menu.py index afb0421..8b05194 100644 --- a/pybreeze/pybreeze_ui/menu/automation_menu/load_density_menu/build_load_density_menu.py +++ b/pybreeze/pybreeze_ui/menu/automation_menu/load_density_menu/build_load_density_menu.py @@ -8,7 +8,7 @@ from pybreeze.pybreeze_ui.menu.menu_utils import open_web_browser if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from PySide6.QtGui import QAction @@ -18,7 +18,7 @@ call_load_density_multi_file_and_send -def set_load_density_menu(ui_we_want_to_set: AutomationEditor): +def set_load_density_menu(ui_we_want_to_set: PyBreezeMainWindow): """ Build menu include LoadDensity feature. :param ui_we_want_to_set: main window to add menu. @@ -131,7 +131,7 @@ def create_project() -> None: print(repr(error), file=sys.stderr) -def add_load_density_gui(ui_we_want_to_set: AutomationEditor) -> None: +def add_load_density_gui(ui_we_want_to_set: PyBreezeMainWindow) -> None: ui_we_want_to_set.tab_widget.addTab( LoadDensityWidget(), "LoadDensity GUI" ) diff --git a/pybreeze/pybreeze_ui/menu/automation_menu/mail_thunder_menu/build_mail_thunder_menu.py b/pybreeze/pybreeze_ui/menu/automation_menu/mail_thunder_menu/build_mail_thunder_menu.py index cb30cbe..dd78517 100644 --- a/pybreeze/pybreeze_ui/menu/automation_menu/mail_thunder_menu/build_mail_thunder_menu.py +++ b/pybreeze/pybreeze_ui/menu/automation_menu/mail_thunder_menu/build_mail_thunder_menu.py @@ -7,7 +7,7 @@ from pybreeze.pybreeze_ui.menu.menu_utils import open_web_browser if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from PySide6.QtGui import QAction @@ -15,7 +15,7 @@ from pybreeze.extend.process_executor.mail_thunder.mail_thunder_process import call_mail_thunder -def set_mail_thunder_menu(ui_we_want_to_set: AutomationEditor): +def set_mail_thunder_menu(ui_we_want_to_set: PyBreezeMainWindow): """ Build menu include LoadDensity feature. :param ui_we_want_to_set: main window to add menu. diff --git a/pybreeze/pybreeze_ui/menu/automation_menu/test_pioneer_menu/build_test_pioneer_menu.py b/pybreeze/pybreeze_ui/menu/automation_menu/test_pioneer_menu/build_test_pioneer_menu.py index f114c5f..d458e6c 100644 --- a/pybreeze/pybreeze_ui/menu/automation_menu/test_pioneer_menu/build_test_pioneer_menu.py +++ b/pybreeze/pybreeze_ui/menu/automation_menu/test_pioneer_menu/build_test_pioneer_menu.py @@ -9,14 +9,14 @@ init_and_start_test_pioneer_process if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow from PySide6.QtGui import QAction from je_editor import language_wrapper from test_pioneer import create_template_dir -def set_test_pioneer_menu(ui_we_want_to_set: AutomationEditor): +def set_test_pioneer_menu(ui_we_want_to_set: PyBreezeMainWindow): """ Build menu include Test Pioneer feature. :param ui_we_want_to_set: main window to add menu. @@ -44,7 +44,7 @@ def set_test_pioneer_menu(ui_we_want_to_set: AutomationEditor): ) -def check_file(ui_we_want_to_set: AutomationEditor): +def check_file(ui_we_want_to_set: PyBreezeMainWindow): dialog = QFileDialog(ui_we_want_to_set) dialog.setNameFilter("Yaml (*.yml)") file_tuple = dialog.getOpenFileName() diff --git a/pybreeze/pybreeze_ui/menu/automation_menu/web_runner_menu/build_webrunner_menu.py b/pybreeze/pybreeze_ui/menu/automation_menu/web_runner_menu/build_webrunner_menu.py index 591d28f..f1733e7 100644 --- a/pybreeze/pybreeze_ui/menu/automation_menu/web_runner_menu/build_webrunner_menu.py +++ b/pybreeze/pybreeze_ui/menu/automation_menu/web_runner_menu/build_webrunner_menu.py @@ -7,7 +7,7 @@ from pybreeze.pybreeze_ui.menu.menu_utils import open_web_browser if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow import sys from PySide6.QtGui import QAction @@ -16,7 +16,7 @@ call_web_runner_test_with_send, call_web_runner_test_multi_file, call_web_runner_test_multi_file_and_send -def set_web_runner_menu(ui_we_want_to_set: AutomationEditor): +def set_web_runner_menu(ui_we_want_to_set: PyBreezeMainWindow): """ Build menu include WebRunner feature. :param ui_we_want_to_set: main window to add menu. diff --git a/pybreeze/pybreeze_ui/menu/build_menubar.py b/pybreeze/pybreeze_ui/menu/build_menubar.py index 38cc998..b5de5cd 100644 --- a/pybreeze/pybreeze_ui/menu/build_menubar.py +++ b/pybreeze/pybreeze_ui/menu/build_menubar.py @@ -2,10 +2,11 @@ from typing import TYPE_CHECKING +from pybreeze.pybreeze_ui.menu.extend_jeditor_tab_menu.jupyter_lab_tab import extend_tab_tools_menu from pybreeze.pybreeze_ui.menu.tools.tools_menu import build_tools_menu, extend_dock_menu if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow from pybreeze.pybreeze_ui.menu.automation_menu.api_testka_menu.build_api_testka_menu import \ set_apitestka_menu @@ -29,19 +30,20 @@ from je_editor import language_wrapper -def add_menu_to_menubar(ui_we_want_to_set: AutomationEditor): +def add_menu_to_menubar(ui_we_want_to_set: PyBreezeMainWindow): ui_we_want_to_set.automation_menu = ui_we_want_to_set.menu.addMenu( language_wrapper.language_word_dict.get("automation_menu_label")) ui_we_want_to_set.install_menu = ui_we_want_to_set.menu.addMenu( language_wrapper.language_word_dict.get("install_menu_label")) - set_apitestka_menu(ui_we_want_to_set) - set_autocontrol_menu(ui_we_want_to_set) - set_automation_file_menu(ui_we_want_to_set) - set_load_density_menu(ui_we_want_to_set) - set_mail_thunder_menu(ui_we_want_to_set) - set_web_runner_menu(ui_we_want_to_set) - set_test_pioneer_menu(ui_we_want_to_set) - build_automation_install_menu(ui_we_want_to_set) - build_tool_install_menu(ui_we_want_to_set) - build_tools_menu(ui_we_want_to_set) - extend_dock_menu(ui_we_want_to_set) + set_apitestka_menu(ui_we_want_to_set=ui_we_want_to_set) + set_autocontrol_menu(ui_we_want_to_set=ui_we_want_to_set) + set_automation_file_menu(ui_we_want_to_set=ui_we_want_to_set) + set_load_density_menu(ui_we_want_to_set=ui_we_want_to_set) + set_mail_thunder_menu(ui_we_want_to_set=ui_we_want_to_set) + set_web_runner_menu(ui_we_want_to_set=ui_we_want_to_set) + set_test_pioneer_menu(ui_we_want_to_set=ui_we_want_to_set) + build_automation_install_menu(ui_we_want_to_set=ui_we_want_to_set) + build_tool_install_menu(ui_we_want_to_set=ui_we_want_to_set) + build_tools_menu(ui_we_want_to_set=ui_we_want_to_set) + extend_dock_menu(ui_we_want_to_set=ui_we_want_to_set) + extend_tab_tools_menu(ui_we_want_to_set=ui_we_want_to_set) diff --git a/pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/__init__.py b/pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/jupyter_lab_tab.py b/pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/jupyter_lab_tab.py new file mode 100644 index 0000000..f262101 --- /dev/null +++ b/pybreeze/pybreeze_ui/menu/extend_jeditor_tab_menu/jupyter_lab_tab.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import je_editor +from PySide6.QtGui import QAction +from PySide6.QtWidgets import QMenu +from je_editor import language_wrapper + +from pybreeze.pybreeze_ui.jupyter_lab_gui.jupyter_lab_widget import JupyterLabWidget +from pybreeze.utils.logging.logger import pybreeze_logger + +if TYPE_CHECKING: + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow + +def extend_tab_tools_menu(ui_we_want_to_set: PyBreezeMainWindow): + jeditor_tab_menu: QMenu = ui_we_want_to_set.tab_menu.tools_menu + + jeditor_tab_menu.add_jupyterlab_action = QAction( + language_wrapper.language_word_dict.get("tab_menu_jupyterlab_tab_name")) + jeditor_tab_menu.add_jupyterlab_action.triggered.connect( + lambda: add_jupyterlab_tab(ui_we_want_to_set) + ) + jeditor_tab_menu.addAction(jeditor_tab_menu.add_jupyterlab_action) + +def add_jupyterlab_tab(ui_we_want_to_set: PyBreezeMainWindow): + pybreeze_logger.info(f"jupyter_lab_tab.py add jupyter tab ui_we_want_to_set: {ui_we_want_to_set}") + ui_we_want_to_set.tab_widget.addTab( + JupyterLabWidget(), + f"{language_wrapper.language_word_dict.get('tab_menu_jupyterlab_tab_name')} " + f"{ui_we_want_to_set.tab_widget.count()}") \ No newline at end of file diff --git a/pybreeze/pybreeze_ui/menu/install_menu/automation_menu/build_automation_install_menu.py b/pybreeze/pybreeze_ui/menu/install_menu/automation_menu/build_automation_install_menu.py index 34bf3ae..8224ab7 100644 --- a/pybreeze/pybreeze_ui/menu/install_menu/automation_menu/build_automation_install_menu.py +++ b/pybreeze/pybreeze_ui/menu/install_menu/automation_menu/build_automation_install_menu.py @@ -8,10 +8,10 @@ from pybreeze.pybreeze_ui.menu.install_menu.install_utils import install_package if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow -def build_automation_install_menu(ui_we_want_to_set: AutomationEditor): +def build_automation_install_menu(ui_we_want_to_set: PyBreezeMainWindow): ui_we_want_to_set.install_automation_menu = ui_we_want_to_set.install_menu.addMenu( language_wrapper.language_word_dict.get("automation_menu_label")) # Try to install AutoControl @@ -58,25 +58,25 @@ def build_automation_install_menu(ui_we_want_to_set: AutomationEditor): ui_we_want_to_set.install_automation_menu.addAction(ui_we_want_to_set.install_mail_thunder_action) -def install_autocontrol(ui_we_want_to_set: AutomationEditor) -> None: +def install_autocontrol(ui_we_want_to_set: PyBreezeMainWindow) -> None: install_package("je_auto_control", ui_we_want_to_set) -def install_api_testka(ui_we_want_to_set: AutomationEditor) -> None: +def install_api_testka(ui_we_want_to_set: PyBreezeMainWindow) -> None: install_package("je_api_testka", ui_we_want_to_set) -def install_load_density(ui_we_want_to_set: AutomationEditor) -> None: +def install_load_density(ui_we_want_to_set: PyBreezeMainWindow) -> None: install_package("je_load_density", ui_we_want_to_set) -def install_web_runner(ui_we_want_to_set: AutomationEditor) -> None: +def install_web_runner(ui_we_want_to_set: PyBreezeMainWindow) -> None: install_package("je_web_runner", ui_we_want_to_set) -def install_automation_file(ui_we_want_to_set: AutomationEditor) -> None: +def install_automation_file(ui_we_want_to_set: PyBreezeMainWindow) -> None: install_package("automation_file", ui_we_want_to_set) -def install_mail_thunder_file(ui_we_want_to_set: AutomationEditor) -> None: +def install_mail_thunder_file(ui_we_want_to_set: PyBreezeMainWindow) -> None: install_package("je_mail_thunder", ui_we_want_to_set) diff --git a/pybreeze/pybreeze_ui/menu/install_menu/install_utils.py b/pybreeze/pybreeze_ui/menu/install_menu/install_utils.py index 62f29ca..ac797db 100644 --- a/pybreeze/pybreeze_ui/menu/install_menu/install_utils.py +++ b/pybreeze/pybreeze_ui/menu/install_menu/install_utils.py @@ -5,10 +5,10 @@ from je_editor import EditorWidget, ShellManager if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow -def install_package(package_text: str, ui_we_want_to_set: AutomationEditor) -> None: +def install_package(package_text: str, ui_we_want_to_set: PyBreezeMainWindow) -> None: widget = ui_we_want_to_set.tab_widget.currentWidget() if isinstance(widget, EditorWidget): widget.python_compiler = ui_we_want_to_set.python_compiler diff --git a/pybreeze/pybreeze_ui/menu/install_menu/tools_menu/build_tool_install_menu.py b/pybreeze/pybreeze_ui/menu/install_menu/tools_menu/build_tool_install_menu.py index 0fed456..77ffc84 100644 --- a/pybreeze/pybreeze_ui/menu/install_menu/tools_menu/build_tool_install_menu.py +++ b/pybreeze/pybreeze_ui/menu/install_menu/tools_menu/build_tool_install_menu.py @@ -8,10 +8,10 @@ from pybreeze.pybreeze_ui.menu.install_menu.install_utils import install_package if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow -def build_tool_install_menu(ui_we_want_to_set: AutomationEditor): +def build_tool_install_menu(ui_we_want_to_set: PyBreezeMainWindow): ui_we_want_to_set.install_tools_menu = ui_we_want_to_set.install_menu.addMenu( language_wrapper.language_word_dict.get("install_menu_tools_install_menu_label")) # Try to install Build Tools @@ -23,7 +23,7 @@ def build_tool_install_menu(ui_we_want_to_set: AutomationEditor): ui_we_want_to_set.install_tools_menu.addAction(ui_we_want_to_set.install_tool_action) -def install_build_tools(ui_we_want_to_set: AutomationEditor) -> None: +def install_build_tools(ui_we_want_to_set: PyBreezeMainWindow) -> None: install_package("setuptools", ui_we_want_to_set) install_package("build", ui_we_want_to_set) install_package("wheel", ui_we_want_to_set) diff --git a/pybreeze/pybreeze_ui/menu/menu_utils.py b/pybreeze/pybreeze_ui/menu/menu_utils.py index f9bca4c..742378d 100644 --- a/pybreeze/pybreeze_ui/menu/menu_utils.py +++ b/pybreeze/pybreeze_ui/menu/menu_utils.py @@ -5,11 +5,11 @@ from je_editor import MainBrowserWidget if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow def open_web_browser( - automation_editor_instance: AutomationEditor, url: str, tab_name: str) -> None: + automation_editor_instance: PyBreezeMainWindow, url: str, tab_name: str) -> None: automation_editor_instance.tab_widget.addTab( MainBrowserWidget(start_url=url), f"{tab_name}{automation_editor_instance.tab_widget.count()}" diff --git a/pybreeze/pybreeze_ui/menu/tools/tools_menu.py b/pybreeze/pybreeze_ui/menu/tools/tools_menu.py index 6edfa05..36c0bad 100644 --- a/pybreeze/pybreeze_ui/menu/tools/tools_menu.py +++ b/pybreeze/pybreeze_ui/menu/tools/tools_menu.py @@ -15,10 +15,10 @@ from pybreeze.pybreeze_ui.extend_ai_gui.skills.skills_send_gui import SkillsSendGUI if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow -def build_tools_menu(ui_we_want_to_set: AutomationEditor): +def build_tools_menu(ui_we_want_to_set: PyBreezeMainWindow): # Menus ui_we_want_to_set.tools_menu = ui_we_want_to_set.menu.addMenu(language_wrapper.language_word_dict.get( "extend_tools_menu_tools_menu" @@ -79,7 +79,7 @@ def build_tools_menu(ui_we_want_to_set: AutomationEditor): ui_we_want_to_set.tools_ai_menu.addAction(ui_we_want_to_set.tools_ai_skill_send_action) -def extend_dock_menu(ui_we_want_to_set: AutomationEditor): +def extend_dock_menu(ui_we_want_to_set: PyBreezeMainWindow): # Sub menu ui_we_want_to_set.dock_ssh_menu = ui_we_want_to_set.dock_menu.addMenu( language_wrapper.language_word_dict.get("extend_tools_menu_dock_ssh_menu") @@ -121,7 +121,7 @@ def extend_dock_menu(ui_we_want_to_set: AutomationEditor): lambda: add_dock(ui_we_want_to_set, "SkillSendGUI")) ui_we_want_to_set.dock_ai_menu.addAction(ui_we_want_to_set.tools_skill_send_dock_action) -def add_dock(ui_we_want_to_set: AutomationEditor, widget_type: str = None): +def add_dock(ui_we_want_to_set: PyBreezeMainWindow, widget_type: str = None): jeditor_logger.info("build_dock_menu.py add_dock_widget " f"ui_we_want_to_set: {ui_we_want_to_set} " f"widget_type: {widget_type}") diff --git a/pybreeze/pybreeze_ui/syntax/syntax_extend.py b/pybreeze/pybreeze_ui/syntax/syntax_extend.py index 547681c..712feae 100644 --- a/pybreeze/pybreeze_ui/syntax/syntax_extend.py +++ b/pybreeze/pybreeze_ui/syntax/syntax_extend.py @@ -5,7 +5,7 @@ from je_editor import EditorWidget if TYPE_CHECKING: - from pybreeze.pybreeze_ui.editor_main.main_ui import AutomationEditor + from pybreeze.pybreeze_ui.editor_main.main_ui import PyBreezeMainWindow from PySide6.QtGui import QColor from pybreeze.pybreeze_ui.syntax.syntax_keyword import \ @@ -15,7 +15,7 @@ from je_editor import syntax_extend_setting_dict -def syntax_extend_package(main_window: AutomationEditor) -> None: +def syntax_extend_package(main_window: PyBreezeMainWindow) -> None: syntax_extend_setting_dict.update({".json": {}}) for package in package_manager.syntax_check_list: syntax_extend_setting_dict.get(".json").update( diff --git a/pybreeze/utils/logging/logger.py b/pybreeze/utils/logging/logger.py index 962763c..2b9acb3 100644 --- a/pybreeze/utils/logging/logger.py +++ b/pybreeze/utils/logging/logger.py @@ -1,11 +1,13 @@ import logging from logging.handlers import RotatingFileHandler +import pybreeze + # 設定 root logger 等級 Set root logger level logging.root.setLevel(logging.DEBUG) # 建立 AutoControlGUI 專用 logger Create dedicated logger -automation_ide_logger = logging.getLogger("AutomationIDE") +pybreeze_logger = logging.getLogger("AutomationIDE") # 日誌格式 Formatter formatter = logging.Formatter( @@ -13,7 +15,7 @@ ) -class AutomationIDELogger(RotatingFileHandler): +class PyBreezeLogger(RotatingFileHandler): """ AutoControlGUILoggingHandler 自訂日誌處理器,繼承 RotatingFileHandler @@ -46,5 +48,5 @@ def emit(self, record: logging.LogRecord) -> None: # 建立並加入檔案處理器 Add file handler to logger -file_handler = AutomationIDELogger() -automation_ide_logger.addHandler(file_handler) +file_handler = PyBreezeLogger() +pybreeze_logger.addHandler(file_handler) diff --git a/stable.toml b/stable.toml index 12741e4..a12699d 100644 --- a/stable.toml +++ b/stable.toml @@ -6,17 +6,18 @@ build-backend = "setuptools.build_meta" [project] name = "pybreeze" -version = "1.0.5" +version = "1.0.8" authors = [ { name = "JE-Chen", email = "jechenmailman@gmail.com" }, ] -description = "AutomationEditor for multi automation" +description = "IDE for multi automation" requires-python = ">=3.10" license-files = ["LICENSE"] dependencies = [ "je-editor", "je_auto_control", "je_web_runner", "je_load_density", "je_api_testka", "je-mail-thunder", - "automation-file", "PySide6==6.10.2", "test_pioneer", "paramiko" + "automation-file", "PySide6==6.11.0", "test_pioneer", "paramiko", + "jupyterlab", ] classifiers = [ "Programming Language :: Python :: 3.10", From e1d98c9af810f103172b68991cad8bf760ff7d80 Mon Sep 17 00:00:00 2001 From: JE-Chen Date: Fri, 27 Mar 2026 16:04:02 +0800 Subject: [PATCH 2/2] Update both version * Fix JupyterLab widget and thread * Rename Logger and log file * Add multi language words --- .gitignore | 3 +- dev.toml | 2 +- .../extend_multi_language/__init__.py | 0 .../extend_multi_language/extend_english.py | 6 + .../extend_traditional_chinese.py | 6 + .../update_language_dict.py | 8 ++ pybreeze/pybreeze_ui/editor_main/main_ui.py | 6 +- .../update_language_dict.py | 8 -- .../jupyter_lab_gui/jupyer_lab_thread.py | 124 +++++++++++++----- .../jupyter_lab_gui/jupyter_lab_widget.py | 47 ++++--- pybreeze/utils/logging/logger.py | 4 +- stable.toml => pyproject.toml | 2 +- 12 files changed, 145 insertions(+), 71 deletions(-) rename pybreeze/{pybreeze_ui => }/extend_multi_language/__init__.py (100%) rename pybreeze/{pybreeze_ui => }/extend_multi_language/extend_english.py (98%) rename pybreeze/{pybreeze_ui => }/extend_multi_language/extend_traditional_chinese.py (98%) create mode 100644 pybreeze/extend_multi_language/update_language_dict.py delete mode 100644 pybreeze/pybreeze_ui/extend_multi_language/update_language_dict.py rename stable.toml => pyproject.toml (98%) diff --git a/.gitignore b/.gitignore index 43ba31f..0a64079 100644 --- a/.gitignore +++ b/.gitignore @@ -149,4 +149,5 @@ dmypy.json .jeditor **/.jeditor **/user_setting.* -bing_cookies.* \ No newline at end of file +bing_cookies.* +**/output \ No newline at end of file diff --git a/dev.toml b/dev.toml index c605106..4bb2008 100644 --- a/dev.toml +++ b/dev.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "pybreeze_dev" -version = "1.0.8" +version = "1.0.10" authors = [ { name = "JE-Chen", email = "jechenmailman@gmail.com" }, ] diff --git a/pybreeze/pybreeze_ui/extend_multi_language/__init__.py b/pybreeze/extend_multi_language/__init__.py similarity index 100% rename from pybreeze/pybreeze_ui/extend_multi_language/__init__.py rename to pybreeze/extend_multi_language/__init__.py diff --git a/pybreeze/pybreeze_ui/extend_multi_language/extend_english.py b/pybreeze/extend_multi_language/extend_english.py similarity index 98% rename from pybreeze/pybreeze_ui/extend_multi_language/extend_english.py rename to pybreeze/extend_multi_language/extend_english.py index 035419b..8a3146f 100644 --- a/pybreeze/pybreeze_ui/extend_multi_language/extend_english.py +++ b/pybreeze/extend_multi_language/extend_english.py @@ -270,5 +270,11 @@ def update_english_word_dict(): "skills_response_label": "Response:", "skills_missing_input": "Please enter API URL and Prompt", "skills_generating": "Generating...", + # JupyterLab GUI + "jupyterlab_init": "Initializing...", + "jupyterlab_downloading": "Downloading...", + "jupyterlab_loading": "Loading...", + "jupyterlab_timeout": "JupyterLab Timeout", + "jupyterlab_init_failed": "JupyterLab init failed", } ) diff --git a/pybreeze/pybreeze_ui/extend_multi_language/extend_traditional_chinese.py b/pybreeze/extend_multi_language/extend_traditional_chinese.py similarity index 98% rename from pybreeze/pybreeze_ui/extend_multi_language/extend_traditional_chinese.py rename to pybreeze/extend_multi_language/extend_traditional_chinese.py index ee01a56..c0f9084 100644 --- a/pybreeze/pybreeze_ui/extend_multi_language/extend_traditional_chinese.py +++ b/pybreeze/extend_multi_language/extend_traditional_chinese.py @@ -270,5 +270,11 @@ def update_traditional_chinese_word_dict(): "skills_response_label": "回傳結果:", "skills_missing_input": "請輸入 API URL 和 Prompt", "skills_generating": "產生中...", + # JupyterLab GUI + "jupyterlab_init": "初始化中...", + "jupyterlab_downloading": "下載中...", + "jupyterlab_loading": "載入中...", + "jupyterlab_timeout": "JupyterLab 啟動超時", + "jupyterlab_init_failed": "JupyterLab 啟動失敗", } ) diff --git a/pybreeze/extend_multi_language/update_language_dict.py b/pybreeze/extend_multi_language/update_language_dict.py new file mode 100644 index 0000000..a25c004 --- /dev/null +++ b/pybreeze/extend_multi_language/update_language_dict.py @@ -0,0 +1,8 @@ +from pybreeze.extend_multi_language.extend_english import update_english_word_dict +from pybreeze.extend_multi_language.extend_traditional_chinese import \ + update_traditional_chinese_word_dict + + +def update_language_dict(): + update_traditional_chinese_word_dict() + update_english_word_dict() diff --git a/pybreeze/pybreeze_ui/editor_main/main_ui.py b/pybreeze/pybreeze_ui/editor_main/main_ui.py index 7f80f74..9919930 100644 --- a/pybreeze/pybreeze_ui/editor_main/main_ui.py +++ b/pybreeze/pybreeze_ui/editor_main/main_ui.py @@ -4,17 +4,15 @@ from pathlib import Path from typing import List, Dict, Type -from pybreeze.utils.logging.logger import pybreeze_logger - environ["LOCUST_SKIP_MONKEY_PATCH"] = "1" from PySide6.QtCore import QTimer, QCoreApplication from PySide6.QtGui import QIcon -from PySide6.QtWidgets import QApplication, QWidget, QSystemTrayIcon +from PySide6.QtWidgets import QApplication, QWidget from je_editor import EditorMain, language_wrapper from qt_material import apply_stylesheet -from pybreeze.pybreeze_ui.extend_multi_language.update_language_dict import update_language_dict +from pybreeze.extend_multi_language.update_language_dict import update_language_dict from pybreeze.pybreeze_ui.menu.build_menubar import add_menu_to_menubar from pybreeze.pybreeze_ui.syntax.syntax_extend import \ syntax_extend_package diff --git a/pybreeze/pybreeze_ui/extend_multi_language/update_language_dict.py b/pybreeze/pybreeze_ui/extend_multi_language/update_language_dict.py deleted file mode 100644 index bfb9aec..0000000 --- a/pybreeze/pybreeze_ui/extend_multi_language/update_language_dict.py +++ /dev/null @@ -1,8 +0,0 @@ -from pybreeze.pybreeze_ui.extend_multi_language.extend_english import update_english_word_dict -from pybreeze.pybreeze_ui.extend_multi_language.extend_traditional_chinese import \ - update_traditional_chinese_word_dict - - -def update_language_dict(): - update_traditional_chinese_word_dict() - update_english_word_dict() diff --git a/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyer_lab_thread.py b/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyer_lab_thread.py index f1940d7..6a43c36 100644 --- a/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyer_lab_thread.py +++ b/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyer_lab_thread.py @@ -1,8 +1,14 @@ +import os import socket import subprocess import sys -from PySide6.QtCore import QThread, Signal import time +import traceback + +from PySide6.QtCore import QThread, Signal +from je_editor import language_wrapper + +from pybreeze.utils.logging.logger import pybreeze_logger def find_free_port(): @@ -13,41 +19,93 @@ def find_free_port(): return port -class JupyterServerThread(QThread): - server_ready = Signal(str) +def get_venv_python(): + # 如果在 venv 中 + if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): + return sys.executable + + # 嘗試從常見位置找 venv + possible_paths = [ + os.path.join(os.getcwd(), "venv", "Scripts", "python.exe"), + os.path.join(os.getcwd(), ".venv", "Scripts", "python.exe"), + ] - def __init__(self): - super().__init__() - self.process = None + for path in possible_paths: + if os.path.exists(path): + return path + + raise RuntimeError("找不到 venv 的 python.exe") + + +def is_jupyter_installed(python_exe): + result = subprocess.run( + [python_exe, "-m", "pip", "show", "jupyterlab"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + return result.returncode == 0 + + +class JupyterLauncherThread(QThread): + server_ready = Signal(str) + status_update = Signal(str) + error_occurred = Signal(str) def run(self): - port = find_free_port() - - cmd = [ - sys.executable, - "-m", - "jupyterlab", - "--no-browser", - f"--ServerApp.port={port}", - "--ServerApp.token=", - "--ServerApp.password=", - "--ServerApp.allow_origin=*", - "--ServerApp.disable_check_xsrf=True", - ] - - self.process = subprocess.Popen(cmd) - - # 輪詢 port,直到可連 - while True: - try: - s = socket.create_connection(("localhost", port), timeout=0.5) - s.close() - break - except OSError: - time.sleep(0.1) - - # Server ready,發射 signal - self.server_ready.emit(f"http://localhost:{port}/lab") + try: + python_exe = get_venv_python() + + if not is_jupyter_installed(python_exe): + self.status_update.emit(language_wrapper.language_word_dict.get("jupyterlab_downloading")) + + result = subprocess.run([ + python_exe, + "-m", + "pip", + "install", + "jupyterlab", + "-U" + ], capture_output=True, text=True) + + if result.returncode != 0: + raise RuntimeError(result.stderr) + + self.status_update.emit(language_wrapper.language_word_dict.get("jupyterlab_loading")) + + port = find_free_port() + + self.process = subprocess.Popen([ + python_exe, + "-m", + "jupyterlab", + "--no-browser", + f"--ServerApp.port={port}", + "--ServerApp.token=", + "--ServerApp.password=", + "--ServerApp.allow_origin=*", + "--ServerApp.disable_check_xsrf=True", + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + start_time = time.time() + + while True: + if time.time() - start_time > 30: + raise TimeoutError("JupyterLab 啟動超時") + + try: + s = socket.create_connection(("localhost", port), timeout=0.5) + s.close() + break + except OSError: + time.sleep(0.2) + + self.server_ready.emit(f"http://localhost:{port}/lab") + + except Exception: + err = traceback.format_exc() + print(err) + self.error_occurred.emit(err) + pybreeze_logger.info(err) def stop(self): if self.process: diff --git a/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py b/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py index a151cfa..4324057 100644 --- a/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py +++ b/pybreeze/pybreeze_ui/jupyter_lab_gui/jupyter_lab_widget.py @@ -2,9 +2,11 @@ from PySide6.QtCore import QUrl from PySide6.QtWebEngineWidgets import QWebEngineView -from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget +from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel +from je_editor import language_wrapper -from pybreeze.pybreeze_ui.jupyter_lab_gui.jupyer_lab_thread import JupyterServerThread +from pybreeze.pybreeze_ui.jupyter_lab_gui.jupyer_lab_thread import JupyterLauncherThread +from pybreeze.utils.logging.logger import pybreeze_logger class JupyterLabWidget(QWidget): @@ -14,38 +16,41 @@ def __init__(self): layout = QVBoxLayout(self) + self.status_label = QLabel(language_wrapper.language_word_dict.get("jupyterlab_init")) + layout.addWidget(self.status_label) + self.browser = QWebEngineView() + self.browser.hide() layout.addWidget(self.browser) - # 啟動 Jupyter server thread - self.thread = JupyterServerThread() + self.thread = JupyterLauncherThread() + self.thread.status_update.connect(self.update_status) self.thread.server_ready.connect(self.load_lab) + self.thread.error_occurred.connect(self.show_error) self.thread.start() - def load_lab(self, url: str): - """Load JupyterLab URL into WebEngine""" - - if not url: - print("Invalid JupyterLab URL") - return - - print("JupyterLab running at:", url) + def update_status(self, text): + self.status_label.setText(text) - qurl = QUrl(url) + def load_lab(self, url): + if self.status_label: + self.status_label.setParent(None) + self.status_label.deleteLater() + self.status_label = None - if not qurl.isValid(): - print("Invalid QUrl:", url) - return + self.browser.setUrl(QUrl(url)) + self.browser.show() - self.browser.setUrl(qurl) + def show_error(self, msg): + self.status_label.setText(language_wrapper.language_word_dict.get("jupyterlab_init_failed")) + print(msg) + pybreeze_logger.info(msg) def closeEvent(self, event): - - if hasattr(self, "thread") and self.thread.isRunning(): + if self.thread.isRunning(): self.thread.stop() self.thread.quit() self.thread.wait() - event.accept() @@ -53,6 +58,6 @@ def closeEvent(self, event): app = QApplication(sys.argv) win = JupyterLabWidget() - win.show() + win.showMaximized() sys.exit(app.exec()) diff --git a/pybreeze/utils/logging/logger.py b/pybreeze/utils/logging/logger.py index 2b9acb3..7a3f905 100644 --- a/pybreeze/utils/logging/logger.py +++ b/pybreeze/utils/logging/logger.py @@ -7,7 +7,7 @@ logging.root.setLevel(logging.DEBUG) # 建立 AutoControlGUI 專用 logger Create dedicated logger -pybreeze_logger = logging.getLogger("AutomationIDE") +pybreeze_logger = logging.getLogger("Pybreeze") # 日誌格式 Formatter formatter = logging.Formatter( @@ -25,7 +25,7 @@ class PyBreezeLogger(RotatingFileHandler): def __init__( self, - filename: str = "AutomationIDE.log", + filename: str = "PyBreeze.log", mode: str = "w", max_bytes: int = 1073741824, # 1GB backup_count: int = 0, diff --git a/stable.toml b/pyproject.toml similarity index 98% rename from stable.toml rename to pyproject.toml index a12699d..cf21500 100644 --- a/stable.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "pybreeze" -version = "1.0.8" +version = "1.0.12" authors = [ { name = "JE-Chen", email = "jechenmailman@gmail.com" }, ]