MoleditPy 2.2.4__py3-none-any.whl → 2.2.6__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.
@@ -45,7 +45,6 @@ class AlignPlaneDialog(Dialog3DPickingMixin, QDialog):
45
45
  plane_names = {'xy': 'XY', 'xz': 'XZ', 'yz': 'YZ'}
46
46
  self.setWindowTitle(f"Align to {plane_names[self.plane]} Plane")
47
47
  self.setModal(False) # モードレスにしてクリックを阻害しない
48
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
49
48
  layout = QVBoxLayout(self)
50
49
 
51
50
  # Instructions
@@ -47,7 +47,6 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
47
47
  axis_names = {'x': 'X-axis', 'y': 'Y-axis', 'z': 'Z-axis'}
48
48
  self.setWindowTitle(f"Align to {axis_names[self.axis]}")
49
49
  self.setModal(False) # モードレスにしてクリックを阻害しない
50
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
51
50
  layout = QVBoxLayout(self)
52
51
 
53
52
  # Instructions
@@ -45,7 +45,7 @@ class AngleDialog(Dialog3DPickingMixin, QDialog):
45
45
  def init_ui(self):
46
46
  self.setWindowTitle("Adjust Angle")
47
47
  self.setModal(False) # モードレスにしてクリックを阻害しない
48
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
48
+ # 常に前面表示
49
49
  layout = QVBoxLayout(self)
50
50
 
51
51
  # Instructions
Binary file
@@ -38,7 +38,7 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog):
38
38
  def init_ui(self):
39
39
  self.setWindowTitle("Adjust Bond Length")
40
40
  self.setModal(False) # モードレスにしてクリックを阻害しない
41
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
41
+ # 常に前面表示
42
42
  layout = QVBoxLayout(self)
43
43
 
44
44
  # Instructions
@@ -16,7 +16,7 @@ from PyQt6.QtGui import QFont, QColor
16
16
  from rdkit import Chem
17
17
 
18
18
  #Version
19
- VERSION = '2.2.4'
19
+ VERSION = '2.2.6'
20
20
 
21
21
  ATOM_RADIUS = 18
22
22
  BOND_OFFSET = 3.5
@@ -123,7 +123,6 @@ class ConstrainedOptimizationDialog(Dialog3DPickingMixin, QDialog):
123
123
  self.setWindowTitle("Constrained Optimization")
124
124
  self.setModal(False)
125
125
  self.resize(450, 500)
126
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint)
127
126
  layout = QVBoxLayout(self)
128
127
 
129
128
  # 1. 説明
@@ -42,6 +42,7 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
42
42
  self._mouse_press_pos = None
43
43
 
44
44
  self.AddObserver("LeftButtonPressEvent", self.on_left_button_down)
45
+ #self.AddObserver("LeftButtonDoubleClickEvent", self.on_left_button_down)
45
46
  self.AddObserver("RightButtonPressEvent", self.on_right_button_down)
46
47
  self.AddObserver("MouseMoveEvent", self.on_mouse_move)
47
48
  self.AddObserver("LeftButtonReleaseEvent", self.on_left_button_up)
@@ -85,8 +86,12 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
85
86
  if 0 <= closest_atom_idx < mw.current_mol.GetNumAtoms():
86
87
  atom = mw.current_mol.GetAtomWithIdx(int(closest_atom_idx))
87
88
  if atom:
88
- atomic_num = atom.GetAtomicNum()
89
- vdw_radius = pt.GetRvdw(atomic_num)
89
+ try:
90
+ atomic_num = atom.GetAtomicNum()
91
+ vdw_radius = pt.GetRvdw(atomic_num)
92
+ if vdw_radius < 0.1: vdw_radius = 1.5
93
+ except Exception:
94
+ vdw_radius = 1.5
90
95
  click_threshold = vdw_radius * 1.5
91
96
 
92
97
  if distances[closest_atom_idx] < click_threshold:
@@ -148,11 +153,11 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
148
153
  move_group_dialog.update_display()
149
154
  return
150
155
  else:
151
- # 原子以外をクリック - 全選択を解除
152
- move_group_dialog.group_atoms.clear()
153
- move_group_dialog.selected_atoms.clear()
154
- move_group_dialog.clear_atom_labels()
155
- move_group_dialog.update_display()
156
+ # 原子以外をクリック
157
+ # 即座に解除せず、マウスイベントを追跡して回転かクリックかを判定する
158
+ self._mouse_press_pos = self.GetInteractor().GetEventPosition()
159
+ self._mouse_moved_during_drag = False
160
+
156
161
  # カメラ回転を許可
157
162
  super(CustomInteractorStyle, self).OnLeftButtonDown()
158
163
  return
@@ -166,8 +171,9 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
166
171
  # 測定モードが有効な場合の処理
167
172
  if mw.measurement_mode and mw.current_mol:
168
173
  click_pos = self.GetInteractor().GetEventPosition()
169
- self._mouse_press_pos = click_pos # マウスプレス位置を記録
170
- self._mouse_moved_during_drag = False # 移動フラグをリセット
174
+ # Note: We do NOT set _mouse_press_pos here initially.
175
+ # We only set it if we confirm it's a background click (see below).
176
+ self._mouse_moved_during_drag = False # Reset drag flag
171
177
 
172
178
  picker = mw.plotter.picker
173
179
 
@@ -185,17 +191,24 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
185
191
  # クリック閾値チェック
186
192
  atom = mw.current_mol.GetAtomWithIdx(int(closest_atom_idx))
187
193
  if atom:
188
- atomic_num = atom.GetAtomicNum()
189
- vdw_radius = pt.GetRvdw(atomic_num)
194
+ try:
195
+ atomic_num = atom.GetAtomicNum()
196
+ vdw_radius = pt.GetRvdw(atomic_num)
197
+ if vdw_radius < 0.1: vdw_radius = 1.5
198
+ except Exception:
199
+ vdw_radius = 1.5
190
200
  click_threshold = vdw_radius * 1.5
191
201
 
192
202
  if distances[closest_atom_idx] < click_threshold:
193
203
  mw.handle_measurement_atom_selection(int(closest_atom_idx))
194
204
  return # 原子選択処理完了、カメラ回転は無効
195
205
 
206
+
196
207
  # 測定モードで原子以外をクリックした場合は計測選択をクリア
197
- # ただし、これは通常のカメラ回転も許可する
208
+ # ただし、回転操作(ドラッグ)の場合はクリアしないため、
209
+ # ここで _mouse_press_pos を記録し、Upイベントで判定する。
198
210
  self._is_dragging_atom = False
211
+ self._mouse_press_pos = click_pos
199
212
  super().OnLeftButtonDown()
200
213
  return
201
214
 
