ok-script 2.0.23b1__tar.gz → 3.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ok_script-2.0.23b1 → ok_script-3.0.0}/PKG-INFO +11 -1
- {ok_script-2.0.23b1 → ok_script-3.0.0}/README.md +10 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/__init__.py +40 -5
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/events.py +10 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/MainWindow.py +50 -16
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/common/config.py +3 -3
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/__init__.py +2 -2
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/buttons.py +3 -2
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/feedback.py +43 -14
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/icons.py +108 -6
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/qss.py +7 -1
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/tabs.py +3 -2
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/plugins/imported_tabs.py +1 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/settings/settings_page.py +0 -5
- ok_script-3.0.0/ok/ui/qt/shell/lazy_page.py +32 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/shell/nav_icons.py +2 -2
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/shell/run_bar.py +24 -6
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/tasks_page.py +123 -32
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/app.py +22 -7
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok_script.egg-info/PKG-INFO +11 -1
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok_script.egg-info/SOURCES.txt +2 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_kit.py +74 -2
- ok_script-3.0.0/tests/test_lazy_page.py +47 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_main_window_system_theme.py +1 -1
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_shell.py +17 -4
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_tasks_page.py +78 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/develop/SKILL.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/develop/agents/openai.yaml +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/develop/references/windows-input.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/generate-templates/SKILL.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/reload-tasks/SKILL.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/reload-tasks/agents/openai.yaml +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/setup-env/SKILL.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/setup-env/agents/openai.yaml +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/setup-env/assets/ok-script-develop/SKILL.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/setup-project/SKILL.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/setup-project/agents/openai.yaml +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/use_venv/SKILL.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/write-test/SKILL.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/.agents/skills/write-test/agents/openai.yaml +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/LICENSE.txt +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/MANIFEST.in +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/diagnostics/qq_trace.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/docs/agent-setup.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/docs/feature-crops.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/docs/mcp.md +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/examples/mcp_observe.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/__main__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/alas/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/alas/emulator_base.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/alas/emulator_windows.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/alas/platform_windows.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/alas/utils.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/assets/default-icon.png +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/adb/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/adb/deep.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/adb/minitouch.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/adb/nemu_ipc.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/adb/nemu_utils.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/adb/pool.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/adb/timer.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/adb/util.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/windows/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/windows/d3d11.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/capture/windows/dump.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/cli.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/capture_labels.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/code_runner.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/config_schema.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/downloads.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/icons.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/mcp_runner.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/mcp_server.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/notifications.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/plugin_staging.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/plugins.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/screenshot.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/script_editing.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/script_packager.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/script_templates.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/skills.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/start_controller.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/task_manager.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/template_store.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/translation.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/core/ui_config.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/DeviceManager.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/adb.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/base.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/bitblt.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/bitblt_utils.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/browser.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/desktop_duplication.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/hwnd_window.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/image.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/nemu_ipc.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/types.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/update.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/windows_graphics.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/capture_methods/windows_graphics_hdr.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/adb.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/base.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/browser.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/do_nothing.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/foreground_post_message.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/genshin.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/keys.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/post_message.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/pydirect.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/pynput.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/interaction_methods/swipe.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/device/intercation.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/feature/Box.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/feature/Feature.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/feature/FeatureSet.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/feature/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/gui/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/gui/util/pyappify_startup.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/notification/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/notification/manager.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/notification/messenger_images.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/notification/pipeline.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/notification/ppocr.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/notification/providers.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/notification/system.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/notification/windows_messenger.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ocr/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ocr/download_paddle_model.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Media/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Security/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/Storage/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/UI/ViewManagement/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/UI/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/Windows/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/delegate.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/export.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/idldsl.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/inspectable.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/roapi.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/types.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/rotypes/winstring.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/task/DiagnosisTask.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/task/TaskExecutor.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/task/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/task/exceptions.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/task/task.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/test/OKTestRunner.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/test/RunTests.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/test/TaskTestCase.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/test/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/third_party/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/third_party/paperclip.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/overlay/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/overlay/win32_gdi.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/Communicate.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/MessageWindow.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/StartController.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/about/AboutTab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/about/UpdateCard.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/about/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/common/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/debug/OverlayWidget.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/debug/Screenshot.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/debug/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/debug/diagnosis_page.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/events.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/GettextTranslator.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/en_US.qm +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/en_US.ts +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/es_ES.qm +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/es_ES.ts +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/ja_JP.qm +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/ja_JP.ts +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/ko_KR.qm +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/ko_KR.ts +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/path.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/update.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/zh_CN.qm +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/zh_CN.ts +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/zh_TW.qm +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/i18n/zh_TW.ts +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/cards.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/dialogs.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/gallery.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/inputs.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/labels.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/layout.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/navigation.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/options.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/popover.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/switch.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/theme.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/kit/window.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/overlay/OverlayWindow.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/overlay/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/plugins/PluginTab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/plugins/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/qt.qrc +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/resources.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/settings/GlobalConfigTab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/settings/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/settings/config_section.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/shell/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/shell/connection.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/shell/diagnostics.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/shell/log_page.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/start/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/EditTaskTab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/MarkUpWindow.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/ModifyListDialog.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/PythonHighlighter.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/RecordScript.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/ScheduleTaskTab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/ScriptPackager.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/TaskManger.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/TaskOpButton.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/TemplateFactory.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/TemplateTab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/config_editors.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/config_form.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/mouse_buttons.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/tasks/schedule_index_sync.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/Alert.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/InitWorker.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/download.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/pip_util.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/pyappify_startup.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/touch_scroll.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/util/windows_thumbnail.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/Card.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/CustomTab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/FlowLayout.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/ImageWidget.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/ImportWarningDialog.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/ListTableWidgetItem.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/NumericTableWidgetItem.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/SortingTableWidgetItem.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/StartLoadingDialog.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/Tab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/TabWidget.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/UpdateConfigWidgetItem.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/YesNonWidgetItem.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/ui/qt/widget/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/update/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/update/copy_ok_folder.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/update/inline_ok_requirements.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/Analytics.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/GlobalConfig.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/__init__.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/blur.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/clazz.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/collection.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/color.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/config.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/explorer.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/file.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/gpu_driver_settings.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/handler.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/i18n.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/logger.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/partial_sync_repo.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/print_hwnd.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/process.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/version.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/window.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok/util/windows_schedule.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok_script.egg-info/dependency_links.txt +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok_script.egg-info/entry_points.txt +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok_script.egg-info/not-zip-safe +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok_script.egg-info/requires.txt +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/ok_script.egg-info/top_level.txt +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/pyproject.toml +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/setup.cfg +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/setup.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_app.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_base_interaction.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_blur.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_box.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_capture_update.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_cli.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_core_events.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_core_ui_services.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_device_manager.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_diagnosis_page.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_executor_jobs.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_explorer.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_global_config.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_gpu_driver_settings.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_handler.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_headless_imports.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_inline_ok_requirements.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_log_page.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_logger.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_main_window_notifications.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_main_window_startup_tab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_markup_window.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_mcp_cleanup.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_mcp_runner.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_mcp_server.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_mcp_setting.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_missing_frame.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_mouse_buttons.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_navigation_state.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_nemu_ipc_capture.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_notifications.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_ok_mutex.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_overlay_custom_draw.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_overlay_lifecycle.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_package_metadata.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_plugin_staging.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_plugin_tabs.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_plugins.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_process.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_process_mutex.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_pyappify_startup_update.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_qt_events.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_qt_page_layout.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_runtime_startup.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_schedule_startup.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_schedule_task_index_sync.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_screenshot_naming.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_screenshot_rendering.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_settings_page.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_skills.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_start_controller.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_task_executor_next_frame.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_task_executor_queue.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_task_reloading.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_template_compression.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_template_generation.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_template_tab.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_touch_scroll.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_translation_fallback.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_ui_config.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_version.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_windows_graphics_capture.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_windows_graphics_hdr.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_windows_schedule_target_parse.py +0 -0
- {ok_script-2.0.23b1 → ok_script-3.0.0}/tests/test_windows_schedule_xml.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ok-script
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Vibe Coding Automation Framework
|
|
5
5
|
Author-email: ok-oldking <firedcto@gmail.com>
|
|
6
6
|
License: ok-script License
|
|
@@ -230,6 +230,16 @@ python -m venv .venv
|
|
|
230
230
|
- [白荆回廊](https://github.com/ok-oldking/ok-baijing)(停止维护)
|
|
231
231
|
- [崩坏:星穹铁道](https://github.com/Shasnow/ok-starrailassistant)(停止维护)
|
|
232
232
|
|
|
233
|
+
## 开发 ok-script
|
|
234
|
+
|
|
235
|
+
在本仓库中测试界面改动时,可以用 [main.py](main.py) 中的演示配置直接从源码启动 GUI:
|
|
236
|
+
|
|
237
|
+
```powershell
|
|
238
|
+
.\.venv\Scripts\python.exe main.py
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
它以记事本为目标窗口,无需游戏,并注册了一个演示一次性任务和一个触发任务,让任务、计划任务和模板页面都有内容。设置和日志保存在已被 git 忽略的 `configs/` 和 `logs/` 目录中。需要测试其他选项时,修改 `main.py` 中的 `config` 字典或演示任务即可。
|
|
242
|
+
|
|
233
243
|
## 社区与发布
|
|
234
244
|
|
|
235
245
|
- 开发者 QQ 群:938132715
|
|
@@ -106,6 +106,16 @@ python -m venv .venv
|
|
|
106
106
|
- [白荆回廊](https://github.com/ok-oldking/ok-baijing)(停止维护)
|
|
107
107
|
- [崩坏:星穹铁道](https://github.com/Shasnow/ok-starrailassistant)(停止维护)
|
|
108
108
|
|
|
109
|
+
## 开发 ok-script
|
|
110
|
+
|
|
111
|
+
在本仓库中测试界面改动时,可以用 [main.py](main.py) 中的演示配置直接从源码启动 GUI:
|
|
112
|
+
|
|
113
|
+
```powershell
|
|
114
|
+
.\.venv\Scripts\python.exe main.py
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
它以记事本为目标窗口,无需游戏,并注册了一个演示一次性任务和一个触发任务,让任务、计划任务和模板页面都有内容。设置和日志保存在已被 git 忽略的 `configs/` 和 `logs/` 目录中。需要测试其他选项时,修改 `main.py` 中的 `config` 字典或演示任务即可。
|
|
118
|
+
|
|
109
119
|
## 社区与发布
|
|
110
120
|
|
|
111
121
|
- 开发者 QQ 群:938132715
|
|
@@ -310,6 +310,8 @@ class App(_OverlayConfigMixin):
|
|
|
310
310
|
self.global_config = task_executor.global_config if task_executor else None
|
|
311
311
|
from ok.ui.qt.util.app import init_app_config
|
|
312
312
|
self.app, self.locale = init_app_config()
|
|
313
|
+
from ok.ui.qt.common.config import cfg as ui_config
|
|
314
|
+
ui_config.language_changed.connect(self._on_language_changed)
|
|
313
315
|
install_qt_event_dispatcher()
|
|
314
316
|
self.app.aboutToQuit.connect(self._close_device_manager)
|
|
315
317
|
if task_executor is not None:
|
|
@@ -461,6 +463,44 @@ class App(_OverlayConfigMixin):
|
|
|
461
463
|
self.do_show_main()
|
|
462
464
|
|
|
463
465
|
def do_show_main(self):
|
|
466
|
+
self._create_main_window()
|
|
467
|
+
self.main_window.show()
|
|
468
|
+
self.main_window.bring_to_front()
|
|
469
|
+
|
|
470
|
+
logger.debug(f'show_main_window end')
|
|
471
|
+
|
|
472
|
+
def _on_language_changed(self):
|
|
473
|
+
# The signal comes from a widget in the window being replaced; rebuild once it returns.
|
|
474
|
+
from PySide6.QtCore import QTimer
|
|
475
|
+
QTimer.singleShot(0, self._apply_language)
|
|
476
|
+
|
|
477
|
+
def _apply_language(self):
|
|
478
|
+
from ok.ui.qt.util.app import install_translator
|
|
479
|
+
self.locale = install_translator(self.app)
|
|
480
|
+
self.po_translation = None
|
|
481
|
+
if og.executor is not None:
|
|
482
|
+
og.executor.locale = self.locale
|
|
483
|
+
og.executor.load_tr()
|
|
484
|
+
logger.info(f'language changed to {self.locale.name()}')
|
|
485
|
+
|
|
486
|
+
old_window = self.main_window
|
|
487
|
+
if old_window is None:
|
|
488
|
+
return
|
|
489
|
+
# Every page translates its text on construction, so build a fresh window.
|
|
490
|
+
visible = old_window.isVisible()
|
|
491
|
+
old_window.update_ok_config()
|
|
492
|
+
old_window.dispose()
|
|
493
|
+
self._create_main_window()
|
|
494
|
+
# Startup work in the first showEvent has already run for this process.
|
|
495
|
+
self.main_window.shown = True
|
|
496
|
+
self.main_window.switch_to(self.main_window.setting_tab)
|
|
497
|
+
if visible:
|
|
498
|
+
self.main_window.show()
|
|
499
|
+
self.main_window.bring_to_front()
|
|
500
|
+
old_window.hide()
|
|
501
|
+
old_window.deleteLater()
|
|
502
|
+
|
|
503
|
+
def _create_main_window(self):
|
|
464
504
|
from ok.ui.qt.MainWindow import MainWindow
|
|
465
505
|
|
|
466
506
|
self.main_window = MainWindow(self, self.config, self.ok_config, self.icon, self.title, self.version,
|
|
@@ -482,11 +522,6 @@ class App(_OverlayConfigMixin):
|
|
|
482
522
|
if hasattr(og.my_app, 'on_show_main_window'):
|
|
483
523
|
og.my_app.on_show_main_window(self.main_window)
|
|
484
524
|
|
|
485
|
-
self.main_window.show()
|
|
486
|
-
self.main_window.bring_to_front()
|
|
487
|
-
|
|
488
|
-
logger.debug(f'show_main_window end')
|
|
489
|
-
|
|
490
525
|
def exec(self):
|
|
491
526
|
logger.info('app.exec()')
|
|
492
527
|
import signal
|
|
@@ -45,6 +45,10 @@ class EventSignal:
|
|
|
45
45
|
elif callback in self._subscribers:
|
|
46
46
|
self._subscribers.remove(callback)
|
|
47
47
|
|
|
48
|
+
def disconnect_where(self, predicate: Callable[[Callable[..., Any]], bool]) -> None:
|
|
49
|
+
with self._lock:
|
|
50
|
+
self._subscribers = [callback for callback in self._subscribers if not predicate(callback)]
|
|
51
|
+
|
|
48
52
|
def emit(self, *args: Any, **kwargs: Any) -> None:
|
|
49
53
|
with self._lock:
|
|
50
54
|
subscribers = tuple(self._subscribers)
|
|
@@ -94,6 +98,12 @@ class EventBus:
|
|
|
94
98
|
self.any.emit(EventMessage(name, args, kwargs))
|
|
95
99
|
return forward
|
|
96
100
|
|
|
101
|
+
def disconnect_where(self, predicate: Callable[[Callable[..., Any]], bool]) -> None:
|
|
102
|
+
"""Remove every subscriber of every event for which ``predicate`` is true."""
|
|
103
|
+
self.any.disconnect_where(predicate)
|
|
104
|
+
for name in self._event_names:
|
|
105
|
+
getattr(self, name).disconnect_where(predicate)
|
|
106
|
+
|
|
97
107
|
def emit_draw_box(self, key=None, boxes=None, color=None, frame=None, debug=True):
|
|
98
108
|
self.draw_box.emit(key, boxes, color, frame, debug)
|
|
99
109
|
|
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import threading
|
|
3
3
|
|
|
4
4
|
import pyappify
|
|
5
|
-
from PySide6.QtCore import QCoreApplication, QEvent, QSize, Qt, QTimer
|
|
5
|
+
from PySide6.QtCore import QCoreApplication, QEvent, QObject, QSize, Qt, QTimer
|
|
6
6
|
from PySide6.QtGui import QColor
|
|
7
7
|
from PySide6.QtWidgets import QApplication, QHBoxLayout, QMenu, QStackedWidget, QSystemTrayIcon, QVBoxLayout
|
|
8
8
|
|
|
@@ -31,9 +31,10 @@ from ok.util.config import Config
|
|
|
31
31
|
|
|
32
32
|
from ok.ui.qt.Communicate import communicate
|
|
33
33
|
from ok.ui.qt.common.config import cfg as ui_config
|
|
34
|
-
from ok.ui.qt.kit import Dialog, KitWindow, NavigationView, Theme as KitTheme,
|
|
34
|
+
from ok.ui.qt.kit import Dialog, KitWindow, NavigationView, Theme as KitTheme, set_toast_anchor, show_toast, \
|
|
35
35
|
theme as kit_theme
|
|
36
36
|
from ok.ui.qt.kit.theme import set_style_property
|
|
37
|
+
from ok.ui.qt.shell.lazy_page import LazyPage
|
|
37
38
|
from ok.ui.qt.shell.nav_icons import nav_icon
|
|
38
39
|
from ok.ui.qt.shell.run_bar import RunBar
|
|
39
40
|
from ok.ui.qt.util.Alert import alert_error
|
|
@@ -46,8 +47,6 @@ from ok.util.logger import Logger
|
|
|
46
47
|
|
|
47
48
|
logger = Logger.get_logger(__name__)
|
|
48
49
|
|
|
49
|
-
PAGE_MARGIN = 12
|
|
50
|
-
|
|
51
50
|
SECTION_RUN = "run"
|
|
52
51
|
SECTION_GAME = "game"
|
|
53
52
|
SECTION_DEVELOP = "develop"
|
|
@@ -114,10 +113,11 @@ class MainWindow(KitWindow):
|
|
|
114
113
|
body.setSpacing(0)
|
|
115
114
|
body.addWidget(self.run_bar)
|
|
116
115
|
body.addLayout(pages, 1)
|
|
117
|
-
|
|
116
|
+
# Keep tips flush with the bottom edge of the title bar.
|
|
117
|
+
set_toast_anchor(self, self.title_bar, gap=0)
|
|
118
118
|
|
|
119
119
|
self.schedule_tab = None
|
|
120
|
-
self.global_config_tabs = []
|
|
120
|
+
self.global_config_tabs = [] # [(config, LazyPage)]
|
|
121
121
|
|
|
122
122
|
# Prepare custom tabs and separate them by add_after_default_tabs
|
|
123
123
|
before_custom_tabs = []
|
|
@@ -155,9 +155,13 @@ class MainWindow(KitWindow):
|
|
|
155
155
|
self.edit_task_tab = EditTaskTab()
|
|
156
156
|
self.add_page(self.edit_task_tab, "pen", self.tr('Script'), SECTION_DEVELOP)
|
|
157
157
|
|
|
158
|
+
# Settings, Templates and global config pages are rarely opened, so they are
|
|
159
|
+
# built on first use (LazyPage) to keep window construction fast.
|
|
158
160
|
if og.task_manager.has_custom or debug:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
+
def template_tab():
|
|
162
|
+
from ok.ui.qt.tasks.TemplateTab import TemplateTab
|
|
163
|
+
return TemplateTab(config=config)
|
|
164
|
+
self.template_tab = LazyPage(template_tab)
|
|
161
165
|
self.add_page(self.template_tab, "image", self.tr('Templates'), SECTION_DEVELOP)
|
|
162
166
|
|
|
163
167
|
from ok.ui.qt.debug.diagnosis_page import DiagnosisPage
|
|
@@ -192,9 +196,11 @@ class MainWindow(KitWindow):
|
|
|
192
196
|
notification_tab = None
|
|
193
197
|
for name, config_obj, option in global_config.get_all_visible_configs():
|
|
194
198
|
if getattr(option, 'show_at_tab', False):
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
def global_config_tab(config_obj=config_obj, option=option):
|
|
200
|
+
from ok.ui.qt.settings.GlobalConfigTab import GlobalConfigTab
|
|
201
|
+
return GlobalConfigTab(config_obj, option)
|
|
202
|
+
config_tab = LazyPage(global_config_tab)
|
|
203
|
+
self.global_config_tabs.append((config_obj, config_tab))
|
|
198
204
|
if name == NOTIFICATION_OPTION_NAME:
|
|
199
205
|
notification_tab = config_tab
|
|
200
206
|
else:
|
|
@@ -204,8 +210,10 @@ class MainWindow(KitWindow):
|
|
|
204
210
|
self.notification_tab = notification_tab
|
|
205
211
|
self.add_page(notification_tab, "bell", self.app.tr(NOTIFICATION_OPTION_NAME), SECTION_GAME)
|
|
206
212
|
|
|
207
|
-
|
|
208
|
-
|
|
213
|
+
def settings_page():
|
|
214
|
+
from ok.ui.qt.settings.settings_page import SettingsPage
|
|
215
|
+
return SettingsPage()
|
|
216
|
+
self.setting_tab = LazyPage(settings_page)
|
|
209
217
|
self.add_page(self.setting_tab, "gear", self.tr('Settings'), bottom=True)
|
|
210
218
|
|
|
211
219
|
from ok.ui.qt.about.AboutTab import AboutTab
|
|
@@ -275,6 +283,8 @@ class MainWindow(KitWindow):
|
|
|
275
283
|
def switch_to(self, widget):
|
|
276
284
|
if self.stack.indexOf(widget) < 0:
|
|
277
285
|
return
|
|
286
|
+
if isinstance(widget, LazyPage):
|
|
287
|
+
widget.page()
|
|
278
288
|
self.stack.setCurrentWidget(widget)
|
|
279
289
|
self.navigation.set_current(widget.objectName())
|
|
280
290
|
|
|
@@ -442,12 +452,12 @@ class MainWindow(KitWindow):
|
|
|
442
452
|
logger.debug(f'bring_to_front native activation failed: {e}')
|
|
443
453
|
|
|
444
454
|
def goto_global_config(self, key):
|
|
445
|
-
for config_tab in self.global_config_tabs:
|
|
446
|
-
if
|
|
455
|
+
for config_obj, config_tab in self.global_config_tabs:
|
|
456
|
+
if key in config_obj:
|
|
447
457
|
self.switch_to(config_tab)
|
|
448
458
|
return
|
|
449
459
|
self.switch_to(self.setting_tab)
|
|
450
|
-
self.setting_tab.goto_config(key)
|
|
460
|
+
self.setting_tab.page().goto_config(key)
|
|
451
461
|
|
|
452
462
|
def tray_quit(self):
|
|
453
463
|
logger.info('main window tray_quit')
|
|
@@ -669,6 +679,30 @@ class MainWindow(KitWindow):
|
|
|
669
679
|
except Exception as e:
|
|
670
680
|
logger.error(f'Error importing .okscript file: {e}')
|
|
671
681
|
|
|
682
|
+
def dispose(self):
|
|
683
|
+
"""Release app-wide hooks so a replacement window can take over, e.g. after a language change."""
|
|
684
|
+
if self.update_check_timer is not None:
|
|
685
|
+
self.update_check_timer.stop()
|
|
686
|
+
self.tray.hide()
|
|
687
|
+
self.run_bar.dispose()
|
|
688
|
+
notification_manager = getattr(self, 'notification_manager', None)
|
|
689
|
+
if notification_manager is not None:
|
|
690
|
+
# Stopping waits for the pipeline thread; keep the GUI thread free.
|
|
691
|
+
threading.Thread(target=notification_manager.stop, name='NotificationStop', daemon=True).start()
|
|
692
|
+
controller = getattr(self, '_imported_tab_controller', None)
|
|
693
|
+
if controller is not None:
|
|
694
|
+
controller.dispose()
|
|
695
|
+
for page in list(self._pages.values()):
|
|
696
|
+
cleanup = getattr(page, 'dispose', None) or getattr(page, 'stop_worker', None)
|
|
697
|
+
if callable(cleanup):
|
|
698
|
+
try:
|
|
699
|
+
cleanup()
|
|
700
|
+
except Exception as error:
|
|
701
|
+
logger.error(f'Failed to dispose page {page.objectName()}', error)
|
|
702
|
+
# The event bus holds strong references; drop the ones owned by this window's widgets.
|
|
703
|
+
owners = {id(obj) for obj in (self, *self.findChildren(QObject))}
|
|
704
|
+
communicate.disconnect_where(lambda callback: id(getattr(callback, '__self__', None)) in owners)
|
|
705
|
+
|
|
672
706
|
def closeEvent(self, event):
|
|
673
707
|
if self.app.exit_event.is_set():
|
|
674
708
|
logger.info("Window closed exit_event.is_set")
|
|
@@ -39,13 +39,13 @@ def deserialize_language(value) -> Language:
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class UiConfig(QObject):
|
|
42
|
-
"""The language
|
|
42
|
+
"""The language and the theme mode, both applied live.
|
|
43
43
|
|
|
44
44
|
The file also accepts the older qfluentwidgets layout
|
|
45
45
|
(``MainWindow.Language`` and ``QFluentWidgets.ThemeMode``).
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
language_changed = Signal()
|
|
49
49
|
theme_changed = Signal()
|
|
50
50
|
|
|
51
51
|
def __init__(self, path: str):
|
|
@@ -83,7 +83,7 @@ class UiConfig(QObject):
|
|
|
83
83
|
return
|
|
84
84
|
self.language = language
|
|
85
85
|
self.save()
|
|
86
|
-
self.
|
|
86
|
+
self.language_changed.emit()
|
|
87
87
|
|
|
88
88
|
def set_theme_mode(self, mode: str) -> None:
|
|
89
89
|
mode = getattr(mode, "value", mode)
|
|
@@ -8,7 +8,7 @@ its children; plain Qt inputs inside it are styled too.
|
|
|
8
8
|
from ok.ui.qt.kit.buttons import Button
|
|
9
9
|
from ok.ui.qt.kit.cards import Card, SettingGroup, SettingRow
|
|
10
10
|
from ok.ui.qt.kit.dialogs import Dialog, confirm
|
|
11
|
-
from ok.ui.qt.kit.feedback import Pill, ProgressRing, StatusDot, Toast,
|
|
11
|
+
from ok.ui.qt.kit.feedback import Pill, ProgressRing, StatusDot, Toast, set_toast_anchor, set_toast_top_margin, show_toast
|
|
12
12
|
from ok.ui.qt.kit.icons import ICON_NAMES, icon, load_icon
|
|
13
13
|
from ok.ui.qt.kit.inputs import ComboBox, NumberBox
|
|
14
14
|
from ok.ui.qt.kit.labels import Label
|
|
@@ -25,5 +25,5 @@ __all__ = [
|
|
|
25
25
|
"Button", "Card", "ComboBox", "Dialog", "FlowLayout", "ICON_NAMES", "KitWindow", "Label", "NavItem",
|
|
26
26
|
"NavigationView", "NumberBox", "OptionCard", "OptionGroup", "Pill", "Popover", "ProgressRing", "SettingGroup",
|
|
27
27
|
"SettingRow", "StatusDot", "Switch", "TabItem", "TabStrip", "Theme", "TitleBar", "Toast", "confirm", "icon",
|
|
28
|
-
"load_icon", "
|
|
28
|
+
"load_icon", "set_toast_anchor", "set_toast_top_margin", "show_toast", "theme",
|
|
29
29
|
]
|
|
@@ -6,7 +6,7 @@ from ok.ui.qt.kit.icons import icon as kit_icon
|
|
|
6
6
|
from ok.ui.qt.kit.theme import set_style_property, theme
|
|
7
7
|
|
|
8
8
|
VARIANTS = ("secondary", "primary", "tonal", "subtle", "danger")
|
|
9
|
-
SIZES = {"normal": (36, 14,
|
|
9
|
+
SIZES = {"normal": (36, 14, 18), "small": (30, 10, 16)}
|
|
10
10
|
|
|
11
11
|
_ICON_COLOR = {"primary": "on_accent", "danger": "danger"}
|
|
12
12
|
|
|
@@ -48,7 +48,8 @@ class Button(QPushButton):
|
|
|
48
48
|
height, padding, _ = SIZES[size]
|
|
49
49
|
layout = QHBoxLayout(self)
|
|
50
50
|
layout.setContentsMargins(padding, 0, padding, 0)
|
|
51
|
-
|
|
51
|
+
# Glyphs leave a few pixels of air inside their box, so this reads wider than it is.
|
|
52
|
+
layout.setSpacing(5)
|
|
52
53
|
layout.addStretch(1)
|
|
53
54
|
layout.addWidget(self._icon_label)
|
|
54
55
|
layout.addWidget(self._text_label)
|
|
@@ -125,13 +125,15 @@ class ProgressRing(QWidget):
|
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
class Toast(QWidget):
|
|
128
|
-
"""A message card shown at the
|
|
128
|
+
"""A message card shown at the top center of a window; see :func:`show_toast`."""
|
|
129
|
+
|
|
130
|
+
WIDTH = 560
|
|
129
131
|
|
|
130
132
|
def __init__(self, message: str, title: str = None, tone: str = "neutral", action: tuple = None, parent=None):
|
|
131
133
|
super().__init__(parent)
|
|
132
134
|
self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
|
|
133
135
|
self.tone = _check_tone(tone)
|
|
134
|
-
self.setFixedWidth(
|
|
136
|
+
self.setFixedWidth(self.WIDTH)
|
|
135
137
|
|
|
136
138
|
icon_label = QLabel(self)
|
|
137
139
|
icon_label.setPixmap(kit_icon(_TONE_ICON[tone], _TONE_COLOR[tone], 2).pixmap(QSize(18, 18), self.devicePixelRatioF()))
|
|
@@ -165,15 +167,19 @@ class Toast(QWidget):
|
|
|
165
167
|
|
|
166
168
|
|
|
167
169
|
class _ToastHost(QObject):
|
|
168
|
-
"""Stacks toasts
|
|
170
|
+
"""Stacks toasts down from a window's top center, below ``anchor`` if set."""
|
|
169
171
|
|
|
170
172
|
SPACING = 8
|
|
173
|
+
MARGIN = 16
|
|
174
|
+
ANCHOR_GAP = 4
|
|
171
175
|
|
|
172
176
|
def __init__(self, window: QWidget):
|
|
173
177
|
super().__init__(window)
|
|
174
178
|
self.window = window
|
|
175
179
|
self.toasts = []
|
|
176
|
-
self.
|
|
180
|
+
self.anchor = None
|
|
181
|
+
self.anchor_gap = self.ANCHOR_GAP
|
|
182
|
+
self.top_margin = self.MARGIN
|
|
177
183
|
window.installEventFilter(self)
|
|
178
184
|
|
|
179
185
|
def add(self, toast: Toast, duration: int) -> None:
|
|
@@ -182,8 +188,7 @@ class _ToastHost(QObject):
|
|
|
182
188
|
toast.destroyed.connect(lambda *_: self._forget(toast))
|
|
183
189
|
toast.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
184
190
|
self.toasts.append(toast)
|
|
185
|
-
|
|
186
|
-
toast.resize(toast.width(), toast.layout().totalHeightForWidth(toast.width()))
|
|
191
|
+
self._fit(toast)
|
|
187
192
|
toast.show()
|
|
188
193
|
toast.raise_()
|
|
189
194
|
self.layout()
|
|
@@ -198,16 +203,30 @@ class _ToastHost(QObject):
|
|
|
198
203
|
self.toasts.remove(toast)
|
|
199
204
|
self.layout()
|
|
200
205
|
|
|
206
|
+
def _fit(self, toast) -> None:
|
|
207
|
+
# Narrow windows shrink the toast; wrapped labels need height-for-width,
|
|
208
|
+
# since adjustSize() would leave gaps.
|
|
209
|
+
width = max(240, min(Toast.WIDTH, self.window.width() - 2 * self.MARGIN))
|
|
210
|
+
toast.setFixedWidth(width)
|
|
211
|
+
toast.resize(width, toast.layout().totalHeightForWidth(width))
|
|
212
|
+
|
|
213
|
+
def _top(self) -> int:
|
|
214
|
+
anchor = self.anchor
|
|
215
|
+
if anchor is not None and shiboken6.isValid(anchor) and anchor.isVisible():
|
|
216
|
+
return anchor.mapTo(self.window, anchor.rect().bottomLeft()).y() + 1 + self.anchor_gap
|
|
217
|
+
return self.top_margin
|
|
218
|
+
|
|
201
219
|
def layout(self) -> None:
|
|
202
220
|
self.toasts = [toast for toast in self.toasts if toast.isVisible()]
|
|
203
|
-
y = self.
|
|
204
|
-
for toast in
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
y -= self.SPACING
|
|
221
|
+
y = self._top()
|
|
222
|
+
for toast in self.toasts:
|
|
223
|
+
toast.move((self.window.width() - toast.width()) // 2, y)
|
|
224
|
+
y += toast.height() + self.SPACING
|
|
208
225
|
|
|
209
226
|
def eventFilter(self, watched, event) -> bool:
|
|
210
227
|
if watched is self.window and event.type() == QEvent.Type.Resize:
|
|
228
|
+
for toast in self.toasts:
|
|
229
|
+
self._fit(toast)
|
|
211
230
|
self.layout()
|
|
212
231
|
return False
|
|
213
232
|
|
|
@@ -231,9 +250,19 @@ def show_toast(parent: QWidget, message: str, title: str = None, tone: str = "ne
|
|
|
231
250
|
return toast
|
|
232
251
|
|
|
233
252
|
|
|
234
|
-
def
|
|
235
|
-
"""
|
|
253
|
+
def set_toast_anchor(widget: QWidget, anchor: QWidget, *, gap: int = 4) -> None:
|
|
254
|
+
"""Show toasts below ``anchor`` with the requested gap in logical pixels."""
|
|
255
|
+
|
|
256
|
+
host = toast_host(widget)
|
|
257
|
+
host.anchor = anchor
|
|
258
|
+
host.anchor_gap = max(0, int(gap))
|
|
259
|
+
host.layout()
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def set_toast_top_margin(widget: QWidget, margin: int) -> None:
|
|
263
|
+
"""Position toasts directly below the window's top edge."""
|
|
236
264
|
|
|
237
265
|
host = toast_host(widget)
|
|
238
|
-
host.
|
|
266
|
+
host.anchor = None
|
|
267
|
+
host.top_margin = max(0, int(margin))
|
|
239
268
|
host.layout()
|
|
@@ -17,9 +17,10 @@ from PySide6.QtGui import QColor, QIcon, QIconEngine, QImage, QPainter, QPixmap
|
|
|
17
17
|
from PySide6.QtSvg import QSvgRenderer
|
|
18
18
|
|
|
19
19
|
_PATHS = {
|
|
20
|
-
"play": '<path d="M7
|
|
21
|
-
"pause": '<
|
|
22
|
-
|
|
20
|
+
"play": '<path d="M7 3.5v17L20.5 12z" fill="currentColor" stroke="currentColor" stroke-width="1.5"/>',
|
|
21
|
+
"pause": '<rect x="5.5" y="4" width="4.5" height="16" rx="1.25" fill="currentColor" stroke="none"/>'
|
|
22
|
+
'<rect x="14" y="4" width="4.5" height="16" rx="1.25" fill="currentColor" stroke="none"/>',
|
|
23
|
+
"stop": '<rect x="4.5" y="4.5" width="15" height="15" rx="2.5" fill="currentColor" stroke="none"/>',
|
|
23
24
|
"power": '<path d="M12 3v9M6.3 6.8a8 8 0 1 0 11.4 0"/>',
|
|
24
25
|
"check": '<path d="M5 12.5l4.5 4.5L19 7"/>',
|
|
25
26
|
"x": '<path d="M6 6l12 12M18 6 6 18"/>',
|
|
@@ -219,6 +220,100 @@ class _SvgFileIconEngine(QIconEngine):
|
|
|
219
220
|
return _SvgFileIconEngine(self.data)
|
|
220
221
|
|
|
221
222
|
|
|
223
|
+
class _RasterFileIconEngine(QIconEngine):
|
|
224
|
+
"""Fit raster artwork by its visible bounds, preserving its aspect ratio."""
|
|
225
|
+
|
|
226
|
+
def __init__(self, image: QImage):
|
|
227
|
+
super().__init__()
|
|
228
|
+
self.image = image
|
|
229
|
+
|
|
230
|
+
def paint(self, painter, rect, mode, state):
|
|
231
|
+
scale = min(rect.width() / self.image.width(), rect.height() / self.image.height())
|
|
232
|
+
width, height = self.image.width() * scale, self.image.height() * scale
|
|
233
|
+
target = QRectF(rect.x() + (rect.width() - width) / 2,
|
|
234
|
+
rect.y() + (rect.height() - height) / 2, width, height)
|
|
235
|
+
painter.save()
|
|
236
|
+
painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
|
|
237
|
+
if mode == QIcon.Mode.Disabled:
|
|
238
|
+
painter.setOpacity(painter.opacity() * 0.4)
|
|
239
|
+
painter.drawImage(target, self.image)
|
|
240
|
+
painter.restore()
|
|
241
|
+
|
|
242
|
+
def pixmap(self, size, mode, state):
|
|
243
|
+
return self.scaledPixmap(size, mode, state, 1.0)
|
|
244
|
+
|
|
245
|
+
def scaledPixmap(self, size, mode, state, scale):
|
|
246
|
+
image = QImage(round(size.width() * scale), round(size.height() * scale),
|
|
247
|
+
QImage.Format.Format_ARGB32_Premultiplied)
|
|
248
|
+
image.fill(Qt.GlobalColor.transparent)
|
|
249
|
+
painter = QPainter(image)
|
|
250
|
+
self.paint(painter, image.rect(), mode, state)
|
|
251
|
+
painter.end()
|
|
252
|
+
result = QPixmap.fromImage(image)
|
|
253
|
+
result.setDevicePixelRatio(scale)
|
|
254
|
+
return result
|
|
255
|
+
|
|
256
|
+
def clone(self):
|
|
257
|
+
return _RasterFileIconEngine(self.image)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class _ColorizedIconEngine(QIconEngine):
|
|
261
|
+
"""Tint an icon's alpha mask using a color resolved for the current theme."""
|
|
262
|
+
|
|
263
|
+
def __init__(self, source: QIcon, color):
|
|
264
|
+
super().__init__()
|
|
265
|
+
self.source = source
|
|
266
|
+
self.color = color
|
|
267
|
+
|
|
268
|
+
def paint(self, painter, rect, mode, state):
|
|
269
|
+
size = QSize(max(1, round(rect.width())), max(1, round(rect.height())))
|
|
270
|
+
pixmap = self.scaledPixmap(size, mode, state, painter.device().devicePixelRatioF())
|
|
271
|
+
painter.drawPixmap(rect, pixmap)
|
|
272
|
+
|
|
273
|
+
def pixmap(self, size, mode, state):
|
|
274
|
+
return self.scaledPixmap(size, mode, state, 1.0)
|
|
275
|
+
|
|
276
|
+
def scaledPixmap(self, size, mode, state, scale):
|
|
277
|
+
from ok.ui.qt.kit.theme import theme
|
|
278
|
+
|
|
279
|
+
value = self.color
|
|
280
|
+
if isinstance(value, dict):
|
|
281
|
+
value = value.get("dark" if theme.is_dark() else "light", "text")
|
|
282
|
+
color = theme.color(value) if isinstance(value, str) and not value.startswith("#") else QColor(value)
|
|
283
|
+
if mode == QIcon.Mode.Disabled:
|
|
284
|
+
color.setAlphaF(color.alphaF() * 0.4)
|
|
285
|
+
image = self.source.pixmap(size, scale, QIcon.Mode.Normal, state).toImage()
|
|
286
|
+
painter = QPainter(image)
|
|
287
|
+
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceIn)
|
|
288
|
+
painter.fillRect(image.rect(), color)
|
|
289
|
+
painter.end()
|
|
290
|
+
result = QPixmap.fromImage(image)
|
|
291
|
+
result.setDevicePixelRatio(image.devicePixelRatio())
|
|
292
|
+
return result
|
|
293
|
+
|
|
294
|
+
def clone(self):
|
|
295
|
+
return _ColorizedIconEngine(self.source, self.color)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def tinted_icon(source: QIcon, color) -> QIcon:
|
|
299
|
+
"""Recolor artwork at paint time; ``color`` can supply light/dark shades."""
|
|
300
|
+
|
|
301
|
+
return QIcon(_ColorizedIconEngine(source, color))
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _trim_transparent(image: QImage) -> QImage:
|
|
305
|
+
rgba = image.convertToFormat(QImage.Format.Format_RGBA8888)
|
|
306
|
+
# Ignore nearly invisible export fringes when measuring the artwork.
|
|
307
|
+
occupied = [i for i, alpha in enumerate(bytes(rgba.constBits())[3::4]) if alpha >= 16]
|
|
308
|
+
if not occupied:
|
|
309
|
+
return image
|
|
310
|
+
width = rgba.width()
|
|
311
|
+
left = min(i % width for i in occupied)
|
|
312
|
+
right = max(i % width for i in occupied)
|
|
313
|
+
top, bottom = occupied[0] // width, occupied[-1] // width
|
|
314
|
+
return image.copy(left, top, right - left + 1, bottom - top + 1)
|
|
315
|
+
|
|
316
|
+
|
|
222
317
|
def icon(name: str, color=None, stroke_width: float = 1.75) -> QIcon:
|
|
223
318
|
"""A bundled icon.
|
|
224
319
|
|
|
@@ -230,25 +325,32 @@ def icon(name: str, color=None, stroke_width: float = 1.75) -> QIcon:
|
|
|
230
325
|
|
|
231
326
|
|
|
232
327
|
@lru_cache(maxsize=256)
|
|
233
|
-
def _load_file_icon(path: str, mtime: float) -> QIcon:
|
|
328
|
+
def _load_file_icon(path: str, mtime: float, trim_transparent: bool = False) -> QIcon:
|
|
234
329
|
if path.lower().endswith(".svg"):
|
|
235
330
|
data = Path(path).read_bytes()
|
|
236
331
|
engine = _SvgFileIconEngine(data)
|
|
237
332
|
if not engine.renderer.isValid():
|
|
238
333
|
raise ValueError(f"invalid SVG icon: {path}")
|
|
239
334
|
return QIcon(engine)
|
|
335
|
+
if trim_transparent:
|
|
336
|
+
image = QImage(path)
|
|
337
|
+
if image.isNull():
|
|
338
|
+
raise ValueError(f"unsupported icon file: {path}")
|
|
339
|
+
return QIcon(_RasterFileIconEngine(_trim_transparent(image)))
|
|
240
340
|
result = QIcon(path)
|
|
241
341
|
if result.isNull():
|
|
242
342
|
raise ValueError(f"unsupported icon file: {path}")
|
|
243
343
|
return result
|
|
244
344
|
|
|
245
345
|
|
|
246
|
-
def load_icon(source, base_dir=None) -> QIcon:
|
|
346
|
+
def load_icon(source, base_dir=None, *, trim_transparent: bool = False) -> QIcon:
|
|
247
347
|
"""Load an app-provided icon.
|
|
248
348
|
|
|
249
349
|
``source`` is a path to an ``.svg`` or raster file (relative paths are
|
|
250
350
|
resolved against ``base_dir`` or the working directory), a ``QIcon``, or
|
|
251
351
|
``None`` for no icon.
|
|
352
|
+
``trim_transparent`` fits raster artwork by its alpha bounds at paint time;
|
|
353
|
+
the source file stays unchanged.
|
|
252
354
|
"""
|
|
253
355
|
|
|
254
356
|
if source is None:
|
|
@@ -261,4 +363,4 @@ def load_icon(source, base_dir=None) -> QIcon:
|
|
|
261
363
|
path = path.resolve()
|
|
262
364
|
if not path.is_file():
|
|
263
365
|
raise FileNotFoundError(f"icon file not found: {path}")
|
|
264
|
-
return _load_file_icon(str(path), path.stat().st_mtime)
|
|
366
|
+
return _load_file_icon(str(path), path.stat().st_mtime, trim_transparent)
|
|
@@ -218,12 +218,13 @@ QAbstractItemView {{
|
|
|
218
218
|
selection-color: {text};
|
|
219
219
|
outline: 0;
|
|
220
220
|
}}
|
|
221
|
+
QTableView::item {{ padding: 0 12px; }}
|
|
221
222
|
QHeaderView::section {{
|
|
222
223
|
background: {surface_alt};
|
|
223
224
|
color: {text_secondary};
|
|
224
225
|
border: 0;
|
|
225
226
|
border-bottom: 1px solid {border};
|
|
226
|
-
padding: 6px
|
|
227
|
+
padding: 6px 12px;
|
|
227
228
|
font-weight: 600;
|
|
228
229
|
}}
|
|
229
230
|
|
|
@@ -387,6 +388,11 @@ QLabel#taskTile {{
|
|
|
387
388
|
font-size: 12px;
|
|
388
389
|
font-weight: 600;
|
|
389
390
|
}}
|
|
391
|
+
TaskIconTile#taskTile {{
|
|
392
|
+
background: transparent;
|
|
393
|
+
border: 0;
|
|
394
|
+
border-radius: 0;
|
|
395
|
+
}}
|
|
390
396
|
QPushButton#sectionHeader {{
|
|
391
397
|
background: transparent;
|
|
392
398
|
border: 0;
|
|
@@ -54,8 +54,9 @@ class TabItem(QPushButton):
|
|
|
54
54
|
self.icon_label.hide()
|
|
55
55
|
return
|
|
56
56
|
icon = source if isinstance(source, QIcon) else kit_icon(source, "text" if self.isChecked() else "text_secondary")
|
|
57
|
-
|
|
58
|
-
self.icon_label.
|
|
57
|
+
size = 20 if isinstance(source, QIcon) else 16
|
|
58
|
+
self.icon_label.setPixmap(icon.pixmap(QSize(size, size), self.devicePixelRatioF()))
|
|
59
|
+
self.icon_label.setFixedSize(size, size)
|
|
59
60
|
self.icon_label.show()
|
|
60
61
|
|
|
61
62
|
def _on_toggled(self, checked: bool) -> None:
|
|
@@ -18,6 +18,7 @@ class ImportedTaskTab(TasksPage):
|
|
|
18
18
|
def __init__(self, namespace, name):
|
|
19
19
|
self.namespace = namespace
|
|
20
20
|
self.imported_file_name = namespace
|
|
21
|
+
self.state_name = f"tasks_page_{namespace}"
|
|
21
22
|
super().__init__(title=og.app.tr(name))
|
|
22
23
|
|
|
23
24
|
def collect_tasks(self):
|