MoleditPy 2.3.0__py3-none-any.whl → 2.3.2__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.
@@ -16,7 +16,7 @@ from PyQt6.QtGui import QFont, QColor
16
16
  from rdkit import Chem
17
17
 
18
18
  #Version
19
- VERSION = '2.3.0'
19
+ VERSION = '2.3.2'
20
20
 
21
21
  ATOM_RADIUS = 18
22
22
  BOND_OFFSET = 3.5
@@ -1452,18 +1452,22 @@ class MainWindowMainInit(object):
1452
1452
  # 1. Custom Plugin Openers
1453
1453
  # 1. Custom Plugin Openers
1454
1454
  if ext_with_dot in self.plugin_manager.file_openers:
1455
- opener = self.plugin_manager.file_openers[ext_with_dot]
1456
- try:
1457
- opener['callback'](file_path)
1458
- self.current_file_path = file_path
1459
- self.update_window_title()
1460
- return
1461
- except Exception as e:
1462
- print(f"Plugin opener failed: {e}")
1463
- QMessageBox.warning(self, "Plugin Error", f"Error opening file with plugin '{opener.get('plugin', 'Unknown')}':\n{e}")
1464
- # Fallback to standard logic if plugin fails? Or stop?
1465
- # Generally if a plugin claims it, we stop. But here we let it fall through if it errors?
1466
- # Let's simple check next.
1455
+ openers = self.plugin_manager.file_openers[ext_with_dot]
1456
+ # Iterate through openers (already sorted by priority)
1457
+ for opener_info in openers:
1458
+ try:
1459
+ callback = opener_info['callback']
1460
+ # Try to call the opener
1461
+ callback(file_path)
1462
+
1463
+ self.current_file_path = file_path
1464
+ self.update_window_title()
1465
+ return # Success
1466
+ except Exception as e:
1467
+ print(f"Plugin opener failed for '{opener_info.get('plugin', 'Unknown')}': {e}")
1468
+ # If this opener fails, try the next one or fall through to default
1469
+ continue
1470
+
1467
1471
 
1468
1472
  if file_ext in ['mol', 'sdf']:
1469
1473
  self.load_mol_file_for_3d_viewing(file_path)
@@ -1969,7 +1973,6 @@ class MainWindowMainInit(object):
1969
1973
  a.setData(PLUGIN_ACTION_TAG)
1970
1974
  menu.addAction(a)
1971
1975
 
1972
- # 5. Integrate File Openers into Import Menu
1973
1976
  # 5. Integrate File Openers into Import Menu
1974
1977
  if hasattr(self, 'import_menu') and self.plugin_manager.file_openers:
1975
1978
  # Add separator
@@ -1978,16 +1981,32 @@ class MainWindowMainInit(object):
1978
1981
 
1979
1982
  # Group by Plugin Name
1980
1983
  plugin_map = {}
1981
- for ext, info in self.plugin_manager.file_openers.items():
1982
- p_name = info.get('plugin', 'Plugin')
1983
- if p_name not in plugin_map:
1984
- plugin_map[p_name] = {}
1985
- plugin_map[p_name][ext] = info['callback']
1984
+ for ext, openers_list in self.plugin_manager.file_openers.items():
1985
+ # Handles potential multiple openers for same extension
1986
+ for info in openers_list:
1987
+ p_name = info.get('plugin', 'Plugin')
1988
+ if p_name not in plugin_map:
1989
+ plugin_map[p_name] = {}
1990
+ # We can only register one callback per plugin per extension in the menu for now.
1991
+ # Since we process them, let's just take the one present (if a plugin registers multiple openers for same ext - weird but ok)
1992
+ plugin_map[p_name][ext] = info['callback']
1986
1993
 
1987
- for p_name, ext_map in plugin_map.items():
1994
+ for p_name, ext_map in sorted(plugin_map.items()):
1988
1995
  # Create combined label: "Import .ext1/.ext2 (PluginName)..."
1989
1996
  extensions = sorted(ext_map.keys())
1990
1997
  ext_str = "/".join(extensions)