@@ -219,8 +232,12 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
219
232
  # RDKitのMolオブジェクトから原子を安全に取得
220
233
  atom = mw.current_mol.GetAtomWithIdx(int(closest_atom_idx))
221
234
  if atom:
222
- atomic_num = atom.GetAtomicNum()
223
- vdw_radius = pt.GetRvdw(atomic_num)
235
+ try:
236
+ atomic_num = atom.GetAtomicNum()
237
+ vdw_radius = pt.GetRvdw(atomic_num)
238
+ if vdw_radius < 0.1: vdw_radius = 1.5
239
+ except Exception:
240
+ vdw_radius = 1.5
224
241
  click_threshold = vdw_radius * 1.5
225
242
 
226
243
  if distances[closest_atom_idx] < click_threshold:
@@ -265,8 +282,12 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
265
282
  if 0 <= closest_atom_idx < mw.current_mol.GetNumAtoms():
266
283
  atom = mw.current_mol.GetAtomWithIdx(int(closest_atom_idx))
267
284
  if atom:
268
- atomic_num = atom.GetAtomicNum()
269
- vdw_radius = pt.GetRvdw(atomic_num)
285
+ try:
286
+ atomic_num = atom.GetAtomicNum()
287
+ vdw_radius = pt.GetRvdw(atomic_num)
288
+ if vdw_radius < 0.1: vdw_radius = 1.5
289
+ except Exception:
290
+ vdw_radius = 1.5
270
291
  click_threshold = vdw_radius * 1.5
271
292
 
272
293
  if distances[closest_atom_idx] < click_threshold:
@@ -458,37 +479,23 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
458
479
  move_group_dialog.on_atom_picked(clicked_atom)
459
480
  except Exception as e:
460
481
  print(f"Error in toggle: {e}")
461
-
462
- # 状態をリセット(完全なクリーンアップ)
463
- move_group_dialog._is_dragging_group_vtk = False
464
- move_group_dialog._drag_start_pos = None
465
- move_group_dialog._mouse_moved = False
466
- if hasattr(move_group_dialog, '_initial_positions'):
467
- delattr(move_group_dialog, '_initial_positions')
468
- if hasattr(move_group_dialog, '_drag_atom_idx'):
469
- delattr(move_group_dialog, '_drag_atom_idx')
470
-
471
- # CustomInteractorStyleの状態もクリア
472
- self._is_dragging_atom = False
473
- self.is_dragging = False
474
- self._mouse_moved_during_drag = False
475
- self._mouse_press_pos = None
476
-
477
- try:
478
- mw.plotter.setCursor(Qt.CursorShape.ArrowCursor)
479
- except Exception:
480
- pass
481
- return
482
+
483
+ # Move Groupモードでの背景クリック判定(選択解除)
484
+ # グループドラッグでなく、マウス移動もなかった(=回転操作でない)場合
485
+ # かつ、mouse_press_pos が記録されている(背景クリックで開始した)場合
486
+ if move_group_dialog and not getattr(move_group_dialog, '_is_dragging_group_vtk', False):
487
+ if not self._mouse_moved_during_drag and self._mouse_press_pos is not None:
488
+ # 背景クリック -> 選択解除
489
+ move_group_dialog.group_atoms.clear()
490
+ move_group_dialog.selected_atoms.clear()
491
+ move_group_dialog.clear_atom_labels()
492
+ move_group_dialog.update_display()
482
493
 
483
494
  # 計測モードで、マウスが動いていない場合(つまりクリック)の処理
495
+ # _mouse_press_pos が None でない = 背景をクリックしたことを意味する(Downイベントでそう設定したため)
484
496
  if mw.measurement_mode and not self._mouse_moved_during_drag and self._mouse_press_pos is not None:
485
- click_pos = self.GetInteractor().GetEventPosition()
486
- picker = mw.plotter.picker
487
- picker.Pick(click_pos[0], click_pos[1], 0, mw.plotter.renderer)
488
-
489
- # 原子がクリックされていない場合は測定選択をクリア
490
- if picker.GetActor() is not mw.atom_actor:
491
- mw.clear_measurement_selection()
497
+ # 背景クリック -> 測定選択をクリア
498
+ mw.clear_measurement_selection()
492
499
 
493
500
  if self._is_dragging_atom:
494
501
  # カスタムドラッグの後始末
@@ -594,7 +601,7 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
594
601
  # カメラ回転の後始末を親クラスに任せます
595
602
  super().OnLeftButtonUp()
596
603
 
597
- # 状態をリセット(完全なクリーンアップ)
604
+ # 状態をリセット(完全なクリーンアップ) - すべてのチェックの後に実行
598
605
  self._is_dragging_atom = False
599
606
  self.is_dragging = False
600
607
  self._mouse_press_pos = None
@@ -602,12 +609,6 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
602
609
 
603
610
  # Move Group関連の状態もクリア
604
611
  try:
605
- move_group_dialog = None
606
- for widget in QApplication.topLevelWidgets():
607
- if isinstance(widget, MoveGroupDialog) and widget.isVisible():
608
- move_group_dialog = widget
609
- break
610
-
611
612
  if move_group_dialog:
612
613
  move_group_dialog._is_dragging_group_vtk = False
613
614
  move_group_dialog._drag_start_pos = None
@@ -619,12 +620,11 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
619
620
  except Exception:
620
621
  pass
621
622
 
622
- # ピックリセットは測定モードで実際に問題が発生した場合のみ行う
623
- # (通常のドラッグ回転では行わない)
624
-
625
623
  # ボタンを離した後のカーソル表示を最新の状態に更新
626
- self.on_mouse_move(obj, event)
627
-
624
+ try:
625
+ mw.plotter.setCursor(Qt.CursorShape.ArrowCursor)
626
+ except Exception:
627
+ pass
628
628
  # 2Dビューにフォーカスを戻し、ショートカットキーなどが使えるようにする
629
629
  if mw and mw.view_2d:
630
630
  mw.view_2d.setFocus()
@@ -11,6 +11,7 @@ DOI: 10.5281/zenodo.17268532
11
11
  """
12
12
 
13
13
  from pyvistaqt import QtInteractor
14
+ import time
14
15
 
15
16
 
16
17
  class CustomQtInteractor(QtInteractor):
@@ -24,6 +25,7 @@ class CustomQtInteractor(QtInteractor):
24
25
  # the VTK interactor and causing unexpected behaviour in the 3D view.
25
26
  self._last_click_time = 0.0
26
27
  self._click_count = 0
28
+ self._ignore_next_release = False
27
29
 
28
30
  def wheelEvent(self, event):
29
31
  """
