moleditpy 1.8.2__tar.gz → 1.8.4__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.8.2 → moleditpy-1.8.4}/PKG-INFO +1 -1
- {moleditpy-1.8.2 → moleditpy-1.8.4}/pyproject.toml +1 -1
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy/main.py +113 -52
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy.egg-info/PKG-INFO +1 -1
- {moleditpy-1.8.2 → moleditpy-1.8.4}/README.md +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/setup.cfg +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy/__init__.py +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy/assets/icon.ico +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy/assets/icon.png +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy.egg-info/SOURCES.txt +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy.egg-info/dependency_links.txt +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy.egg-info/entry_points.txt +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/src/moleditpy.egg-info/requires.txt +0 -0
- {moleditpy-1.8.2 → moleditpy-1.8.4}/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.8.
|
|
3
|
+
Version: 1.8.4
|
|
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,7 @@ DOI 10.5281/zenodo.17268532
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
#Version
|
|
14
|
-
VERSION = '1.8.
|
|
14
|
+
VERSION = '1.8.4'
|
|
15
15
|
|
|
16
16
|
print("-----------------------------------------------------")
|
|
17
17
|
print("MoleditPy — A Python-based molecular editing software")
|
|
@@ -245,10 +245,10 @@ class TranslationDialog(Dialog3DPickingMixin, QDialog):
|
|
|
245
245
|
self.apply_button.clicked.connect(self.apply_translation)
|
|
246
246
|
self.apply_button.setEnabled(False)
|
|
247
247
|
button_layout.addWidget(self.apply_button)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
button_layout.addWidget(
|
|
248
|
+
|
|
249
|
+
close_button = QPushButton("Close")
|
|
250
|
+
close_button.clicked.connect(self.reject)
|
|
251
|
+
button_layout.addWidget(close_button)
|
|
252
252
|
|
|
253
253
|
layout.addLayout(button_layout)
|
|
254
254
|
|
|
@@ -337,8 +337,6 @@ class TranslationDialog(Dialog3DPickingMixin, QDialog):
|
|
|
337
337
|
# キラルラベルを更新
|
|
338
338
|
self.main_window.update_chiral_labels()
|
|
339
339
|
|
|
340
|
-
self.accept()
|
|
341
|
-
|
|
342
340
|
except Exception as e:
|
|
343
341
|
QMessageBox.critical(self, "Error", f"Failed to apply translation: {str(e)}")
|
|
344
342
|
|
|
@@ -680,10 +678,10 @@ class SymmetrizeDialog(QDialog):
|
|
|
680
678
|
self.apply_button.clicked.connect(self.apply_symmetrization)
|
|
681
679
|
self.apply_button.setDefault(True)
|
|
682
680
|
button_layout.addWidget(self.apply_button)
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
button_layout.addWidget(
|
|
681
|
+
|
|
682
|
+
close_button = QPushButton("Close")
|
|
683
|
+
close_button.clicked.connect(self.reject)
|
|
684
|
+
button_layout.addWidget(close_button)
|
|
687
685
|
|
|
688
686
|
layout.addLayout(button_layout)
|
|
689
687
|
|
|
@@ -813,8 +811,6 @@ class SymmetrizeDialog(QDialog):
|
|
|
813
811
|
f"Molecular structure has been symmetrized according to {point_group['name']} point group."
|
|
814
812
|
)
|
|
815
813
|
|
|
816
|
-
self.accept()
|
|
817
|
-
|
|
818
814
|
except Exception as e:
|
|
819
815
|
QMessageBox.critical(self, "Error", f"Failed to apply symmetrization: {str(e)}")
|
|
820
816
|
print(f"Symmetrization error: {e}")
|
|
@@ -1866,12 +1862,12 @@ class MirrorDialog(QDialog):
|
|
|
1866
1862
|
|
|
1867
1863
|
apply_button = QPushButton("Apply Mirror")
|
|
1868
1864
|
apply_button.clicked.connect(self.apply_mirror)
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1865
|
+
|
|
1866
|
+
close_button = QPushButton("Close")
|
|
1867
|
+
close_button.clicked.connect(self.reject)
|
|
1868
|
+
|
|
1873
1869
|
button_layout.addWidget(apply_button)
|
|
1874
|
-
button_layout.addWidget(
|
|
1870
|
+
button_layout.addWidget(close_button)
|
|
1875
1871
|
|
|
1876
1872
|
layout.addLayout(button_layout)
|
|
1877
1873
|
|
|
@@ -1924,8 +1920,6 @@ class MirrorDialog(QDialog):
|
|
|
1924
1920
|
plane_names = ["XY", "XZ", "YZ"]
|
|
1925
1921
|
self.main_window.statusBar().showMessage(f"Molecule mirrored across {plane_names[plane_id]} plane.")
|
|
1926
1922
|
|
|
1927
|
-
self.accept()
|
|
1928
|
-
|
|
1929
1923
|
except Exception as e:
|
|
1930
1924
|
QMessageBox.critical(self, "Error", f"Failed to apply mirror transformation: {str(e)}")
|
|
1931
1925
|
|
|
@@ -1977,10 +1971,10 @@ class PlanarizationDialog(Dialog3DPickingMixin, QDialog):
|
|
|
1977
1971
|
self.apply_button.clicked.connect(self.apply_planarization)
|
|
1978
1972
|
self.apply_button.setEnabled(False)
|
|
1979
1973
|
button_layout.addWidget(self.apply_button)
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
button_layout.addWidget(
|
|
1974
|
+
|
|
1975
|
+
close_button = QPushButton("Close")
|
|
1976
|
+
close_button.clicked.connect(self.reject)
|
|
1977
|
+
button_layout.addWidget(close_button)
|
|
1984
1978
|
|
|
1985
1979
|
layout.addLayout(button_layout)
|
|
1986
1980
|
|
|
@@ -2147,8 +2141,6 @@ class PlanarizationDialog(Dialog3DPickingMixin, QDialog):
|
|
|
2147
2141
|
# キラルラベルを更新
|
|
2148
2142
|
self.main_window.update_chiral_labels()
|
|
2149
2143
|
|
|
2150
|
-
self.accept()
|
|
2151
|
-
|
|
2152
2144
|
except Exception as e:
|
|
2153
2145
|
QMessageBox.critical(self, "Error", f"Failed to apply planarization: {str(e)}")
|
|
2154
2146
|
|
|
@@ -2221,9 +2213,9 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
|
|
|
2221
2213
|
self.apply_button.setEnabled(False)
|
|
2222
2214
|
button_layout.addWidget(self.apply_button)
|
|
2223
2215
|
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
button_layout.addWidget(
|
|
2216
|
+
close_button = QPushButton("Close")
|
|
2217
|
+
close_button.clicked.connect(self.reject)
|
|
2218
|
+
button_layout.addWidget(close_button)
|
|
2227
2219
|
|
|
2228
2220
|
layout.addLayout(button_layout)
|
|
2229
2221
|
|
|
@@ -2396,7 +2388,6 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
|
|
|
2396
2388
|
self.main_window.update_chiral_labels()
|
|
2397
2389
|
|
|
2398
2390
|
QMessageBox.information(self, "Success", f"Alignment to {self.axis.upper()}-axis completed.")
|
|
2399
|
-
self.accept()
|
|
2400
2391
|
|
|
2401
2392
|
except Exception as e:
|
|
2402
2393
|
QMessageBox.critical(self, "Error", f"Failed to apply alignment: {str(e)}")
|
|
@@ -3315,7 +3306,6 @@ class MoleculeScene(QGraphicsScene):
|
|
|
3315
3306
|
return needs_update
|
|
3316
3307
|
|
|
3317
3308
|
def mousePressEvent(self, event):
|
|
3318
|
-
self.clear_all_problem_flags()
|
|
3319
3309
|
self.press_pos = event.scenePos()
|
|
3320
3310
|
self.mouse_moved_since_press = False
|
|
3321
3311
|
self.data_changed_in_event = False
|
|
@@ -7131,9 +7121,8 @@ class MainWindow(QMainWindow):
|
|
|
7131
7121
|
self.mode_actions['select'].setChecked(True)
|
|
7132
7122
|
|
|
7133
7123
|
def trigger_conversion(self):
|
|
7134
|
-
|
|
7135
|
-
if not
|
|
7136
|
-
# 3Dビューと関連データをクリア
|
|
7124
|
+
# 2Dエディタに原子が存在しない場合は3Dビューをクリア
|
|
7125
|
+
if not self.data.atoms:
|
|
7137
7126
|
self.plotter.clear()
|
|
7138
7127
|
self.current_mol = None
|
|
7139
7128
|
self.analysis_action.setEnabled(False)
|
|
@@ -7141,8 +7130,13 @@ class MainWindow(QMainWindow):
|
|
|
7141
7130
|
self.view_2d.setFocus()
|
|
7142
7131
|
return
|
|
7143
7132
|
|
|
7144
|
-
|
|
7145
|
-
|
|
7133
|
+
mol = self.data.to_rdkit_mol(use_2d_stereo=False)
|
|
7134
|
+
|
|
7135
|
+
# 分子オブジェクトが作成できない場合でも化学的問題をチェック
|
|
7136
|
+
if not mol or mol.GetNumAtoms() == 0:
|
|
7137
|
+
# RDKitでの変換に失敗した場合は、独自の化学的問題チェックを実行
|
|
7138
|
+
self.check_chemistry_problems_fallback()
|
|
7139
|
+
return
|
|
7146
7140
|
|
|
7147
7141
|
# 原子プロパティを保存(ワーカープロセスで失われるため)
|
|
7148
7142
|
self.original_atom_properties = {}
|
|
@@ -7156,6 +7150,8 @@ class MainWindow(QMainWindow):
|
|
|
7156
7150
|
|
|
7157
7151
|
problems = Chem.DetectChemistryProblems(mol)
|
|
7158
7152
|
if problems:
|
|
7153
|
+
# 化学的問題が見つかった場合は既存のフラグをクリアしてから新しい問題を表示
|
|
7154
|
+
self.scene.clear_all_problem_flags()
|
|
7159
7155
|
self.statusBar().showMessage(f"Error: {len(problems)} chemistry problem(s) found.")
|
|
7160
7156
|
# 既存の選択状態をクリア
|
|
7161
7157
|
self.scene.clearSelection()
|
|
@@ -7175,6 +7171,9 @@ class MainWindow(QMainWindow):
|
|
|
7175
7171
|
self.view_2d.setFocus()
|
|
7176
7172
|
return
|
|
7177
7173
|
|
|
7174
|
+
# 化学的問題がない場合のみフラグをクリアして3D変換を実行
|
|
7175
|
+
self.scene.clear_all_problem_flags()
|
|
7176
|
+
|
|
7178
7177
|
try:
|
|
7179
7178
|
Chem.SanitizeMol(mol)
|
|
7180
7179
|
except Exception:
|
|
@@ -7251,6 +7250,63 @@ class MainWindow(QMainWindow):
|
|
|
7251
7250
|
|
|
7252
7251
|
self.view_2d.setFocus()
|
|
7253
7252
|
|
|
7253
|
+
def check_chemistry_problems_fallback(self):
|
|
7254
|
+
"""RDKit変換が失敗した場合の化学的問題チェック(独自実装)"""
|
|
7255
|
+
try:
|
|
7256
|
+
# 既存のフラグをクリア
|
|
7257
|
+
self.scene.clear_all_problem_flags()
|
|
7258
|
+
|
|
7259
|
+
# 簡易的な化学的問題チェック
|
|
7260
|
+
problem_atoms = []
|
|
7261
|
+
|
|
7262
|
+
for atom_id, atom_data in self.data.atoms.items():
|
|
7263
|
+
atom_item = atom_data.get('item')
|
|
7264
|
+
if not atom_item:
|
|
7265
|
+
continue
|
|
7266
|
+
|
|
7267
|
+
symbol = atom_data['symbol']
|
|
7268
|
+
charge = atom_data.get('charge', 0)
|
|
7269
|
+
|
|
7270
|
+
# 結合数を計算
|
|
7271
|
+
bond_count = 0
|
|
7272
|
+
for (id1, id2), bond_data in self.data.bonds.items():
|
|
7273
|
+
if id1 == atom_id or id2 == atom_id:
|
|
7274
|
+
bond_count += bond_data.get('order', 1)
|
|
7275
|
+
|
|
7276
|
+
# 基本的な価数チェック
|
|
7277
|
+
is_problematic = False
|
|
7278
|
+
if symbol == 'C' and bond_count > 4:
|
|
7279
|
+
is_problematic = True
|
|
7280
|
+
elif symbol == 'N' and bond_count > 3 and charge == 0:
|
|
7281
|
+
is_problematic = True
|
|
7282
|
+
elif symbol == 'O' and bond_count > 2 and charge == 0:
|
|
7283
|
+
is_problematic = True
|
|
7284
|
+
elif symbol == 'H' and bond_count > 1:
|
|
7285
|
+
is_problematic = True
|
|
7286
|
+
elif symbol in ['F', 'Cl', 'Br', 'I'] and bond_count > 1 and charge == 0:
|
|
7287
|
+
is_problematic = True
|
|
7288
|
+
|
|
7289
|
+
if is_problematic:
|
|
7290
|
+
problem_atoms.append(atom_item)
|
|
7291
|
+
|
|
7292
|
+
if problem_atoms:
|
|
7293
|
+
# 問題のある原子に赤枠を設定
|
|
7294
|
+
for atom_item in problem_atoms:
|
|
7295
|
+
atom_item.has_problem = True
|
|
7296
|
+
atom_item.update()
|
|
7297
|
+
|
|
7298
|
+
self.statusBar().showMessage(f"Error: {len(problem_atoms)} chemistry problem(s) found (valence issues).")
|
|
7299
|
+
else:
|
|
7300
|
+
self.statusBar().showMessage("Error: Invalid chemical structure (RDKit conversion failed).")
|
|
7301
|
+
|
|
7302
|
+
self.scene.clearSelection()
|
|
7303
|
+
self.view_2d.setFocus()
|
|
7304
|
+
|
|
7305
|
+
except Exception as e:
|
|
7306
|
+
print(f"Error in fallback chemistry check: {e}")
|
|
7307
|
+
self.statusBar().showMessage("Error: Invalid chemical structure.")
|
|
7308
|
+
self.view_2d.setFocus()
|
|
7309
|
+
|
|
7254
7310
|
def optimize_3d_structure(self):
|
|
7255
7311
|
"""現在の3D分子構造を力場で最適化する"""
|
|
7256
7312
|
if not self.current_mol:
|
|
@@ -9100,9 +9156,20 @@ class MainWindow(QMainWindow):
|
|
|
9100
9156
|
|
|
9101
9157
|
def clean_up_2d_structure(self):
|
|
9102
9158
|
self.statusBar().showMessage("Optimizing 2D structure...")
|
|
9159
|
+
|
|
9160
|
+
# 最初に既存の化学的問題フラグをクリア
|
|
9161
|
+
self.scene.clear_all_problem_flags()
|
|
9162
|
+
|
|
9163
|
+
# 2Dエディタに原子が存在しない場合
|
|
9164
|
+
if not self.data.atoms:
|
|
9165
|
+
self.statusBar().showMessage("Error: No atoms to optimize.")
|
|
9166
|
+
return
|
|
9167
|
+
|
|
9103
9168
|
mol = self.data.to_rdkit_mol()
|
|
9104
9169
|
if mol is None or mol.GetNumAtoms() == 0:
|
|
9105
|
-
|
|
9170
|
+
# RDKit変換が失敗した場合は化学的問題をチェック
|
|
9171
|
+
self.check_chemistry_problems_fallback()
|
|
9172
|
+
return
|
|
9106
9173
|
|
|
9107
9174
|
try:
|
|
9108
9175
|
# 安定版:原子IDとRDKit座標の確実なマッピング
|
|
@@ -11161,9 +11228,9 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog):
|
|
|
11161
11228
|
self.apply_button.setEnabled(False)
|
|
11162
11229
|
button_layout.addWidget(self.apply_button)
|
|
11163
11230
|
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
button_layout.addWidget(
|
|
11231
|
+
close_button = QPushButton("Close")
|
|
11232
|
+
close_button.clicked.connect(self.reject)
|
|
11233
|
+
button_layout.addWidget(close_button)
|
|
11167
11234
|
|
|
11168
11235
|
layout.addLayout(button_layout)
|
|
11169
11236
|
|
|
@@ -11345,8 +11412,6 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog):
|
|
|
11345
11412
|
|
|
11346
11413
|
# キラルラベルを更新
|
|
11347
11414
|
self.main_window.update_chiral_labels()
|
|
11348
|
-
|
|
11349
|
-
self.accept()
|
|
11350
11415
|
|
|
11351
11416
|
def adjust_bond_length(self, new_distance):
|
|
11352
11417
|
"""結合長を調整"""
|
|
@@ -11512,9 +11577,9 @@ class AngleDialog(Dialog3DPickingMixin, QDialog):
|
|
|
11512
11577
|
self.apply_button.setEnabled(False)
|
|
11513
11578
|
button_layout.addWidget(self.apply_button)
|
|
11514
11579
|
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
button_layout.addWidget(
|
|
11580
|
+
close_button = QPushButton("Close")
|
|
11581
|
+
close_button.clicked.connect(self.reject)
|
|
11582
|
+
button_layout.addWidget(close_button)
|
|
11518
11583
|
|
|
11519
11584
|
layout.addLayout(button_layout)
|
|
11520
11585
|
|
|
@@ -11723,8 +11788,6 @@ class AngleDialog(Dialog3DPickingMixin, QDialog):
|
|
|
11723
11788
|
|
|
11724
11789
|
# キラルラベルを更新
|
|
11725
11790
|
self.main_window.update_chiral_labels()
|
|
11726
|
-
|
|
11727
|
-
self.accept()
|
|
11728
11791
|
|
|
11729
11792
|
def adjust_angle(self, new_angle_deg):
|
|
11730
11793
|
"""角度を調整(均等回転オプション付き)"""
|
|
@@ -11911,9 +11974,9 @@ class DihedralDialog(Dialog3DPickingMixin, QDialog):
|
|
|
11911
11974
|
self.apply_button.setEnabled(False)
|
|
11912
11975
|
button_layout.addWidget(self.apply_button)
|
|
11913
11976
|
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
button_layout.addWidget(
|
|
11977
|
+
close_button = QPushButton("Close")
|
|
11978
|
+
close_button.clicked.connect(self.reject)
|
|
11979
|
+
button_layout.addWidget(close_button)
|
|
11917
11980
|
|
|
11918
11981
|
layout.addLayout(button_layout)
|
|
11919
11982
|
|
|
@@ -12121,8 +12184,6 @@ class DihedralDialog(Dialog3DPickingMixin, QDialog):
|
|
|
12121
12184
|
|
|
12122
12185
|
# キラルラベルを更新
|
|
12123
12186
|
self.main_window.update_chiral_labels()
|
|
12124
|
-
|
|
12125
|
-
self.accept()
|
|
12126
12187
|
|
|
12127
12188
|
def adjust_dihedral(self, new_dihedral_deg):
|
|
12128
12189
|
"""二面角を調整(改善されたアルゴリズム)"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moleditpy
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.4
|
|
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
|