1998
+
1999
+ # TRUNCATION LOGIC
2000
+ MAX_EXT_LEN = 30
2001
+ if len(ext_str) > MAX_EXT_LEN:
2002
+ # Find last slash within limit
2003
+ cutoff = ext_str.rfind('/', 0, MAX_EXT_LEN)
2004
+ if cutoff != -1:
2005
+ ext_str = ext_str[:cutoff] + "/..."
2006
+ else:
2007
+ # Fallback if first extension is super long (unlikely but safe)
2008
+ ext_str = ext_str[:MAX_EXT_LEN] + "..."
2009
+
1991
2010
  label = f"Import {ext_str} ({p_name})..."
1992
2011
 
1993
2012
  # Create combined filter: "PluginName Files (*.ext1 *.ext2)"
@@ -313,12 +313,8 @@ class MainWindowUiManager(object):
313
313
  event.acceptProposedAction()
314
314
  return
315
315
 
316
- # Plugin-registered file openers
317
- if self.plugin_manager and hasattr(self.plugin_manager, 'file_openers'):
318
- for ext in self.plugin_manager.file_openers.keys():
319
- if file_lower.endswith(ext):
320
- event.acceptProposedAction()
321
- return
316
+ # 2. Plugin drop handlers (Drop専用ハンドラ)
317
+ # プラグインが「Dropを受け入れる」と明示している場合のみ許可
322
318
 
323
319
  # Plugin drop handlers (accept more liberally for custom logic)
324
320
  # A plugin drop handler might handle it, so accept
@@ -104,7 +104,7 @@ class PluginContext:
104
104
  """
105
105
  self._manager.register_optimization_method(self._plugin_name, method_name, callback)
106
106
 
107
- def register_file_opener(self, extension: str, callback: Callable[[str], None]):
107
+ def register_file_opener(self, extension: str, callback: Callable[[str], None], priority: int = 0):
108
108
  """
109
109
  Register a handler for opening a specific file extension.
110
110
 
@@ -112,19 +112,11 @@ class PluginContext:
112
112
  extension: File extension including dot, e.g. ".xyz".
113
113
  callback: Function taking (file_path) -> None.
114
114
  Should load the file into the main window.
115
+ priority: Higher priority handlers are tried first (default 0).
115
116
  """
116
- self._manager.register_file_opener(self._plugin_name, extension, callback)
117
+ self._manager.register_file_opener(self._plugin_name, extension, callback, priority)
118
+
117
119
 
118
- def register_file_opener(self, extension: str, callback: Callable[[str], None]):
119
- """
120
- Register a handler for opening a specific file extension.
121
-
122
- Args:
123
- extension: File extension including dot, e.g. ".xyz".
124
- callback: Function taking (file_path) -> None.
125
- Should load the file into the main window.
126
- """
127
- self._manager.register_file_opener(self._plugin_name, extension, callback)
128
120
 
129
121
  def add_analysis_tool(self, label: str, callback: Callable):
130
122
  """
@@ -46,7 +46,7 @@ class PluginManager:
46
46
  # Extended Registries (Added to prevent lazy initialization "monkey patching")
47
47
  self.export_actions = []
48
48
  self.optimization_methods = {}
49
- self.file_openers = {}
49
+ self.file_openers = {} # ext -> list of {'plugin':..., 'callback':..., 'priority':...}
50
50
  self.analysis_tools = []
51
51
  self.save_handlers = {}
52
52
  self.load_handlers = {}
@@ -149,7 +149,6 @@ class PluginManager:
149
149
  dest_path = os.path.join(self.plugin_dir, filename)
150
150
  if os.path.exists(dest_path):
151
151
  if os.path.isdir(dest_path):
152
- import shutil
153
152
  shutil.rmtree(dest_path)
154
153
  shutil.copy2(file_path, dest_path)
155
154
  msg = f"Installed {filename}"
@@ -346,14 +345,23 @@ class PluginManager:
346
345
  'plugin': plugin_name, 'callback': callback, 'label': method_name
347
346
  }
348
347
 
349
- def register_file_opener(self, plugin_name, extension, callback):
348
+ def register_file_opener(self, plugin_name, extension, callback, priority=0):
350
349
  # Normalize extension to lowercase
351
350
  ext = extension.lower()
352
351
  if not ext.startswith('.'):
353
352
  ext = '.' + ext
354
- self.file_openers[ext] = {
355
- 'plugin': plugin_name, 'callback': callback
356
- }
353
+
354
+ if ext not in self.file_openers:
355
+ self.file_openers[ext] = []
356
+
357
+ self.file_openers[ext].append({
358
+ 'plugin': plugin_name,
359
+ 'callback': callback,
360
+ 'priority': priority
361
+ })
362
+
363
+ # Sort by priority descending
364
+ self.file_openers[ext].sort(key=lambda x: x['priority'], reverse=True)
357
365
 