@@ -40,17 +42,58 @@ class CustomQtInteractor(QtInteractor):
40
42
  """
41
43
  Qtのマウスリリースイベントをオーバーライドし、
42
44
  3Dビューでの全ての操作完了後に2Dビューへフォーカスを戻す。
45
+ また、Ghost Release(対応するPressがないRelease)をフィルタリングする。
43
46
  """
47
+ if self._ignore_next_release:
48
+ self._ignore_next_release = False
49
+ event.accept()
50
+ return
51
+
44
52
  super().mouseReleaseEvent(event) # 親クラスのイベントを先に処理
45
53
  if self.main_window and hasattr(self.main_window, 'view_2d'):
46
54
  self.main_window.view_2d.setFocus()
47
55
 
56
+ def mousePressEvent(self, event):
57
+ """
58
+ Custom mouse press handling to track accumulated clicks and filter out
59
+ triple-clicks.
60
+ """
61
+ current_time = time.time()
62
+ # Reset count if too much time has passed (0.5s is standard double-click time)
63
+ if current_time - self._last_click_time > 0.5:
64
+ self._click_count = 0
65
+
66
+ self._click_count += 1
67
+ self._last_click_time = current_time
68
+
69
+ # If this is the 3rd click (or more), swallow it to prevent
70
+ # the internal state desync that happens with rapid clicking sequences.
71
+ if self._click_count >= 3:
72
+ self._ignore_next_release = True
73
+ event.accept()
74
+ return
75
+
76
+ super().mousePressEvent(event)
77
+
48
78
  def mouseDoubleClickEvent(self, event):
49
79
  """Ignore mouse double-clicks on the 3D widget to avoid accidental actions.
50
80
 
51
81
  Swallow the double-click event so it doesn't trigger selection, editing,
52
82
  or camera jumps. We intentionally do not call the superclass handler.
83
+ Crucially, we also flag the NEXT release event to be swallowed, preventing
84
+ a "Ghost Release" (Release without Press) from reaching VTK.
53
85
  """
86
+ current_time = time.time()
87
+ self._last_click_time = current_time
88
+ # Set to 2 to ensure the next click counts as 3rd
89
+ if current_time - self._last_click_time < 0.5:
90
+ self._click_count = 2
91
+ else:
92
+ self._click_count = 2 # Force sync
93
+
94
+ # We must ignore the release event that follows this double-click event
95
+ self._ignore_next_release = True
96
+
54
97
  try:
55
98
  # Accept the event to mark it handled and prevent further processing.
56
99
  event.accept()
@@ -31,6 +31,11 @@ class Dialog3DPickingMixin:
31
31
  event.type() == QEvent.Type.MouseButtonPress and
32
32
  event.button() == Qt.MouseButton.LeftButton):
33
33
 
34
+ # Start tracking for smart selection (click vs drag)
35
+ self._mouse_press_pos = event.pos()
36
+ self._mouse_moved = False
37
+
38
+
34
39
  try:
35
40
  # VTKイベント座標を取得(元のロジックと同じ)
36
41
  interactor = self.main_window.plotter.interactor
@@ -48,8 +53,12 @@ class Dialog3DPickingMixin:
48
53
  # クリック閾値チェック(元のロジックと同じ)
49
54
  atom = self.mol.GetAtomWithIdx(int(closest_atom_idx))
50
55
  if atom:
51
- atomic_num = atom.GetAtomicNum()
52
- vdw_radius = pt.GetRvdw(atomic_num)
56
+ try:
57
+ atomic_num = atom.GetAtomicNum()
58
+ vdw_radius = pt.GetRvdw(atomic_num)
59
+ if vdw_radius < 0.1: vdw_radius = 1.5
60
+ except Exception:
61
+ vdw_radius = 1.5
53
62
  click_threshold = vdw_radius * 1.5
54
63
 
55
64
  if distances[closest_atom_idx] < click_threshold:
@@ -63,15 +72,14 @@ class Dialog3DPickingMixin:
63
72
  except Exception:
64
73
  pass
65
74
  self.on_atom_picked(int(closest_atom_idx))
75
+
76
+ # We picked an atom, so stop tracking for background click
77
+ self._mouse_press_pos = None
66
78
  return True
67
79
 
68
- # 原子以外をクリックした場合は選択をクリア(Measurementモードと同じロジック)
69
- if hasattr(self, 'clear_selection'):
70
- self.clear_selection()
71
- # We did not actually pick an atom; do NOT consume the event here so
72
- # the interactor and CustomInteractorStyle can handle camera rotation
73
- # and other behaviors. Returning False (or calling the base
74
- # implementation) allows normal processing to continue.
80
+ # 原子以外をクリックした場合
81
+ # 即時には解除せず、回転操作(ドラッグ)を許可する。
82
+ # 実際の解除は MouseButtonRelease イベントで行う。
75
83
  return False
76
84
 
77
85
  except Exception as e:
@@ -80,6 +88,31 @@ class Dialog3DPickingMixin:
80
88
  # event pipeline continue so the UI remains responsive.
81
89
  return False
82
90
 
91
+ # Add movement tracking for smart selection
92
+ elif (obj == self.main_window.plotter.interactor and
93
+ event.type() == QEvent.Type.MouseMove):
94
+ if hasattr(self, '_mouse_press_pos') and self._mouse_press_pos is not None:
95
+ # Check if moved significantly
96
+ diff = event.pos() - self._mouse_press_pos
97
+ if diff.manhattanLength() > 3:
98
+ self._mouse_moved = True
99
+
100
+ # Add release handling for smart selection
101
+ elif (obj == self.main_window.plotter.interactor and
102
+ event.type() == QEvent.Type.MouseButtonRelease and
103
+ event.button() == Qt.MouseButton.LeftButton):
104
+
105
+ if hasattr(self, '_mouse_press_pos') and self._mouse_press_pos is not None:
106
+ if not getattr(self, '_mouse_moved', False):
107
+ # Pure click (no drag) on background -> Clear selection
108
+ if hasattr(self, 'clear_selection'):
109
+ self.clear_selection()
110
+
111
+ # Reset state
112
+ self._mouse_press_pos = None
113
+ self._mouse_moved = False
114
+
115
+
83
116
  return super().eventFilter(obj, event)
84
117
 
85
118
  def enable_picking(self):
@@ -47,7 +47,7 @@ class DihedralDialog(Dialog3DPickingMixin, QDialog):
47
47
  def init_ui(self):
48
48
  self.setWindowTitle("Adjust Dihedral Angle")
49
49
  self.setModal(False) # モードレスにしてクリックを阻害しない
50
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
50
+ # 常に前面表示
51
51
  layout = QVBoxLayout(self)
52
52
 
53
53
  # Instructions
@@ -596,8 +596,10 @@ class MainWindowAppState(object):
596
596
  json_data["last_successful_optimization_method"] = None
