moleditpy 0.3.4__tar.gz → 0.3.6__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.
- {moleditpy-0.3.4 → moleditpy-0.3.6}/PKG-INFO +3 -1
- {moleditpy-0.3.4 → moleditpy-0.3.6}/README.md +2 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/pyproject.toml +1 -1
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy/main.py +168 -15
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy.egg-info/PKG-INFO +3 -1
- {moleditpy-0.3.4 → moleditpy-0.3.6}/setup.cfg +0 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy/__init__.py +0 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy/assets/icon.ico +0 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy/assets/icon.png +0 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy.egg-info/SOURCES.txt +0 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy.egg-info/dependency_links.txt +0 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy.egg-info/entry_points.txt +0 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy.egg-info/requires.txt +0 -0
- {moleditpy-0.3.4 → moleditpy-0.3.6}/src/moleditpy.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moleditpy
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: A program to edit molecular structures using Python.
|
|
5
5
|
Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/HiroYokoyama/python_molecular_editor
|
|
@@ -33,6 +33,8 @@ A cross-platform, simple, and intuitive molecular structure editor built in Pyth
|
|
|
33
33
|
ライセンス: Apache-2.0
|
|
34
34
|
リポジトリ: [https://github.com/HiroYokoyama/python\_molecular\_editor](https://github.com/HiroYokoyama/python_molecular_editor)
|
|
35
35
|
|
|
36
|
+

|
|
37
|
+
|
|
36
38
|
-----
|
|
37
39
|
|
|
38
40
|
## 概要
|
|
@@ -9,6 +9,8 @@ A cross-platform, simple, and intuitive molecular structure editor built in Pyth
|
|
|
9
9
|
ライセンス: Apache-2.0
|
|
10
10
|
リポジトリ: [https://github.com/HiroYokoyama/python\_molecular\_editor](https://github.com/HiroYokoyama/python_molecular_editor)
|
|
11
11
|
|
|
12
|
+

|
|
13
|
+
|
|
12
14
|
-----
|
|
13
15
|
|
|
14
16
|
## 概要
|
|
@@ -1332,7 +1332,79 @@ class MoleculeScene(QGraphicsScene):
|
|
|
1332
1332
|
(b.atom1 is atom2 and b.atom2 is atom1):
|
|
1333
1333
|
return b
|
|
1334
1334
|
return None
|
|
1335
|
-
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
class ZoomableView(QGraphicsView):
|
|
1338
|
+
""" マウスホイールでのズームと中ボタンでのパン機能を追加したQGraphicsView """
|
|
1339
|
+
def __init__(self, scene, parent=None):
|
|
1340
|
+
super().__init__(scene, parent)
|
|
1341
|
+
self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
|
|
1342
|
+
self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorViewCenter)
|
|
1343
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
|
|
1344
|
+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
|
|
1345
|
+
self.setDragMode(QGraphicsView.DragMode.NoDrag) # デフォルトはドラッグなし
|
|
1346
|
+
|
|
1347
|
+
self._is_panning = False
|
|
1348
|
+
self._pan_start_pos = QPointF()
|
|
1349
|
+
|
|
1350
|
+
def wheelEvent(self, event):
|
|
1351
|
+
""" マウスホイールを回した際のイベント """
|
|
1352
|
+
if event.modifiers() & Qt.KeyboardModifier.ControlModifier:
|
|
1353
|
+
zoom_in_factor = 1.1
|
|
1354
|
+
zoom_out_factor = 1 / zoom_in_factor
|
|
1355
|
+
|
|
1356
|
+
transform = self.transform()
|
|
1357
|
+
current_scale = transform.m11()
|
|
1358
|
+
min_scale, max_scale = 0.05, 20.0
|
|
1359
|
+
|
|
1360
|
+
if event.angleDelta().y() > 0:
|
|
1361
|
+
if max_scale > current_scale:
|
|
1362
|
+
self.scale(zoom_in_factor, zoom_in_factor)
|
|
1363
|
+
else:
|
|
1364
|
+
if min_scale < current_scale:
|
|
1365
|
+
self.scale(zoom_out_factor, zoom_out_factor)
|
|
1366
|
+
|
|
1367
|
+
event.accept()
|
|
1368
|
+
else:
|
|
1369
|
+
super().wheelEvent(event)
|
|
1370
|
+
|
|
1371
|
+
def mousePressEvent(self, event):
|
|
1372
|
+
""" 中ボタンが押されたらパン(視点移動)モードを開始 """
|
|
1373
|
+
if event.button() == Qt.MouseButton.MiddleButton:
|
|
1374
|
+
self._is_panning = True
|
|
1375
|
+
self._pan_start_pos = event.pos()
|
|
1376
|
+
self.setCursor(Qt.CursorShape.SizeAllCursor)
|
|
1377
|
+
event.accept()
|
|
1378
|
+
else:
|
|
1379
|
+
super().mousePressEvent(event)
|
|
1380
|
+
|
|
1381
|
+
def mouseMoveEvent(self, event):
|
|
1382
|
+
""" パンモード中にマウスを動かしたら、その分だけ視点を移動させる """
|
|
1383
|
+
if self._is_panning:
|
|
1384
|
+
delta = self.mapToScene(event.pos()) - self.mapToScene(self._pan_start_pos)
|
|
1385
|
+
self.translate(delta.x(), delta.y())
|
|
1386
|
+
self._pan_start_pos = event.pos()
|
|
1387
|
+
event.accept()
|
|
1388
|
+
else:
|
|
1389
|
+
super().mouseMoveEvent(event)
|
|
1390
|
+
|
|
1391
|
+
def mouseReleaseEvent(self, event):
|
|
1392
|
+
""" 中ボタンを離したらパンモードを終了 """
|
|
1393
|
+
if event.button() == Qt.MouseButton.MiddleButton:
|
|
1394
|
+
self._is_panning = False
|
|
1395
|
+
# 現在の描画モードに応じたカーソルに戻す
|
|
1396
|
+
current_mode = self.scene().mode if self.scene() else 'select'
|
|
1397
|
+
if current_mode == 'select':
|
|
1398
|
+
self.setCursor(Qt.CursorShape.ArrowCursor)
|
|
1399
|
+
elif current_mode.startswith(('atom', 'bond', 'template')):
|
|
1400
|
+
self.setCursor(Qt.CursorShape.CrossCursor)
|
|
1401
|
+
elif current_mode.startswith(('charge', 'radical')):
|
|
1402
|
+
self.setCursor(Qt.CursorShape.PointingHandCursor)
|
|
1403
|
+
else:
|
|
1404
|
+
self.setCursor(Qt.CursorShape.ArrowCursor)
|
|
1405
|
+
event.accept()
|
|
1406
|
+
else:
|
|
1407
|
+
super().mouseReleaseEvent(event)
|
|
1336
1408
|
|
|
1337
1409
|
class CalculationWorker(QObject):
|
|
1338
1410
|
# --- 変更点1: 新しいシグナルを追加 ---
|
|
@@ -1559,7 +1631,7 @@ class MainWindow(QMainWindow):
|
|
|
1559
1631
|
self.scene.setSceneRect(-4000,-4000,4000,4000)
|
|
1560
1632
|
self.scene.setBackgroundBrush(QColor("#FFFFFF"))
|
|
1561
1633
|
|
|
1562
|
-
self.view_2d=
|
|
1634
|
+
self.view_2d=ZoomableView(self.scene)
|
|
1563
1635
|
self.view_2d.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
1564
1636
|
self.view_2d.setSizePolicy(
|
|
1565
1637
|
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
|
|
@@ -1830,7 +1902,7 @@ class MainWindow(QMainWindow):
|
|
|
1830
1902
|
menu_bar = self.menuBar()
|
|
1831
1903
|
|
|
1832
1904
|
file_menu = menu_bar.addMenu("&File")
|
|
1833
|
-
load_mol_action = QAction("
|
|
1905
|
+
load_mol_action = QAction("Import MOL...", self); load_mol_action.triggered.connect(self.load_mol_file)
|
|
1834
1906
|
file_menu.addAction(load_mol_action)
|
|
1835
1907
|
file_menu.addSeparator()
|
|
1836
1908
|
save_mol_action = QAction("Save 2D as MOL...", self); save_mol_action.triggered.connect(self.save_as_mol)
|
|
@@ -1900,6 +1972,29 @@ class MainWindow(QMainWindow):
|
|
|
1900
1972
|
clear_all_action.triggered.connect(self.clear_all); edit_menu.addAction(clear_all_action)
|
|
1901
1973
|
|
|
1902
1974
|
view_menu = menu_bar.addMenu("&View")
|
|
1975
|
+
|
|
1976
|
+
zoom_in_action = QAction("Zoom In", self)
|
|
1977
|
+
zoom_in_action.setShortcut(QKeySequence.StandardKey.ZoomIn) # Ctrl +
|
|
1978
|
+
zoom_in_action.triggered.connect(self.zoom_in)
|
|
1979
|
+
view_menu.addAction(zoom_in_action)
|
|
1980
|
+
|
|
1981
|
+
zoom_out_action = QAction("Zoom Out", self)
|
|
1982
|
+
zoom_out_action.setShortcut(QKeySequence.StandardKey.ZoomOut) # Ctrl -
|
|
1983
|
+
zoom_out_action.triggered.connect(self.zoom_out)
|
|
1984
|
+
view_menu.addAction(zoom_out_action)
|
|
1985
|
+
|
|
1986
|
+
reset_zoom_action = QAction("Reset Zoom", self)
|
|
1987
|
+
reset_zoom_action.setShortcut(QKeySequence("Ctrl+0"))
|
|
1988
|
+
reset_zoom_action.triggered.connect(self.reset_zoom)
|
|
1989
|
+
view_menu.addAction(reset_zoom_action)
|
|
1990
|
+
|
|
1991
|
+
fit_action = QAction("Fit to View", self)
|
|
1992
|
+
fit_action.setShortcut(QKeySequence("Ctrl+9"))
|
|
1993
|
+
fit_action.triggered.connect(self.fit_to_view)
|
|
1994
|
+
view_menu.addAction(fit_action)
|
|
1995
|
+
|
|
1996
|
+
view_menu.addSeparator()
|
|
1997
|
+
|
|
1903
1998
|
self.toggle_chiral_action = QAction("Show Chiral Labels", self, checkable=True)
|
|
1904
1999
|
self.toggle_chiral_action.setChecked(self.show_chiral_labels)
|
|
1905
2000
|
self.toggle_chiral_action.triggered.connect(self.toggle_chiral_labels_display)
|
|
@@ -2362,33 +2457,50 @@ class MainWindow(QMainWindow):
|
|
|
2362
2457
|
if push_to_undo:
|
|
2363
2458
|
self.push_undo_state()
|
|
2364
2459
|
|
|
2365
|
-
|
|
2366
2460
|
def load_mol_file(self):
|
|
2367
2461
|
options = QFileDialog.Option.DontUseNativeDialog
|
|
2368
|
-
file_path, _ = QFileDialog.getOpenFileName(self, "
|
|
2462
|
+
file_path, _ = QFileDialog.getOpenFileName(self, "Import MOL File", "", "Chemical Files (*.mol *.sdf);;All Files (*)", options=options)
|
|
2369
2463
|
if not file_path: return
|
|
2370
2464
|
try:
|
|
2371
|
-
suppl = Chem.SDMolSupplier(file_path, removeHs=
|
|
2465
|
+
suppl = Chem.SDMolSupplier(file_path, removeHs=True)
|
|
2372
2466
|
mol = next(suppl, None)
|
|
2373
2467
|
if mol is None: raise ValueError("Failed to read molecule from file.")
|
|
2468
|
+
|
|
2469
|
+
Chem.Kekulize(mol)
|
|
2470
|
+
|
|
2374
2471
|
self.clear_2d_editor(push_to_undo=False)
|
|
2375
2472
|
self.current_mol = None; self.plotter.clear(); self.analysis_action.setEnabled(False)
|
|
2376
2473
|
|
|
2377
|
-
if mol.GetNumConformers() == 0:
|
|
2474
|
+
if mol.GetNumConformers() == 0:
|
|
2475
|
+
AllChem.Compute2DCoords(mol)
|
|
2378
2476
|
|
|
2379
2477
|
conf = mol.GetConformer(); SCALE_FACTOR = 50.0
|
|
2380
|
-
|
|
2478
|
+
|
|
2479
|
+
view_center = self.view_2d.mapToScene(self.view_2d.viewport().rect().center())
|
|
2480
|
+
|
|
2381
2481
|
positions = [conf.GetAtomPosition(i) for i in range(mol.GetNumAtoms())]
|
|
2382
|
-
|
|
2383
|
-
|
|
2482
|
+
if positions:
|
|
2483
|
+
mol_center_x = sum(p.x for p in positions) / len(positions)
|
|
2484
|
+
mol_center_y = sum(p.y for p in positions) / len(positions)
|
|
2485
|
+
else:
|
|
2486
|
+
mol_center_x, mol_center_y = 0.0, 0.0
|
|
2487
|
+
|
|
2488
|
+
rdkit_idx_to_my_id = {}
|
|
2384
2489
|
for i in range(mol.GetNumAtoms()):
|
|
2385
|
-
atom = mol.GetAtomWithIdx(i)
|
|
2490
|
+
atom = mol.GetAtomWithIdx(i)
|
|
2491
|
+
pos = conf.GetAtomPosition(i)
|
|
2386
2492
|
charge = atom.GetFormalCharge()
|
|
2387
|
-
|
|
2493
|
+
|
|
2494
|
+
relative_x = pos.x - mol_center_x
|
|
2495
|
+
relative_y = pos.y - mol_center_y
|
|
2496
|
+
|
|
2497
|
+
scene_x = (relative_x * SCALE_FACTOR) + view_center.x()
|
|
2498
|
+
scene_y = (-relative_y * SCALE_FACTOR) + view_center.y()
|
|
2499
|
+
|
|
2388
2500
|
atom_id = self.scene.create_atom(atom.GetSymbol(), QPointF(scene_x, scene_y), charge=charge)
|
|
2389
2501
|
rdkit_idx_to_my_id[i] = atom_id
|
|
2502
|
+
|
|
2390
2503
|
for bond in mol.GetBonds():
|
|
2391
|
-
# (以降のBond読み込み部分は変更なし)
|
|
2392
2504
|
b_idx,e_idx=bond.GetBeginAtomIdx(),bond.GetEndAtomIdx()
|
|
2393
2505
|
b_type = bond.GetBondTypeAsDouble(); b_dir = bond.GetBondDir()
|
|
2394
2506
|
stereo = 0
|
|
@@ -2796,14 +2908,18 @@ class MainWindow(QMainWindow):
|
|
|
2796
2908
|
self.statusBar().showMessage(f"3D chiral label drawing error: {e}")
|
|
2797
2909
|
|
|
2798
2910
|
|
|
2911
|
+
# MainWindow クラス内 (約2800行目付近)
|
|
2912
|
+
|
|
2799
2913
|
def toggle_chiral_labels_display(self, checked):
|
|
2800
2914
|
"""Viewメニューのアクションに応じてキラルラベル表示を切り替える"""
|
|
2801
2915
|
self.show_chiral_labels = checked
|
|
2916
|
+
|
|
2917
|
+
if self.current_mol:
|
|
2918
|
+
self.draw_molecule_3d(self.current_mol)
|
|
2919
|
+
|
|
2802
2920
|
if checked:
|
|
2803
|
-
draw_molecule_3d()
|
|
2804
2921
|
self.statusBar().showMessage("Chiral labels: will be (re)computed after Convert→3D.")
|
|
2805
2922
|
else:
|
|
2806
|
-
draw_molecule_3d()
|
|
2807
2923
|
self.statusBar().showMessage("Chiral labels disabled.")
|
|
2808
2924
|
|
|
2809
2925
|
|
|
@@ -2892,6 +3008,43 @@ class MainWindow(QMainWindow):
|
|
|
2892
3008
|
else:
|
|
2893
3009
|
event.ignore()
|
|
2894
3010
|
|
|
3011
|
+
def zoom_in(self):
|
|
3012
|
+
""" ビューを 20% 拡大する """
|
|
3013
|
+
self.view_2d.scale(1.2, 1.2)
|
|
3014
|
+
|
|
3015
|
+
def zoom_out(self):
|
|
3016
|
+
""" ビューを 20% 縮小する """
|
|
3017
|
+
self.view_2d.scale(1/1.2, 1/1.2)
|
|
3018
|
+
|
|
3019
|
+
def reset_zoom(self):
|
|
3020
|
+
""" ビューの拡大率をデフォルト (50%) にリセットする """
|
|
3021
|
+
transform = QTransform()
|
|
3022
|
+
transform.scale(0.5, 0.5)
|
|
3023
|
+
self.view_2d.setTransform(transform)
|
|
3024
|
+
|
|
3025
|
+
def fit_to_view(self):
|
|
3026
|
+
""" シーン上のすべてのアイテムがビューに収まるように調整する """
|
|
3027
|
+
if not self.scene.items():
|
|
3028
|
+
self.reset_zoom()
|
|
3029
|
+
return
|
|
3030
|
+
|
|
3031
|
+
bounds = self.scene.itemsBoundingRect()
|
|
3032
|
+
visible_items_rect = QRectF()
|
|
3033
|
+
for item in self.scene.items():
|
|
3034
|
+
if item.isVisible() and not isinstance(item, TemplatePreviewItem):
|
|
3035
|
+
if visible_items_rect.isEmpty():
|
|
3036
|
+
visible_items_rect = item.sceneBoundingRect()
|
|
3037
|
+
else:
|
|
3038
|
+
visible_items_rect = visible_items_rect.united(item.sceneBoundingRect())
|
|
3039
|
+
|
|
3040
|
+
if not visible_items_rect.isEmpty():
|
|
3041
|
+
self.view_2d.fitInView(visible_items_rect, Qt.AspectRatioMode.KeepAspectRatio)
|
|
3042
|
+
self.view_2d.scale(0.6, 0.6)
|
|
3043
|
+
else:
|
|
3044
|
+
self.reset_zoom()
|
|
3045
|
+
|
|
3046
|
+
|
|
2895
3047
|
# --- Application Execution ---
|
|
2896
3048
|
if __name__ == '__main__':
|
|
2897
3049
|
main()
|
|
3050
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moleditpy
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: A program to edit molecular structures using Python.
|
|
5
5
|
Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/HiroYokoyama/python_molecular_editor
|
|
@@ -33,6 +33,8 @@ A cross-platform, simple, and intuitive molecular structure editor built in Pyth
|
|
|
33
33
|
ライセンス: Apache-2.0
|
|
34
34
|
リポジトリ: [https://github.com/HiroYokoyama/python\_molecular\_editor](https://github.com/HiroYokoyama/python_molecular_editor)
|
|
35
35
|
|
|
36
|
+

|
|
37
|
+
|
|
36
38
|
-----
|
|
37
39
|
|
|
38
40
|
## 概要
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|