MoleditPy-linux 4.8.3__tar.gz → 4.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/PKG-INFO +2 -2
  2. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/README.md +1 -1
  3. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/pyproject.toml +1 -1
  4. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/MoleditPy_linux.egg-info/PKG-INFO +2 -2
  5. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/MoleditPy_linux.egg-info/SOURCES.txt +1 -0
  6. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/core/mol_geometry.py +53 -2
  7. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/atom_item.py +7 -7
  8. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/bond_item.py +5 -5
  9. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/edit_actions_logic.py +20 -1
  10. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/main_window_init.py +1 -1
  11. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/molecular_scene_handler.py +2 -2
  12. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/settings_dialog.py +4 -0
  13. moleditpy_linux-4.9.0/src/moleditpy_linux/ui/settings_tabs/settings_2d_cleanup_tab.py +146 -0
  14. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +6 -5
  15. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/utils/default_settings.py +11 -6
  16. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/LICENSE +0 -0
  17. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/setup.cfg +0 -0
  18. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
  19. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
  20. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
  21. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
  22. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/__init__.py +0 -0
  23. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/__main__.py +0 -0
  24. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/assets/file_icon.ico +0 -0
  25. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/assets/icon.icns +0 -0
  26. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/assets/icon.ico +0 -0
  27. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/assets/icon.png +0 -0
  28. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/core/__init__.py +0 -0
  29. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/core/molecular_data.py +0 -0
  30. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/main.py +0 -0
  31. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/plugins/__init__.py +0 -0
  32. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/plugins/plugin_interface.py +0 -0
  33. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/plugins/plugin_manager.py +0 -0
  34. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
  35. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/__init__.py +0 -0
  36. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/about_dialog.py +0 -0
  37. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
  38. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
  39. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/analysis_window.py +0 -0
  40. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
  41. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/app_state.py +0 -0
  42. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/atom_picking.py +0 -0
  43. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
  44. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
  45. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
  46. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/chain_mixin.py +0 -0
  47. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
  48. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/compute_logic.py +0 -0
  49. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
  50. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -0
  51. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
  52. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
  53. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
  54. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
  55. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
  56. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/export_logic.py +0 -0
  57. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
  58. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/io_logic.py +0 -0
  59. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/main_window.py +0 -0
  60. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
  61. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
  62. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
  63. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +0 -0
  64. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
  65. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
  66. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/plugin_menu_manager.py +0 -0
  67. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/preview_molecule.py +0 -0
  68. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
  69. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
  70. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
  71. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
  72. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/string_importers.py +0 -0
  73. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
  74. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
  75. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/translation_dialog.py +0 -0
  76. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/ui_manager.py +0 -0
  77. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
  78. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/view_3d_logic.py +0 -0
  79. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
  80. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/utils/__init__.py +0 -0
  81. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/utils/constants.py +0 -0
  82. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/utils/hit_radius.py +0 -0
  83. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
  84. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/src/moleditpy_linux/utils/suppress_log.py +0 -0
  85. {moleditpy_linux-4.8.3 → moleditpy_linux-4.9.0}/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.8.3