597
597
 
598
598
  # Plugin State Persistence (Phase 3)
599
+ # Start with preserved data from missing plugins
600
+ plugin_data = self._preserved_plugin_data.copy() if self._preserved_plugin_data else {}
601
+
599
602
  if self.plugin_manager and self.plugin_manager.save_handlers:
600
- plugin_data = {}
601
603
  for name, callback in self.plugin_manager.save_handlers.items():
602
604
  try:
603
605
  p_state = callback()
@@ -606,8 +608,8 @@ class MainWindowAppState(object):
606
608
  except Exception as e:
607
609
  print(f"Error saving state for plugin {name}: {e}")
608
610
 
609
- if plugin_data:
610
- json_data['plugins'] = plugin_data
611
+ if plugin_data:
612
+ json_data['plugins'] = plugin_data
611
613
 
612
614
  return json_data
613
615
 
@@ -629,14 +631,19 @@ class MainWindowAppState(object):
629
631
  self.last_successful_optimization_method = None
630
632
 
631
633
  # Plugin State Restoration (Phase 3)
632
- if "plugins" in json_data and self.plugin_manager and self.plugin_manager.load_handlers:
634
+ self._preserved_plugin_data = {} # Reset preserved data on new load
635
+ if "plugins" in json_data:
633
636
  plugin_data = json_data["plugins"]
634
637
  for name, p_state in plugin_data.items():
635
- if name in self.plugin_manager.load_handlers:
638
+ if self.plugin_manager and name in self.plugin_manager.load_handlers:
636
639
  try:
637
640
  self.plugin_manager.load_handlers[name](p_state)
638
641
  except Exception as e:
639
642
  print(f"Error loading state for plugin {name}: {e}")
643
+ else:
644
+ # No handler found (plugin disabled or missing)
645
+ # Preserve data so it's not lost on next save
646
+ self._preserved_plugin_data[name] = p_state
640
647
 
641
648
 
642
649
  # 2D構造データの復元
@@ -697,7 +704,7 @@ class MainWindowAppState(object):
697
704
  for atom in self.data.atoms.values():
698
705
  atom['item'].update_style()
699
706
  # 3D構造データの復元
700
- if "3d_structure" in json_data:
707
+ if "3d_structure" in json_data and json_data["3d_structure"] is not None:
701
708
  structure_3d = json_data["3d_structure"]
702
709
 
703
710
  # 制約データの復元 (JSONはタプルをリストとして保存するので、タプルに再変換)
@@ -263,7 +263,7 @@ class MainWindowDialogManager(object):
263
263
  self.measurement_action.setChecked(False)
264
264
  self.toggle_measurement_mode(False)
265
265
 
266
- dialog = TranslationDialog(self.current_mol, self)
266
+ dialog = TranslationDialog(self.current_mol, self, parent=self)
267
267
  self.active_3d_dialogs.append(dialog) # 参照を保持
268
268
  dialog.show() # execではなくshowを使用してモードレス表示
269
269
  dialog.accepted.connect(lambda: self.statusBar().showMessage("Translation applied."))
@@ -279,7 +279,7 @@ class MainWindowDialogManager(object):
279
279
  self.measurement_action.setChecked(False)
280
280
  self.toggle_measurement_mode(False)
281
281
 
282
- dialog = MoveGroupDialog(self.current_mol, self)
282
+ dialog = MoveGroupDialog(self.current_mol, self, parent=self)
283
283
  self.active_3d_dialogs.append(dialog)
284
284
  dialog.show()
285
285
  dialog.accepted.connect(lambda: self.statusBar().showMessage("Group transformation applied."))
@@ -302,7 +302,7 @@ class MainWindowDialogManager(object):
302
302
  self.measurement_action.setChecked(False)
303
303
  self.toggle_measurement_mode(False)
304
304
 
305
- dialog = AlignPlaneDialog(self.current_mol, self, plane, preselected_atoms)
305
+ dialog = AlignPlaneDialog(self.current_mol, self, plane, preselected_atoms, parent=self)
306
306
  self.active_3d_dialogs.append(dialog) # 参照を保持
307
307
  dialog.show() # execではなくshowを使用してモードレス表示
308
308
  dialog.accepted.connect(lambda: self.statusBar().showMessage(f"Atoms alignd to {plane.upper()} plane."))
@@ -325,7 +325,7 @@ class MainWindowDialogManager(object):
325
325
  self.measurement_action.setChecked(False)
326
326
  self.toggle_measurement_mode(False)
327
327
 
328
- dialog = PlanarizeDialog(self.current_mol, self, preselected_atoms)
328
+ dialog = PlanarizeDialog(self.current_mol, self, preselected_atoms, parent=self)
329
329
  self.active_3d_dialogs.append(dialog)
330
330
  dialog.show()
331
331
  dialog.accepted.connect(lambda: self.statusBar().showMessage("Selection planarized to best-fit plane."))
@@ -348,7 +348,7 @@ class MainWindowDialogManager(object):
348
348
  self.measurement_action.setChecked(False)
349
349
  self.toggle_measurement_mode(False)
350
350
 
351
- dialog = AlignmentDialog(self.current_mol, self, axis, preselected_atoms)
351
+ dialog = AlignmentDialog(self.current_mol, self, axis, preselected_atoms, parent=self)
352
352
  self.active_3d_dialogs.append(dialog) # 参照を保持
353
353
  dialog.show() # execではなくshowを使用してモードレス表示
354
354
  dialog.accepted.connect(lambda: self.statusBar().showMessage(f"Atoms aligned to {axis.upper()}-axis."))
@@ -371,7 +371,7 @@ class MainWindowDialogManager(object):
371
371
  self.measurement_action.setChecked(False)
372
372
  self.toggle_measurement_mode(False)
373
373
 
374
- dialog = BondLengthDialog(self.current_mol, self, preselected_atoms)
374
+ dialog = BondLengthDialog(self.current_mol, self, preselected_atoms, parent=self)
375
375
  self.active_3d_dialogs.append(dialog) # 参照を保持
376
376
  dialog.show() # execではなくshowを使用してモードレス表示
377
377
  dialog.accepted.connect(lambda: self.statusBar().showMessage("Bond length adjusted."))
@@ -394,7 +394,7 @@ class MainWindowDialogManager(object):
394
394
  self.measurement_action.setChecked(False)
395
395
  self.toggle_measurement_mode(False)
396
396
 
397
- dialog = AngleDialog(self.current_mol, self, preselected_atoms)
397
+ dialog = AngleDialog(self.current_mol, self, preselected_atoms, parent=self)
398
398
  self.active_3d_dialogs.append(dialog) # 参照を保持
