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.
Files changed (94) hide show
  1. {moleditpy-4.10.3 → moleditpy-4.11.1}/PKG-INFO +5 -5
  2. {moleditpy-4.10.3 → moleditpy-4.11.1}/README.md +4 -4
  3. {moleditpy-4.10.3 → moleditpy-4.11.1}/pyproject.toml +1 -1
  4. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/PKG-INFO +5 -5
  5. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/SOURCES.txt +7 -0
  6. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/core/mol_geometry.py +2 -2
  7. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/core/molecular_data.py +39 -0
  8. moleditpy-4.11.1/src/moleditpy/core/stereo_check.py +237 -0
  9. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/main.py +15 -9
  10. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/plugins/plugin_interface.py +65 -13
  11. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/plugins/plugin_manager.py +27 -0
  12. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/plugins/plugin_manager_window.py +6 -0
  13. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/about_dialog.py +4 -2
  14. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/align_plane_dialog.py +1 -1
  15. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/alignment_dialog.py +4 -4
  16. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/analysis_window.py +9 -23
  17. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/angle_dialog.py +3 -3
  18. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/atom_item.py +73 -210
  19. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/atom_picking.py +33 -12
  20. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/bond_length_dialog.py +3 -1
  21. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/calculation_worker.py +8 -0
  22. moleditpy-4.11.1/src/moleditpy/ui/chirality_warning_dialog.py +178 -0
  23. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/color_settings_dialog.py +14 -152
  24. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/compute_logic.py +97 -7
  25. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/constrained_optimization_dialog.py +6 -10
  26. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/custom_interactor_style.py +64 -63
  27. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/dialog_3d_picking_mixin.py +20 -11
  28. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/dialog_logic.py +55 -179
  29. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/dihedral_dialog.py +3 -1
  30. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/edit_3d_logic.py +8 -5
  31. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/edit_actions_logic.py +89 -5
  32. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/export_logic.py +9 -4
  33. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/geometry_base_dialog.py +48 -2
  34. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/io_logic.py +37 -14
  35. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/main_window_init.py +1 -12
  36. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/molecular_scene_handler.py +23 -2
  37. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/molecule_scene.py +11 -5
  38. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/move_dialog_mixin.py +7 -6
  39. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/move_group_dialog.py +4 -18
  40. moleditpy-4.11.1/src/moleditpy/ui/periodic_table_dialog.py +80 -0
  41. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/planarize_dialog.py +1 -1
  42. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/plugin_menu_manager.py +0 -9
  43. moleditpy-4.11.1/src/moleditpy/ui/sdf_record_dialog.py +90 -0
  44. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_dialog.py +3 -0
  45. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_2d_tab.py +28 -2
  46. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_3d_tabs.py +58 -9
  47. moleditpy-4.11.1/src/moleditpy/ui/settings_tabs/settings_labels_tab.py +168 -0
  48. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_other_tab.py +1 -1
  49. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/string_importers.py +3 -2
  50. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/translation_dialog.py +27 -6
  51. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/user_template_dialog.py +24 -6
  52. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/view_3d_logic.py +142 -141
  53. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/zoomable_view.py +13 -5
  54. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/constants.py +124 -0
  55. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/default_settings.py +6 -0
  56. moleditpy-4.11.1/src/moleditpy/utils/finite_float.py +26 -0
  57. moleditpy-4.11.1/src/moleditpy/utils/label_style.py +123 -0
  58. moleditpy-4.11.1/src/moleditpy/utils/sdf_records.py +101 -0
  59. moleditpy-4.10.3/src/moleditpy/ui/periodic_table_dialog.py +0 -201
  60. {moleditpy-4.10.3 → moleditpy-4.11.1}/LICENSE +0 -0
  61. {moleditpy-4.10.3 → moleditpy-4.11.1}/setup.cfg +0 -0
  62. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
  63. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/entry_points.txt +0 -0
  64. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/requires.txt +0 -0
  65. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/MoleditPy.egg-info/top_level.txt +0 -0
  66. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/__init__.py +0 -0
  67. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/__main__.py +0 -0
  68. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/assets/file_icon.ico +0 -0
  69. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/assets/icon.icns +0 -0
  70. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/assets/icon.ico +0 -0
  71. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/assets/icon.png +0 -0
  72. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/core/__init__.py +0 -0
  73. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/plugins/__init__.py +0 -0
  74. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/__init__.py +0 -0
  75. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/app_state.py +0 -0
  76. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/base_picking_dialog.py +0 -0
  77. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/bond_item.py +0 -0
  78. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/chain_mixin.py +0 -0
  79. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/custom_qt_interactor.py +0 -0
  80. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/main_window.py +0 -0
  81. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/mirror_dialog.py +0 -0
  82. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/move_selected_atoms_dialog.py +0 -0
  83. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/preview_molecule.py +0 -0
  84. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/__init__.py +0 -0
  85. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_2d_cleanup_tab.py +0 -0
  86. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/settings_tabs/settings_tab_base.py +0 -0
  87. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/template_preview_item.py +0 -0
  88. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/template_preview_view.py +0 -0
  89. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/ui/ui_manager.py +0 -0
  90. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/__init__.py +0 -0
  91. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/hit_radius.py +0 -0
  92. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/sip_isdeleted_safe.py +0 -0
  93. {moleditpy-4.10.3 → moleditpy-4.11.1}/src/moleditpy/utils/suppress_log.py +0 -0
  94. {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.10.3
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
- ![](img/screenshot.png)
735
+ ![MoleditPy screenshot](https://hiroyokoyama.github.io/python_molecular_editor/img/screenshot.png)
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
- ![](img/screenshot.png)
31
+ ![MoleditPy screenshot](https://hiroyokoyama.github.io/python_molecular_editor/img/screenshot.png)
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
 
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "MoleditPy"
7
7
 
8
- version = "4.10.3"
8
+ version = "4.11.1"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 4.10.3
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
- ![](img/screenshot.png)
735
+ ![MoleditPy screenshot](https://hiroyokoyama.github.io/python_molecular_editor/img/screenshot.png)
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 atom_ids}
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 atom_ids:
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
- try:
269
- from moleditpy.plugins.plugin_manager import PluginManager
270
- except ImportError:
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
- confirm = (
303
- input("\nDo you want to proceed with installation? (y/N): ").strip().lower()
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