3
+ Version: 4.9.0
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
@@ -709,7 +709,7 @@ Dynamic: license-file
709
709
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
710
710
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
711
711
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
712
- [![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/coverage_report.md)
712
+ [![codecov](https://codecov.io/gh/HiroYokoyama/python_molecular_editor/graph/badge.svg)](https://codecov.io/gh/HiroYokoyama/python_molecular_editor)
713
713
  [![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/MANUAL_CHECKLIST.md)
714
714
  [![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/pylint-score.txt)
715
715
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
@@ -6,7 +6,7 @@
6
6
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
7
7
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
8
8
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
9
- [![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/coverage_report.md)
9
+ [![codecov](https://codecov.io/gh/HiroYokoyama/python_molecular_editor/graph/badge.svg)](https://codecov.io/gh/HiroYokoyama/python_molecular_editor)
10
10
  [![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/MANUAL_CHECKLIST.md)
11
11
  [![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/pylint-score.txt)
12
12
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "MoleditPy-linux"
7
7
 
8
- version = "4.8.3"
8
+ version = "4.9.0"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 4.8.3
3
+ Version: 4.9.0
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
@@ -709,7 +709,7 @@ Dynamic: license-file
709
709
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
710
710
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
711
711
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
712
- [![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/coverage_report.md)
712
+ [![codecov](https://codecov.io/gh/HiroYokoyama/python_molecular_editor/graph/badge.svg)](https://codecov.io/gh/HiroYokoyama/python_molecular_editor)
713
713
  [![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/MANUAL_CHECKLIST.md)
714
714
  [![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/pylint-score.txt)
715
715
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
@@ -69,6 +69,7 @@ src/moleditpy_linux/ui/user_template_dialog.py
69
69
  src/moleditpy_linux/ui/view_3d_logic.py
70
70
  src/moleditpy_linux/ui/zoomable_view.py
71
71
  src/moleditpy_linux/ui/settings_tabs/__init__.py
72
+ src/moleditpy_linux/ui/settings_tabs/settings_2d_cleanup_tab.py
72
73
  src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py
73
74
  src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py
74
75
  src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py
@@ -591,12 +591,63 @@ def _identify_problems_rdkit(
591
591
  return None
592
592
 
593
593
 
594
- def optimize_2d_coords(mol: Chem.Mol) -> Dict[int, Tuple[float, float]]:
594
+ _TARGET_BOND_LENGTH = 1.5 # matches RDKit's default (non-CoordGen) bond length
595
+
596
+
597
+ def _normalize_bond_length(mol: Chem.Mol, target_length: float) -> None:
598
+ """Rescale a molecule's 2D conformer so its average bond length matches target_length."""
599
+ if mol.GetNumBonds() == 0:
600
+ return
601
+ conf = mol.GetConformer()
602
+ total_len = 0.0
603
+ for bond in mol.GetBonds():
604
+ p1 = conf.GetAtomPosition(bond.GetBeginAtomIdx())
605
+ p2 = conf.GetAtomPosition(bond.GetEndAtomIdx())
606
+ total_len += ((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) ** 0.5
607
+ avg_len = total_len / mol.GetNumBonds()
608
+ if avg_len <= 1e-6:
609
+ return
610
+ factor = target_length / avg_len
611
+ for i in range(mol.GetNumAtoms()):
612
+ pos = conf.GetAtomPosition(i)
613
+ conf.SetAtomPosition(i, (pos.x * factor, pos.y * factor, pos.z))
614
+
615
+
616
+ def optimize_2d_coords(
617
+ mol: Chem.Mol,
618
+ prefer_coordgen: bool = False,
619
+ canonical_orientation: bool = True,
620
+ use_ring_templates: bool = False,
621
+ straighten_bonds: bool = False,
622
+ avoid_clashes: bool = False,
623
+ ) -> Dict[int, Tuple[float, float]]:
595
624
  """Generate 2D coordinates using RDKit and return a map of (x, y) tuples."""
596
625
  from rdkit.Chem import rdDepictor
597
626
 
598
- rdDepictor.Compute2DCoords(mol)
627
+ sample_kwargs = (
628
+ {"nFlipsPerSample": 3, "nSample": 200, "sampleSeed": 0xF00D}
629
+ if avoid_clashes
630
+ else {}
631
+ )
632
+
633
+ rdDepictor.SetPreferCoordGen(prefer_coordgen)
634
+ try:
635
+ rdDepictor.Compute2DCoords(
636
+ mol,
637
+ canonOrient=canonical_orientation,
638
+ useRingTemplates=use_ring_templates,
639
+ **sample_kwargs, # type: ignore[arg-type] # RDKit's generated overload differs by platform.
640
+ )
641
+ if straighten_bonds:
642
+ rdDepictor.StraightenDepiction(mol)
643
+ finally:
644
+ rdDepictor.SetPreferCoordGen(False)
645
+ # CoordGen and the default generator use different internal bond-length
646
+ # conventions (~1.0 vs ~1.5), so normalize here to keep the on-screen
647
+ # scale consistent regardless of which algorithm produced the layout.
648
+ _normalize_bond_length(mol, _TARGET_BOND_LENGTH)
599
649
  conf = mol.GetConformer()
650
+
600
651
  new_positions = {}
601
652
  for rdkit_atom in mol.GetAtoms():
602
653
  if rdkit_atom.HasProp("_original_atom_id"):
@@ -95,18 +95,18 @@ class AtomItem(QGraphicsItem):
95
95
  | QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges
96
96
  )
97
97
  self.setZValue(1)
98
+ self.font: QFont = QFont(FONT_FAMILY, 22, FONT_WEIGHT_BOLD)
98
99
  self.update_style()
99
100
  self.setAcceptHoverEvents(True)
100
101
  self.hovered: bool = False
101
102
  self.has_problem: bool = False
102
103
  self.is_visible: bool = True
103
- self.font: QFont = QFont(FONT_FAMILY, 20, FONT_WEIGHT_BOLD)
104
104
 
105
105
  def update_style(self) -> None:
106
106
  """Refresh font, color, and visibility based on current scene settings."""
107
107
  if sip_isdeleted_safe(self):
108
108
  return
109
- font_size = 20
109
+ font_size = 22
110
110
  font_family = FONT_FAMILY
111
111
  font_bold = True
112
112
  font_italic = False
@@ -114,7 +114,7 @@ class AtomItem(QGraphicsItem):
114
114
 
115
115
  scene: Any = self.scene()
116
116
  if scene is not None:
117
- font_size = scene.get_setting("atom_font_size_2d", 20)
117
+ font_size = scene.get_setting("atom_font_size_2d", 22)
118
118
  font_family = scene.get_setting("atom_font_family_2d", FONT_FAMILY)
119
119
  font_bold = scene.get_setting("atom_font_bold_2d", True)
120
120
  font_italic = scene.get_setting("atom_font_italic_2d", False)
@@ -136,14 +136,14 @@ class AtomItem(QGraphicsItem):
136
136
 
137
137
  def visual_rect(self) -> QRectF:
138
138
  """Return the rectangle the atom draws into; highlights use this."""
139
- font_size = 20
139
+ font_size = 22
140
140
  font_family = FONT_FAMILY
141
141
  font_bold = True
142
142
  font_italic = False
143
143
  font_underline = False
144
144
  scene: Any = self.scene()
145
145
  if scene is not None:
146
- font_size = scene.get_setting("atom_font_size_2d", 20)
146
+ font_size = scene.get_setting("atom_font_size_2d", 22)
147
147
  font_family = scene.get_setting("atom_font_family_2d", FONT_FAMILY)
148
148
  font_bold = scene.get_setting("atom_font_bold_2d", True)
149
149
  font_italic = scene.get_setting("atom_font_italic_2d", False)
@@ -265,14 +265,14 @@ class AtomItem(QGraphicsItem):
265
265
  if not self.is_visible:
266
266
  return path
267
267
 
268
- font_size = 20
268
+ font_size = 22
269
269
  font_family = FONT_FAMILY
270
270
  font_bold = True
271
271
  font_italic = False
272
272
  font_underline = False
273
273
  scene: Any = self.scene()
274
274
  if scene is not None:
275
- font_size = scene.get_setting("atom_font_size_2d", 20)
275
+ font_size = scene.get_setting("atom_font_size_2d", 22)
276
276
  font_family = scene.get_setting("atom_font_family_2d", FONT_FAMILY)
277
277
  font_bold = scene.get_setting("atom_font_bold_2d", True)
278
278
  font_italic = scene.get_setting("atom_font_italic_2d", False)
@@ -254,7 +254,7 @@ class BondItem(QGraphicsItem):
254
254
  else "bond_spacing_double_2d"
255
255
  )
256
256
  bond_offset = scene.get_setting(key, 3.5)
257
- wedge_width = scene.get_setting("bond_wedge_width_2d", 6.0)
257
+ wedge_width = scene.get_setting("bond_wedge_width_2d", 8.0)
258
258
 
259
259
  # Qt prunes clicks whose shape() escapes boundingRect().
260
260
  extra = (getattr(self, "order", 1) - 1) * bond_offset + 50 + wedge_width
@@ -270,7 +270,7 @@ class BondItem(QGraphicsItem):
270
270
  font_size = 20
271
271
  font_family = FONT_FAMILY
272
272
  if scene is not None:
273
- font_size = scene.get_setting("atom_font_size_2d", 20)
273
+ font_size = scene.get_setting("atom_font_size_2d", 22)
274
274
  font_family = scene.get_setting("atom_font_family_2d", FONT_FAMILY)
275
275
 
276
276
  font = QFont(font_family, font_size, FONT_WEIGHT_BOLD)
@@ -363,11 +363,11 @@ class BondItem(QGraphicsItem):
363
363
  bond_color = QColor(color_hex)
364
364
 
365
365
  # Width and Cap Style
366
- bond_width = scene.get_setting("bond_width_2d", 2.0)
366
+ bond_width = scene.get_setting("bond_width_2d", 3.0)
367
367
  cap_style_str = scene.get_setting("bond_cap_style_2d", "Round")
368
368
 
369
369
  # Wedge/Dash settings
370
- wedge_width_half = scene.get_setting("bond_wedge_width_2d", 6.0)
370
+ wedge_width_half = scene.get_setting("bond_wedge_width_2d", 8.0)
371
371
  num_dashes = int(scene.get_setting("bond_dash_count_2d", 8))
372
372
 
373
373
  # Cap Style logic
@@ -531,7 +531,7 @@ class BondItem(QGraphicsItem):
531
531
  font_family = FONT_FAMILY
532
532
  _sc: Any = self.scene()
533
533
  if _sc is not None:
534
- font_size = _sc.get_setting("atom_font_size_2d", 20)
534
+ font_size = _sc.get_setting("atom_font_size_2d", 22)
535
535
  font_family = _sc.get_setting(
536
536
  "atom_font_family_2d", FONT_FAMILY
537
537
  )
@@ -1154,7 +1154,26 @@ class EditActionsManager:
1154
1154
  try:
1155
1155
  from moleditpy_linux.core.mol_geometry import optimize_2d_coords
1156
1156
 
1157
- new_positions = optimize_2d_coords(mol)
1157
+ scene = self.host.init_manager.scene
1158
+ prefer_coordgen = bool(scene.get_setting("prefer_coordgen_2d", False))
1159
+ canonical_orientation = bool(
1160
+ scene.get_setting("cleanup_canonical_orientation_2d", True)
1161
+ )
1162
+ use_ring_templates = bool(
1163
+ scene.get_setting("cleanup_use_ring_templates_2d", False)
1164
+ )
1165
+ straighten_bonds = bool(
1166
+ scene.get_setting("cleanup_straighten_bonds_2d", False)
1167
+ )
1168
+ avoid_clashes = bool(scene.get_setting("cleanup_avoid_clashes_2d", False))
1169
+ new_positions = optimize_2d_coords(
1170
+ mol,
1171
+ prefer_coordgen,
1172
+ canonical_orientation,
1173
+ use_ring_templates,
1174
+ straighten_bonds,
1175
+ avoid_clashes,
1176
+ )
1158
1177
 
1159
1178
  if not new_positions:
1160
1179
  self.host.statusBar().showMessage( # type: ignore[union-attr]
@@ -225,7 +225,7 @@ class MainInitManager:
225
225
  font = None
226
226
  if scene is not None:
227
227
  family = scene.get_setting("atom_font_family_2d", FONT_FAMILY)
228
- size = scene.get_setting("atom_font_size_2d", 20)
228
+ size = scene.get_setting("atom_font_size_2d", 22)
229
229
  bold = scene.get_setting("atom_font_bold_2d", True)
230
230
  font = QFont(
231
231
  family, size, QFont.Weight.Bold if bold else QFont.Weight.Normal
@@ -369,7 +369,7 @@ class TemplateMixin:
369
369
  and self.get_setting("template_fusing_enabled_2d", True)
370
370
  and not alt_pressed
371
371
  ):
372
- map_threshold = self.get_setting("template_fusing_distance_2d", 7.0)
372
+ map_threshold = self.get_setting("template_fusing_distance_2d", 5.0)
373
373
  for i, p in enumerate(points):
374
374
  if atom_items[i] is not None:
375
375
  continue
@@ -548,7 +548,7 @@ class TemplateMixin:
548
548
  # Snap individual preview vertices to nearby atoms to reflect template fusing visually
549
549
  vertex_atoms: List[Optional[AtomItem]] = [None] * len(points)
550
550
  if self.get_setting("template_fusing_enabled_2d", True) and not alt_pressed:
551
- fuse_dist = self.get_setting("template_fusing_distance_2d", 7.0)
551
+ fuse_dist = self.get_setting("template_fusing_distance_2d", 5.0)
552
552
  mapped_atoms = set(self.template_context.get("items", []))
553
553
  used_indices = set()
554
554
  click_map_threshold = max(0.5 * bond_len, 8.0)
@@ -23,6 +23,7 @@ from PyQt6.QtWidgets import (
23
23
  )
24
24
 
25
25
  from .settings_tabs.settings_2d_tab import Settings2DTab
26
+ from .settings_tabs.settings_2d_cleanup_tab import Settings2DCleanupTab
26
27
  from .settings_tabs.settings_3d_tabs import Settings3DSceneTab, SettingsModelTab
27
28
  from .settings_tabs.settings_other_tab import SettingsOtherTab
28
29
 
@@ -35,6 +36,7 @@ class SettingsDialog(QDialog):
35
36
  def __init__(self, current_settings: Any, parent: Any = None) -> None:
36
37
  super().__init__(parent)
37
38
  self.tab_2d: Any = None
39
+ self.tab_2d_cleanup: Any = None
38
40
  self.tab_bs: Any = None
39
41
  self.tab_cpk: Any = None
40
42
  self.tab_other: Any = None
@@ -57,6 +59,7 @@ class SettingsDialog(QDialog):
57
59
 
58
60
  # Instantiate Tabs
59
61
  self.tab_2d = Settings2DTab(self.default_settings, self)
62
+ self.tab_2d_cleanup = Settings2DCleanupTab(self.default_settings, self)
60
63
  self.tab_scene = Settings3DSceneTab(self.default_settings, self)
61
64
  self.tab_bs = SettingsModelTab(
62
65
  "ball_stick",
@@ -86,6 +89,7 @@ class SettingsDialog(QDialog):
86
89
 
87
90
  # Add Tabs to Widget
88
91
  self.tab_widget.addTab(self.tab_2d, "2D Settings")
92
+ self.tab_widget.addTab(self.tab_2d_cleanup, "2D Cleanup")
89
93
  self.tab_widget.addTab(self.tab_scene, "3D Scene")
90
94
  self.tab_widget.addTab(self.tab_bs, "Ball & Stick")
91
95
  self.tab_widget.addTab(self.tab_cpk, "CPK (Space-filling)")
@@ -0,0 +1,146 @@
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 collections.abc import Mapping
14
+ from typing import Any, Optional
15
+
16
+ from PyQt6.QtWidgets import (
17
+ QCheckBox,
18
+ QFormLayout,
19
+ QWidget,
20
+ )
21
+ from .settings_tab_base import SettingsTabBase
22
+
23
+
24
+ class Settings2DCleanupTab(SettingsTabBase):
25
+ """Settings tab for the 'Clean Up 2D Structure' layout options."""
26
+
27
+ def __init__(
28
+ self, default_settings: Mapping[str, Any], parent: Optional[QWidget] = None
29
+ ) -> None:
30
+ super().__init__(default_settings, parent)
31
+ self.prefer_coordgen_2d_checkbox: Any = None
32
+ self.cleanup_canonical_orientation_2d_checkbox: Any = None
33
+ self.cleanup_use_ring_templates_2d_checkbox: Any = None
34
+ self.cleanup_straighten_bonds_2d_checkbox: Any = None
35
+ self.cleanup_avoid_clashes_2d_checkbox: Any = None
36
+ # (checkbox, base tooltip) pairs for options CoordGen silently ignores.
37
+ self._coordgen_ignored: list[tuple[Any, str]] = []
38
+ self._setup_ui()
39
+
40
+ def _setup_ui(self) -> None:
41
+ form_layout = QFormLayout(self)
42
+
43
+ self.prefer_coordgen_2d_checkbox = QCheckBox()
44
+ self.prefer_coordgen_2d_checkbox.setToolTip(
45
+ "If checked, 'Clean Up 2D Structure' uses RDKit's CoordGen "
46
+ "algorithm, which typically produces nicer ring layouts than "
47
+ "the default generator."
48
+ )
49
+ form_layout.addRow(
50
+ "Prefer CoordGen for Cleanup:", self.prefer_coordgen_2d_checkbox
51
+ )
52
+
53
+ canonical_tooltip = (
54
+ "If checked, 'Clean Up 2D Structure' rotates the result into "
55
+ "RDKit's canonical orientation. Note that cleanup always "
56
+ "regenerates coordinates, so unchecking this keeps the "
57
+ "generator's raw orientation, not the pre-cleanup one."
58
+ )
59
+ self.cleanup_canonical_orientation_2d_checkbox = QCheckBox()
60
+ self.cleanup_canonical_orientation_2d_checkbox.setToolTip(canonical_tooltip)
61
+ form_layout.addRow(
62
+ "Canonical Orientation:", self.cleanup_canonical_orientation_2d_checkbox
63
+ )
64
+
65
+ ring_templates_tooltip = (
66
+ "If checked, 'Clean Up 2D Structure' uses RDKit's built-in "
67
+ "template shapes for strained polycyclic ring systems. Only a "
68
+ "small set of ring systems has a template, so most structures "
69
+ "are unaffected."
70
+ )
71
+ self.cleanup_use_ring_templates_2d_checkbox = QCheckBox()
72
+ self.cleanup_use_ring_templates_2d_checkbox.setToolTip(ring_templates_tooltip)
73
+ form_layout.addRow(
74
+ "Use Ring Templates:", self.cleanup_use_ring_templates_2d_checkbox
75
+ )
76
+
77
+ self.cleanup_straighten_bonds_2d_checkbox = QCheckBox()
78
+ self.cleanup_straighten_bonds_2d_checkbox.setToolTip(
79
+ "If checked, 'Clean Up 2D Structure' rotates the result so most "
80
+ "bonds align to 30°/90° angles."
81
+ )
82
+ form_layout.addRow(
83
+ "Straighten Bonds:", self.cleanup_straighten_bonds_2d_checkbox
84
+ )
85
+
86
+ avoid_clashes_tooltip = (
87
+ "If checked, 'Clean Up 2D Structure' samples extra layouts to "
88
+ "reduce atom/bond overlap. Mainly affects flexible acyclic "
89
+ "chains, and can be slower on large molecules."
90
+ )
91
+ self.cleanup_avoid_clashes_2d_checkbox = QCheckBox()
92
+ self.cleanup_avoid_clashes_2d_checkbox.setToolTip(avoid_clashes_tooltip)
93
+ form_layout.addRow("Avoid Clashes:", self.cleanup_avoid_clashes_2d_checkbox)
94
+
95
+ # RDKit's CoordGen algorithm silently ignores canonOrient,
96
+ # useRingTemplates and the clash-avoidance sampling parameters, so
97
+ # these are grayed out while it is active. StraightenDepiction runs
98
+ # afterwards on the finished depiction, so it still applies.
99
+ self._coordgen_ignored = [
100
+ (self.cleanup_canonical_orientation_2d_checkbox, canonical_tooltip),
101
+ (self.cleanup_use_ring_templates_2d_checkbox, ring_templates_tooltip),
102
+ (self.cleanup_avoid_clashes_2d_checkbox, avoid_clashes_tooltip),
103
+ ]
104
+ self.prefer_coordgen_2d_checkbox.toggled.connect(
105
+ self._update_coordgen_dependent_options
106
+ )
107
+
108
+ def _update_coordgen_dependent_options(self, prefer_coordgen: bool) -> None:
109
+ suffix = " (not applicable when Prefer CoordGen is checked)"
110
+ for checkbox, base_tooltip in self._coordgen_ignored:
111
+ checkbox.setEnabled(not prefer_coordgen)
112
+ checkbox.setToolTip(base_tooltip + (suffix if prefer_coordgen else ""))
113
+
114
+ def update_ui(self, settings_dict: Mapping[str, Any]) -> None:
115
+ prefer_coordgen = settings_dict.get("prefer_coordgen_2d", False)
116
+ self.prefer_coordgen_2d_checkbox.setChecked(prefer_coordgen)
117
+ self._update_coordgen_dependent_options(prefer_coordgen)
118
+ self.cleanup_canonical_orientation_2d_checkbox.setChecked(
119
+ settings_dict.get("cleanup_canonical_orientation_2d", True)
120
+ )
121
+ self.cleanup_use_ring_templates_2d_checkbox.setChecked(
122
+ settings_dict.get("cleanup_use_ring_templates_2d", False)
123
+ )
124
+ self.cleanup_straighten_bonds_2d_checkbox.setChecked(
125
+ settings_dict.get("cleanup_straighten_bonds_2d", False)
126
+ )
127
+ self.cleanup_avoid_clashes_2d_checkbox.setChecked(
128
+ settings_dict.get("cleanup_avoid_clashes_2d", False)
129
+ )
130
+
131
+ def get_settings(self) -> dict[str, Any]:
132
+ return {
133
+ "prefer_coordgen_2d": self.prefer_coordgen_2d_checkbox.isChecked(),
134
+ "cleanup_canonical_orientation_2d": (
135
+ self.cleanup_canonical_orientation_2d_checkbox.isChecked()
136
+ ),
137
+ "cleanup_use_ring_templates_2d": (
138
+ self.cleanup_use_ring_templates_2d_checkbox.isChecked()
139
+ ),
140
+ "cleanup_straighten_bonds_2d": (
141
+ self.cleanup_straighten_bonds_2d_checkbox.isChecked()
142
+ ),
143
+ "cleanup_avoid_clashes_2d": (
144
+ self.cleanup_avoid_clashes_2d_checkbox.isChecked()
145
+ ),
146
+ }
@@ -170,6 +170,7 @@ class Settings2DTab(SettingsTabBase):
170
170
  self.atom_font_underline_2d_btn.setFont(underline_font)
171
171
 
172
172
  style_row = QHBoxLayout()
173
+ style_row.setContentsMargins(0, 0, 0, 0)
173
174
  style_row.setSpacing(4)
174
175
  style_row.addWidget(self.atom_font_bold_2d_btn)
175
176
  style_row.addWidget(self.atom_font_italic_2d_btn)
@@ -301,13 +302,13 @@ class Settings2DTab(SettingsTabBase):
301
302
  self._update_color_buttons()
302
303
 
303
304
  self.bond_width_2d_slider.setValue(
304
- int(settings_dict.get("bond_width_2d", 2.0) * 10)
305
+ int(settings_dict.get("bond_width_2d", 3.0) * 10)
305
306
  )
306
307
  self.bond_spacing_double_2d_slider.setValue(
307
- int(settings_dict.get("bond_spacing_double_2d", 3.5) * 10)
308
+ int(settings_dict.get("bond_spacing_double_2d", 5.0) * 10)
308
309
  )
309
310
  self.bond_spacing_triple_2d_slider.setValue(
310
- int(settings_dict.get("bond_spacing_triple_2d", 3.5) * 10)
311
+ int(settings_dict.get("bond_spacing_triple_2d", 6.0) * 10)
311
312
  )
312
313
 
313
314
  cap_style = settings_dict.get("bond_cap_style_2d", "Round")
@@ -316,7 +317,7 @@ class Settings2DTab(SettingsTabBase):
316
317
  self.bond_cap_style_2d_combo.setCurrentIndex(idx)
317
318
 
318
319
  self.bond_wedge_width_2d_slider.setValue(
319
- int(settings_dict.get("bond_wedge_width_2d", 6.0) * 10)
320
+ int(settings_dict.get("bond_wedge_width_2d", 8.0) * 10)
320
321
  )
321
322
  self.bond_dash_count_2d_slider.setValue(
322
323
  settings_dict.get("bond_dash_count_2d", 8)
@@ -326,7 +327,7 @@ class Settings2DTab(SettingsTabBase):
326
327
  self.atom_font_family_2d_combo.setCurrentFont(QFont(font_family))
327
328
 
328
329
  self.atom_font_size_2d_slider.setValue(
329
- settings_dict.get("atom_font_size_2d", 20)
330
+ settings_dict.get("atom_font_size_2d", 22)
330
331
  )
331
332
  self.atom_font_bold_2d_btn.setChecked(
332
333
  settings_dict.get("atom_font_bold_2d", True)
@@ -66,22 +66,27 @@ DEFAULT_SETTINGS = {
66
66
  "bond_color": "#808080",
67
67
  "cpk_colors": {},
68
68
  # --- 2D Settings ---
69
- "bond_width_2d": 2.0,
70
- "bond_spacing_double_2d": 3.5,
71
- "bond_spacing_triple_2d": 3.5,
72
- "atom_font_size_2d": 20,
69
+ "bond_width_2d": 3.0,
70
+ "bond_spacing_double_2d": 5.0,
71
+ "bond_spacing_triple_2d": 6.0,
72
+ "atom_font_size_2d": 22,
73
73
  "background_color_2d": "#FFFFFF",
74
74
  "bond_color_2d": "#222222",
75
75
  "atom_use_bond_color_2d": False,
76
76
  "bond_cap_style_2d": "Round",
77
- "bond_wedge_width_2d": 6.0,
77
+ "bond_wedge_width_2d": 8.0,
78
78
  "bond_dash_count_2d": 8,
79
79
  "atom_font_family_2d": "Arial",
80
80
  "atom_font_bold_2d": True,
81
81
  "atom_font_italic_2d": False,
82
82
  "atom_font_underline_2d": False,
83
83
  "template_fusing_enabled_2d": True,
84
- "template_fusing_distance_2d": 7.0,
84
+ "template_fusing_distance_2d": 5.0,
85
85
  "template_snapping_distance_2d": 14.0,
86
86
  "bond_snapping_distance_2d": 14.0,
87
+ "prefer_coordgen_2d": False,
88
+ "cleanup_canonical_orientation_2d": True,
89
+ "cleanup_use_ring_templates_2d": False,
90
+ "cleanup_straighten_bonds_2d": False,
91
+ "cleanup_avoid_clashes_2d": False,
87
92
  }
File without changes