MoleditPy 1.18.1__py3-none-any.whl → 2.0.1__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.
- moleditpy/modules/constants.py +1 -1
- moleditpy/modules/main_window.py +4 -0
- moleditpy/modules/main_window_main_init.py +73 -1
- moleditpy/modules/molecule_scene.py +34 -1
- moleditpy/modules/plugin_manager.py +85 -0
- {moleditpy-1.18.1.dist-info → moleditpy-2.0.1.dist-info}/METADATA +3 -1
- {moleditpy-1.18.1.dist-info → moleditpy-2.0.1.dist-info}/RECORD +11 -10
- {moleditpy-1.18.1.dist-info → moleditpy-2.0.1.dist-info}/WHEEL +0 -0
- {moleditpy-1.18.1.dist-info → moleditpy-2.0.1.dist-info}/entry_points.txt +0 -0
- {moleditpy-1.18.1.dist-info → moleditpy-2.0.1.dist-info}/licenses/LICENSE +0 -0
- {moleditpy-1.18.1.dist-info → moleditpy-2.0.1.dist-info}/top_level.txt +0 -0
moleditpy/modules/constants.py
CHANGED
moleditpy/modules/main_window.py
CHANGED
|
@@ -211,6 +211,10 @@ class MainWindow(QMainWindow):
|
|
|
211
211
|
# --- MOVED TO main_window_main_init.py ---
|
|
212
212
|
return self.main_window_main_init.init_menu_bar()
|
|
213
213
|
|
|
214
|
+
def update_plugin_menu(self, plugin_menu):
|
|
215
|
+
# --- MOVED TO main_window_main_init.py ---
|
|
216
|
+
return self.main_window_main_init.update_plugin_menu(plugin_menu)
|
|
217
|
+
|
|
214
218
|
def init_worker_thread(self):
|
|
215
219
|
# --- MOVED TO main_window_main_init.py ---
|
|
216
220
|
return self.main_window_main_init.init_worker_thread()
|
|
@@ -46,12 +46,16 @@ from PyQt6.QtCore import (
|
|
|
46
46
|
Qt, QPointF, QRectF, QLineF, QUrl, QTimer
|
|
47
47
|
)
|
|
48
48
|
import platform
|
|
49
|
-
import subprocess
|
|
50
49
|
try:
|
|
51
50
|
import winreg
|
|
52
51
|
except Exception:
|
|
53
52
|
winreg = None
|
|
54
53
|
|
|
54
|
+
try:
|
|
55
|
+
from .plugin_manager import PluginManager
|
|
56
|
+
except Exception:
|
|
57
|
+
from modules.plugin_manager import PluginManager
|
|
58
|
+
|
|
55
59
|
|
|
56
60
|
def detect_system_dark_mode():
|
|
57
61
|
"""Return True if the OS prefers dark app theme, False if light, or None if unknown.
|
|
@@ -253,6 +257,14 @@ class MainWindowMainInit(object):
|
|
|
253
257
|
# 3D編集ダイアログの参照を保持
|
|
254
258
|
self.active_3d_dialogs = []
|
|
255
259
|
|
|
260
|
+
|
|
261
|
+
# プラグインマネージャーの初期化
|
|
262
|
+
try:
|
|
263
|
+
self.plugin_manager = PluginManager()
|
|
264
|
+
except Exception as e:
|
|
265
|
+
print(f"Failed to initialize PluginManager: {e}")
|
|
266
|
+
self.plugin_manager = None
|
|
267
|
+
|
|
256
268
|
self.init_ui()
|
|
257
269
|
self.init_worker_thread()
|
|
258
270
|
self._setup_3d_picker()
|
|
@@ -286,6 +298,7 @@ class MainWindowMainInit(object):
|
|
|
286
298
|
self.update_atom_id_menu_text()
|
|
287
299
|
self.update_atom_id_menu_state()
|
|
288
300
|
|
|
301
|
+
|
|
289
302
|
# 初期化完了を設定
|
|
290
303
|
self.initialization_complete = True
|
|
291
304
|
self.update_window_title() # 初期化完了後にタイトルを更新
|
|
@@ -1188,6 +1201,27 @@ class MainWindowMainInit(object):
|
|
|
1188
1201
|
edit_3d_menu.addAction(constrained_opt_action)
|
|
1189
1202
|
self.constrained_opt_action = constrained_opt_action
|
|
1190
1203
|
|
|
1204
|
+
# Plugin menu
|
|
1205
|
+
plugin_menu = menu_bar.addMenu("&Plugin")
|
|
1206
|
+
|
|
1207
|
+
open_plugin_dir_action = QAction("Open Plugin Directory", self)
|
|
1208
|
+
if self.plugin_manager:
|
|
1209
|
+
open_plugin_dir_action.triggered.connect(self.plugin_manager.open_plugin_folder)
|
|
1210
|
+
else:
|
|
1211
|
+
open_plugin_dir_action.setEnabled(False)
|
|
1212
|
+
plugin_menu.addAction(open_plugin_dir_action)
|
|
1213
|
+
|
|
1214
|
+
reload_plugins_action = QAction("Reload Plugins", self)
|
|
1215
|
+
reload_plugins_action.triggered.connect(lambda: self.update_plugin_menu(plugin_menu))
|
|
1216
|
+
plugin_menu.addAction(reload_plugins_action)
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
plugin_menu.addSeparator()
|
|
1221
|
+
|
|
1222
|
+
# Initial population of plugins
|
|
1223
|
+
self.update_plugin_menu(plugin_menu)
|
|
1224
|
+
|
|
1191
1225
|
settings_menu = menu_bar.addMenu("&Settings")
|
|
1192
1226
|
# 1) 3D View settings (existing)
|
|
1193
1227
|
view_settings_action = QAction("3D View Settings...", self)
|
|
@@ -1675,3 +1709,41 @@ class MainWindowMainInit(object):
|
|
|
1675
1709
|
except Exception as e:
|
|
1676
1710
|
print(f"Error saving settings: {e}")
|
|
1677
1711
|
|
|
1712
|
+
def update_plugin_menu(self, plugin_menu):
|
|
1713
|
+
"""Discovers plugins and updates the plugin menu actions."""
|
|
1714
|
+
if not self.plugin_manager:
|
|
1715
|
+
return
|
|
1716
|
+
|
|
1717
|
+
# Clear existing plugin actions
|
|
1718
|
+
plugin_menu.clear()
|
|
1719
|
+
|
|
1720
|
+
# Re-add static actions
|
|
1721
|
+
open_plugin_dir_action = QAction("Open Plugin Directory", self)
|
|
1722
|
+
open_plugin_dir_action.triggered.connect(self.plugin_manager.open_plugin_folder)
|
|
1723
|
+
plugin_menu.addAction(open_plugin_dir_action)
|
|
1724
|
+
|
|
1725
|
+
reload_plugins_action = QAction("Reload Plugins", self)
|
|
1726
|
+
reload_plugins_action.triggered.connect(lambda: self.update_plugin_menu(plugin_menu))
|
|
1727
|
+
plugin_menu.addAction(reload_plugins_action)
|
|
1728
|
+
|
|
1729
|
+
explore_plugins_action = QAction("Explore Plugins", self)
|
|
1730
|
+
explore_plugins_action.triggered.connect(
|
|
1731
|
+
lambda: QDesktopServices.openUrl(QUrl("https://github.com/HiroYokoyama/moleditpy-plugins"))
|
|
1732
|
+
)
|
|
1733
|
+
plugin_menu.addAction(explore_plugins_action)
|
|
1734
|
+
|
|
1735
|
+
plugin_menu.addSeparator()
|
|
1736
|
+
|
|
1737
|
+
# Add dynamic plugin actions
|
|
1738
|
+
plugins = self.plugin_manager.discover_plugins(self)
|
|
1739
|
+
if not plugins:
|
|
1740
|
+
no_plugin_action = QAction("(No plugins found)", self)
|
|
1741
|
+
no_plugin_action.setEnabled(False)
|
|
1742
|
+
plugin_menu.addAction(no_plugin_action)
|
|
1743
|
+
else:
|
|
1744
|
+
for p in plugins:
|
|
1745
|
+
# Use default param in lambda to capture the current p
|
|
1746
|
+
action = QAction(p['name'], self)
|
|
1747
|
+
action.triggered.connect(lambda checked, mod=p['module']: self.plugin_manager.run_plugin(mod, self.mw if hasattr(self, 'mw') else self))
|
|
1748
|
+
plugin_menu.addAction(action)
|
|
1749
|
+
|
|
@@ -115,6 +115,15 @@ class MoleculeScene(QGraphicsScene):
|
|
|
115
115
|
}
|
|
116
116
|
self.reinitialize_items()
|
|
117
117
|
|
|
118
|
+
|
|
119
|
+
def update_all_items(self):
|
|
120
|
+
"""全てのアイテムを強制的に再描画する"""
|
|
121
|
+
for item in self.items():
|
|
122
|
+
if isinstance(item, (AtomItem, BondItem)):
|
|
123
|
+
item.update()
|
|
124
|
+
if self.views():
|
|
125
|
+
self.views()[0].viewport().update()
|
|
126
|
+
|
|
118
127
|
def reinitialize_items(self):
|
|
119
128
|
self.template_preview = TemplatePreviewItem(); self.addItem(self.template_preview)
|
|
120
129
|
self.template_preview.hide(); self.template_preview_points = []; self.template_context = {}
|
|
@@ -185,6 +194,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
185
194
|
# Delete the entire rectangular selection
|
|
186
195
|
data_changed = self.delete_items(set(selected_items))
|
|
187
196
|
if data_changed:
|
|
197
|
+
self.update_all_items()
|
|
188
198
|
self.window.push_undo_state()
|
|
189
199
|
self.press_pos = None
|
|
190
200
|
event.accept()
|
|
@@ -207,6 +217,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
207
217
|
logging.error(f"Error clearing E/Z label: {e}", exc_info=True)
|
|
208
218
|
if hasattr(self.window, 'statusBar'):
|
|
209
219
|
self.window.statusBar().showMessage(f"Error clearing E/Z label: {e}", 5000)
|
|
220
|
+
self.update_all_items() # エラー時も整合性維持のため再描画
|
|
210
221
|
# AtomItemは何もしない
|
|
211
222
|
# --- 通常の処理 ---
|
|
212
223
|
elif isinstance(item, AtomItem):
|
|
@@ -233,6 +244,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
233
244
|
data_changed = self.delete_items({item})
|
|
234
245
|
|
|
235
246
|
if data_changed:
|
|
247
|
+
self.update_all_items()
|
|
236
248
|
self.window.push_undo_state()
|
|
237
249
|
self.press_pos = None
|
|
238
250
|
event.accept()
|
|
@@ -342,7 +354,9 @@ class MoleculeScene(QGraphicsScene):
|
|
|
342
354
|
self.data_changed_in_event = True
|
|
343
355
|
# イベント処理をここで完了させ、下のアイテムが選択されるのを防ぐ
|
|
344
356
|
self.start_atom=None; self.start_pos = None; self.press_pos = None
|
|
345
|
-
if self.data_changed_in_event:
|
|
357
|
+
if self.data_changed_in_event:
|
|
358
|
+
self.update_all_items()
|
|
359
|
+
self.window.push_undo_state()
|
|
346
360
|
return
|
|
347
361
|
|
|
348
362
|
released_item = self.itemAt(end_pos, self.views()[0].transform())
|
|
@@ -384,11 +398,13 @@ class MoleculeScene(QGraphicsScene):
|
|
|
384
398
|
else: # current_stereo == 4
|
|
385
399
|
new_stereo = 0 # E -> None
|
|
386
400
|
self.update_bond_stereo(b, new_stereo)
|
|
401
|
+
self.update_all_items() # 強制再描画
|
|
387
402
|
self.window.push_undo_state() # ここでUndo stackに積む
|
|
388
403
|
except Exception as e:
|
|
389
404
|
logging.error(f"Error in E/Z stereo toggle: {e}", exc_info=True)
|
|
390
405
|
if hasattr(self.window, 'statusBar'):
|
|
391
406
|
self.window.statusBar().showMessage(f"Error changing E/Z stereochemistry: {e}", 5000)
|
|
407
|
+
self.update_all_items() # エラー時も整合性維持のため再描画
|
|
392
408
|
return # この後の処理は行わない
|
|
393
409
|
elif self.bond_stereo != 0 and b.order == self.bond_order and b.stereo == self.bond_stereo:
|
|
394
410
|
# 方向性を反転させる
|
|
@@ -488,6 +504,10 @@ class MoleculeScene(QGraphicsScene):
|
|
|
488
504
|
# 原子移動後に測定ラベルの位置を更新
|
|
489
505
|
self.window.update_2d_measurement_labels()
|
|
490
506
|
if self.views(): self.views()[0].viewport().update()
|
|
507
|
+
|
|
508
|
+
if self.data_changed_in_event:
|
|
509
|
+
self.update_all_items()
|
|
510
|
+
|
|
491
511
|
self.start_atom=None; self.start_pos = None; self.press_pos = None; self.temp_line = None
|
|
492
512
|
self.template_context = {}
|
|
493
513
|
# Clear user template data when switching modes
|
|
@@ -512,6 +532,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
512
532
|
self.data.atoms[item.atom_id]['charge'] = item.charge
|
|
513
533
|
item.update_style()
|
|
514
534
|
|
|
535
|
+
self.update_all_items()
|
|
515
536
|
self.window.push_undo_state()
|
|
516
537
|
|
|
517
538
|
event.accept()
|
|
@@ -635,6 +656,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
635
656
|
|
|
636
657
|
except Exception as e:
|
|
637
658
|
logging.error(f"Error creating bond: {e}", exc_info=True)
|
|
659
|
+
self.update_all_items() # エラーリカバリー
|
|
638
660
|
|
|
639
661
|
def add_molecule_fragment(self, points, bonds_info, existing_items=None, symbol='C'):
|
|
640
662
|
"""
|
|
@@ -1265,6 +1287,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1265
1287
|
print(f"Error during delete_items operation: {e}")
|
|
1266
1288
|
|
|
1267
1289
|
traceback.print_exc()
|
|
1290
|
+
self.update_all_items() # エラーリカバリー
|
|
1268
1291
|
return False
|
|
1269
1292
|
def purge_deleted_items(self):
|
|
1270
1293
|
"""Purge and release any held deleted-wrapper references.
|
|
@@ -1536,6 +1559,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1536
1559
|
|
|
1537
1560
|
# 計算した情報を使って、その場にフラグメントを追加
|
|
1538
1561
|
self.add_molecule_fragment(points, bonds_info, existing_items=existing_items)
|
|
1562
|
+
self.update_all_items()
|
|
1539
1563
|
self.window.push_undo_state()
|
|
1540
1564
|
|
|
1541
1565
|
# --- 動作2: カーソルが空白領域にある場合 (モード切替) ---
|
|
@@ -1561,6 +1585,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1561
1585
|
atom.radical = (atom.radical + 1) % 3
|
|
1562
1586
|
self.data.atoms[atom.atom_id]['radical'] = atom.radical
|
|
1563
1587
|
atom.update_style()
|
|
1588
|
+
self.update_all_items()
|
|
1564
1589
|
self.window.push_undo_state()
|
|
1565
1590
|
event.accept()
|
|
1566
1591
|
return
|
|
@@ -1581,6 +1606,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1581
1606
|
atom.charge += delta
|
|
1582
1607
|
self.data.atoms[atom.atom_id]['charge'] = atom.charge
|
|
1583
1608
|
atom.update_style()
|
|
1609
|
+
self.update_all_items()
|
|
1584
1610
|
self.window.push_undo_state()
|
|
1585
1611
|
event.accept()
|
|
1586
1612
|
return
|
|
@@ -1610,6 +1636,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1610
1636
|
for atom in atoms_to_update:
|
|
1611
1637
|
atom.update_style()
|
|
1612
1638
|
|
|
1639
|
+
self.update_all_items()
|
|
1613
1640
|
self.window.push_undo_state()
|
|
1614
1641
|
event.accept()
|
|
1615
1642
|
return
|
|
@@ -1688,6 +1715,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1688
1715
|
bond.update()
|
|
1689
1716
|
|
|
1690
1717
|
if any_bond_changed:
|
|
1718
|
+
self.update_all_items()
|
|
1691
1719
|
self.window.push_undo_state()
|
|
1692
1720
|
|
|
1693
1721
|
if key in [Qt.Key.Key_1, Qt.Key.Key_2, Qt.Key.Key_3, Qt.Key.Key_W, Qt.Key.Key_D]:
|
|
@@ -1697,11 +1725,13 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1697
1725
|
if isinstance(self.hovered_item, BondItem) and self.hovered_item.order == 2:
|
|
1698
1726
|
if event.key() == Qt.Key.Key_Z:
|
|
1699
1727
|
self.update_bond_stereo(self.hovered_item, 3) # Z-isomer
|
|
1728
|
+
self.update_all_items()
|
|
1700
1729
|
self.window.push_undo_state()
|
|
1701
1730
|
event.accept()
|
|
1702
1731
|
return
|
|
1703
1732
|
elif event.key() == Qt.Key.Key_E:
|
|
1704
1733
|
self.update_bond_stereo(self.hovered_item, 4) # E-isomer
|
|
1734
|
+
self.update_all_items()
|
|
1705
1735
|
self.window.push_undo_state()
|
|
1706
1736
|
event.accept()
|
|
1707
1737
|
return
|
|
@@ -1809,6 +1839,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1809
1839
|
self.create_bond(start_atom, new_atom_item, bond_order=target_order, bond_stereo=0)
|
|
1810
1840
|
|
|
1811
1841
|
self.clearSelection()
|
|
1842
|
+
self.update_all_items()
|
|
1812
1843
|
self.window.push_undo_state()
|
|
1813
1844
|
event.accept()
|
|
1814
1845
|
return
|
|
@@ -1839,6 +1870,7 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1839
1870
|
items_to_process.add(item_at_cursor)
|
|
1840
1871
|
|
|
1841
1872
|
if self.delete_items(items_to_process):
|
|
1873
|
+
self.update_all_items()
|
|
1842
1874
|
self.window.push_undo_state()
|
|
1843
1875
|
self.window.statusBar().showMessage("Deleted selected items.")
|
|
1844
1876
|
|
|
@@ -1973,3 +2005,4 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1973
2005
|
traceback.print_exc()
|
|
1974
2006
|
if hasattr(self.window, 'statusBar'):
|
|
1975
2007
|
self.window.statusBar().showMessage(f"Error updating bond stereochemistry: {e}", 5000)
|
|
2008
|
+
self.update_all_items() # エラーリカバリー
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
plugin_manager.py
|
|
6
|
+
Manages discovery, loading, and execution of external plugins.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import importlib.util
|
|
12
|
+
import traceback
|
|
13
|
+
from PyQt6.QtGui import QDesktopServices
|
|
14
|
+
from PyQt6.QtCore import QUrl
|
|
15
|
+
from PyQt6.QtWidgets import QMessageBox
|
|
16
|
+
|
|
17
|
+
class PluginManager:
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.plugin_dir = os.path.join(os.path.expanduser('~'), '.moleditpy', 'plugins')
|
|
20
|
+
self.plugins = [] # List of {"name": str, "module": module_obj}
|
|
21
|
+
|
|
22
|
+
def ensure_plugin_dir(self):
|
|
23
|
+
"""Creates the plugin directory if it creates doesn't exist."""
|
|
24
|
+
if not os.path.exists(self.plugin_dir):
|
|
25
|
+
try:
|
|
26
|
+
os.makedirs(self.plugin_dir)
|
|
27
|
+
except OSError as e:
|
|
28
|
+
print(f"Error creating plugin directory: {e}")
|
|
29
|
+
|
|
30
|
+
def open_plugin_folder(self):
|
|
31
|
+
"""Opens the plugin directory in the OS file explorer."""
|
|
32
|
+
self.ensure_plugin_dir()
|
|
33
|
+
QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir))
|
|
34
|
+
|
|
35
|
+
def discover_plugins(self, parent=None):
|
|
36
|
+
"""
|
|
37
|
+
Scans the plugin directory for .py files and attempts to import them.
|
|
38
|
+
Returns a list of valid loaded plugins.
|
|
39
|
+
"""
|
|
40
|
+
self.ensure_plugin_dir()
|
|
41
|
+
self.plugins = []
|
|
42
|
+
|
|
43
|
+
if not os.path.exists(self.plugin_dir):
|
|
44
|
+
return []
|
|
45
|
+
|
|
46
|
+
for filename in os.listdir(self.plugin_dir):
|
|
47
|
+
if filename.endswith(".py") and not filename.startswith("__"):
|
|
48
|
+
filepath = os.path.join(self.plugin_dir, filename)
|
|
49
|
+
try:
|
|
50
|
+
# Dynamically import the module
|
|
51
|
+
spec = importlib.util.spec_from_file_location(filename[:-3], filepath)
|
|
52
|
+
if spec and spec.loader:
|
|
53
|
+
module = importlib.util.module_from_spec(spec)
|
|
54
|
+
sys.modules[spec.name] = module # helper for relative imports if needed
|
|
55
|
+
spec.loader.exec_module(module)
|
|
56
|
+
|
|
57
|
+
# Check for required attributes
|
|
58
|
+
plugin_name = getattr(module, 'PLUGIN_NAME', filename[:-3])
|
|
59
|
+
|
|
60
|
+
# Validate that it has a run function
|
|
61
|
+
if hasattr(module, 'run') and callable(module.run):
|
|
62
|
+
self.plugins.append({
|
|
63
|
+
'name': plugin_name,
|
|
64
|
+
'module': module
|
|
65
|
+
})
|
|
66
|
+
else:
|
|
67
|
+
print(f"Plugin {filename} skipped: Missing 'run(main_window)' function.")
|
|
68
|
+
except Exception as e:
|
|
69
|
+
# Robust error handling with user notification
|
|
70
|
+
msg = f"Failed to load plugin {filename}:\n{e}"
|
|
71
|
+
print(msg)
|
|
72
|
+
traceback.print_exc()
|
|
73
|
+
if parent:
|
|
74
|
+
QMessageBox.warning(parent, "Plugin Load Error", msg)
|
|
75
|
+
|
|
76
|
+
return self.plugins
|
|
77
|
+
|
|
78
|
+
def run_plugin(self, module, main_window):
|
|
79
|
+
"""Executes the plugin's run method."""
|
|
80
|
+
try:
|
|
81
|
+
module.run(main_window)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
QMessageBox.critical(main_window, "Plugin Error", f"Error running plugin '{getattr(module, 'PLUGIN_NAME', 'Unknown')}':\n{e}")
|
|
84
|
+
traceback.print_exc()
|
|
85
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.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
|
|
@@ -759,6 +759,7 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
|
|
|
759
759
|
* Import structures from **MOL/SDF** files or **SMILES** strings.
|
|
760
760
|
* Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
|
|
761
761
|
* Export 2D and 3D views as high-resolution PNG images.
|
|
762
|
+
* **Plugin System:** Extend functionality with Python scripts. Place custom scripts in `~/.moleditpy/plugins` to add new features to the "Plugin" menu.
|
|
762
763
|
|
|
763
764
|
## Installation and Execution
|
|
764
765
|
|
|
@@ -873,6 +874,7 @@ This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**.
|
|
|
873
874
|
* **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
|
|
874
875
|
* 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
|
|
875
876
|
* 2Dおよび3Dビューを高解像度のPNG画像としてエクスポートできます。
|
|
877
|
+
* **プラグインシステム:** Pythonスクリプトで機能を拡張できます。`~/.moleditpy/plugins` にスクリプトを配置することで、「Plugin」メニューに独自の機能を追加できます。
|
|
876
878
|
|
|
877
879
|
## インストールと実行
|
|
878
880
|
|
|
@@ -12,20 +12,20 @@ moleditpy/modules/bond_item.py,sha256=eVkEeKvM4igYI67DYxpey3FllqDyt_iWDo4VPYMhaP
|
|
|
12
12
|
moleditpy/modules/bond_length_dialog.py,sha256=k5x_DhK9Q8CSwouKhEo_kLRRdaYHDaK84KDNmuDNLvY,14868
|
|
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=
|
|
15
|
+
moleditpy/modules/constants.py,sha256=m8tYMuiXp0w4IUeFW1hbSogm6-7g0voDQs5kJygquQA,4702
|
|
16
16
|
moleditpy/modules/constrained_optimization_dialog.py,sha256=IEdNVhFoNSEMeA5ABpUH9Q88-YzDXFloQM2gwnPwnHY,30150
|
|
17
17
|
moleditpy/modules/custom_interactor_style.py,sha256=NjsXE2a43IDNEanZBlcG9eR4ZIERT1MsQC6lbfesapQ,38453
|
|
18
18
|
moleditpy/modules/custom_qt_interactor.py,sha256=MFaTuDh-FPeFBS4303CqxsxmsOIOW4QXUz6USwI8PHQ,2451
|
|
19
19
|
moleditpy/modules/dialog3_d_picking_mixin.py,sha256=2Sut0J5ltXMtrUJ9R3o1oZ4ysed27mdSIqLpWxmGdyM,5037
|
|
20
20
|
moleditpy/modules/dihedral_dialog.py,sha256=rgry7LqyX9JMAR7d82QSroTPoKT3xz18EgKN1GzYZx4,18088
|
|
21
|
-
moleditpy/modules/main_window.py,sha256=
|
|
21
|
+
moleditpy/modules/main_window.py,sha256=fXt2QTY7BjTMHEjGcsAb4Z-nu4_hRKayqLq8cA92bKc,35967
|
|
22
22
|
moleditpy/modules/main_window_app_state.py,sha256=ONdjo-ZAPdat1_ToFodzuUep3c8qbKRPX6glnY24c9o,33602
|
|
23
23
|
moleditpy/modules/main_window_compute.py,sha256=DsyMvGD2gnB-9XC6SU-ICuScyyQxubfaDYAk8hkY-vk,51911
|
|
24
24
|
moleditpy/modules/main_window_dialog_manager.py,sha256=S1ROCWqzB2uVSKHY4o5CbTtJAEjSyEXGPC1AgmdVgK4,20065
|
|
25
25
|
moleditpy/modules/main_window_edit_3d.py,sha256=uY203adPg3CLyAdbG2NCThG2Am0WduzPDan9rXAlc14,19841
|
|
26
26
|
moleditpy/modules/main_window_edit_actions.py,sha256=lUFxNFTUQzeXN8CNlb4_4S9j4M1EEq8kpJmh9dCzM3M,64818
|
|
27
27
|
moleditpy/modules/main_window_export.py,sha256=e0HA_jeaokIOBunQqGxUn5QKdniCmE8GrsE1Igy6zm8,38351
|
|
28
|
-
moleditpy/modules/main_window_main_init.py,sha256=
|
|
28
|
+
moleditpy/modules/main_window_main_init.py,sha256=ecRDVqOLMieqRDYTzsCRTjZSqj0fUBuFjqFacbC0lCg,78515
|
|
29
29
|
moleditpy/modules/main_window_molecular_parsers.py,sha256=Ex4-urYsKf6PyHp4XToOhgXzuYWa_n7q--QmHci4OCU,48401
|
|
30
30
|
moleditpy/modules/main_window_project_io.py,sha256=q1vEmWQDqla32HVkmk8-j0OY9ut5TI5NJ4ikahewkEo,17259
|
|
31
31
|
moleditpy/modules/main_window_string_importers.py,sha256=mQVDv2Dj4MwnPgMRe2IqdAAKnB_quE6QfYeAgCjfv28,10892
|
|
@@ -34,10 +34,11 @@ moleditpy/modules/main_window_view_3d.py,sha256=MQCW2GnqGUpdJO4-GeR_18z7dS5K8C2G
|
|
|
34
34
|
moleditpy/modules/main_window_view_loaders.py,sha256=Dbdgv4TY_ZkX8Qyaevwr-mBJYJ59CBzRTEks-U1FiGw,14462
|
|
35
35
|
moleditpy/modules/mirror_dialog.py,sha256=c3v4qY6R4FAljzk4EPaDjL9ZdZMjLQSFLqDMXz2fBUk,4696
|
|
36
36
|
moleditpy/modules/molecular_data.py,sha256=8gE9ByYg3kSBfb1zANsyad_BVBTm6WOLF7NsZIYuG2E,13250
|
|
37
|
-
moleditpy/modules/molecule_scene.py,sha256=
|
|
37
|
+
moleditpy/modules/molecule_scene.py,sha256=c5GNL7LrzerXYWshmpY_6Rg-2cBlF7xUt_orxTYC5pQ,96408
|
|
38
38
|
moleditpy/modules/move_group_dialog.py,sha256=65HVXTJSaQ9lp03XFhI1l7OzUsXmH_aqd8OgwjpjfGg,27174
|
|
39
39
|
moleditpy/modules/periodic_table_dialog.py,sha256=ItEZUts1XCietz9paY-spvbzxh6SXak3GnikwqkHZCw,4006
|
|
40
40
|
moleditpy/modules/planarize_dialog.py,sha256=yY8o-SxT8vGEHVWnjDTXecRv5NUaEejEsXH-836Xk8g,8681
|
|
41
|
+
moleditpy/modules/plugin_manager.py,sha256=Qbje9i-b4f81k_gxiDkfH8bv2eNTTya1563L93USQQA,3441
|
|
41
42
|
moleditpy/modules/settings_dialog.py,sha256=Nr7yE8UmYRi3VObWvRlrnv0DnjSjmYXbvqryZ02O12k,65348
|
|
42
43
|
moleditpy/modules/template_preview_item.py,sha256=Ks3C35pYFuLT5G4fsloI7ljE6ESXoYyGvLkM22qcmt0,6673
|
|
43
44
|
moleditpy/modules/template_preview_view.py,sha256=4OCHZDO51BvJpKdfrBWJ4_4WfLfFSKxsVIyf7I-Kj2E,3350
|
|
@@ -47,9 +48,9 @@ moleditpy/modules/zoomable_view.py,sha256=hjwljui13QpvjvxJHY4Evot4jMQvxRBQUNH5HU
|
|
|
47
48
|
moleditpy/modules/assets/icon.icns,sha256=wD5R6-Vw7K662tVKhu2E1ImN0oUuyAP4youesEQsn9c,139863
|
|
48
49
|
moleditpy/modules/assets/icon.ico,sha256=RfgFcx7-dHY_2STdsOQCQziY5SNhDr3gPnjO6jzEDPI,147975
|
|
49
50
|
moleditpy/modules/assets/icon.png,sha256=kCFN1WacYIdy0GN6SFEbNA00ef39pCczBnFdkkBI8Bs,147110
|
|
50
|
-
moleditpy-
|
|
51
|
-
moleditpy-
|
|
52
|
-
moleditpy-
|
|
53
|
-
moleditpy-
|
|
54
|
-
moleditpy-
|
|
55
|
-
moleditpy-
|
|
51
|
+
moleditpy-2.0.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
52
|
+
moleditpy-2.0.1.dist-info/METADATA,sha256=tj2oawfvI4p70dB5JTtoPv1BWVwq13oLosjSfeBSqiw,58985
|
|
53
|
+
moleditpy-2.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
54
|
+
moleditpy-2.0.1.dist-info/entry_points.txt,sha256=yH1h9JjALhok1foXT3-hYrC4ufoZt8b7oiBcsdnGNNM,54
|
|
55
|
+
moleditpy-2.0.1.dist-info/top_level.txt,sha256=ARICrS4ihlPXqywlKl6o-oJa3Qz3gZRWu_VZsQ3_c44,10
|
|
56
|
+
moleditpy-2.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|