MoleditPy 4.10.3__tar.gz → 4.11.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-4.10.3 → moleditpy-4.11.1}/PKG-INFO +5 -5
- {moleditpy-4.10.3 → moleditpy-4.11.1}/README.md +4 -4
- {moleditpy-4.10.3 → moleditpy-4.11.1}/pyproject.toml +1 -1
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/PKG-INFO +5 -5
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/SOURCES.txt +7 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/core/mol_geometry.py +2 -2
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/core/molecular_data.py +39 -0
- moleditpy-4.11.1/src/moleditpy/core/stereo_check.py +237 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/main.py +15 -9
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/plugins/plugin_interface.py +65 -13
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/plugins/plugin_manager.py +27 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/plugins/plugin_manager_window.py +6 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/about_dialog.py +4 -2
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/align_plane_dialog.py +1 -1
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/alignment_dialog.py +4 -4
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/analysis_window.py +9 -23
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/angle_dialog.py +3 -3
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/atom_item.py +73 -210
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/atom_picking.py +33 -12
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/bond_length_dialog.py +3 -1
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/calculation_worker.py +8 -0
- moleditpy-4.11.1/src/moleditpy/ui/chirality_warning_dialog.py +178 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/color_settings_dialog.py +14 -152
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/compute_logic.py +97 -7
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/constrained_optimization_dialog.py +6 -10
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/custom_interactor_style.py +64 -63
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/dialog_3d_picking_mixin.py +20 -11
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/dialog_logic.py +55 -179
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/dihedral_dialog.py +3 -1
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/edit_3d_logic.py +8 -5
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/edit_actions_logic.py +89 -5
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/export_logic.py +9 -4
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/geometry_base_dialog.py +48 -2
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/io_logic.py +37 -14
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/main_window_init.py +1 -12
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/molecular_scene_handler.py +23 -2
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/molecule_scene.py +11 -5
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/move_dialog_mixin.py +7 -6
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/move_group_dialog.py +4 -18
- moleditpy-4.11.1/src/moleditpy/ui/periodic_table_dialog.py +80 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/planarize_dialog.py +1 -1
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/plugin_menu_manager.py +0 -9
- moleditpy-4.11.1/src/moleditpy/ui/sdf_record_dialog.py +90 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_dialog.py +3 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_2d_tab.py +28 -2
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_3d_tabs.py +58 -9
- moleditpy-4.11.1/src/moleditpy/ui/settings_tabs/settings_labels_tab.py +168 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_other_tab.py +1 -1
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/string_importers.py +3 -2
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/translation_dialog.py +27 -6
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/user_template_dialog.py +24 -6
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/view_3d_logic.py +142 -141
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/zoomable_view.py +13 -5
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/constants.py +124 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/default_settings.py +6 -0
- moleditpy-4.11.1/src/moleditpy/utils/finite_float.py +26 -0
- moleditpy-4.11.1/src/moleditpy/utils/label_style.py +123 -0
- moleditpy-4.11.1/src/moleditpy/utils/sdf_records.py +101 -0
- moleditpy-4.10.3/src/moleditpy/ui/periodic_table_dialog.py +0 -201
- {moleditpy-4.10.3 → moleditpy-4.11.1}/LICENSE +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/setup.cfg +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/entry_points.txt +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/requires.txt +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/top_level.txt +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/__init__.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/__main__.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/assets/file_icon.ico +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/assets/icon.icns +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/assets/icon.ico +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/assets/icon.png +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/core/__init__.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/plugins/__init__.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/__init__.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/app_state.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/base_picking_dialog.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/bond_item.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/chain_mixin.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/custom_qt_interactor.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/main_window.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/mirror_dialog.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/move_selected_atoms_dialog.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/preview_molecule.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/__init__.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_2d_cleanup_tab.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/template_preview_item.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/template_preview_view.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/ui_manager.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/__init__.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/hit_radius.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/suppress_log.py +0 -0
- {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/system_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.11.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
|
|
@@ -729,10 +729,10 @@ Dynamic: license-file
|
|
|
729
729
|
|
|
730
730
|
-----
|
|
731
731
|
<p align="center">
|
|
732
|
-
<img src="img/icon.png" width="500" alt="MoleditPy Icon">
|
|
732
|
+
<img src="https://hiroyokoyama.github.io/python_molecular_editor/img/icon.png" width="500" alt="MoleditPy Icon">
|
|
733
733
|
</p>
|
|
734
734
|
|
|
735
|
-

|
|
735
|
+

|
|
736
736
|
|
|
737
737
|
|
|
738
738
|
## Overview
|
|
@@ -846,7 +846,7 @@ moleditpy
|
|
|
846
846
|
|
|
847
847
|
## License & Disclaimer
|
|
848
848
|
|
|
849
|
-
This project is licensed under the GNU General Public License v3.0 (GPLv3) - see the [LICENSE](LICENSE) file for details. As open-source software, it is provided 'as is' without warranty of any kind, and the author assumes no responsibility or liability for the results. Although outputs have been carefully verified, users are strongly encouraged to independently check and validate them for critical applications (such as publications). If you encounter any bugs, please open an issue.
|
|
849
|
+
This project is licensed under the GNU General Public License v3.0 (GPLv3) - see the [LICENSE](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/LICENSE) file for details. As open-source software, it is provided 'as is' without warranty of any kind, and the author assumes no responsibility or liability for the results. Although outputs have been carefully verified, users are strongly encouraged to independently check and validate them for critical applications (such as publications). If you encounter any bugs, please open an issue.
|
|
850
850
|
|
|
851
851
|
## Citation
|
|
852
852
|
|
|
@@ -982,7 +982,7 @@ moleditpy
|
|
|
982
982
|
|
|
983
983
|
## ライセンス & 免責事項
|
|
984
984
|
|
|
985
|
-
このプロジェクトは GNU General Public License v3.0 (GPLv3) のもとでライセンスされています。詳細は [LICENSE](LICENSE) ファイルを参照してください。オープンソースソフトウェアとして、本ソフトウェアは「現状のまま」提供され、いかなる明示または黙示の保証も行いません。また、本ソフトウェアを使用した結果について、作者は一切の責任や義務を負いません。出力結果は慎重に検証されていますが、学術論文の作成など重要な用途においては、ユーザーご自身で結果を独立して確認および検証することを強くお勧めします。バグに遭遇した場合は、Issueを作成してください。
|
|
985
|
+
このプロジェクトは GNU General Public License v3.0 (GPLv3) のもとでライセンスされています。詳細は [LICENSE](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/LICENSE) ファイルを参照してください。オープンソースソフトウェアとして、本ソフトウェアは「現状のまま」提供され、いかなる明示または黙示の保証も行いません。また、本ソフトウェアを使用した結果について、作者は一切の責任や義務を負いません。出力結果は慎重に検証されていますが、学術論文の作成など重要な用途においては、ユーザーご自身で結果を独立して確認および検証することを強くお勧めします。バグに遭遇した場合は、Issueを作成してください。
|
|
986
986
|
|
|
987
987
|
## 引用
|
|
988
988
|
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
|
|
26
26
|
-----
|
|
27
27
|
<p align="center">
|
|
28
|
-
<img src="img/icon.png" width="500" alt="MoleditPy Icon">
|
|
28
|
+
<img src="https://hiroyokoyama.github.io/python_molecular_editor/img/icon.png" width="500" alt="MoleditPy Icon">
|
|
29
29
|
</p>
|
|
30
30
|
|
|
31
|
-

|
|
31
|
+

|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
## Overview
|
|
@@ -142,7 +142,7 @@ moleditpy
|
|
|
142
142
|
|
|
143
143
|
## License & Disclaimer
|
|
144
144
|
|
|
145
|
-
This project is licensed under the GNU General Public License v3.0 (GPLv3) - see the [LICENSE](LICENSE) file for details. As open-source software, it is provided 'as is' without warranty of any kind, and the author assumes no responsibility or liability for the results. Although outputs have been carefully verified, users are strongly encouraged to independently check and validate them for critical applications (such as publications). If you encounter any bugs, please open an issue.
|
|
145
|
+
This project is licensed under the GNU General Public License v3.0 (GPLv3) - see the [LICENSE](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/LICENSE) file for details. As open-source software, it is provided 'as is' without warranty of any kind, and the author assumes no responsibility or liability for the results. Although outputs have been carefully verified, users are strongly encouraged to independently check and validate them for critical applications (such as publications). If you encounter any bugs, please open an issue.
|
|
146
146
|
|
|
147
147
|
## Citation
|
|
148
148
|
|
|
@@ -278,7 +278,7 @@ moleditpy
|
|
|
278
278
|
|
|
279
279
|
## ライセンス & 免責事項
|
|
280
280
|
|
|
281
|
-
このプロジェクトは GNU General Public License v3.0 (GPLv3) のもとでライセンスされています。詳細は [LICENSE](LICENSE) ファイルを参照してください。オープンソースソフトウェアとして、本ソフトウェアは「現状のまま」提供され、いかなる明示または黙示の保証も行いません。また、本ソフトウェアを使用した結果について、作者は一切の責任や義務を負いません。出力結果は慎重に検証されていますが、学術論文の作成など重要な用途においては、ユーザーご自身で結果を独立して確認および検証することを強くお勧めします。バグに遭遇した場合は、Issueを作成してください。
|
|
281
|
+
このプロジェクトは GNU General Public License v3.0 (GPLv3) のもとでライセンスされています。詳細は [LICENSE](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/LICENSE) ファイルを参照してください。オープンソースソフトウェアとして、本ソフトウェアは「現状のまま」提供され、いかなる明示または黙示の保証も行いません。また、本ソフトウェアを使用した結果について、作者は一切の責任や義務を負いません。出力結果は慎重に検証されていますが、学術論文の作成など重要な用途においては、ユーザーご自身で結果を独立して確認および検証することを強くお勧めします。バグに遭遇した場合は、Issueを作成してください。
|
|
282
282
|
|
|
283
283
|
## 引用
|
|
284
284
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.11.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
|
|
@@ -729,10 +729,10 @@ Dynamic: license-file
|
|
|
729
729
|
|
|
730
730
|
-----
|
|
731
731
|
<p align="center">
|
|
732
|
-
<img src="img/icon.png" width="500" alt="MoleditPy Icon">
|
|
732
|
+
<img src="https://hiroyokoyama.github.io/python_molecular_editor/img/icon.png" width="500" alt="MoleditPy Icon">
|
|
733
733
|
</p>
|
|
734
734
|
|
|
735
|
-

|
|
735
|
+

|
|
736
736
|
|
|
737
737
|
|
|
738
738
|
## Overview
|
|
@@ -846,7 +846,7 @@ moleditpy
|
|
|
846
846
|
|
|
847
847
|
## License & Disclaimer
|
|
848
848
|
|
|
849
|
-
This project is licensed under the GNU General Public License v3.0 (GPLv3) - see the [LICENSE](LICENSE) file for details. As open-source software, it is provided 'as is' without warranty of any kind, and the author assumes no responsibility or liability for the results. Although outputs have been carefully verified, users are strongly encouraged to independently check and validate them for critical applications (such as publications). If you encounter any bugs, please open an issue.
|
|
849
|
+
This project is licensed under the GNU General Public License v3.0 (GPLv3) - see the [LICENSE](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/LICENSE) file for details. As open-source software, it is provided 'as is' without warranty of any kind, and the author assumes no responsibility or liability for the results. Although outputs have been carefully verified, users are strongly encouraged to independently check and validate them for critical applications (such as publications). If you encounter any bugs, please open an issue.
|
|
850
850
|
|
|
851
851
|
## Citation
|
|
852
852
|
|
|
@@ -982,7 +982,7 @@ moleditpy
|
|
|
982
982
|
|
|
983
983
|
## ライセンス & 免責事項
|
|
984
984
|
|
|
985
|
-
このプロジェクトは GNU General Public License v3.0 (GPLv3) のもとでライセンスされています。詳細は [LICENSE](LICENSE) ファイルを参照してください。オープンソースソフトウェアとして、本ソフトウェアは「現状のまま」提供され、いかなる明示または黙示の保証も行いません。また、本ソフトウェアを使用した結果について、作者は一切の責任や義務を負いません。出力結果は慎重に検証されていますが、学術論文の作成など重要な用途においては、ユーザーご自身で結果を独立して確認および検証することを強くお勧めします。バグに遭遇した場合は、Issueを作成してください。
|
|
985
|
+
このプロジェクトは GNU General Public License v3.0 (GPLv3) のもとでライセンスされています。詳細は [LICENSE](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/LICENSE) ファイルを参照してください。オープンソースソフトウェアとして、本ソフトウェアは「現状のまま」提供され、いかなる明示または黙示の保証も行いません。また、本ソフトウェアを使用した結果について、作者は一切の責任や義務を負いません。出力結果は慎重に検証されていますが、学術論文の作成など重要な用途においては、ユーザーご自身で結果を独立して確認および検証することを強くお勧めします。バグに遭遇した場合は、Issueを作成してください。
|
|
986
986
|
|
|
987
987
|
## 引用
|
|
988
988
|
|
|
@@ -17,6 +17,7 @@ src/moleditpy/assets/icon.png
|
|
|
17
17
|
src/moleditpy/core/__init__.py
|
|
18
18
|
src/moleditpy/core/mol_geometry.py
|
|
19
19
|
src/moleditpy/core/molecular_data.py
|
|
20
|
+
src/moleditpy/core/stereo_check.py
|
|
20
21
|
src/moleditpy/plugins/__init__.py
|
|
21
22
|
src/moleditpy/plugins/plugin_interface.py
|
|
22
23
|
src/moleditpy/plugins/plugin_manager.py
|
|
@@ -35,6 +36,7 @@ src/moleditpy/ui/bond_item.py
|
|
|
35
36
|
src/moleditpy/ui/bond_length_dialog.py
|
|
36
37
|
src/moleditpy/ui/calculation_worker.py
|
|
37
38
|
src/moleditpy/ui/chain_mixin.py
|
|
39
|
+
src/moleditpy/ui/chirality_warning_dialog.py
|
|
38
40
|
src/moleditpy/ui/color_settings_dialog.py
|
|
39
41
|
src/moleditpy/ui/compute_logic.py
|
|
40
42
|
src/moleditpy/ui/constrained_optimization_dialog.py
|
|
@@ -60,6 +62,7 @@ src/moleditpy/ui/periodic_table_dialog.py
|
|
|
60
62
|
src/moleditpy/ui/planarize_dialog.py
|
|
61
63
|
src/moleditpy/ui/plugin_menu_manager.py
|
|
62
64
|
src/moleditpy/ui/preview_molecule.py
|
|
65
|
+
src/moleditpy/ui/sdf_record_dialog.py
|
|
63
66
|
src/moleditpy/ui/settings_dialog.py
|
|
64
67
|
src/moleditpy/ui/string_importers.py
|
|
65
68
|
src/moleditpy/ui/template_preview_item.py
|
|
@@ -73,12 +76,16 @@ src/moleditpy/ui/settings_tabs/__init__.py
|
|
|
73
76
|
src/moleditpy/ui/settings_tabs/settings_2d_cleanup_tab.py
|
|
74
77
|
src/moleditpy/ui/settings_tabs/settings_2d_tab.py
|
|
75
78
|
src/moleditpy/ui/settings_tabs/settings_3d_tabs.py
|
|
79
|
+
src/moleditpy/ui/settings_tabs/settings_labels_tab.py
|
|
76
80
|
src/moleditpy/ui/settings_tabs/settings_other_tab.py
|
|
77
81
|
src/moleditpy/ui/settings_tabs/settings_tab_base.py
|
|
78
82
|
src/moleditpy/utils/__init__.py
|
|
79
83
|
src/moleditpy/utils/constants.py
|
|
80
84
|
src/moleditpy/utils/default_settings.py
|
|
85
|
+
src/moleditpy/utils/finite_float.py
|
|
81
86
|
src/moleditpy/utils/hit_radius.py
|
|
87
|
+
src/moleditpy/utils/label_style.py
|
|
88
|
+
src/moleditpy/utils/sdf_records.py
|
|
82
89
|
src/moleditpy/utils/sip_isdeleted_safe.py
|
|
83
90
|
src/moleditpy/utils/suppress_log.py
|
|
84
91
|
src/moleditpy/utils/system_utils.py
|
|
@@ -720,7 +720,7 @@ def resolve_2d_overlaps(
|
|
|
720
720
|
return []
|
|
721
721
|
|
|
722
722
|
# Union-Find for overlap groups
|
|
723
|
-
parent = {aid: aid for aid in
|
|
723
|
+
parent = {aid: aid for aid in ids_list}
|
|
724
724
|
|
|
725
725
|
def find_set(aid: int) -> int:
|
|
726
726
|
"""Find representative root for an element in disjoint-set."""
|
|
@@ -743,7 +743,7 @@ def resolve_2d_overlaps(
|
|
|
743
743
|
unite_sets(id1, id2)
|
|
744
744
|
|
|
745
745
|
groups_by_root: Dict[int, List[int]] = {}
|
|
746
|
-
for aid in
|
|
746
|
+
for aid in ids_list:
|
|
747
747
|
root = find_set(aid)
|
|
748
748
|
groups_by_root.setdefault(root, []).append(aid)
|
|
749
749
|
|
|
@@ -14,6 +14,7 @@ from __future__ import annotations
|
|
|
14
14
|
import logging
|
|
15
15
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
16
16
|
from rdkit import Chem
|
|
17
|
+
from rdkit.Chem import rdCIPLabeler
|
|
17
18
|
|
|
18
19
|
from ..utils.constants import ANGSTROM_PER_PIXEL
|
|
19
20
|
|
|
@@ -117,6 +118,38 @@ def _to_v3000_block(
|
|
|
117
118
|
return "\n".join(lines) + "\n"
|
|
118
119
|
|
|
119
120
|
|
|
121
|
+
def align_ez_to_cip(mol: Chem.Mol, wanted: Dict[int, str]) -> None:
|
|
122
|
+
"""Make labelled double bonds read as the wanted CIP E/Z, flipping if needed.
|
|
123
|
+
|
|
124
|
+
*wanted* maps bond index to "E" or "Z". The bonds' STEREOE/STEREOZ is
|
|
125
|
+
encoded against whichever neighbors were picked as stereo atoms, which
|
|
126
|
+
need not be the CIP-higher ones (on CH3-C(Cl)=CH-CH3 the methyl is picked
|
|
127
|
+
over Cl), so a "Z" label could encode E. Ask the CIP labeler what each
|
|
128
|
+
bond currently means and flip the ones that read the other way. Bonds
|
|
129
|
+
with no CIP E/Z (two identical substituents on one end) are left alone.
|
|
130
|
+
"""
|
|
131
|
+
if not wanted:
|
|
132
|
+
return
|
|
133
|
+
probe = Chem.Mol(mol)
|
|
134
|
+
try:
|
|
135
|
+
rdCIPLabeler.AssignCIPLabels(probe)
|
|
136
|
+
except (RuntimeError, ValueError) as e:
|
|
137
|
+
logging.warning("CIP labelling for E/Z labels failed: %s", e)
|
|
138
|
+
return
|
|
139
|
+
flipped = {
|
|
140
|
+
Chem.BondStereo.STEREOZ: Chem.BondStereo.STEREOE,
|
|
141
|
+
Chem.BondStereo.STEREOE: Chem.BondStereo.STEREOZ,
|
|
142
|
+
}
|
|
143
|
+
for bond_idx, label in wanted.items():
|
|
144
|
+
probe_bond = probe.GetBondWithIdx(bond_idx)
|
|
145
|
+
if not probe_bond.HasProp("_CIPCode"):
|
|
146
|
+
continue
|
|
147
|
+
if probe_bond.GetProp("_CIPCode") != label:
|
|
148
|
+
bond = mol.GetBondWithIdx(bond_idx)
|
|
149
|
+
if bond.GetStereo() in flipped:
|
|
150
|
+
bond.SetStereo(flipped[bond.GetStereo()])
|
|
151
|
+
|
|
152
|
+
|
|
120
153
|
class MolecularData:
|
|
121
154
|
"""In-memory graph of atoms and bonds, independent of any UI framework."""
|
|
122
155
|
|
|
@@ -355,6 +388,7 @@ class MolecularData:
|
|
|
355
388
|
return None
|
|
356
389
|
|
|
357
390
|
# Overwrite based on labels (E/Z has highest priority) ---
|
|
391
|
+
ez_wanted: Dict[int, str] = {}
|
|
358
392
|
for bond_idx, info in bond_stereo_info.items():
|
|
359
393
|
stereo_type = info["type"]
|
|
360
394
|
bond = final_mol.GetBondWithIdx(bond_idx)
|
|
@@ -402,6 +436,7 @@ class MolecularData:
|
|
|
402
436
|
bond.SetStereo(Chem.BondStereo.STEREOZ)
|
|
403
437
|
elif stereo_type == 4:
|
|
404
438
|
bond.SetStereo(Chem.BondStereo.STEREOE)
|
|
439
|
+
ez_wanted[bond_idx] = "Z" if stereo_type == 3 else "E"
|
|
405
440
|
|
|
406
441
|
# Clear BondDir (wedge/dash) of adjacent single bonds assigned via coordinates to avoid label conflicts
|
|
407
442
|
b1 = final_mol.GetBondBetweenAtoms(begin_atom_idx, neigh1_idx)
|
|
@@ -411,6 +446,10 @@ class MolecularData:
|
|
|
411
446
|
if b2 is not None:
|
|
412
447
|
b2.SetBondDir(Chem.BondDir.NONE)
|
|
413
448
|
|
|
449
|
+
# The labels are CIP E/Z; the neighbors picked above are not always
|
|
450
|
+
# the CIP-higher ones.
|
|
451
|
+
align_ez_to_cip(final_mol, ez_wanted)
|
|
452
|
+
|
|
414
453
|
# Finalization (cache update + stereochemistry reassignment)
|
|
415
454
|
final_mol.UpdatePropertyCache(strict=False)
|
|
416
455
|
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
MoleditPy — A Python-based molecular editing software
|
|
6
|
+
|
|
7
|
+
Author: Hiromichi Yokoyama
|
|
8
|
+
License: GPL-3.0 license
|
|
9
|
+
Repo: https://github.com/HiroYokoyama/python_molecular_editor
|
|
10
|
+
DOI: 10.5281/zenodo.17268532
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from typing import Dict, FrozenSet, List, Optional, Tuple
|
|
18
|
+
|
|
19
|
+
from rdkit import Chem
|
|
20
|
+
from rdkit.Chem import rdCIPLabeler
|
|
21
|
+
|
|
22
|
+
from .molecular_data import MolecularData
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class ChiralityMismatch:
|
|
27
|
+
"""A stereocenter whose 3D configuration differs from the 2D drawing."""
|
|
28
|
+
|
|
29
|
+
atom_id: int
|
|
30
|
+
symbol: str
|
|
31
|
+
rdkit_index: Optional[int]
|
|
32
|
+
drawn: str
|
|
33
|
+
actual: Optional[str]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class EZMismatch:
|
|
38
|
+
"""A labelled double bond whose 3D configuration differs from the label."""
|
|
39
|
+
|
|
40
|
+
atom_ids: Tuple[int, int]
|
|
41
|
+
symbols: Tuple[str, str]
|
|
42
|
+
rdkit_bond_index: Optional[int]
|
|
43
|
+
rdkit_atom_indices: Optional[Tuple[int, int]]
|
|
44
|
+
drawn: str
|
|
45
|
+
actual: Optional[str]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _cip_labels_by_atom_id(mol: Chem.Mol) -> Dict[int, str]:
|
|
49
|
+
"""Map editor atom IDs to CIP labels ('R'/'S') for every labelled center."""
|
|
50
|
+
rdCIPLabeler.AssignCIPLabels(mol)
|
|
51
|
+
labels: Dict[int, str] = {}
|
|
52
|
+
for atom in mol.GetAtoms():
|
|
53
|
+
if atom.HasProp("_CIPCode") and atom.HasProp("_original_atom_id"):
|
|
54
|
+
labels[atom.GetIntProp("_original_atom_id")] = atom.GetProp("_CIPCode")
|
|
55
|
+
return labels
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def drawn_chirality(data: MolecularData) -> Dict[int, str]:
|
|
59
|
+
"""CIP labels of the stereocenters the 2D drawing specifies with wedges."""
|
|
60
|
+
# Nothing is specified without a wedge or hash; skip CIP labelling.
|
|
61
|
+
if not any(bond.get("stereo") in (1, 2) for bond in data.bonds.values()):
|
|
62
|
+
return {}
|
|
63
|
+
mol = data.to_rdkit_mol(use_2d_stereo=False)
|
|
64
|
+
if mol is None:
|
|
65
|
+
return {}
|
|
66
|
+
Chem.AssignChiralTypesFromBondDirs(mol)
|
|
67
|
+
Chem.AssignStereochemistry(mol, cleanIt=True, force=True)
|
|
68
|
+
return _cip_labels_by_atom_id(mol)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def actual_chirality(mol_3d: Chem.Mol) -> Dict[int, str]:
|
|
72
|
+
"""CIP labels of the stereocenters in a 3D structure, read from coordinates."""
|
|
73
|
+
if mol_3d.GetNumConformers() == 0:
|
|
74
|
+
return {}
|
|
75
|
+
mol = Chem.Mol(mol_3d)
|
|
76
|
+
Chem.AssignStereochemistryFrom3D(mol)
|
|
77
|
+
return _cip_labels_by_atom_id(mol)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def find_chirality_mismatches(
|
|
81
|
+
data: MolecularData, mol_3d: Chem.Mol
|
|
82
|
+
) -> List[ChiralityMismatch]:
|
|
83
|
+
"""Stereocenters drawn in 2D whose configuration the 3D structure does not keep.
|
|
84
|
+
|
|
85
|
+
Only centers the drawing specifies (wedge or hash) are compared; an
|
|
86
|
+
unspecified center can come out either way. A center that is no longer a
|
|
87
|
+
stereocenter in 3D is reported with ``actual`` None.
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
drawn = drawn_chirality(data)
|
|
91
|
+
if not drawn:
|
|
92
|
+
return []
|
|
93
|
+
actual = actual_chirality(mol_3d)
|
|
94
|
+
except (RuntimeError, ValueError) as e:
|
|
95
|
+
logging.warning("Chirality check failed: %s", e)
|
|
96
|
+
return []
|
|
97
|
+
|
|
98
|
+
index_of = {
|
|
99
|
+
atom.GetIntProp("_original_atom_id"): atom.GetIdx()
|
|
100
|
+
for atom in mol_3d.GetAtoms()
|
|
101
|
+
if atom.HasProp("_original_atom_id")
|
|
102
|
+
}
|
|
103
|
+
return [
|
|
104
|
+
ChiralityMismatch(
|
|
105
|
+
atom_id=atom_id,
|
|
106
|
+
symbol=str(data.atoms[atom_id]["symbol"]),
|
|
107
|
+
rdkit_index=index_of.get(atom_id),
|
|
108
|
+
drawn=label,
|
|
109
|
+
actual=actual.get(atom_id),
|
|
110
|
+
)
|
|
111
|
+
for atom_id, label in sorted(drawn.items())
|
|
112
|
+
if actual.get(atom_id) != label and atom_id in data.atoms
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _bond_cip_by_atom_ids(
|
|
117
|
+
mol: Chem.Mol,
|
|
118
|
+
) -> Dict[FrozenSet[int], Tuple[int, str]]:
|
|
119
|
+
"""Map editor atom-ID pairs to (bond index, CIP E/Z) for every labelled bond.
|
|
120
|
+
|
|
121
|
+
Expects CIP labels already assigned on *mol*.
|
|
122
|
+
"""
|
|
123
|
+
out: Dict[FrozenSet[int], Tuple[int, str]] = {}
|
|
124
|
+
for bond in mol.GetBonds():
|
|
125
|
+
begin, end = bond.GetBeginAtom(), bond.GetEndAtom()
|
|
126
|
+
if not (
|
|
127
|
+
bond.HasProp("_CIPCode")
|
|
128
|
+
and begin.HasProp("_original_atom_id")
|
|
129
|
+
and end.HasProp("_original_atom_id")
|
|
130
|
+
):
|
|
131
|
+
continue
|
|
132
|
+
key = frozenset(
|
|
133
|
+
(begin.GetIntProp("_original_atom_id"), end.GetIntProp("_original_atom_id"))
|
|
134
|
+
)
|
|
135
|
+
out[key] = (bond.GetIdx(), bond.GetProp("_CIPCode"))
|
|
136
|
+
return out
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def drawn_ez(data: MolecularData) -> Dict[Tuple[int, int], str]:
|
|
140
|
+
"""E/Z labels ("E"/"Z") of the drawn double bonds that have a CIP E/Z.
|
|
141
|
+
|
|
142
|
+
A label on a bond with two identical groups at one end is dropped: that
|
|
143
|
+
bond has no E/Z, so the label cannot be checked.
|
|
144
|
+
"""
|
|
145
|
+
labelled = {
|
|
146
|
+
(id1, id2): ("Z" if bond["stereo"] == 3 else "E")
|
|
147
|
+
for (id1, id2), bond in data.bonds.items()
|
|
148
|
+
if bond.get("stereo") in (3, 4)
|
|
149
|
+
}
|
|
150
|
+
if not labelled:
|
|
151
|
+
return {}
|
|
152
|
+
drawn_mol = data.to_rdkit_mol(use_2d_stereo=False)
|
|
153
|
+
if drawn_mol is None:
|
|
154
|
+
return {}
|
|
155
|
+
rdCIPLabeler.AssignCIPLabels(drawn_mol)
|
|
156
|
+
comparable = _bond_cip_by_atom_ids(drawn_mol)
|
|
157
|
+
return {
|
|
158
|
+
pair: label for pair, label in labelled.items() if frozenset(pair) in comparable
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def actual_ez(mol_3d: Chem.Mol) -> Dict[int, str]:
|
|
163
|
+
"""CIP E/Z ("E"/"Z") of every stereo double bond in a 3D structure.
|
|
164
|
+
|
|
165
|
+
Keyed by RDKit bond index and read from the coordinates, with the same
|
|
166
|
+
labeller as find_ez_mismatches, so a label and the check always agree.
|
|
167
|
+
"""
|
|
168
|
+
if mol_3d.GetNumConformers() == 0:
|
|
169
|
+
return {}
|
|
170
|
+
probe = Chem.Mol(mol_3d)
|
|
171
|
+
Chem.AssignStereochemistryFrom3D(probe)
|
|
172
|
+
rdCIPLabeler.AssignCIPLabels(probe)
|
|
173
|
+
return {
|
|
174
|
+
bond.GetIdx(): bond.GetProp("_CIPCode")
|
|
175
|
+
for bond in probe.GetBonds()
|
|
176
|
+
if bond.HasProp("_CIPCode") and bond.GetProp("_CIPCode") in ("E", "Z")
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def find_ez_mismatches(data: MolecularData, mol_3d: Chem.Mol) -> List[EZMismatch]:
|
|
181
|
+
"""Double bonds labelled E or Z in 2D whose 3D configuration differs.
|
|
182
|
+
|
|
183
|
+
Only bonds with a CIP E/Z are compared (see drawn_ez).
|
|
184
|
+
"""
|
|
185
|
+
if mol_3d.GetNumConformers() == 0:
|
|
186
|
+
return []
|
|
187
|
+
try:
|
|
188
|
+
drawn = drawn_ez(data)
|
|
189
|
+
if not drawn:
|
|
190
|
+
return []
|
|
191
|
+
probe = Chem.Mol(mol_3d)
|
|
192
|
+
Chem.AssignStereochemistryFrom3D(probe)
|
|
193
|
+
rdCIPLabeler.AssignCIPLabels(probe)
|
|
194
|
+
actual = _bond_cip_by_atom_ids(probe)
|
|
195
|
+
except (RuntimeError, ValueError) as e:
|
|
196
|
+
logging.warning("E/Z check failed: %s", e)
|
|
197
|
+
return []
|
|
198
|
+
|
|
199
|
+
bond_of: Dict[FrozenSet[int], Chem.Bond] = {
|
|
200
|
+
frozenset(
|
|
201
|
+
(
|
|
202
|
+
b.GetBeginAtom().GetIntProp("_original_atom_id"),
|
|
203
|
+
b.GetEndAtom().GetIntProp("_original_atom_id"),
|
|
204
|
+
)
|
|
205
|
+
): b
|
|
206
|
+
for b in mol_3d.GetBonds()
|
|
207
|
+
if b.GetBeginAtom().HasProp("_original_atom_id")
|
|
208
|
+
and b.GetEndAtom().HasProp("_original_atom_id")
|
|
209
|
+
}
|
|
210
|
+
mismatches: List[EZMismatch] = []
|
|
211
|
+
for (id1, id2), label in sorted(drawn.items()):
|
|
212
|
+
if id1 not in data.atoms or id2 not in data.atoms:
|
|
213
|
+
continue
|
|
214
|
+
key = frozenset((id1, id2))
|
|
215
|
+
got = actual.get(key)
|
|
216
|
+
got_label = got[1] if got else None
|
|
217
|
+
if got_label == label:
|
|
218
|
+
continue
|
|
219
|
+
bond = bond_of.get(key)
|
|
220
|
+
mismatches.append(
|
|
221
|
+
EZMismatch(
|
|
222
|
+
atom_ids=(id1, id2),
|
|
223
|
+
symbols=(
|
|
224
|
+
str(data.atoms[id1]["symbol"]),
|
|
225
|
+
str(data.atoms[id2]["symbol"]),
|
|
226
|
+
),
|
|
227
|
+
rdkit_bond_index=bond.GetIdx() if bond is not None else None,
|
|
228
|
+
rdkit_atom_indices=(
|
|
229
|
+
(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx())
|
|
230
|
+
if bond is not None
|
|
231
|
+
else None
|
|
232
|
+
),
|
|
233
|
+
drawn=label,
|
|
234
|
+
actual=got_label,
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
return mismatches
|
|
@@ -166,6 +166,9 @@ def _read_startup_log_settings() -> tuple[bool, bool]:
|
|
|
166
166
|
try:
|
|
167
167
|
with open(settings_path, encoding="utf-8") as f:
|
|
168
168
|
data = json.load(f)
|
|
169
|
+
# Valid JSON that is not an object ([] or null) must not stop startup.
|
|
170
|
+
if not isinstance(data, dict):
|
|
171
|
+
return False, False
|
|
169
172
|
return bool(data.get("log_to_file", False)), bool(
|
|
170
173
|
data.get("log_level_debug", False)
|
|
171
174
|
)
|
|
@@ -265,10 +268,9 @@ def main() -> None:
|
|
|
265
268
|
print(f"Error: Plugin path not found: {plugin_path}")
|
|
266
269
|
sys.exit(1)
|
|
267
270
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
from .plugins.plugin_manager import PluginManager
|
|
271
|
+
# Relative: an absolute "moleditpy" import would pick up the other
|
|
272
|
+
# package when both the main and Linux builds are installed.
|
|
273
|
+
from .plugins.plugin_manager import PluginManager
|
|
272
274
|
|
|
273
275
|
pm = PluginManager()
|
|
274
276
|
sha256 = pm.compute_sha256(plugin_path)
|
|
@@ -299,9 +301,15 @@ def main() -> None:
|
|
|
299
301
|
print(f" SHA-256: {sha256}")
|
|
300
302
|
print("=" * 40)
|
|
301
303
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
304
|
+
try:
|
|
305
|
+
confirm = (
|
|
306
|
+
input("\nDo you want to proceed with installation? (y/N): ")
|
|
307
|
+
.strip()
|
|
308
|
+
.lower()
|
|
309
|
+
)
|
|
310
|
+
except EOFError:
|
|
311
|
+
# No terminal to answer from (piped or scripted run): treat as No.
|
|
312
|
+
confirm = ""
|
|
305
313
|
if confirm == "y":
|
|
306
314
|
success, msg = pm.install_plugin(plugin_path)
|
|
307
315
|
if success:
|
|
@@ -321,8 +329,6 @@ def main() -> None:
|
|
|
321
329
|
|
|
322
330
|
if sys.platform == "win32":
|
|
323
331
|
try:
|
|
324
|
-
from PyQt6.QtCore import QTimer
|
|
325
|
-
|
|
326
332
|
QTimer.singleShot(100, lambda: window.setWindowIcon(window.windowIcon()))
|
|
327
333
|
except (
|
|
328
334
|
Exception
|