MoleditPy-linux 4.2.4__tar.gz → 4.3.1__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.
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/PKG-INFO +11 -8
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/README.md +10 -7
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/pyproject.toml +1 -1
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/MoleditPy_linux.egg-info/PKG-INFO +11 -8
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/main.py +123 -4
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/plugins/plugin_manager.py +16 -7
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/plugins/plugin_manager_window.py +2 -3
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/align_plane_dialog.py +2 -3
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/alignment_dialog.py +1 -2
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/app_state.py +13 -5
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/bond_item.py +2 -2
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/compute_logic.py +6 -3
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +36 -27
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/custom_interactor_style.py +15 -13
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +1 -1
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/dialog_logic.py +2 -3
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/edit_actions_logic.py +13 -1
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/export_logic.py +2 -2
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/io_logic.py +63 -20
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/main_window.py +1 -1
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/main_window_init.py +9 -6
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/mirror_dialog.py +2 -4
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/molecular_scene_handler.py +10 -7
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/molecule_scene.py +3 -3
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/move_group_dialog.py +1 -1
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +1 -1
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/planarize_dialog.py +2 -2
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/plugin_menu_manager.py +57 -42
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/template_preview_view.py +2 -2
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/translation_dialog.py +1 -1
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/ui_manager.py +6 -1
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/user_template_dialog.py +8 -10
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/view_3d_logic.py +17 -13
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/LICENSE +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/setup.cfg +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/__init__.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/__main__.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/assets/file_icon.ico +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/assets/icon.icns +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/assets/icon.ico +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/assets/icon.png +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/core/__init__.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/core/mol_geometry.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/core/molecular_data.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/plugins/__init__.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/plugins/plugin_interface.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/__init__.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/about_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/analysis_window.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/atom_item.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/atom_picking.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/string_importers.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/utils/__init__.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/utils/constants.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/utils/default_settings.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/utils/suppress_log.py +0 -0
- {moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/utils/system_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy-linux
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.1
|
|
4
4
|
Summary: A cross-platform, simple, and intuitive molecular structure editor built in Python. It allows 2D molecular drawing and 3D structure visualization. It supports exporting structure files for input to DFT calculation software.
|
|
5
5
|
Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -770,16 +770,17 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
|
|
|
770
770
|
* **Stereochemistry Display:** Automatically identifies and displays R/S labels for chiral centers in the 3D view after conversion.
|
|
771
771
|
* **File I/O:**
|
|
772
772
|
* Save and load entire sessions, including 2D/3D data and constraints, with the native `.pmeprj` project file.
|
|
773
|
-
* Import structures from **MOL/SDF** files or **SMILES** strings.
|
|
773
|
+
* Import structures from **MOL/SDF/XYZ** files or **SMILES/InChI** strings.
|
|
774
774
|
* Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
|
|
775
|
-
* Export 2D
|
|
775
|
+
* Export 2D views as high-resolution PNG or SVG images, and 3D views as PNG images.
|
|
776
|
+
* Export the 3D model as **STL** or **OBJ/MTL** files for 3D printing and rendering.
|
|
776
777
|
* **Security note on `.pmeraw`:** the legacy `.pmeraw` project format uses Python pickle, which can execute arbitrary code when loaded. Only open `.pmeraw` files that you created yourself; prefer the JSON-based `.pmeprj` format for sharing.
|
|
777
778
|
|
|
778
779
|
### 4. Programmable & Extensible
|
|
779
780
|
|
|
780
781
|
* **Python Plugin System:** Drop your Python scripts into the plugin folder, and they instantly become part of the application menu.
|
|
781
782
|
* **Downloadable Plugins:** Explore and download specialized plugins from the [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/).
|
|
782
|
-
* **Full API Access:** Plugins have direct access to the `MainWindow`, `RDKit` molecule objects, and
|
|
783
|
+
* **Full API Access:** Plugins have direct access to the `MainWindow`, `RDKit` molecule objects, and the Qt graphics items of the 2D scene, allowing for limitless customization.
|
|
783
784
|
* **Rapid Prototyping:** Ideal for researchers who need to test new algorithms or workflow automations on the fly.
|
|
784
785
|
|
|
785
786
|
## Installation and Execution
|
|
@@ -835,7 +836,7 @@ moleditpy
|
|
|
835
836
|
* **GUI and 2D Drawing (PyQt6):** The editor is built on a `QGraphicsScene`, where custom `AtomItem` and `BondItem` objects are interactively manipulated. The Undo/Redo feature is implemented by serializing the application state.
|
|
836
837
|
* **Chemical Calculations (RDKit / Open Babel):** RDKit is used to generate molecule objects from 2D data, perform 3D coordinate generation, and calculate properties. Open Babel serves as a fallback for 3D conversion. All heavy computations are run on a separate `QThread` to keep the GUI responsive.
|
|
837
838
|
* **3D Visualization (PyVista / pyvistaqt):** 3D rendering is achieved by generating PyVista meshes (spheres and cylinders) from RDKit conformer coordinates. A custom `vtkInteractorStyle` enables direct drag-and-drop editing of atoms in the 3D view.
|
|
838
|
-
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The
|
|
839
|
+
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The `MainWindow` acts as a composition hub that delegates all work to specialized manager objects (state, I/O, 3D view, dialogs), ensuring long-term maintainability and easier verification.
|
|
839
840
|
|
|
840
841
|
## License & Disclaimer
|
|
841
842
|
|
|
@@ -905,16 +906,17 @@ Additionally, please cite the plugins you used.
|
|
|
905
906
|
* **立体化学表示:** 3D変換後、キラル中心を自動的に認識し、R/Sラベルを3Dビューに表示します。
|
|
906
907
|
* **ファイル入出力:**
|
|
907
908
|
* 2D/3Dデータや制約情報を含むセッション全体を、独自のプロジェクトファイル (`.pmeprj`) として保存・読み込みできます。
|
|
908
|
-
* **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
|
|
909
|
+
* **MOL/SDF/XYZ**ファイルや**SMILES/InChI**文字列から構造をインポートできます。
|
|
909
910
|
* 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
|
|
910
|
-
* 2D
|
|
911
|
+
* 2Dビューを高解像度のPNG・SVG画像として、3DビューをPNG画像としてエクスポートできます。
|
|
912
|
+
* 3Dモデルを**STL**または**OBJ/MTL**ファイルとしてエクスポートでき、3Dプリントやレンダリングに利用できます。
|
|
911
913
|
* **`.pmeraw` に関するセキュリティ上の注意:** 旧形式の `.pmeraw` プロジェクトは Python の pickle を使用しており、読み込み時に任意のコードが実行される可能性があります。自分で作成した `.pmeraw` ファイルのみを開き、共有には JSON ベースの `.pmeprj` 形式を使用してください。
|
|
912
914
|
|
|
913
915
|
### 4. プログラマブルで拡張可能
|
|
914
916
|
|
|
915
917
|
* **Pythonプラグインシステム:** Pythonスクリプトをプラグインフォルダに入れるだけで、即座にアプリケーションメニューの一部として機能します。
|
|
916
918
|
* **プラグインのダウンロード:** [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/) から特化したプラグインを探索・ダウンロードできます。
|
|
917
|
-
* **フルAPIアクセス:** プラグインは `MainWindow`、`RDKit`
|
|
919
|
+
* **フルAPIアクセス:** プラグインは `MainWindow`、`RDKit` 分子オブジェクト、2D シーンの Qt グラフィックスアイテムに直接アクセスでき、無限のカスタマイズが可能です。
|
|
918
920
|
* **迅速なプロトタイピング:** 新しいアルゴリズムやワークフローの自動化をその場でテストしたい研究者に最適です。
|
|
919
921
|
|
|
920
922
|
## インストールと実行
|
|
@@ -970,6 +972,7 @@ moleditpy
|
|
|
970
972
|
* **GUIと2D描画 (PyQt6):** `QGraphicsScene`上にカスタムの`AtomItem`(原子)と`BondItem`(結合)を配置し、対話的に操作します。Undo/Redo機能は、アプリケーションの状態をシリアライズしてスタックに保存することで実現しています。
|
|
971
973
|
* **化学計算 (RDKit / Open Babel):** 2DデータからRDKit分子オブジェクトを生成し、3D座標生成や分子特性計算を実行します。RDKitでの3D座標生成が失敗した際は、Open Babelにフォールバックします。重い計算処理は別スレッド (`QThread`) で実行し、GUIの応答性を維持しています。
|
|
972
974
|
* **3D可視化 (PyVista / pyvistaqt):** RDKitのコンフォーマ座標からPyVistaのメッシュ(球や円柱)を生成して描画します。カスタムの`vtkInteractorStyle`を実装し、3Dビュー内での原子の直接的なドラッグ&ドロップ編集を可能にしています。
|
|
975
|
+
* **モジュラーアーキテクチャ:** コードベースは `core` ロジック、`ui` コンポーネント、`utils` の専用パッケージに整理されています。`MainWindow` は、状態管理・ファイル入出力・3D ビュー・ダイアログなどを専門のマネージャーオブジェクトに委譲するコンポジションハブとして機能し、長期的な保守性と検証の容易さを確保しています。
|
|
973
976
|
|
|
974
977
|
## ライセンス & 免責事項
|
|
975
978
|
|
|
@@ -67,16 +67,17 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
|
|
|
67
67
|
* **Stereochemistry Display:** Automatically identifies and displays R/S labels for chiral centers in the 3D view after conversion.
|
|
68
68
|
* **File I/O:**
|
|
69
69
|
* Save and load entire sessions, including 2D/3D data and constraints, with the native `.pmeprj` project file.
|
|
70
|
-
* Import structures from **MOL/SDF** files or **SMILES** strings.
|
|
70
|
+
* Import structures from **MOL/SDF/XYZ** files or **SMILES/InChI** strings.
|
|
71
71
|
* Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
|
|
72
|
-
* Export 2D
|
|
72
|
+
* Export 2D views as high-resolution PNG or SVG images, and 3D views as PNG images.
|
|
73
|
+
* Export the 3D model as **STL** or **OBJ/MTL** files for 3D printing and rendering.
|
|
73
74
|
* **Security note on `.pmeraw`:** the legacy `.pmeraw` project format uses Python pickle, which can execute arbitrary code when loaded. Only open `.pmeraw` files that you created yourself; prefer the JSON-based `.pmeprj` format for sharing.
|
|
74
75
|
|
|
75
76
|
### 4. Programmable & Extensible
|
|
76
77
|
|
|
77
78
|
* **Python Plugin System:** Drop your Python scripts into the plugin folder, and they instantly become part of the application menu.
|
|
78
79
|
* **Downloadable Plugins:** Explore and download specialized plugins from the [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/).
|
|
79
|
-
* **Full API Access:** Plugins have direct access to the `MainWindow`, `RDKit` molecule objects, and
|
|
80
|
+
* **Full API Access:** Plugins have direct access to the `MainWindow`, `RDKit` molecule objects, and the Qt graphics items of the 2D scene, allowing for limitless customization.
|
|
80
81
|
* **Rapid Prototyping:** Ideal for researchers who need to test new algorithms or workflow automations on the fly.
|
|
81
82
|
|
|
82
83
|
## Installation and Execution
|
|
@@ -132,7 +133,7 @@ moleditpy
|
|
|
132
133
|
* **GUI and 2D Drawing (PyQt6):** The editor is built on a `QGraphicsScene`, where custom `AtomItem` and `BondItem` objects are interactively manipulated. The Undo/Redo feature is implemented by serializing the application state.
|
|
133
134
|
* **Chemical Calculations (RDKit / Open Babel):** RDKit is used to generate molecule objects from 2D data, perform 3D coordinate generation, and calculate properties. Open Babel serves as a fallback for 3D conversion. All heavy computations are run on a separate `QThread` to keep the GUI responsive.
|
|
134
135
|
* **3D Visualization (PyVista / pyvistaqt):** 3D rendering is achieved by generating PyVista meshes (spheres and cylinders) from RDKit conformer coordinates. A custom `vtkInteractorStyle` enables direct drag-and-drop editing of atoms in the 3D view.
|
|
135
|
-
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The
|
|
136
|
+
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The `MainWindow` acts as a composition hub that delegates all work to specialized manager objects (state, I/O, 3D view, dialogs), ensuring long-term maintainability and easier verification.
|
|
136
137
|
|
|
137
138
|
## License & Disclaimer
|
|
138
139
|
|
|
@@ -202,16 +203,17 @@ Additionally, please cite the plugins you used.
|
|
|
202
203
|
* **立体化学表示:** 3D変換後、キラル中心を自動的に認識し、R/Sラベルを3Dビューに表示します。
|
|
203
204
|
* **ファイル入出力:**
|
|
204
205
|
* 2D/3Dデータや制約情報を含むセッション全体を、独自のプロジェクトファイル (`.pmeprj`) として保存・読み込みできます。
|
|
205
|
-
* **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
|
|
206
|
+
* **MOL/SDF/XYZ**ファイルや**SMILES/InChI**文字列から構造をインポートできます。
|
|
206
207
|
* 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
|
|
207
|
-
* 2D
|
|
208
|
+
* 2Dビューを高解像度のPNG・SVG画像として、3DビューをPNG画像としてエクスポートできます。
|
|
209
|
+
* 3Dモデルを**STL**または**OBJ/MTL**ファイルとしてエクスポートでき、3Dプリントやレンダリングに利用できます。
|
|
208
210
|
* **`.pmeraw` に関するセキュリティ上の注意:** 旧形式の `.pmeraw` プロジェクトは Python の pickle を使用しており、読み込み時に任意のコードが実行される可能性があります。自分で作成した `.pmeraw` ファイルのみを開き、共有には JSON ベースの `.pmeprj` 形式を使用してください。
|
|
209
211
|
|
|
210
212
|
### 4. プログラマブルで拡張可能
|
|
211
213
|
|
|
212
214
|
* **Pythonプラグインシステム:** Pythonスクリプトをプラグインフォルダに入れるだけで、即座にアプリケーションメニューの一部として機能します。
|
|
213
215
|
* **プラグインのダウンロード:** [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/) から特化したプラグインを探索・ダウンロードできます。
|
|
214
|
-
* **フルAPIアクセス:** プラグインは `MainWindow`、`RDKit`
|
|
216
|
+
* **フルAPIアクセス:** プラグインは `MainWindow`、`RDKit` 分子オブジェクト、2D シーンの Qt グラフィックスアイテムに直接アクセスでき、無限のカスタマイズが可能です。
|
|
215
217
|
* **迅速なプロトタイピング:** 新しいアルゴリズムやワークフローの自動化をその場でテストしたい研究者に最適です。
|
|
216
218
|
|
|
217
219
|
## インストールと実行
|
|
@@ -267,6 +269,7 @@ moleditpy
|
|
|
267
269
|
* **GUIと2D描画 (PyQt6):** `QGraphicsScene`上にカスタムの`AtomItem`(原子)と`BondItem`(結合)を配置し、対話的に操作します。Undo/Redo機能は、アプリケーションの状態をシリアライズしてスタックに保存することで実現しています。
|
|
268
270
|
* **化学計算 (RDKit / Open Babel):** 2DデータからRDKit分子オブジェクトを生成し、3D座標生成や分子特性計算を実行します。RDKitでの3D座標生成が失敗した際は、Open Babelにフォールバックします。重い計算処理は別スレッド (`QThread`) で実行し、GUIの応答性を維持しています。
|
|
269
271
|
* **3D可視化 (PyVista / pyvistaqt):** RDKitのコンフォーマ座標からPyVistaのメッシュ(球や円柱)を生成して描画します。カスタムの`vtkInteractorStyle`を実装し、3Dビュー内での原子の直接的なドラッグ&ドロップ編集を可能にしています。
|
|
272
|
+
* **モジュラーアーキテクチャ:** コードベースは `core` ロジック、`ui` コンポーネント、`utils` の専用パッケージに整理されています。`MainWindow` は、状態管理・ファイル入出力・3D ビュー・ダイアログなどを専門のマネージャーオブジェクトに委譲するコンポジションハブとして機能し、長期的な保守性と検証の容易さを確保しています。
|
|
270
273
|
|
|
271
274
|
## ライセンス & 免責事項
|
|
272
275
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy-linux
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.1
|
|
4
4
|
Summary: A cross-platform, simple, and intuitive molecular structure editor built in Python. It allows 2D molecular drawing and 3D structure visualization. It supports exporting structure files for input to DFT calculation software.
|
|
5
5
|
Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -770,16 +770,17 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
|
|
|
770
770
|
* **Stereochemistry Display:** Automatically identifies and displays R/S labels for chiral centers in the 3D view after conversion.
|
|
771
771
|
* **File I/O:**
|
|
772
772
|
* Save and load entire sessions, including 2D/3D data and constraints, with the native `.pmeprj` project file.
|
|
773
|
-
* Import structures from **MOL/SDF** files or **SMILES** strings.
|
|
773
|
+
* Import structures from **MOL/SDF/XYZ** files or **SMILES/InChI** strings.
|
|
774
774
|
* Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
|
|
775
|
-
* Export 2D
|
|
775
|
+
* Export 2D views as high-resolution PNG or SVG images, and 3D views as PNG images.
|
|
776
|
+
* Export the 3D model as **STL** or **OBJ/MTL** files for 3D printing and rendering.
|
|
776
777
|
* **Security note on `.pmeraw`:** the legacy `.pmeraw` project format uses Python pickle, which can execute arbitrary code when loaded. Only open `.pmeraw` files that you created yourself; prefer the JSON-based `.pmeprj` format for sharing.
|
|
777
778
|
|
|
778
779
|
### 4. Programmable & Extensible
|
|
779
780
|
|
|
780
781
|
* **Python Plugin System:** Drop your Python scripts into the plugin folder, and they instantly become part of the application menu.
|
|
781
782
|
* **Downloadable Plugins:** Explore and download specialized plugins from the [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/).
|
|
782
|
-
* **Full API Access:** Plugins have direct access to the `MainWindow`, `RDKit` molecule objects, and
|
|
783
|
+
* **Full API Access:** Plugins have direct access to the `MainWindow`, `RDKit` molecule objects, and the Qt graphics items of the 2D scene, allowing for limitless customization.
|
|
783
784
|
* **Rapid Prototyping:** Ideal for researchers who need to test new algorithms or workflow automations on the fly.
|
|
784
785
|
|
|
785
786
|
## Installation and Execution
|
|
@@ -835,7 +836,7 @@ moleditpy
|
|
|
835
836
|
* **GUI and 2D Drawing (PyQt6):** The editor is built on a `QGraphicsScene`, where custom `AtomItem` and `BondItem` objects are interactively manipulated. The Undo/Redo feature is implemented by serializing the application state.
|
|
836
837
|
* **Chemical Calculations (RDKit / Open Babel):** RDKit is used to generate molecule objects from 2D data, perform 3D coordinate generation, and calculate properties. Open Babel serves as a fallback for 3D conversion. All heavy computations are run on a separate `QThread` to keep the GUI responsive.
|
|
837
838
|
* **3D Visualization (PyVista / pyvistaqt):** 3D rendering is achieved by generating PyVista meshes (spheres and cylinders) from RDKit conformer coordinates. A custom `vtkInteractorStyle` enables direct drag-and-drop editing of atoms in the 3D view.
|
|
838
|
-
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The
|
|
839
|
+
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The `MainWindow` acts as a composition hub that delegates all work to specialized manager objects (state, I/O, 3D view, dialogs), ensuring long-term maintainability and easier verification.
|
|
839
840
|
|
|
840
841
|
## License & Disclaimer
|
|
841
842
|
|
|
@@ -905,16 +906,17 @@ Additionally, please cite the plugins you used.
|
|
|
905
906
|
* **立体化学表示:** 3D変換後、キラル中心を自動的に認識し、R/Sラベルを3Dビューに表示します。
|
|
906
907
|
* **ファイル入出力:**
|
|
907
908
|
* 2D/3Dデータや制約情報を含むセッション全体を、独自のプロジェクトファイル (`.pmeprj`) として保存・読み込みできます。
|
|
908
|
-
* **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
|
|
909
|
+
* **MOL/SDF/XYZ**ファイルや**SMILES/InChI**文字列から構造をインポートできます。
|
|
909
910
|
* 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
|
|
910
|
-
* 2D
|
|
911
|
+
* 2Dビューを高解像度のPNG・SVG画像として、3DビューをPNG画像としてエクスポートできます。
|
|
912
|
+
* 3Dモデルを**STL**または**OBJ/MTL**ファイルとしてエクスポートでき、3Dプリントやレンダリングに利用できます。
|
|
911
913
|
* **`.pmeraw` に関するセキュリティ上の注意:** 旧形式の `.pmeraw` プロジェクトは Python の pickle を使用しており、読み込み時に任意のコードが実行される可能性があります。自分で作成した `.pmeraw` ファイルのみを開き、共有には JSON ベースの `.pmeprj` 形式を使用してください。
|
|
912
914
|
|
|
913
915
|
### 4. プログラマブルで拡張可能
|
|
914
916
|
|
|
915
917
|
* **Pythonプラグインシステム:** Pythonスクリプトをプラグインフォルダに入れるだけで、即座にアプリケーションメニューの一部として機能します。
|
|
916
918
|
* **プラグインのダウンロード:** [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/) から特化したプラグインを探索・ダウンロードできます。
|
|
917
|
-
* **フルAPIアクセス:** プラグインは `MainWindow`、`RDKit`
|
|
919
|
+
* **フルAPIアクセス:** プラグインは `MainWindow`、`RDKit` 分子オブジェクト、2D シーンの Qt グラフィックスアイテムに直接アクセスでき、無限のカスタマイズが可能です。
|
|
918
920
|
* **迅速なプロトタイピング:** 新しいアルゴリズムやワークフローの自動化をその場でテストしたい研究者に最適です。
|
|
919
921
|
|
|
920
922
|
## インストールと実行
|
|
@@ -970,6 +972,7 @@ moleditpy
|
|
|
970
972
|
* **GUIと2D描画 (PyQt6):** `QGraphicsScene`上にカスタムの`AtomItem`(原子)と`BondItem`(結合)を配置し、対話的に操作します。Undo/Redo機能は、アプリケーションの状態をシリアライズしてスタックに保存することで実現しています。
|
|
971
973
|
* **化学計算 (RDKit / Open Babel):** 2DデータからRDKit分子オブジェクトを生成し、3D座標生成や分子特性計算を実行します。RDKitでの3D座標生成が失敗した際は、Open Babelにフォールバックします。重い計算処理は別スレッド (`QThread`) で実行し、GUIの応答性を維持しています。
|
|
972
974
|
* **3D可視化 (PyVista / pyvistaqt):** RDKitのコンフォーマ座標からPyVistaのメッシュ(球や円柱)を生成して描画します。カスタムの`vtkInteractorStyle`を実装し、3Dビュー内での原子の直接的なドラッグ&ドロップ編集を可能にしています。
|
|
975
|
+
* **モジュラーアーキテクチャ:** コードベースは `core` ロジック、`ui` コンポーネント、`utils` の専用パッケージに整理されています。`MainWindow` は、状態管理・ファイル入出力・3D ビュー・ダイアログなどを専門のマネージャーオブジェクトに委譲するコンポジションハブとして機能し、長期的な保守性と検証の容易さを確保しています。
|
|
973
976
|
|
|
974
977
|
## ライセンス & 免責事項
|
|
975
978
|
|
|
@@ -11,20 +11,23 @@ DOI: 10.5281/zenodo.17268532
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
import ctypes
|
|
14
|
+
import hashlib
|
|
14
15
|
import json
|
|
15
16
|
import logging
|
|
16
17
|
import logging.handlers
|
|
17
18
|
import os
|
|
18
19
|
import sys
|
|
19
20
|
import argparse
|
|
20
|
-
|
|
21
|
+
import time
|
|
22
|
+
import traceback
|
|
23
|
+
from typing import Any, Dict, Optional
|
|
21
24
|
|
|
22
25
|
from .utils.constants import VERSION
|
|
23
26
|
|
|
24
27
|
# VERSION is resolved above (before Qt) so --version works without launching the app.
|
|
25
28
|
|
|
26
|
-
from PyQt6.QtCore import QtMsgType, qInstallMessageHandler
|
|
27
|
-
from PyQt6.QtWidgets import QApplication
|
|
29
|
+
from PyQt6.QtCore import QtMsgType, QThread, QTimer, qInstallMessageHandler
|
|
30
|
+
from PyQt6.QtWidgets import QApplication, QMessageBox
|
|
28
31
|
|
|
29
32
|
from .ui.main_window import MainWindow
|
|
30
33
|
|
|
@@ -49,6 +52,109 @@ def _qt_message_handler(mode: QtMsgType, _context: Any, message: Optional[str])
|
|
|
49
52
|
logging.log(_QT_LOG_LEVEL.get(mode, logging.WARNING), "Qt: %s", message)
|
|
50
53
|
|
|
51
54
|
|
|
55
|
+
class _ErrorDialogHandler(logging.Handler):
|
|
56
|
+
"""Surface ERROR/CRITICAL log records in a dialog, once per unique message.
|
|
57
|
+
|
|
58
|
+
The uncaught-exception hook routes through here too (it logs at ERROR with
|
|
59
|
+
``exc_info``), so every genuine failure — logged explicitly or bubbling out
|
|
60
|
+
of Qt — gets one dialog. Warnings/info never reach it (handler level is
|
|
61
|
+
ERROR). All state is per-instance; the one instance is created and attached
|
|
62
|
+
in :func:`setup_logging` (only for a real GUI run, never headless).
|
|
63
|
+
|
|
64
|
+
Guards: GUI thread + live QApplication only (a worker-thread record, where a
|
|
65
|
+
QMessageBox is unsafe, stays log-only); a time-windowed dedup so a fast
|
|
66
|
+
repeating error (e.g. from a QTimer slot) collapses to one dialog while a
|
|
67
|
+
genuine retry seconds later surfaces again; and ``extra={"no_dialog": True}``
|
|
68
|
+
to opt a record out. ``emit`` never raises.
|
|
69
|
+
|
|
70
|
+
The dialog is shown non-blocking (``show``, not ``exec``): it never spins a
|
|
71
|
+
nested event loop, so it cannot freeze the app — or a GUI-enabled test run —
|
|
72
|
+
while it is on screen.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
# Identical errors within this many seconds share one dialog; a repeat
|
|
76
|
+
# after it surfaces again.
|
|
77
|
+
_DEDUP_WINDOW_S = 10.0
|
|
78
|
+
|
|
79
|
+
def __init__(self, log_path: Optional[str] = None) -> None:
|
|
80
|
+
super().__init__(level=logging.ERROR)
|
|
81
|
+
# signature -> monotonic timestamp of its last shown dialog.
|
|
82
|
+
self._last_shown: Dict[str, float] = {}
|
|
83
|
+
# Held so a non-blocking box is not garbage-collected before it closes.
|
|
84
|
+
self._open_boxes: set = set()
|
|
85
|
+
self._log_path = log_path
|
|
86
|
+
|
|
87
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
88
|
+
try:
|
|
89
|
+
if getattr(record, "no_dialog", False):
|
|
90
|
+
return
|
|
91
|
+
app = QApplication.instance()
|
|
92
|
+
if app is None or QThread.currentThread() is not app.thread():
|
|
93
|
+
return
|
|
94
|
+
if app.property("moleditpy_shutting_down"):
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
message = record.getMessage()
|
|
98
|
+
# Source location (as the terminal log shows), then any traceback.
|
|
99
|
+
detail = f"{record.pathname}:{record.lineno}"
|
|
100
|
+
if record.exc_info:
|
|
101
|
+
detail += "\n\n" + "".join(traceback.format_exception(*record.exc_info))
|
|
102
|
+
|
|
103
|
+
signature = hashlib.sha1(
|
|
104
|
+
(message + detail).encode("utf-8", "replace")
|
|
105
|
+
).hexdigest()
|
|
106
|
+
now = time.monotonic()
|
|
107
|
+
last = self._last_shown.get(signature)
|
|
108
|
+
if last is not None and now - last < self._DEDUP_WINDOW_S:
|
|
109
|
+
return
|
|
110
|
+
self._last_shown[signature] = now
|
|
111
|
+
|
|
112
|
+
# Defer so a site's own QMessageBox.critical (raised right after the
|
|
113
|
+
# log call) is already modal when _show checks activeModalWidget.
|
|
114
|
+
QTimer.singleShot(0, lambda: self._show(message, detail))
|
|
115
|
+
except Exception: # pragma: no cover - logging must never raise
|
|
116
|
+
self.handleError(record)
|
|
117
|
+
|
|
118
|
+
def _details(self) -> str:
|
|
119
|
+
"""Where to find the full error text, honest about file logging."""
|
|
120
|
+
if self._log_path:
|
|
121
|
+
return (
|
|
122
|
+
"Full details are in the terminal output and the log file:\n"
|
|
123
|
+
f"{self._log_path}"
|
|
124
|
+
)
|
|
125
|
+
return (
|
|
126
|
+
"Full details are in the terminal output. Enable "
|
|
127
|
+
"Settings ▸ Other ▸ 'Save log to file' to also keep them "
|
|
128
|
+
"on disk."
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def _show(self, message: str, detail: str) -> None:
|
|
132
|
+
"""Show one non-blocking error dialog, unless another modal is up.
|
|
133
|
+
|
|
134
|
+
Sites (and plugins) log the error and then immediately raise their own
|
|
135
|
+
``QMessageBox.critical``; by the time this deferred call fires, that
|
|
136
|
+
modal is up, so we detect it and stay quiet — surfacing the generic
|
|
137
|
+
dialog only for errors nobody else reports.
|
|
138
|
+
"""
|
|
139
|
+
if QApplication.activeModalWidget() is not None:
|
|
140
|
+
return
|
|
141
|
+
try:
|
|
142
|
+
box = QMessageBox()
|
|
143
|
+
box.setIcon(QMessageBox.Icon.Critical)
|
|
144
|
+
box.setWindowTitle("MoleditPy — Error")
|
|
145
|
+
box.setText(message or "An unexpected error occurred.")
|
|
146
|
+
box.setInformativeText(self._details())
|
|
147
|
+
if detail:
|
|
148
|
+
box.setDetailedText(detail)
|
|
149
|
+
box.setStandardButtons(QMessageBox.StandardButton.Ok)
|
|
150
|
+
box.setModal(True)
|
|
151
|
+
self._open_boxes.add(box)
|
|
152
|
+
box.finished.connect(lambda _result, b=box: self._open_boxes.discard(b))
|
|
153
|
+
box.show()
|
|
154
|
+
except Exception:
|
|
155
|
+
logging.debug("Failed to show error dialog", exc_info=True)
|
|
156
|
+
|
|
157
|
+
|
|
52
158
|
def _read_startup_log_settings() -> tuple[bool, bool]:
|
|
53
159
|
"""Read log_to_file and log_level_debug from settings.json before Qt starts.
|
|
54
160
|
|
|
@@ -78,6 +184,7 @@ def setup_logging() -> None:
|
|
|
78
184
|
force=True,
|
|
79
185
|
)
|
|
80
186
|
|
|
187
|
+
active_log_path: Optional[str] = None
|
|
81
188
|
if log_to_file:
|
|
82
189
|
log_dir = os.path.join(os.path.expanduser("~"), ".moleditpy")
|
|
83
190
|
try:
|
|
@@ -90,11 +197,23 @@ def setup_logging() -> None:
|
|
|
90
197
|
fh.setFormatter(logging.Formatter(fmt))
|
|
91
198
|
logging.getLogger().addHandler(fh)
|
|
92
199
|
logging.info("File logging enabled: %s", log_path)
|
|
200
|
+
active_log_path = log_path
|
|
93
201
|
except OSError as e:
|
|
94
202
|
logging.warning("Could not open log file: %s", e)
|
|
95
203
|
|
|
204
|
+
# Surface every ERROR/CRITICAL record in a dialog (see _ErrorDialogHandler);
|
|
205
|
+
# uncaught exceptions flow through here too via handle_exception's log call.
|
|
206
|
+
# It is a GUI feature, so it is skipped in headless mode (MOLEDITPY_HEADLESS),
|
|
207
|
+
# where a blocking modal would hang the run.
|
|
208
|
+
if not os.environ.get("MOLEDITPY_HEADLESS"):
|
|
209
|
+
logging.getLogger().addHandler(_ErrorDialogHandler(log_path=active_log_path))
|
|
210
|
+
|
|
96
211
|
def handle_exception(exc_type: Any, exc_value: Any, exc_traceback: Any) -> None:
|
|
97
|
-
"""Log unhandled exceptions
|
|
212
|
+
"""Log unhandled exceptions; the ERROR dialog handler surfaces them.
|
|
213
|
+
|
|
214
|
+
The dialog fires only for genuine failures — this log call and explicit
|
|
215
|
+
logging.error/critical calls — never for warnings/info.
|
|
216
|
+
"""
|
|
98
217
|
if issubclass(exc_type, KeyboardInterrupt):
|
|
99
218
|
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
|
100
219
|
return
|
{moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/plugins/plugin_manager.py
RENAMED
|
@@ -113,7 +113,7 @@ class PluginManager:
|
|
|
113
113
|
try:
|
|
114
114
|
os.makedirs(self.plugin_dir)
|
|
115
115
|
except OSError as e:
|
|
116
|
-
logging.
|
|
116
|
+
logging.warning(f"Error creating plugin directory: {e}")
|
|
117
117
|
|
|
118
118
|
def open_plugin_folder(self) -> None:
|
|
119
119
|
"""Opens the plugin directory in the OS file explorer."""
|
|
@@ -381,7 +381,9 @@ class PluginManager:
|
|
|
381
381
|
module.initialize(context)
|
|
382
382
|
except Exception as e: # plugins have full app access; catch everything to isolate faults
|
|
383
383
|
status = f"Error (Init): {e}"
|
|
384
|
-
logging.
|
|
384
|
+
logging.warning(
|
|
385
|
+
"Plugin %s initialize error", plugin_name, exc_info=True
|
|
386
|
+
)
|
|
385
387
|
elif has_autorun:
|
|
386
388
|
try:
|
|
387
389
|
if self.main_window:
|
|
@@ -390,7 +392,9 @@ class PluginManager:
|
|
|
390
392
|
status = "Skipped (No MW)"
|
|
391
393
|
except Exception as e: # plugins have full app access; catch everything to isolate faults
|
|
392
394
|
status = f"Error (Autorun): {e}"
|
|
393
|
-
logging.
|
|
395
|
+
logging.warning(
|
|
396
|
+
"Plugin %s autorun error", plugin_name, exc_info=True
|
|
397
|
+
)
|
|
394
398
|
elif not has_run:
|
|
395
399
|
status = "No Entry Point"
|
|
396
400
|
|
|
@@ -409,7 +413,9 @@ class PluginManager:
|
|
|
409
413
|
)
|
|
410
414
|
|
|
411
415
|
except Exception as e: # plugins have full app access; isolate any load failure to prevent crashing discovery
|
|
412
|
-
logging.
|
|
416
|
+
logging.warning(
|
|
417
|
+
"Failed to load plugin %s: %s", module_name, e, exc_info=True
|
|
418
|
+
)
|
|
413
419
|
|
|
414
420
|
def run_plugin(self, module: Any, main_window: Any) -> None:
|
|
415
421
|
"""Executes the plugin's run method (Legacy manual trigger)."""
|
|
@@ -620,7 +626,7 @@ class PluginManager:
|
|
|
620
626
|
except (RuntimeError, ValueError, TypeError):
|
|
621
627
|
continue
|
|
622
628
|
except (RuntimeError, AttributeError) as e:
|
|
623
|
-
logging.
|
|
629
|
+
logging.warning(f"Error retrieving selected atom indices: {e}")
|
|
624
630
|
|
|
625
631
|
return selected_indices
|
|
626
632
|
|
|
@@ -640,8 +646,11 @@ class PluginManager:
|
|
|
640
646
|
try:
|
|
641
647
|
handler["callback"]()
|
|
642
648
|
except Exception as e: # plugins have full app access; catch everything to prevent data loss on document reset
|
|
643
|
-
logging.
|
|
644
|
-
"Error in document reset handler for %s: %s",
|
|
649
|
+
logging.warning(
|
|
650
|
+
"Error in document reset handler for %s: %s",
|
|
651
|
+
handler["plugin"],
|
|
652
|
+
e,
|
|
653
|
+
exc_info=True,
|
|
645
654
|
)
|
|
646
655
|
|
|
647
656
|
def get_plugin_info_safe(self, file_path: str) -> Dict[str, str]:
|
{moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/plugins/plugin_manager_window.py
RENAMED
|
@@ -10,6 +10,7 @@ Repo: https://github.com/HiroYokoyama/python_molecular_editor
|
|
|
10
10
|
DOI: 10.5281/zenodo.17268532
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
import logging
|
|
13
14
|
import os
|
|
14
15
|
import shutil
|
|
15
16
|
from typing import Any, Optional
|
|
@@ -213,9 +214,7 @@ class PluginManagerWindow(QDialog):
|
|
|
213
214
|
f"Removed '{plugin.get('name', 'Unknown')}'.",
|
|
214
215
|
)
|
|
215
216
|
except OSError as e:
|
|
216
|
-
|
|
217
|
-
self, "Error", f"Failed to delete plugin: {e}"
|
|
218
|
-
)
|
|
217
|
+
logging.exception("Failed to delete plugin: %s", e)
|
|
219
218
|
else:
|
|
220
219
|
QMessageBox.warning(
|
|
221
220
|
self, "Error", f"Plugin file not found:\n{filepath}"
|
{moleditpy_linux-4.2.4 → moleditpy_linux-4.3.1}/src/moleditpy_linux/ui/align_plane_dialog.py
RENAMED
|
@@ -162,7 +162,7 @@ class AlignPlaneDialog(BasePickingDialog):
|
|
|
162
162
|
self.update_display()
|
|
163
163
|
|
|
164
164
|
except (AttributeError, RuntimeError, TypeError, KeyError) as e:
|
|
165
|
-
logging.
|
|
165
|
+
logging.warning("Failed to select all atoms", exc_info=True)
|
|
166
166
|
QMessageBox.warning(self, "Warning", f"Failed to select all atoms: {e}")
|
|
167
167
|
|
|
168
168
|
def update_display(self) -> None:
|
|
@@ -285,5 +285,4 @@ class AlignPlaneDialog(BasePickingDialog):
|
|
|
285
285
|
self.show_atom_labels()
|
|
286
286
|
|
|
287
287
|
except (AttributeError, RuntimeError, ValueError, TypeError) as e:
|
|
288
|
-
logging.exception("Failed to apply align")
|
|
289
|
-
QMessageBox.critical(self, "Error", f"Failed to apply align: {str(e)}")
|
|
288
|
+
logging.exception("Failed to apply align: %s", e)
|
|
@@ -276,8 +276,7 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
|
|
|
276
276
|
)
|
|
277
277
|
|
|
278
278
|
except (AttributeError, RuntimeError, ValueError, TypeError) as e:
|
|
279
|
-
logging.exception("Failed to apply alignment")
|
|
280
|
-
QMessageBox.critical(self, "Error", f"Failed to apply alignment: {str(e)}")
|
|
279
|
+
logging.exception("Failed to apply alignment: %s", e)
|
|
281
280
|
|
|
282
281
|
def closeEvent(self, event: Optional[QCloseEvent]) -> None:
|
|
283
282
|
"""Clean up when the dialog is closed."""
|
|
@@ -223,7 +223,11 @@ class StateManager:
|
|
|
223
223
|
self.host.view_3d_manager.draw_molecule_3d(
|
|
224
224
|
self.host.view_3d_manager.current_mol
|
|
225
225
|
)
|
|
226
|
-
|
|
226
|
+
# draw_molecule_3d already preserves the camera; only refit
|
|
227
|
+
# on a fresh load, not on undo/redo (which restores state).
|
|
228
|
+
if self.host.view_3d_manager.plotter and not getattr(
|
|
229
|
+
self.host, "is_restoring_state", False
|
|
230
|
+
):
|
|
227
231
|
self.host.view_3d_manager.plotter.reset_camera()
|
|
228
232
|
|
|
229
233
|
self.host.ui_manager.enable_3d_features(True)
|
|
@@ -232,7 +236,7 @@ class StateManager:
|
|
|
232
236
|
self.host.clear_3d_view()
|
|
233
237
|
self.host.ui_manager.enable_3d_features(False)
|
|
234
238
|
except (RuntimeError, ValueError, TypeError) as e:
|
|
235
|
-
logging.
|
|
239
|
+
logging.warning(f"Could not load 3D model from state data: {e}")
|
|
236
240
|
self.host.update_status_message(f"Error loading 3D model: {e}", 5000)
|
|
237
241
|
self.host.set_current_molecule(None)
|
|
238
242
|
self.host.ui_manager.enable_3d_features(False)
|
|
@@ -528,7 +532,9 @@ class StateManager:
|
|
|
528
532
|
p_state = callback()
|
|
529
533
|
plugin_data[name] = p_state
|
|
530
534
|
except Exception: # plugins have full app access; catch everything to prevent save corruption
|
|
531
|
-
logging.
|
|
535
|
+
logging.warning(
|
|
536
|
+
"Error saving state for plugin %s", name, exc_info=True
|
|
537
|
+
)
|
|
532
538
|
|
|
533
539
|
if plugin_data:
|
|
534
540
|
json_data["plugins"] = plugin_data
|
|
@@ -564,7 +570,9 @@ class StateManager:
|
|
|
564
570
|
try:
|
|
565
571
|
load_hand(p_state)
|
|
566
572
|
except Exception: # plugins have full app access; catch everything to prevent load corruption
|
|
567
|
-
logging.
|
|
573
|
+
logging.warning(
|
|
574
|
+
"Error loading state for plugin %s", name, exc_info=True
|
|
575
|
+
)
|
|
568
576
|
else:
|
|
569
577
|
# No handler found (plugin disabled or missing)
|
|
570
578
|
# Preserve data so it's not lost on next save
|
|
@@ -671,5 +679,5 @@ class StateManager:
|
|
|
671
679
|
"Suppressed non-critical error", exc_info=True
|
|
672
680
|
)
|
|
673
681
|
except (RuntimeError, ValueError, TypeError, binascii.Error) as e:
|
|
674
|
-
logging.
|
|
682
|
+
logging.warning(f"Could not restore 3D molecular data: {e}")
|
|
675
683
|
self.host.set_current_molecule(None)
|
|
@@ -96,7 +96,7 @@ class BondItem(QGraphicsItem):
|
|
|
96
96
|
view.viewport().update() # type: ignore[union-attr]
|
|
97
97
|
|
|
98
98
|
except (AttributeError, RuntimeError, TypeError):
|
|
99
|
-
logging.
|
|
99
|
+
logging.warning("Error in BondItem.set_stereo", exc_info=True)
|
|
100
100
|
# Continue without crashing
|
|
101
101
|
self.stereo = new_stereo
|
|
102
102
|
|
|
@@ -604,7 +604,7 @@ class BondItem(QGraphicsItem):
|
|
|
604
604
|
self.setPos(self.atom1.pos())
|
|
605
605
|
self.update()
|
|
606
606
|
except (AttributeError, RuntimeError):
|
|
607
|
-
logging.
|
|
607
|
+
logging.warning("Error updating bond position", exc_info=True)
|
|
608
608
|
# Continue without crashing
|
|
609
609
|
|
|
610
610
|
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent) -> None: # type: ignore[override]
|
|
@@ -390,7 +390,9 @@ class ComputeManager:
|
|
|
390
390
|
try:
|
|
391
391
|
success = bool(entry["callback"](mol))
|
|
392
392
|
except Exception: # plugins have full app access; isolate failures
|
|
393
|
-
logging.
|
|
393
|
+
logging.warning(
|
|
394
|
+
"Plugin optimization method '%s' failed", label, exc_info=True
|
|
395
|
+
)
|
|
394
396
|
self._refresh_ui_state()
|
|
395
397
|
self.host.update_status_message(
|
|
396
398
|
f"Plugin optimization '{label}' failed (see log)."
|
|
@@ -431,7 +433,7 @@ class ComputeManager:
|
|
|
431
433
|
try:
|
|
432
434
|
Chem.SanitizeMol(mol)
|
|
433
435
|
except (ValueError, RuntimeError) as e:
|
|
434
|
-
logging.
|
|
436
|
+
logging.warning(f"Sanitization failed: {e}")
|
|
435
437
|
self.host.statusBar().showMessage("Error: Invalid chemical structure.") # type: ignore[union-attr]
|
|
436
438
|
return None
|
|
437
439
|
return mol
|
|
@@ -587,7 +589,8 @@ class ComputeManager:
|
|
|
587
589
|
msg = str(message)
|
|
588
590
|
|
|
589
591
|
self._remove_calculating_text()
|
|
590
|
-
|
|
592
|
+
# Full restore like the success path, else Export stays disabled on error.
|
|
593
|
+
self._refresh_ui_state()
|
|
591
594
|
|
|
592
595
|
if msg == "Halt" or msg == "Halted":
|
|
593
596
|
self.host.statusBar().showMessage("Halted") # type: ignore[union-attr]
|