moleditpy 1.1.7__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.7 → moleditpy-1.2.0}/PKG-INFO +1 -1
- {moleditpy-1.1.7 → moleditpy-1.2.0}/pyproject.toml +1 -1
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy/main.py +101 -6
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy.egg-info/PKG-INFO +1 -1
- {moleditpy-1.1.7 → moleditpy-1.2.0}/README.md +0 -0
- {moleditpy-1.1.7 → moleditpy-1.2.0}/setup.cfg +0 -0
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy/__init__.py +0 -0
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy/assets/icon.ico +0 -0
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy/assets/icon.png +0 -0
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy.egg-info/SOURCES.txt +0 -0
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy.egg-info/dependency_links.txt +0 -0
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy.egg-info/entry_points.txt +0 -0
- {moleditpy-1.1.7 → moleditpy-1.2.0}/src/moleditpy.egg-info/requires.txt +0 -0
- {moleditpy-1.1.7 → 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
|
|
|
@@ -2077,6 +2089,7 @@ class MainWindow(QMainWindow):
|
|
|
2077
2089
|
# 1. 右パネル全体は「垂直」レイアウトにする
|
|
2078
2090
|
right_layout = QVBoxLayout(right_pane)
|
|
2079
2091
|
self.plotter = QtInteractor(right_pane)
|
|
2092
|
+
self.plotter.setAcceptDrops(False)
|
|
2080
2093
|
self.plotter.setSizePolicy(
|
|
2081
2094
|
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
|
|
2082
2095
|
)
|
|
@@ -2363,6 +2376,9 @@ class MainWindow(QMainWindow):
|
|
|
2363
2376
|
menu_bar = self.menuBar()
|
|
2364
2377
|
|
|
2365
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)
|
|
2366
2382
|
load_mol_action = QAction("Import MOL...", self); load_mol_action.triggered.connect(self.load_mol_file)
|
|
2367
2383
|
file_menu.addAction(load_mol_action)
|
|
2368
2384
|
file_menu.addSeparator()
|
|
@@ -3019,13 +3035,84 @@ class MainWindow(QMainWindow):
|
|
|
3019
3035
|
if push_to_undo:
|
|
3020
3036
|
self.push_undo_state()
|
|
3021
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
|
+
|
|
3022
3109
|
def load_mol_file(self):
|
|
3023
3110
|
options = QFileDialog.Option.DontUseNativeDialog
|
|
3024
3111
|
file_path, _ = QFileDialog.getOpenFileName(self, "Import MOL File", "", "Chemical Files (*.mol *.sdf);;All Files (*)", options=options)
|
|
3025
3112
|
if not file_path: return
|
|
3026
3113
|
try:
|
|
3027
3114
|
self.dragged_atom_info = None
|
|
3028
|
-
suppl = Chem.SDMolSupplier(file_path, removeHs=
|
|
3115
|
+
suppl = Chem.SDMolSupplier(file_path, removeHs=False)
|
|
3029
3116
|
mol = next(suppl, None)
|
|
3030
3117
|
if mol is None: raise ValueError("Failed to read molecule from file.")
|
|
3031
3118
|
|
|
@@ -3035,9 +3122,16 @@ class MainWindow(QMainWindow):
|
|
|
3035
3122
|
self.current_mol = None; self.plotter.clear(); self.analysis_action.setEnabled(False)
|
|
3036
3123
|
|
|
3037
3124
|
if mol.GetNumConformers() == 0:
|
|
3125
|
+
# 座標情報がないMOLファイルの場合、座標を生成し、
|
|
3126
|
+
# ファイルから読み取った立体情報を元に、くさび結合を再設定する
|
|
3038
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()
|
|
3039
3133
|
|
|
3040
|
-
|
|
3134
|
+
SCALE_FACTOR = 50.0
|
|
3041
3135
|
|
|
3042
3136
|
view_center = self.view_2d.mapToScene(self.view_2d.viewport().rect().center())
|
|
3043
3137
|
|
|
@@ -3075,6 +3169,7 @@ class MainWindow(QMainWindow):
|
|
|
3075
3169
|
self.scene.create_bond(a1_item, a2_item, bond_order=int(b_type), bond_stereo=stereo)
|
|
3076
3170
|
|
|
3077
3171
|
self.statusBar().showMessage(f"Successfully loaded {file_path}")
|
|
3172
|
+
self.fit_to_view()
|
|
3078
3173
|
self.reset_undo_stack()
|
|
3079
3174
|
except Exception as e: self.statusBar().showMessage(f"Error loading file: {e}")
|
|
3080
3175
|
|
|
@@ -3152,7 +3247,7 @@ class MainWindow(QMainWindow):
|
|
|
3152
3247
|
for i in range(num_atoms):
|
|
3153
3248
|
pos=conf.GetAtomPosition(i); symbol=self.current_mol.GetAtomWithIdx(i).GetSymbol()
|
|
3154
3249
|
xyz_lines.append(f"{symbol} {pos.x:.6f} {pos.y:.6f} {pos.z:.6f}")
|
|
3155
|
-
with open(file_path,'w') as f: f.write("\n".join(xyz_lines))
|
|
3250
|
+
with open(file_path,'w') as f: f.write("\n".join(xyz_lines) + "\n")
|
|
3156
3251
|
self.statusBar().showMessage(f"Successfully saved to {file_path}")
|
|
3157
3252
|
except Exception as e: self.statusBar().showMessage(f"Error saving file: {e}")
|
|
3158
3253
|
|
|
@@ -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
|