399
399
  dialog.show() # execではなくshowを使用してモードレス表示
400
400
  dialog.accepted.connect(lambda: self.statusBar().showMessage("Angle adjusted."))
@@ -417,7 +417,7 @@ class MainWindowDialogManager(object):
417
417
  self.measurement_action.setChecked(False)
418
418
  self.toggle_measurement_mode(False)
419
419
 
420
- dialog = DihedralDialog(self.current_mol, self, preselected_atoms)
420
+ dialog = DihedralDialog(self.current_mol, self, preselected_atoms, parent=self)
421
421
  self.active_3d_dialogs.append(dialog) # 参照を保持
422
422
  dialog.show() # execではなくshowを使用してモードレス表示
423
423
  dialog.accepted.connect(lambda: self.statusBar().showMessage("Dihedral angle adjusted."))
@@ -453,7 +453,7 @@ class MainWindowDialogManager(object):
453
453
  self.measurement_action.setChecked(False)
454
454
  self.toggle_measurement_mode(False)
455
455
 
456
- dialog = ConstrainedOptimizationDialog(self.current_mol, self)
456
+ dialog = ConstrainedOptimizationDialog(self.current_mol, self, parent=self)
457
457
  self.active_3d_dialogs.append(dialog) # 参照を保持
458
458
  dialog.show() # モードレス表示
459
459
  dialog.finished.connect(lambda: self.remove_dialog_from_list(dialog))
@@ -1211,7 +1211,10 @@ class MainWindowEditActions(object):
1211
1211
 
1212
1212
  atom = mol.GetAtomWithIdx(idx)
1213
1213
  # GetRvdw() はファンデルワールス半径を返す
1214
- vdw_radii.append(pt.GetRvdw(atom.GetAtomicNum()))
1214
+ try:
1215
+ vdw_radii.append(pt.GetRvdw(atom.GetAtomicNum()))
1216
+ except RuntimeError:
1217
+ vdw_radii.append(1.5)
1215
1218
 
1216
1219
  positions_np = np.array(positions)
1217
1220
  vdw_radii_np = np.array(vdw_radii)
@@ -264,6 +264,9 @@ class MainWindowMainInit(object):
264
264
  except Exception as e:
265
265
  print(f"Failed to initialize PluginManager: {e}")
266
266
  self.plugin_manager = None
267
+
268
+ # ロードされていないプラグインのデータを保持する辞書
269
+ self._preserved_plugin_data = {}
267
270
 
268
271
  self.init_ui()
269
272
  self.init_worker_thread()
@@ -1832,6 +1835,14 @@ class MainWindowMainInit(object):
1832
1835
  if not found_sub:
1833
1836
  current_menu = current_menu.addMenu(part)
1834
1837
 
1838
+ # If last action was NOT from a plugin, insert a separator
1839
+ actions = current_menu.actions()
1840
+ if actions:
1841
+ last_action = actions[-1]
1842
+ if not last_action.isSeparator() and last_action.data() != PLUGIN_ACTION_TAG:
1843
+ sep = current_menu.addSeparator()
1844
+ sep.setData(PLUGIN_ACTION_TAG)
1845
+
1835
1846
  # Add action
1836
1847
  action_text = text if text else parts[-1]
1837
1848
  action = QAction(action_text, self)
@@ -219,7 +219,15 @@ class MainWindowView3d(object):
219
219
  if current_style == 'cpk':
220
220
  atom_scale = self.settings.get('cpk_atom_scale', 1.0)
221
221
  resolution = self.settings.get('cpk_resolution', 32)
222
- rad = np.array([pt.GetRvdw(pt.GetAtomicNumber(s)) * atom_scale for s in sym])
222
+ # Safe VDW lookup to handle custom elements like 'Bq'
223
+ def get_safe_rvdw(s):
224
+ try:
225
+ r = pt.GetRvdw(pt.GetAtomicNumber(s))
226
+ return r if r > 0.1 else 1.5
227
+ except Exception:
228
+ return 1.5
229
+
230
+ rad = np.array([get_safe_rvdw(s) * atom_scale for s in sym])
223
231
  elif current_style == 'wireframe':
224
232
  # Wireframeでは原子を描画しないので、この設定は実際には使用されない
225
233
  resolution = self.settings.get('wireframe_resolution', 6)
@@ -42,7 +42,6 @@ class MoveGroupDialog(Dialog3DPickingMixin, QDialog):
42
42
  def init_ui(self):
43
43
  self.setWindowTitle("Move Group")
44
44
  self.setModal(False)
45
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint)
46
45
  self.resize(300,400) # ウィンドウサイズを設定
47
46
  layout = QVBoxLayout(self)
48
47
 
@@ -195,9 +194,12 @@ class MoveGroupDialog(Dialog3DPickingMixin, QDialog):
195
194
  if 0 <= closest_atom_idx < self.mol.GetNumAtoms():
196
195
  atom = self.mol.GetAtomWithIdx(int(closest_atom_idx))
197
196
  if atom:
198
- atomic_num = atom.GetAtomicNum()
199
- pt = Chem.GetPeriodicTable()
200
- vdw_radius = pt.GetRvdw(atomic_num)
197
+ try:
198
+ atomic_num = atom.GetAtomicNum()
199
+ vdw_radius = pt.GetRvdw(atomic_num)
200
+ if vdw_radius < 0.1: vdw_radius = 1.5
201
+ except Exception:
202
+ vdw_radius = 1.5
201
203
  click_threshold = vdw_radius * 1.5
202
204
 
203
205
  if distances[closest_atom_idx] < click_threshold:
@@ -349,7 +351,7 @@ class MoveGroupDialog(Dialog3DPickingMixin, QDialog):
349
351
  return False
350
352
 
351
353
  # その他のイベントは親クラスに渡す
352
- return False
354
+ return super().eventFilter(obj, event)
353
355
 
354
356
  def on_atom_picked(self, atom_idx):
355
357
  """原子がピックされたときに、その原子が属する連結成分全体を選択(複数グループ対応)"""
@@ -46,7 +46,6 @@ class PlanarizeDialog(Dialog3DPickingMixin, QDialog):
46
46
  def init_ui(self):
47
47
  self.setWindowTitle("Planarize")
48
48
  self.setModal(False)
49
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint)
50
49
  layout = QVBoxLayout(self)
51
50
 
52
51
  instruction_label = QLabel("Click atoms in the 3D view to select them for planarization (minimum 3 required).")
