MoleditPy 2.2.0a0__py3-none-any.whl → 2.2.0a1__py3-none-any.whl

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 (26) hide show
  1. moleditpy/modules/constants.py +1 -1
  2. moleditpy/modules/main_window_main_init.py +73 -103
  3. moleditpy/modules/plugin_manager.py +10 -0
  4. moleditpy/plugins/Analysis/ms_spectrum_neo.py +919 -0
  5. moleditpy/plugins/File/animated_xyz_giffer.py +583 -0
  6. moleditpy/plugins/File/cube_viewer.py +689 -0
  7. moleditpy/plugins/File/gaussian_fchk_freq_analyzer.py +1148 -0
  8. moleditpy/plugins/File/mapped_cube_viewer.py +552 -0
  9. moleditpy/plugins/File/orca_out_freq_analyzer.py +1226 -0
  10. moleditpy/plugins/File/paste_xyz.py +336 -0
  11. moleditpy/plugins/Input Generator/gaussian_input_generator_neo.py +930 -0
  12. moleditpy/plugins/Input Generator/orca_input_generator_neo.py +1028 -0
  13. moleditpy/plugins/Input Generator/orca_xyz2inp_gui.py +286 -0
  14. moleditpy/plugins/Optimization/all-trans_optimizer.py +65 -0
  15. moleditpy/plugins/Optimization/complex_molecule_untangler.py +268 -0
  16. moleditpy/plugins/Optimization/conf_search.py +224 -0
  17. moleditpy/plugins/Utility/atom_colorizer.py +547 -0
  18. moleditpy/plugins/Utility/console.py +163 -0
  19. moleditpy/plugins/Utility/pubchem_ressolver.py +244 -0
  20. moleditpy/plugins/Utility/vdw_radii_overlay.py +303 -0
  21. {moleditpy-2.2.0a0.dist-info → moleditpy-2.2.0a1.dist-info}/METADATA +1 -1
  22. {moleditpy-2.2.0a0.dist-info → moleditpy-2.2.0a1.dist-info}/RECORD +26 -9
  23. {moleditpy-2.2.0a0.dist-info → moleditpy-2.2.0a1.dist-info}/WHEEL +0 -0
  24. {moleditpy-2.2.0a0.dist-info → moleditpy-2.2.0a1.dist-info}/entry_points.txt +0 -0
  25. {moleditpy-2.2.0a0.dist-info → moleditpy-2.2.0a1.dist-info}/licenses/LICENSE +0 -0
  26. {moleditpy-2.2.0a0.dist-info → moleditpy-2.2.0a1.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,7 @@ from PyQt6.QtGui import QFont, QColor
16
16
  from rdkit import Chem
17
17
 
18
18
  #Version
19
- VERSION = '2.2.0a0'
19
+ VERSION = '2.2.0a1'
20
20
 
21
21
  ATOM_RADIUS = 18
22
22
  BOND_OFFSET = 3.5
@@ -1755,10 +1755,32 @@ class MainWindowMainInit(object):
1755
1755
  """Discovers plugins and updates the plugin menu actions."""
1756
1756
  if not self.plugin_manager:
1757
1757
  return
1758
+
1759
+ PLUGIN_ACTION_TAG = "plugin_managed"
1760
+
1761
+ # Helper to clear tagged actions from a menu
1762
+ def clear_plugin_actions(menu):
1763
+ if not menu: return
1764
+ for act in list(menu.actions()):
1765
+ if act.data() == PLUGIN_ACTION_TAG:
1766
+ menu.removeAction(act)
1767
+ # Recurse into submenus to clean deep actions
1768
+ elif act.menu():
1769
+ clear_plugin_actions(act.menu())
1758
1770
 
1759
- # Clear existing plugin actions
1771
+ # Clear existing plugin actions from main Plugin menu
1760
1772
  plugin_menu.clear()
1761
1773
 
1774
+ # Clear tagged actions from ALL top-level menus in the Menu Bar
1775
+ # This ensures we catch actions added to standard menus (File, Edit) OR custom menus
1776
+ for top_action in self.menuBar().actions():
1777
+ if top_action.menu():
1778
+ clear_plugin_actions(top_action.menu())
1779
+
1780
+ # Clear Export menu (if button exists)
1781
+ if hasattr(self, 'export_button') and self.export_button.menu():
1782
+ clear_plugin_actions(self.export_button.menu())
1783
+
1762
1784
  # Only keep the Manager action
1763
1785
  manage_plugins_action = QAction("Plugin Manager...", self)
1764
1786
  def show_plugin_manager():
@@ -1814,9 +1836,9 @@ class MainWindowMainInit(object):
1814
1836
  action_text = text if text else parts[-1]
1815
1837
  action = QAction(action_text, self)
1816
1838
  action.triggered.connect(callback)
1839
+ action.setData(PLUGIN_ACTION_TAG) # TAG THE ACTION
1817
1840
  current_menu.addAction(action)
1818
1841
 
1819
- # 2. Add Toolbar Buttons (New System)
1820
1842
  # 2. Add Toolbar Buttons (New System)
1821
1843
  # Use dedicated plugin toolbar
1822
1844
  if hasattr(self, 'plugin_toolbar'):
@@ -1840,14 +1862,12 @@ class MainWindowMainInit(object):
1840
1862
  self.plugin_toolbar.hide()
1841
1863
 
1842
1864
  # 3. Legacy Menu Building (Folder based)
1843
-
1844
1865
  if not plugins:
1845
1866
  no_plugin_action = QAction("(No plugins found)", self)
1846
1867
  no_plugin_action.setEnabled(False)
1847
1868
  plugin_menu.addAction(no_plugin_action)
1848
1869
  else:
1849
1870
  # Sort plugins: directories first (to create menus), then alphabetical by name
1850
- # Actually simple sort by rel_folder, name is fine
1851
1871
  plugins.sort(key=lambda x: (x.get('rel_folder', ''), x['name']))
1852
1872
 
1853
1873
  # Dictionary to keep track of created submenus: path -> QMenu
@@ -1855,7 +1875,6 @@ class MainWindowMainInit(object):
1855
1875
 
1856
1876
  for p in plugins:
1857
1877
  # Only add legacy plugins (with 'run' function) to the generic Plugins menu.
1858
- # New plugins (with 'initialize') should register their own menu actions if needed.
1859
1878
  if hasattr(p['module'], 'run'):
1860
1879
  rel_folder = p.get('rel_folder', '')
1861
1880
  # Get or create the parent menu for this plugin
@@ -1883,113 +1902,64 @@ class MainWindowMainInit(object):
1883
1902
 
1884
1903
  # 4. Integrate Export Actions into Export Button and Menu
1885
1904
  if self.plugin_manager.export_actions:
1886
- # Add separator if we have custom exports (and haven't added it yet for this session/update)
1887
- # Since update_plugin_menu clears the dynamic plugin actions but NOT the export menu (which is on a button),
1888
- # we need to be careful not to duplicate.
1889
- # Ideally, we should clear custom actions from the export menu first.
1890
- # For simplicity, we'll just check existence.
1891
-
1892
1905
  if hasattr(self, 'export_button') and self.export_button.menu():
1893
- # Naive approach: check if separator/actions exist.
1894
- # Better approach: Add them to a specific section or manage them explicitly.
1895
- # Here we just append if not present.
1906
+ # Add separator
1907
+ sep = self.export_button.menu().addSeparator()
1908
+ sep.setData(PLUGIN_ACTION_TAG)
1896
1909
 
1897
- # Check if we need a separator (if we have built-in actions)
1898
- if self.export_button.menu().actions():
1899
- has_sep = False
1900
- for a in self.export_button.menu().actions():
1901
- if a.isSeparator():
1902
- has_sep = True
1903
- # Get or create the parent menu for this plugin
1904
- # For folder based hierarchy only?
1905
- # ... (Refer to existing code for menu building, skipped here for brevity)
1906
- pass
1910
+ for exp in self.plugin_manager.export_actions:
1911
+ label = exp['label']
1912
+ callback = exp['callback']
1913
+
1914
+ a = QAction(label, self)
1915
+ a.triggered.connect(callback)
1916
+ a.setData(PLUGIN_ACTION_TAG)
1917
+ self.export_button.menu().addAction(a)
1907
1918
 
1908
1919
  # 5. Integrate File Openers into Import Menu
1909
1920
  if hasattr(self, 'import_menu') and self.plugin_manager.file_openers:
1910
- # Add a separator if plugins are present
1911
- has_plugins = len(self.plugin_manager.file_openers) > 0
1912
- if has_plugins:
1913
- self.import_menu.addSeparator()
1914
-
1921
+ # Add separator
1922
+ sep = self.import_menu.addSeparator()
1923
+ sep.setData(PLUGIN_ACTION_TAG)
1924
+
1915
1925
  for ext, info in self.plugin_manager.file_openers.items():
1916
- # ext e.g. .xyz
1917
- # info = {'plugin': name, 'callback': cb}
1918
1926
  label = f"Import {ext} ({info.get('plugin', 'Plugin')})..."
1919
1927
 
1920
- # duplicate check
1921
- exists = False
1922
- for act in self.import_menu.actions():
1923
- if act.text() == label:
1924
- exists = True
1925
- break
1926
-
1927
- if not exists:
1928
- def make_cb(callback):
1929
- def _cb():
1930
- # Standard file dialog to pick file, then callback
1931
- fpath, _ = QFileDialog.getOpenFileName(
1932
- self, f"Import {ext}", "",
1933
- f"{info.get('plugin', 'Plugin')} File (*{ext});;All Files (*)"
1934
- )
1935
- if fpath:
1936
- callback(fpath)
1937
- self.current_file_path = fpath # Update current file path?
1938
- self.update_window_title()
1939
- return _cb
1940
-
1941
- a = QAction(label, self)
1942
- a.triggered.connect(make_cb(info['callback']))
1943
- self.import_menu.addAction(a)
1928
+ def make_cb(callback):
1929
+ def _cb():
1930
+ fpath, _ = QFileDialog.getOpenFileName(
1931
+ self, f"Import {ext}", "",
1932
+ f"{info.get('plugin', 'Plugin')} File (*{ext});;All Files (*)"
1933
+ )
1934
+ if fpath:
1935
+ callback(fpath)
1936
+ self.current_file_path = fpath
1937
+ self.update_window_title()
1938
+ return _cb
1939
+
1940
+ a = QAction(label, self)
1941
+ a.triggered.connect(make_cb(info['callback']))
1942
+ a.setData(PLUGIN_ACTION_TAG)
1943
+ self.import_menu.addAction(a)
1944
1944
 
1945
1945
  # 6. Integrate Analysis Tools into Analysis Menu
1946
- if hasattr(self, 'analysis_action') and self.plugin_manager.analysis_tools:
1947
- # Determine parent menu (Analysis)
1948
- # self.analysis_action is just an action, we need the menu it belongs to?
1949
- # Or did we stash the analysis_menu?
1950
- # Looking at init, analysis_menu wasn't stored as self.analysis_menu, but we can find it via menuBar.
1946
+ # Find Analysis menu again as it might not be defined if cleanup block was generic
1947
+ analysis_menu = None
1948
+ for action in self.menuBar().actions():
1949
+ if action.text().replace('&', '') == 'Analysis':
1950
+ analysis_menu = action.menu()
1951
+ break
1952
+
1953
+ if analysis_menu and self.plugin_manager.analysis_tools:
1954
+ # Add separator
1955
+ sep = analysis_menu.addSeparator()
1956
+ sep.setData(PLUGIN_ACTION_TAG)
1951
1957
 
1952
- # Let's find "Analysis" menu
1953
- analysis_menu = None
1954
- for action in self.menuBar().actions():
1955
- if action.text().replace('&', '') == 'Analysis':
1956
- analysis_menu = action.menu()
1957
- break
1958
-
1959
- if analysis_menu:
1960
- # Add separator if we have plugins
1961
- if self.plugin_manager.analysis_tools:
1962
- analysis_menu.addSeparator()
1963
-
1964
- for tool in self.plugin_manager.analysis_tools:
1965
- label = f"{tool['label']} ({tool.get('plugin', 'Plugin')})"
1966
- # duplicate check
1967
- exists = False
1968
- for act in analysis_menu.actions():
1969
- if act.text() == label:
1970
- exists = True
1971
- break
1972
- if not exists:
1973
- a = QAction(label, self)
1974
- a.triggered.connect(tool['callback'])
1975
- analysis_menu.addAction(a)
1976
-
1977
- # 7. Integrate Export Actions (Continued)
1978
- if self.plugin_manager.export_actions:
1979
- for exp in self.plugin_manager.export_actions:
1980
- label = exp['label']
1981
- callback = exp['callback']
1982
-
1983
- exists = False
1984
- for act in self.export_button.menu().actions():
1985
- if act.text() == label:
1986
- exists = True
1987
- break
1988
-
1989
- if not exists:
1990
- a = QAction(label, self)
1991
- a.triggered.connect(callback)
1992
- self.export_button.menu().addAction(a)
1993
-
1994
- # 5. Integrate File Openers (Implicitly handled during file load) uses PluginManager directly
1958
+ for tool in self.plugin_manager.analysis_tools:
1959
+ label = f"{tool['label']} ({tool.get('plugin', 'Plugin')})"
1960
+
1961
+ a = QAction(label, self)
1962
+ a.triggered.connect(tool['callback'])
1963
+ a.setData(PLUGIN_ACTION_TAG)
1964
+ analysis_menu.addAction(a)
1995
1965
 
@@ -98,8 +98,18 @@ class PluginManager:
98
98
  self.plugins = []
99
99
  self.menu_actions = []
100
100
  self.toolbar_actions = []
101
+ self.context_menu_3d_actions = []
101
102
  self.drop_handlers = []
102
103
 
104
+ # Clear extended registries
105
+ self.export_actions = []
106
+ self.optimization_methods = {}
107
+ self.file_openers = {}
108
+ self.analysis_tools = []
109
+ self.save_handlers = {}
110
+ self.load_handlers = {}
111
+ self.custom_3d_styles = {}
112
+
103
113
  if not os.path.exists(self.plugin_dir):
104
114
  return []
105
115