moleditpy 1.1.8__tar.gz → 1.2.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.
- {moleditpy-1.1.8 → moleditpy-1.2.0}/PKG-INFO +1 -1
- {moleditpy-1.1.8 → moleditpy-1.2.0}/pyproject.toml +1 -1
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy/main.py +99 -5
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy.egg-info/PKG-INFO +1 -1
- {moleditpy-1.1.8 → moleditpy-1.2.0}/README.md +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/setup.cfg +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy/__init__.py +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy/assets/icon.ico +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy/assets/icon.png +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy.egg-info/SOURCES.txt +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy.egg-info/dependency_links.txt +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy.egg-info/entry_points.txt +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/src/moleditpy.egg-info/requires.txt +0 -0
- {moleditpy-1.1.8 → moleditpy-1.2.0}/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: 1.
|
|
3
|
+
Version: 1.2.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
|
Project-URL: Homepage, https://github.com/HiroYokoyama/python_molecular_editor
|
|
@@ -11,7 +11,11 @@ DOI 10.5281/zenodo.17268532
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
#Version
|
|
14
|
-
VERSION = '1.
|
|
14
|
+
VERSION = '1.2.0'
|
|
15
|
+
|
|
16
|
+
print("-----------------------------------------------------")
|
|
17
|
+
print("MoleditPy — A Python-based molecular editing software")
|
|
18
|
+
print("-----------------------------------------------------\n")
|
|
15
19
|
|
|
16
20
|
import sys
|
|
17
21
|
import numpy as np
|
|
@@ -31,7 +35,7 @@ from PyQt6.QtWidgets import (
|
|
|
31
35
|
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
|
32
36
|
QPushButton, QSplitter, QGraphicsView, QGraphicsScene, QGraphicsItem,
|
|
33
37
|
QToolBar, QStatusBar, QGraphicsTextItem, QGraphicsLineItem, QDialog, QGridLayout,
|
|
34
|
-
QFileDialog, QSizePolicy, QLabel, QLineEdit, QToolButton, QMenu, QMessageBox
|
|
38
|
+
QFileDialog, QSizePolicy, QLabel, QLineEdit, QToolButton, QMenu, QMessageBox, QInputDialog
|
|
35
39
|
)
|
|
36
40
|
from PyQt6.QtGui import (
|
|
37
41
|
QPen, QBrush, QColor, QPainter, QAction, QActionGroup, QFont, QPolygonF,
|
|
@@ -190,7 +194,15 @@ class MolecularData:
|
|
|
190
194
|
|
|
191
195
|
for (id1, id2), bond in self.bonds.items():
|
|
192
196
|
idx1, idx2, order = atom_map[id1] + 1, atom_map[id2] + 1, bond['order']
|
|
193
|
-
|
|
197
|
+
stereo_code = 0
|
|
198
|
+
bond_stereo = bond.get('stereo', 0)
|
|
199
|
+
if bond_stereo == 1:
|
|
200
|
+
stereo_code = 1
|
|
201
|
+
elif bond_stereo == 2:
|
|
202
|
+
stereo_code = 6
|
|
203
|
+
|
|
204
|
+
mol_block += f"{idx1:3d}{idx2:3d}{order:3d}{stereo_code:3d} 0 0 0\n"
|
|
205
|
+
|
|
194
206
|
mol_block += "M END\n"
|
|
195
207
|
return mol_block
|
|
196
208
|
|
|
@@ -2364,6 +2376,9 @@ class MainWindow(QMainWindow):
|
|
|
2364
2376
|
menu_bar = self.menuBar()
|
|
2365
2377
|
|
|
2366
2378
|
file_menu = menu_bar.addMenu("&File")
|
|
2379
|
+
import_smiles_action = QAction("Import SMILES...", self)
|
|
2380
|
+
import_smiles_action.triggered.connect(self.import_smiles_dialog)
|
|
2381
|
+
file_menu.addAction(import_smiles_action)
|
|
2367
2382
|
load_mol_action = QAction("Import MOL...", self); load_mol_action.triggered.connect(self.load_mol_file)
|
|
2368
2383
|
file_menu.addAction(load_mol_action)
|
|
2369
2384
|
file_menu.addSeparator()
|
|
@@ -3020,13 +3035,84 @@ class MainWindow(QMainWindow):
|
|
|
3020
3035
|
if push_to_undo:
|
|
3021
3036
|
self.push_undo_state()
|
|
3022
3037
|
|
|
3038
|
+
def import_smiles_dialog(self):
|
|
3039
|
+
"""ユーザーにSMILES文字列の入力を促すダイアログを表示する"""
|
|
3040
|
+
smiles, ok = QInputDialog.getText(self, "Import SMILES", "Enter SMILES string:")
|
|
3041
|
+
if ok and smiles:
|
|
3042
|
+
self.load_from_smiles(smiles)
|
|
3043
|
+
|
|
3044
|
+
def load_from_smiles(self, smiles_string):
|
|
3045
|
+
"""SMILES文字列から分子を読み込み、2Dエディタに表示する"""
|
|
3046
|
+
try:
|
|
3047
|
+
mol = Chem.MolFromSmiles(smiles_string)
|
|
3048
|
+
if mol is None:
|
|
3049
|
+
raise ValueError("Invalid SMILES string.")
|
|
3050
|
+
|
|
3051
|
+
AllChem.Compute2DCoords(mol)
|
|
3052
|
+
Chem.Kekulize(mol)
|
|
3053
|
+
|
|
3054
|
+
AllChem.AssignStereochemistry(mol, cleanIt=True, force=True)
|
|
3055
|
+
conf = mol.GetConformer()
|
|
3056
|
+
AllChem.WedgeMolBonds(mol, conf)
|
|
3057
|
+
|
|
3058
|
+
self.clear_2d_editor(push_to_undo=False)
|
|
3059
|
+
self.current_mol = None
|
|
3060
|
+
self.plotter.clear()
|
|
3061
|
+
self.analysis_action.setEnabled(False)
|
|
3062
|
+
|
|
3063
|
+
conf = mol.GetConformer()
|
|
3064
|
+
SCALE_FACTOR = 50.0
|
|
3065
|
+
|
|
3066
|
+
view_center = self.view_2d.mapToScene(self.view_2d.viewport().rect().center())
|
|
3067
|
+
positions = [conf.GetAtomPosition(i) for i in range(mol.GetNumAtoms())]
|
|
3068
|
+
mol_center_x = sum(p.x for p in positions) / len(positions) if positions else 0.0
|
|
3069
|
+
mol_center_y = sum(p.y for p in positions) / len(positions) if positions else 0.0
|
|
3070
|
+
|
|
3071
|
+
rdkit_idx_to_my_id = {}
|
|
3072
|
+
for i in range(mol.GetNumAtoms()):
|
|
3073
|
+
atom = mol.GetAtomWithIdx(i)
|
|
3074
|
+
pos = conf.GetAtomPosition(i)
|
|
3075
|
+
charge = atom.GetFormalCharge()
|
|
3076
|
+
|
|
3077
|
+
relative_x = pos.x - mol_center_x
|
|
3078
|
+
relative_y = pos.y - mol_center_y
|
|
3079
|
+
|
|
3080
|
+
scene_x = (relative_x * SCALE_FACTOR) + view_center.x()
|
|
3081
|
+
scene_y = (-relative_y * SCALE_FACTOR) + view_center.y()
|
|
3082
|
+
|
|
3083
|
+
atom_id = self.scene.create_atom(atom.GetSymbol(), QPointF(scene_x, scene_y), charge=charge)
|
|
3084
|
+
rdkit_idx_to_my_id[i] = atom_id
|
|
3085
|
+
|
|
3086
|
+
for bond in mol.GetBonds():
|
|
3087
|
+
b_idx, e_idx = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
|
|
3088
|
+
b_type = bond.GetBondTypeAsDouble()
|
|
3089
|
+
b_dir = bond.GetBondDir()
|
|
3090
|
+
stereo = 0
|
|
3091
|
+
if b_dir == Chem.BondDir.BEGINWEDGE:
|
|
3092
|
+
stereo = 1 # Wedge
|
|
3093
|
+
elif b_dir == Chem.BondDir.BEGINDASH:
|
|
3094
|
+
stereo = 2 # Dash
|
|
3095
|
+
|
|
3096
|
+
if b_idx in rdkit_idx_to_my_id and e_idx in rdkit_idx_to_my_id:
|
|
3097
|
+
a1_id, a2_id = rdkit_idx_to_my_id[b_idx], rdkit_idx_to_my_id[e_idx]
|
|
3098
|
+
a1_item = self.data.atoms[a1_id]['item']
|
|
3099
|
+
a2_item = self.data.atoms[a2_id]['item']
|
|
3100
|
+
|
|
3101
|
+
self.scene.create_bond(a1_item, a2_item, bond_order=int(b_type), bond_stereo=stereo)
|
|
3102
|
+
|
|
3103
|
+
self.statusBar().showMessage(f"Successfully loaded from SMILES.")
|
|
3104
|
+
self.reset_undo_stack()
|
|
3105
|
+
self.fit_to_view()
|
|
3106
|
+
except Exception as e:
|
|
3107
|
+
self.statusBar().showMessage(f"Error loading from SMILES: {e}")
|
|
3108
|
+
|
|
3023
3109
|
def load_mol_file(self):
|
|
3024
3110
|
options = QFileDialog.Option.DontUseNativeDialog
|
|
3025
3111
|
file_path, _ = QFileDialog.getOpenFileName(self, "Import MOL File", "", "Chemical Files (*.mol *.sdf);;All Files (*)", options=options)
|
|
3026
3112
|
if not file_path: return
|
|
3027
3113
|
try:
|
|
3028
3114
|
self.dragged_atom_info = None
|
|
3029
|
-
suppl = Chem.SDMolSupplier(file_path, removeHs=
|
|
3115
|
+
suppl = Chem.SDMolSupplier(file_path, removeHs=False)
|
|
3030
3116
|
mol = next(suppl, None)
|
|
3031
3117
|
if mol is None: raise ValueError("Failed to read molecule from file.")
|
|
3032
3118
|
|
|
@@ -3036,9 +3122,16 @@ class MainWindow(QMainWindow):
|
|
|
3036
3122
|
self.current_mol = None; self.plotter.clear(); self.analysis_action.setEnabled(False)
|
|
3037
3123
|
|
|
3038
3124
|
if mol.GetNumConformers() == 0:
|
|
3125
|
+
# 座標情報がないMOLファイルの場合、座標を生成し、
|
|
3126
|
+
# ファイルから読み取った立体情報を元に、くさび結合を再設定する
|
|
3039
3127
|
AllChem.Compute2DCoords(mol)
|
|
3128
|
+
AllChem.AssignStereochemistry(mol, cleanIt=True, force=True)
|
|
3129
|
+
conf_for_wedge = mol.GetConformer()
|
|
3130
|
+
AllChem.WedgeMolBonds(mol, conf_for_wedge)
|
|
3131
|
+
|
|
3132
|
+
conf = mol.GetConformer()
|
|
3040
3133
|
|
|
3041
|
-
|
|
3134
|
+
SCALE_FACTOR = 50.0
|
|
3042
3135
|
|
|
3043
3136
|
view_center = self.view_2d.mapToScene(self.view_2d.viewport().rect().center())
|
|
3044
3137
|
|
|
@@ -3076,6 +3169,7 @@ class MainWindow(QMainWindow):
|
|
|
3076
3169
|
self.scene.create_bond(a1_item, a2_item, bond_order=int(b_type), bond_stereo=stereo)
|
|
3077
3170
|
|
|
3078
3171
|
self.statusBar().showMessage(f"Successfully loaded {file_path}")
|
|
3172
|
+
self.fit_to_view()
|
|
3079
3173
|
self.reset_undo_stack()
|
|
3080
3174
|
except Exception as e: self.statusBar().showMessage(f"Error loading file: {e}")
|
|
3081
3175
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moleditpy
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.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
|
Project-URL: Homepage, https://github.com/HiroYokoyama/python_molecular_editor
|
|
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
|
|
File without changes
|