@@ -65,6 +65,24 @@ class PluginContext:
65
65
  """
66
66
  return self._manager.get_main_window()
67
67
 
68
+ @property
69
+ def current_molecule(self) -> Any:
70
+ """
71
+ Get or set the current molecule (RDKit Mol object).
72
+ """
73
+ mw = self._manager.get_main_window()
74
+ if mw:
75
+ return mw.current_mol
76
+ return None
77
+
78
+ @current_molecule.setter
79
+ def current_molecule(self, mol: Any):
80
+ mw = self._manager.get_main_window()
81
+ if mw:
82
+ mw.current_mol = mol
83
+ if hasattr(mw, 'draw_molecule_3d'):
84
+ mw.draw_molecule_3d(mol)
85
+
68
86
  def add_export_action(self, label: str, callback: Callable):
69
87
  """
70
88
  Register a custom export action.
@@ -31,7 +31,6 @@ class TranslationDialog(Dialog3DPickingMixin, QDialog):
31
31
  def init_ui(self):
32
32
  self.setWindowTitle("Translation")
33
33
  self.setModal(False) # モードレスにしてクリックを阻害しない
34
- self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
35
34
  layout = QVBoxLayout(self)
36
35
 
37
36
  # Instructions
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 2.2.4
3
+ Version: 2.2.6
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
@@ -705,7 +705,9 @@ Dynamic: license-file
705
705
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.17268532.svg)](https://doi.org/10.5281/zenodo.17268532)
706
706
  [![Powered by RDKit](https://img.shields.io/badge/Powered%20by-RDKit-3838ff.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAFVBMVEXc3NwUFP8UPP9kZP+MjP+0tP////9ZXZotAAAAAXRSTlMAQObYZgAAAAFiS0dEBmFmuH0AAAAHdElNRQfmAwsPGi+MyC9RAAAAQElEQVQI12NgQABGQUEBMENISUkRLKBsbGwEEhIyBgJFsICLC0iIUdnExcUZwnANQWfApKCK4doRBsKtQFgKAQC5Ww1JEHSEkAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMi0wMy0xMVQxNToyNjo0NyswMDowMDzr2J4AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjItMDMtMTFUMTU6MjY6NDcrMDA6MDBNtmAiAAAAAElFTkSuQmCC)](https://www.rdkit.org/)
707
707
 
708
- **MoleditPy** is a cross-platform, intuitive molecular editor built in Python. It provides a seamless workflow for drawing 2D molecular structures, visualizing them in 3D, and performing interactive geometric manipulations. Its powerful editing and export capabilities make it an ideal tool for preparing input files for DFT calculation software.
708
+ [🇯🇵 日本語 (Japanese)](#japanese)
709
+
710
+ **MoleditPy** is a **programmable** and cross-platform molecular editor built in Python. It streamlines the workflow from 2D drawing to 3D visualization, making it an **ideal tool for rapidly preparing input files for DFT calculations**. Designed as an **open platform**, it also allows users to freely extend its capabilities, writing custom Python scripts to manipulate molecular data, automate tasks, or integrate new cheminformatics algorithms seamlessly.
709
711
 
710
712
  **Author**: HiroYokoyama
711
713
  **License**: GPL-v3
@@ -714,7 +716,7 @@ Dynamic: license-file
714
716
  -----
715
717
  ![](img/icon.png)
716
718
  ![](img/screenshot.png)
717
- -----
719
+
718
720
 
719
721
  ## Overview
720
722
 
@@ -759,7 +761,14 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
759
761
  * Import structures from **MOL/SDF** files or **SMILES** strings.
760
762
  * Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
761
763
  * 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.
764
+ * Export 2D and 3D views as high-resolution PNG images.
765
+
766
+ ### 4. Programmable & Extensible
767
+
768
+ * **Python Plugin System:** Drop your Python scripts into the plugin folder, and they instantly become part of the application menu.
769
+ * **Downloadable Plugins:** Explore and download specialized plugins from the [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/).
770
+ * **Full API Access:** Plugins have direct access to the `MainWindow`, `RDKit` molecule objects, and `PyGraphics` items, allowing for limitless customization.
771
+ * **Rapid Prototyping:** Ideal for researchers who need to test new algorithms or workflow automations on the fly.
763
772
 
764
773
  ## Installation and Execution
765
774
 
@@ -821,9 +830,11 @@ This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**.
821
830
 
822
831
  -----
823
832
 
833
+ <div id="japanese"></div>
834
+
824
835
  # MoleditPy — A Python Molecular Editor
825
836
 
826
- **MoleditPy**は、Pythonで構築されたクロスプラットフォームかつ直感的な分子エディターです。2Dでの分子描画から3D構造の可視化・編集、さらにはインタラクティブな幾何学的操作まで、シームレスなワークフローを提供します。その強力な編集機能とエクスポート機能により、**DFT計算ソフトウェアのインプット作成に最適なツール**です。
837
+ **MoleditPy**は、Pythonで構築された**機能拡張が自由自在な**分子エディタープラットフォームです。2D描画から3Dへの変換により、**DFT計算用インプットの迅速な作成に最適なツール**であると同時に、**Pythonスクリプトを用いて必要な機能をユーザー自身が手軽に追加・開発できる**柔軟な環境を提供します。
827
838
 
828
839
  **作者**: HiroYokoyama
829
840
  **ライセンス**: GPL-v3
@@ -874,7 +885,14 @@ This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**.
874
885
  * **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
875
886
  * 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
876
887
  * 2Dおよび3Dビューを高解像度のPNG画像としてエクスポートできます。
877
- * **プラグインシステム:** Pythonスクリプトで機能を拡張できます。`~/.moleditpy/plugins` にスクリプトを配置することで、「Plugin」メニューに独自の機能を追加できます。
888
+ * 2Dおよび3Dビューを高解像度のPNG画像としてエクスポートできます。
889
+
890
+ ### 4. プログラマブルで拡張可能
891
+
892
+ * **Pythonプラグインシステム:** Pythonスクリプトをプラグインフォルダに入れるだけで、即座にアプリケーションメニューの一部として機能します。
893
+ * **プラグインのダウンロード:** [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/) から特化したプラグインを探索・ダウンロードできます。
894
+ * **フルAPIアクセス:** プラグインは `MainWindow`、`RDKit` 分子オブジェクト、`PyGraphics` アイテムに直接アクセスでき、無限のカスタマイズが可能です。
895
+ * **迅速なプロトタイピング:** 新しいアルゴリズムやワークフローの自動化をその場でテストしたい研究者に最適です。
878
896
 
879
897
  ## インストールと実行
880
898
 
@@ -3,56 +3,57 @@ moleditpy/__main__.py,sha256=bYu_h7lhc6PIl1-I_VI4_Q5MdBHmVrMiO4DYsy9jbiY,829
3
3
  moleditpy/main.py,sha256=0D8_CXe4xdgqlu2TWoT2pi35K9y2b4ilFj6LWD9Qk7w,1178
4
4
  moleditpy/modules/__init__.py,sha256=BndB5rKHIcdjtctu9csFxHm14khdUdN1BRO4aSzT3IQ,1727
5
5
  moleditpy/modules/about_dialog.py,sha256=Edt09PZbsU9qd94tJCY57CDHNmRVFfa0DSPLgP7IGG0,3683
6
- moleditpy/modules/align_plane_dialog.py,sha256=A4sGfnpWSLM5v4g2ADUVmZnWtKEaQxeGQk7ZRLTRj58,11944
7
- moleditpy/modules/alignment_dialog.py,sha256=lZOWo1Y7uKt31nxZAguNDHNZxKww_aLcg-JPilM2CPQ,11419
6
+ moleditpy/modules/align_plane_dialog.py,sha256=SEC32l8z2x1W1Sf1Gu001OQEtinuvqGuCGHRELmtU5s,11834
7
+ moleditpy/modules/alignment_dialog.py,sha256=CPubSJI52mNSXmWt7oKQ2un5lXnN8_K4Z-LMM7wWTxQ,11309
8
8
  moleditpy/modules/analysis_window.py,sha256=zjP5ipSTpKw8oLr1eKdoxW8Bk1SslGlPqsVucD-x_5w,9403
9
- moleditpy/modules/angle_dialog.py,sha256=ytuE5VqT0dskgob1x08B6VW06qnslgCdAs_dwS2ANgY,17837
9
+ moleditpy/modules/angle_dialog.py,sha256=uc2WbvSfRe892xoEirqpZ78pf2Smwzkinkso6zLWr0Y,17751
10
10
  moleditpy/modules/atom_item.py,sha256=u8ge6B1M9sOGobfzg3tp1-EGXtEUmvdee7Fx6msg8Wk,15566
11
11
  moleditpy/modules/bond_item.py,sha256=eVkEeKvM4igYI67DYxpey3FllqDyt_iWDo4VPYMhaPk,19137
12
- moleditpy/modules/bond_length_dialog.py,sha256=k5x_DhK9Q8CSwouKhEo_kLRRdaYHDaK84KDNmuDNLvY,14868
12
+ moleditpy/modules/bond_length_dialog.py,sha256=6bFPGssnqlgINuqpxLv-OhjMH3_hspnaH8QtorAyu2M,14782
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=FZjG-RLZs_Xqn8D7MDAGKZAJY4G44Bh2Nm2qnDTFXm8,4702
16
- moleditpy/modules/constrained_optimization_dialog.py,sha256=IEdNVhFoNSEMeA5ABpUH9Q88-YzDXFloQM2gwnPwnHY,30150
17
- moleditpy/modules/custom_interactor_style.py,sha256=NjsXE2a43IDNEanZBlcG9eR4ZIERT1MsQC6lbfesapQ,38453
18
- moleditpy/modules/custom_qt_interactor.py,sha256=MFaTuDh-FPeFBS4303CqxsxmsOIOW4QXUz6USwI8PHQ,2451
19
- moleditpy/modules/dialog3_d_picking_mixin.py,sha256=2Sut0J5ltXMtrUJ9R3o1oZ4ysed27mdSIqLpWxmGdyM,5037
20
- moleditpy/modules/dihedral_dialog.py,sha256=rgry7LqyX9JMAR7d82QSroTPoKT3xz18EgKN1GzYZx4,18088
15
+ moleditpy/modules/constants.py,sha256=_LJDuqJFfR_t_84zzl3p5dhK7C2LB-ZyFuBE4K0dUE4,4702
16
+ moleditpy/modules/constrained_optimization_dialog.py,sha256=REsk4ePsqNmAGPMTS_jckeM7jexrU3krwun8sKqKUCs,30062
17
+ moleditpy/modules/custom_interactor_style.py,sha256=LDNODMJoNHGe1AUSrvqv6PdeJm-hpPmSpWINppnJLt0,38942
18
+ moleditpy/modules/custom_qt_interactor.py,sha256=vCZsDfRO-FtphD5cTP7Ps-5rpHZMIGloaoe6EaKzrsw,4139
19
+ moleditpy/modules/dialog3_d_picking_mixin.py,sha256=z4udbkiX9PYmIGazPXsbftkk_oRRwZhcvlCqbyJzr24,6493
20
+ moleditpy/modules/dihedral_dialog.py,sha256=bOTDO6-b74vEDn_z6OyuBr5cRz3RnRj83PiaEBUyWJA,18002
21
21
  moleditpy/modules/main_window.py,sha256=IL8dH3qPx2TkPgO7amuDgjlFoadh5J59xYUEVhlNZqA,36338
22
- moleditpy/modules/main_window_app_state.py,sha256=DmBfzurTftUIkPZu8465YnxL8Hdx2vHYSbyWdB-kvjs,34757
22
+ moleditpy/modules/main_window_app_state.py,sha256=8YDcGNCSpLTO1NGL9tEvNkXpUcS7JW-uK7TdUGvEqnk,35189
23
23
  moleditpy/modules/main_window_compute.py,sha256=ipIkhH_DONXDnPzh7xeym9X-Yfx8EhsvXYOdyxsAj4c,53347
24
- moleditpy/modules/main_window_dialog_manager.py,sha256=9ZVy5-dlk8SjGU5R6NtbaVZuBQfVK5Nwrc633pEzjq8,19955
24
+ moleditpy/modules/main_window_dialog_manager.py,sha256=QR96LqHAPSOShXbc9cK-Ffq8a16JrXAoMKB0pHjESrQ,20072
25
25
  moleditpy/modules/main_window_edit_3d.py,sha256=CUArB5wcsgq1C7LygAEC6URlbnn4RhRYDa5n-Y-etWI,19731
26
- moleditpy/modules/main_window_edit_actions.py,sha256=-SDLoMQ7S-3u3eBUb-w7BU7OAsFkhQ9ZBjF3Y2jGgZc,64708
26
+ moleditpy/modules/main_window_edit_actions.py,sha256=yEc0Nw-VpN0P4e4neUu7pDuUHPGEcu6eFmwWFrSBIQ8,64815
27
27
  moleditpy/modules/main_window_export.py,sha256=dSVfylsybDDboDuXU9Inotf6YkrKJwgBTqGYSfq1lRE,38241
28
- moleditpy/modules/main_window_main_init.py,sha256=ZxScNJI-OVx_ki5C37fY4Dmm9IIm5eJWVwd7jIKu8ms,90606
28
+ moleditpy/modules/main_window_main_init.py,sha256=Shxs8_vVa7PhfhVZdHYtPniItLHKJ3i9wcamcVofvso,91172
29
29
  moleditpy/modules/main_window_molecular_parsers.py,sha256=KR6vzuqc3nutOcorpYr0QOyX3MFBcxTwDhZX96VgJ9Q,48291
30
30
  moleditpy/modules/main_window_project_io.py,sha256=TWwtuKDuvgcvPZ9IGmW8r1EJJOrgxrIJRnxe_f4C1oM,17149
31
31
  moleditpy/modules/main_window_string_importers.py,sha256=v47wOd4RtjKYcF-aLP-mogGGdYTpTEo3dDyAu79_5MM,10782
32
32
  moleditpy/modules/main_window_ui_manager.py,sha256=HofI6T9EvcSSzPbsdPqkYEEDoB6Hui1Uj2Ll-wwczGA,24016
33
- moleditpy/modules/main_window_view_3d.py,sha256=6eN3_NdpNJrCzMSp17t9_Omm71MwfgqDC8A5DSYHhUo,73942
33
+ moleditpy/modules/main_window_view_3d.py,sha256=CxZxyJHl2isF7KtyVWSI9f8LVbvdZM5H9Gnhm_8ovBM,74227
34
34
  moleditpy/modules/main_window_view_loaders.py,sha256=gklTMo27QnyJ8Gd0ampPdbm9d0Gi-oHWkIqQuGADHmI,14352
35
35
  moleditpy/modules/mirror_dialog.py,sha256=c3v4qY6R4FAljzk4EPaDjL9ZdZMjLQSFLqDMXz2fBUk,4696
36
36
  moleditpy/modules/molecular_data.py,sha256=8gE9ByYg3kSBfb1zANsyad_BVBTm6WOLF7NsZIYuG2E,13250
37
37
  moleditpy/modules/molecule_scene.py,sha256=khdt7h9Mk_D1cMbYeHGtq7P9aFXo0xG-hcShU_H2Y-Q,95911
38
- moleditpy/modules/move_group_dialog.py,sha256=65HVXTJSaQ9lp03XFhI1l7OzUsXmH_aqd8OgwjpjfGg,27174
38
+ moleditpy/modules/move_group_dialog.py,sha256=Fyuy3Uq1KsFsk9qR96r_FxPbAM_-zSfW2dsMQGv7btc,27276
39
39
  moleditpy/modules/periodic_table_dialog.py,sha256=ItEZUts1XCietz9paY-spvbzxh6SXak3GnikwqkHZCw,4006
40
- moleditpy/modules/planarize_dialog.py,sha256=yY8o-SxT8vGEHVWnjDTXecRv5NUaEejEsXH-836Xk8g,8681
41
- moleditpy/modules/plugin_interface.py,sha256=W6Hw1OLqMkcg8XU-S347v2sDBMEoo6iiCSbmL6MjZrA,7639
40
+ moleditpy/modules/planarize_dialog.py,sha256=eaqI1MpF35e-VUMpJATt-EtGG5FhcSUlbAenUaFGabY,8593
41
+ moleditpy/modules/plugin_interface.py,sha256=srzPZ3a_aRTx28NAvWNKRVUDYQNfQOTcjzx-5YW2Pb4,8164
42
42
  moleditpy/modules/plugin_manager.py,sha256=cxbqIE7Rb_KeBd-cG1vF2ySY2qNx8IJVRXjVPyQMFDc,13457
43
43
  moleditpy/modules/plugin_manager_window.py,sha256=UeoPQWTxmckoFIQAuv9jVBzok_9gEKEm9c7JsKRX0P4,10135
44
44
  moleditpy/modules/settings_dialog.py,sha256=Nr7yE8UmYRi3VObWvRlrnv0DnjSjmYXbvqryZ02O12k,65348
45
45
  moleditpy/modules/template_preview_item.py,sha256=djdq3tz73d_fJGOvai3E-V9Hk9q9ZW7skx7BV59mooA,6556
46
46
  moleditpy/modules/template_preview_view.py,sha256=4OCHZDO51BvJpKdfrBWJ4_4WfLfFSKxsVIyf7I-Kj2E,3350
47
- moleditpy/modules/translation_dialog.py,sha256=aWlgTR9mtEMbzGIY1SoQhDltsX-01LtCxjqy5NWtGuA,14663
47
+ moleditpy/modules/translation_dialog.py,sha256=x_GJsbVk-cj4aN2KgmYWDRUDInFlXezAoYoTvX-OT30,14553
48
48
  moleditpy/modules/user_template_dialog.py,sha256=2hARO04DaILgdExx5ubL0GPsxK95VvVRqy7fNfudD_M,30843
49
49
  moleditpy/modules/zoomable_view.py,sha256=hjwljui13QpvjvxJHY4Evot4jMQvxRBQUNH5HUlyFOk,5966
50
+ moleditpy/modules/assets/file_icon.ico,sha256=yyVj084A7HuMNbV073cE_Ag3Ne405qgOP3Mia1ZqLpE,101632
50
51
  moleditpy/modules/assets/icon.icns,sha256=wD5R6-Vw7K662tVKhu2E1ImN0oUuyAP4youesEQsn9c,139863
51
52
  moleditpy/modules/assets/icon.ico,sha256=RfgFcx7-dHY_2STdsOQCQziY5SNhDr3gPnjO6jzEDPI,147975
52
53
  moleditpy/modules/assets/icon.png,sha256=kCFN1WacYIdy0GN6SFEbNA00ef39pCczBnFdkkBI8Bs,147110
53
- moleditpy-2.2.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
54
- moleditpy-2.2.4.dist-info/METADATA,sha256=4DwSe5WZS_c8tGB0qVe71o9m2Rc20x5Up7E26fQvNG8,59275
55
- moleditpy-2.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
- moleditpy-2.2.4.dist-info/entry_points.txt,sha256=yH1h9JjALhok1foXT3-hYrC4ufoZt8b7oiBcsdnGNNM,54
57
- moleditpy-2.2.4.dist-info/top_level.txt,sha256=ARICrS4ihlPXqywlKl6o-oJa3Qz3gZRWu_VZsQ3_c44,10
58
- moleditpy-2.2.4.dist-info/RECORD,,
54
+ moleditpy-2.2.6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
55
+ moleditpy-2.2.6.dist-info/METADATA,sha256=vHsKbmtTEC9mSX2KC3VMASZqxNyvRpQCblso_8fBiN8,60629
56
+ moleditpy-2.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ moleditpy-2.2.6.dist-info/entry_points.txt,sha256=yH1h9JjALhok1foXT3-hYrC4ufoZt8b7oiBcsdnGNNM,54
58
+ moleditpy-2.2.6.dist-info/top_level.txt,sha256=ARICrS4ihlPXqywlKl6o-oJa3Qz3gZRWu_VZsQ3_c44,10
59
+ moleditpy-2.2.6.dist-info/RECORD,,