358
366
  # Analysis Tools registration
359
367
  def register_analysis_tool(self, plugin_name, label, callback):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 2.3.0
3
+ Version: 2.3.2
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
@@ -12,7 +12,7 @@ moleditpy/modules/bond_item.py,sha256=eVkEeKvM4igYI67DYxpey3FllqDyt_iWDo4VPYMhaP
12
12
  moleditpy/modules/bond_length_dialog.py,sha256=6bFPGssnqlgINuqpxLv-OhjMH3_hspnaH8QtorAyu2M,14782
13
13
  moleditpy/modules/calculation_worker.py,sha256=KiGQY7i-QCQofEoE0r65KoQgpEGFcbhmxWv6egfkUdc,42324
14
14
  moleditpy/modules/color_settings_dialog.py,sha256=Ow44BhCOLo0AFb6klO001k6B4drOgKX9DeNBQhZLp5o,15474
15
- moleditpy/modules/constants.py,sha256=J9rIFaD6tWxdMBzGcaYWovarEJy72gXD784k9WVahr8,4702
15
+ moleditpy/modules/constants.py,sha256=wUyC8vBN2vsqgv6G_qfzdsCX3tOihY0xDCGYB9_B1O0,4702
16
16
  moleditpy/modules/constrained_optimization_dialog.py,sha256=REsk4ePsqNmAGPMTS_jckeM7jexrU3krwun8sKqKUCs,30062
17
17
  moleditpy/modules/custom_interactor_style.py,sha256=LDNODMJoNHGe1AUSrvqv6PdeJm-hpPmSpWINppnJLt0,38942
18
18
  moleditpy/modules/custom_qt_interactor.py,sha256=vCZsDfRO-FtphD5cTP7Ps-5rpHZMIGloaoe6EaKzrsw,4139
@@ -25,11 +25,11 @@ moleditpy/modules/main_window_dialog_manager.py,sha256=QR96LqHAPSOShXbc9cK-Ffq8a
25
25
  moleditpy/modules/main_window_edit_3d.py,sha256=CUArB5wcsgq1C7LygAEC6URlbnn4RhRYDa5n-Y-etWI,19731
26
26
  moleditpy/modules/main_window_edit_actions.py,sha256=yEc0Nw-VpN0P4e4neUu7pDuUHPGEcu6eFmwWFrSBIQ8,64815
27
27
  moleditpy/modules/main_window_export.py,sha256=dSVfylsybDDboDuXU9Inotf6YkrKJwgBTqGYSfq1lRE,38241
28
- moleditpy/modules/main_window_main_init.py,sha256=BChZuyXgWfOTFqAM1OUukFgtL5KLusW4t29GnfCN1QE,92018
28
+ moleditpy/modules/main_window_main_init.py,sha256=flZEzOH29Z6pplOqku51RMxgKT_QJzsjQK_EheDvjQo,93007
29
29
  moleditpy/modules/main_window_molecular_parsers.py,sha256=KR6vzuqc3nutOcorpYr0QOyX3MFBcxTwDhZX96VgJ9Q,48291
30
30
  moleditpy/modules/main_window_project_io.py,sha256=TWwtuKDuvgcvPZ9IGmW8r1EJJOrgxrIJRnxe_f4C1oM,17149
31
31
  moleditpy/modules/main_window_string_importers.py,sha256=v47wOd4RtjKYcF-aLP-mogGGdYTpTEo3dDyAu79_5MM,10782
32
- moleditpy/modules/main_window_ui_manager.py,sha256=HofI6T9EvcSSzPbsdPqkYEEDoB6Hui1Uj2Ll-wwczGA,24016
32
+ moleditpy/modules/main_window_ui_manager.py,sha256=A-MBoZOq5xHD_Dg9F2BSbBgeWZBY9wDq_7zHacJjKRA,23796
33
33
  moleditpy/modules/main_window_view_3d.py,sha256=CxZxyJHl2isF7KtyVWSI9f8LVbvdZM5H9Gnhm_8ovBM,74227
34
34
  moleditpy/modules/main_window_view_loaders.py,sha256=gklTMo27QnyJ8Gd0ampPdbm9d0Gi-oHWkIqQuGADHmI,14352
35
35
  moleditpy/modules/mirror_dialog.py,sha256=c3v4qY6R4FAljzk4EPaDjL9ZdZMjLQSFLqDMXz2fBUk,4696
@@ -38,8 +38,8 @@ moleditpy/modules/molecule_scene.py,sha256=khdt7h9Mk_D1cMbYeHGtq7P9aFXo0xG-hcShU
38
38
  moleditpy/modules/move_group_dialog.py,sha256=Fyuy3Uq1KsFsk9qR96r_FxPbAM_-zSfW2dsMQGv7btc,27276
39
39
  moleditpy/modules/periodic_table_dialog.py,sha256=ItEZUts1XCietz9paY-spvbzxh6SXak3GnikwqkHZCw,4006
40
40
  moleditpy/modules/planarize_dialog.py,sha256=eaqI1MpF35e-VUMpJATt-EtGG5FhcSUlbAenUaFGabY,8593
41
- moleditpy/modules/plugin_interface.py,sha256=srzPZ3a_aRTx28NAvWNKRVUDYQNfQOTcjzx-5YW2Pb4,8164
42
- moleditpy/modules/plugin_manager.py,sha256=nb106AfUAnAVyp4LYFmLhKzFY4taeteH8gY5SNf1h1I,20037
41
+ moleditpy/modules/plugin_interface.py,sha256=8_keZsQ6pZEVuEviHXZWlLFs8Yt6J83mwApbX1YL7P0,7791
42
+ moleditpy/modules/plugin_manager.py,sha256=4lXv9stNiiqgY2FRdNDzB41jLcOfc0VzAIF88eMYWlY,20369
43
43
  moleditpy/modules/plugin_manager_window.py,sha256=b4kEv0DaWHZG76ZaFTOxn6CVtA62_0MpPYYr10ehCtA,12544
44
44
  moleditpy/modules/settings_dialog.py,sha256=Nr7yE8UmYRi3VObWvRlrnv0DnjSjmYXbvqryZ02O12k,65348
45
45
  moleditpy/modules/template_preview_item.py,sha256=djdq3tz73d_fJGOvai3E-V9Hk9q9ZW7skx7BV59mooA,6556
@@ -51,9 +51,9 @@ moleditpy/modules/assets/file_icon.ico,sha256=yyVj084A7HuMNbV073cE_Ag3Ne405qgOP3
51
51
  moleditpy/modules/assets/icon.icns,sha256=wD5R6-Vw7K662tVKhu2E1ImN0oUuyAP4youesEQsn9c,139863
52
52
  moleditpy/modules/assets/icon.ico,sha256=RfgFcx7-dHY_2STdsOQCQziY5SNhDr3gPnjO6jzEDPI,147975
53
53
  moleditpy/modules/assets/icon.png,sha256=kCFN1WacYIdy0GN6SFEbNA00ef39pCczBnFdkkBI8Bs,147110
54
- moleditpy-2.3.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
55
- moleditpy-2.3.0.dist-info/METADATA,sha256=KMJCk7r-QqD1yc1YHvb-EoQ65q5Xgfa9gvuO0sSmzEw,60629
56
- moleditpy-2.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- moleditpy-2.3.0.dist-info/entry_points.txt,sha256=yH1h9JjALhok1foXT3-hYrC4ufoZt8b7oiBcsdnGNNM,54
58
- moleditpy-2.3.0.dist-info/top_level.txt,sha256=ARICrS4ihlPXqywlKl6o-oJa3Qz3gZRWu_VZsQ3_c44,10
59
- moleditpy-2.3.0.dist-info/RECORD,,
54
+ moleditpy-2.3.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
55
+ moleditpy-2.3.2.dist-info/METADATA,sha256=tEoGUryE-8GZy8pp44lJg1ClxwIywx5xmo9tNEq-tmw,60629
56
+ moleditpy-2.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ moleditpy-2.3.2.dist-info/entry_points.txt,sha256=yH1h9JjALhok1foXT3-hYrC4ufoZt8b7oiBcsdnGNNM,54
58
+ moleditpy-2.3.2.dist-info/top_level.txt,sha256=ARICrS4ihlPXqywlKl6o-oJa3Qz3gZRWu_VZsQ3_c44,10
59
+ moleditpy-2.3.2.dist-info/RECORD,,