moleditpy 0.3.5__tar.gz → 0.3.7__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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moleditpy
3
- Version: 0.3.5
4
- Summary: A program to edit molecular structures using Python.
3
+ Version: 0.3.7
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
7
7
  Project-URL: Issues, https://github.com/HiroYokoyama/python_molecular_editor/issues
@@ -22,7 +22,11 @@ Requires-Dist: numpy
22
22
  Requires-Dist: pyvista
23
23
  Requires-Dist: pyvistaqt
24
24
 
25
- # moleditpy -- Python Molecular Editor
25
+ # MoleditPy -- Python Molecular Editor
26
+
27
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.17268533.svg)](https://doi.org/10.5281/zenodo.17268533)
28
+ [![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/)
29
+
26
30
 
27
31
  Pythonで構築された、クロスプラットフォームかつシンプルで直感的な分子構造エディターです。2Dでの分子描画と、3D構造可視化ができます。DFT計算ソフト用のインプット用に構造ファイルのエクスポートをサポートします。
28
32
 
@@ -115,7 +119,7 @@ moleditpy
115
119
 
116
120
  -----
117
121
 
118
- ## Windows ショートカットとファイル関連付けガイド
122
+ ## Windows: ショートカットとファイル関連付けガイド
119
123
 
120
124
  Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリックで開いたり、アイコン付きのショートカットからアプリケーションを起動したりするための詳細な設定手順です。
121
125
 
@@ -146,7 +150,7 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
146
150
 
147
151
  1. **新規ショートカットの作成:** デスクトップなどの任意の場所を右クリックし、「新規作成」→\*\*「ショートカット」\*\*を選択します。
148
152
  2. **実行ファイルのパスを入力:** 「項目の場所を入力してください」に、上記の実行ファイルのパスを入力し、「次へ」をクリックします。
149
- 3. **名前を指定:** ショートカットに任意の名前(例: `moleditpy`)を付けて、「完了」をクリックします。
153
+ 3. **名前を指定:** ショートカットに任意の名前(例: `MoleditPy`)を付けて、「完了」をクリックします。
150
154
  4. **アイコンの変更:**
151
155
  * 作成されたショートカットを右クリックし、\*\*「プロパティ」\*\*を選択します。
152
156
  * 「ショートカット」タブの\*\*「アイコンの変更...」\*\*ボタンをクリックします。
@@ -160,12 +164,12 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
160
164
  1. **スタートメニューフォルダのパスを確認:** エクスプローラーのアドレスバーに以下のいずれかのパスを入力し、Enterキーを押します。
161
165
  * **現在のユーザー専用 (推奨):** `%APPDATA%\Microsoft\Windows\Start Menu\Programs`
162
166
  * **すべてのユーザー共通 (管理者権限が必要):** `%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs`
163
- 2. **ショートカットのコピーまたは移動:** 上記で開いたスタートメニューフォルダに、作成したショートカット(例: デスクトップ上の `moleditpy` ショートカット)を**コピーまたは移動**します。(Ctrlキーを押しながらドラッグするとコピーされます。)
167
+ 2. **ショートカットのコピーまたは移動:** 上記で開いたスタートメニューフォルダに、作成したショートカット(例: デスクトップ上の `MoleditPy` ショートカット)を**コピーまたは移動**します。(Ctrlキーを押しながらドラッグするとコピーされます。)
164
168
  3. **確認:** スタートボタンをクリックし、プログラム一覧からショートカットに付けた名前が追加されていることを確認してください。
165
169
 
166
170
  -----
167
171
 
168
- ## macOS向け:Python CLIアプリをAutomator経由で.app化する方法(moleditpy)
172
+ ## macOS: Python CLIアプリをAutomator経由で.app化する方法
169
173
 
170
174
  この手順では、`pip install moleditpy` でインストールしたPythonアプリをmacOS上で\*\*.appアプリケーション\*\*として使えるようにし、独自アイコンを設定します。
171
175
 
@@ -195,7 +199,7 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
195
199
  ### 4\. アプリとして保存
196
200
 
197
201
  1. メニューから\*\*「ファイル」\*\* → **「保存」** を選択します。
198
- 2. 名前を **`moleditpy.app`** にします。
202
+ 2. 名前を **`MoleditPy.app`** にします。
199
203
  3. 保存場所は一時的にデスクトップなど任意で構いません。
200
204
 
201
205
  ### 5\. アプリケーションフォルダーへ移動
@@ -204,14 +208,14 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
204
208
 
205
209
  **Finderからコピーする場合**
206
210
 
207
- 1. Finderで `moleditpy.app` を選択し、`⌘ + C` でコピーします。
211
+ 1. Finderで `MoleditPy.app` を選択し、`⌘ + C` でコピーします。
208
212
  2. Finderメニューの「移動」→\*\*「アプリケーション」\*\* を開きます。
209
213
  3. `⌘ + V` で貼り付けます。
210
214
 
211
215
  **ターミナルからコピーする場合**
212
216
 
213
217
  ```bash
214
- sudo cp -R ~/Desktop/moleditpy.app /Applications/
218
+ sudo cp -R ~/Desktop/MoleditPy.app /Applications/
215
219
  ```
216
220
 
217
221
  ### 6\. アイコンを設定
@@ -226,7 +230,7 @@ sudo cp -R ~/Desktop/moleditpy.app /Applications/
226
230
 
227
231
  1. Finderで上記の `icon.png` を開きます。
228
232
  2. 「プレビュー」で画像を開いた状態で `⌘ + A` で**全選択**、`⌘ + C` で**コピー**します。
229
- 3. Finderで `/Applications` フォルダー内の `moleditpy.app` を選択 → `⌘ + I`(情報を見る)を押します。
233
+ 3. Finderで `/Applications` フォルダー内の `MoleditPy.app` を選択 → `⌘ + I`(情報を見る)を押します。
230
234
  4. 左上の小さいアイコンをクリック(枠が出る) → `⌘ + V` で**貼り付け**ます。
231
235
  これでアプリのアイコンが変更されます。
232
236
 
@@ -240,7 +244,7 @@ chmod +x /Users/<username>/Library/Python/<python_version>/bin/moleditpy
240
244
 
241
245
  ### 8\. 完了
242
246
 
243
- これで `/Applications` にインストールされた **`moleditpy.app`** をダブルクリックするだけでPythonアプリが実行できます。
247
+ これで `/Applications` にインストールされた **`MoleditPy.app`** をダブルクリックするだけでPythonアプリが実行できます。
244
248
 
245
249
  -----
246
250
 
@@ -1,4 +1,8 @@
1
- # moleditpy -- Python Molecular Editor
1
+ # MoleditPy -- Python Molecular Editor
2
+
3
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.17268533.svg)](https://doi.org/10.5281/zenodo.17268533)
4
+ [![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/)
5
+
2
6
 
3
7
  Pythonで構築された、クロスプラットフォームかつシンプルで直感的な分子構造エディターです。2Dでの分子描画と、3D構造可視化ができます。DFT計算ソフト用のインプット用に構造ファイルのエクスポートをサポートします。
4
8
 
@@ -91,7 +95,7 @@ moleditpy
91
95
 
92
96
  -----
93
97
 
94
- ## Windows ショートカットとファイル関連付けガイド
98
+ ## Windows: ショートカットとファイル関連付けガイド
95
99
 
96
100
  Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリックで開いたり、アイコン付きのショートカットからアプリケーションを起動したりするための詳細な設定手順です。
97
101
 
@@ -122,7 +126,7 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
122
126
 
123
127
  1. **新規ショートカットの作成:** デスクトップなどの任意の場所を右クリックし、「新規作成」→\*\*「ショートカット」\*\*を選択します。
124
128
  2. **実行ファイルのパスを入力:** 「項目の場所を入力してください」に、上記の実行ファイルのパスを入力し、「次へ」をクリックします。
125
- 3. **名前を指定:** ショートカットに任意の名前(例: `moleditpy`)を付けて、「完了」をクリックします。
129
+ 3. **名前を指定:** ショートカットに任意の名前(例: `MoleditPy`)を付けて、「完了」をクリックします。
126
130
  4. **アイコンの変更:**
127
131
  * 作成されたショートカットを右クリックし、\*\*「プロパティ」\*\*を選択します。
128
132
  * 「ショートカット」タブの\*\*「アイコンの変更...」\*\*ボタンをクリックします。
@@ -136,12 +140,12 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
136
140
  1. **スタートメニューフォルダのパスを確認:** エクスプローラーのアドレスバーに以下のいずれかのパスを入力し、Enterキーを押します。
137
141
  * **現在のユーザー専用 (推奨):** `%APPDATA%\Microsoft\Windows\Start Menu\Programs`
138
142
  * **すべてのユーザー共通 (管理者権限が必要):** `%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs`
139
- 2. **ショートカットのコピーまたは移動:** 上記で開いたスタートメニューフォルダに、作成したショートカット(例: デスクトップ上の `moleditpy` ショートカット)を**コピーまたは移動**します。(Ctrlキーを押しながらドラッグするとコピーされます。)
143
+ 2. **ショートカットのコピーまたは移動:** 上記で開いたスタートメニューフォルダに、作成したショートカット(例: デスクトップ上の `MoleditPy` ショートカット)を**コピーまたは移動**します。(Ctrlキーを押しながらドラッグするとコピーされます。)
140
144
  3. **確認:** スタートボタンをクリックし、プログラム一覧からショートカットに付けた名前が追加されていることを確認してください。
141
145
 
142
146
  -----
143
147
 
144
- ## macOS向け:Python CLIアプリをAutomator経由で.app化する方法(moleditpy)
148
+ ## macOS: Python CLIアプリをAutomator経由で.app化する方法
145
149
 
146
150
  この手順では、`pip install moleditpy` でインストールしたPythonアプリをmacOS上で\*\*.appアプリケーション\*\*として使えるようにし、独自アイコンを設定します。
147
151
 
@@ -171,7 +175,7 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
171
175
  ### 4\. アプリとして保存
172
176
 
173
177
  1. メニューから\*\*「ファイル」\*\* → **「保存」** を選択します。
174
- 2. 名前を **`moleditpy.app`** にします。
178
+ 2. 名前を **`MoleditPy.app`** にします。
175
179
  3. 保存場所は一時的にデスクトップなど任意で構いません。
176
180
 
177
181
  ### 5\. アプリケーションフォルダーへ移動
@@ -180,14 +184,14 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
180
184
 
181
185
  **Finderからコピーする場合**
182
186
 
183
- 1. Finderで `moleditpy.app` を選択し、`⌘ + C` でコピーします。
187
+ 1. Finderで `MoleditPy.app` を選択し、`⌘ + C` でコピーします。
184
188
  2. Finderメニューの「移動」→\*\*「アプリケーション」\*\* を開きます。
185
189
  3. `⌘ + V` で貼り付けます。
186
190
 
187
191
  **ターミナルからコピーする場合**
188
192
 
189
193
  ```bash
190
- sudo cp -R ~/Desktop/moleditpy.app /Applications/
194
+ sudo cp -R ~/Desktop/MoleditPy.app /Applications/
191
195
  ```
192
196
 
193
197
  ### 6\. アイコンを設定
@@ -202,7 +206,7 @@ sudo cp -R ~/Desktop/moleditpy.app /Applications/
202
206
 
203
207
  1. Finderで上記の `icon.png` を開きます。
204
208
  2. 「プレビュー」で画像を開いた状態で `⌘ + A` で**全選択**、`⌘ + C` で**コピー**します。
205
- 3. Finderで `/Applications` フォルダー内の `moleditpy.app` を選択 → `⌘ + I`(情報を見る)を押します。
209
+ 3. Finderで `/Applications` フォルダー内の `MoleditPy.app` を選択 → `⌘ + I`(情報を見る)を押します。
206
210
  4. 左上の小さいアイコンをクリック(枠が出る) → `⌘ + V` で**貼り付け**ます。
207
211
  これでアプリのアイコンが変更されます。
208
212
 
@@ -216,7 +220,7 @@ chmod +x /Users/<username>/Library/Python/<python_version>/bin/moleditpy
216
220
 
217
221
  ### 8\. 完了
218
222
 
219
- これで `/Applications` にインストールされた **`moleditpy.app`** をダブルクリックするだけでPythonアプリが実行できます。
223
+ これで `/Applications` にインストールされた **`MoleditPy.app`** をダブルクリックするだけでPythonアプリが実行できます。
220
224
 
221
225
  -----
222
226
 
@@ -5,13 +5,13 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "moleditpy"
7
7
 
8
- version = "0.3.5"
8
+ version = "0.3.7"
9
9
 
10
10
  authors = [
11
11
  { name="HiroYokoyama", email="titech.yoko.hiro@gmail.com" },
12
12
  ]
13
13
 
14
- description = "A program to edit molecular structures using Python."
14
+ description = "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."
15
15
 
16
16
  readme = "README.md"
17
17
 
@@ -1,13 +1,18 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
+
3
4
  """
4
- moleditpy — simple molecule editor
5
- Short: Atom/bond edit, template preview, keyboard shortcuts).
5
+ MoleditPy — A Python-based molecular editing software
6
+
6
7
  Author: HiroYokoyama
7
8
  License: Apache-2.0 license
8
9
  Repo: https://github.com/HiroYokoyama/python_molecular_editor
10
+ DOI 10.5281/zenodo.17268532
9
11
  """
10
12
 
13
+ #Version
14
+ VERSION = "0.3.7"
15
+
11
16
  import sys
12
17
  import numpy as np
13
18
  import pickle
@@ -83,7 +88,7 @@ VDW_RADII = {pt.GetElementSymbol(i): pt.GetRvdw(i) * 0.3 for i in range(1, 119)}
83
88
  def main():
84
89
  # --- Windows タスクバーアイコンのための追加処理 ---
85
90
  if sys.platform == 'win32':
86
- myappid = 'hyoko.moleditpy.0.2' # アプリケーション固有のID(任意)
91
+ myappid = 'hyoko.moleditpy.0.3' # アプリケーション固有のID(任意)
87
92
  ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
88
93
 
89
94
  app = QApplication(sys.argv)
@@ -1332,7 +1337,79 @@ class MoleculeScene(QGraphicsScene):
1332
1337
  (b.atom1 is atom2 and b.atom2 is atom1):
1333
1338
  return b
1334
1339
  return None
1335
-
1340
+
1341
+
1342
+ class ZoomableView(QGraphicsView):
1343
+ """ マウスホイールでのズームと中ボタンでのパン機能を追加したQGraphicsView """
1344
+ def __init__(self, scene, parent=None):
1345
+ super().__init__(scene, parent)
1346
+ self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
1347
+ self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorViewCenter)
1348
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
1349
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
1350
+ self.setDragMode(QGraphicsView.DragMode.NoDrag) # デフォルトはドラッグなし
1351
+
1352
+ self._is_panning = False
1353
+ self._pan_start_pos = QPointF()
1354
+
1355
+ def wheelEvent(self, event):
1356
+ """ マウスホイールを回した際のイベント """
1357
+ if event.modifiers() & Qt.KeyboardModifier.ControlModifier:
1358
+ zoom_in_factor = 1.1
1359
+ zoom_out_factor = 1 / zoom_in_factor
1360
+
1361
+ transform = self.transform()
1362
+ current_scale = transform.m11()
1363
+ min_scale, max_scale = 0.05, 20.0
1364
+
1365
+ if event.angleDelta().y() > 0:
1366
+ if max_scale > current_scale:
1367
+ self.scale(zoom_in_factor, zoom_in_factor)
1368
+ else:
1369
+ if min_scale < current_scale:
1370
+ self.scale(zoom_out_factor, zoom_out_factor)
1371
+
1372
+ event.accept()
1373
+ else:
1374
+ super().wheelEvent(event)
1375
+
1376
+ def mousePressEvent(self, event):
1377
+ """ 中ボタンが押されたらパン(視点移動)モードを開始 """
1378
+ if event.button() == Qt.MouseButton.MiddleButton:
1379
+ self._is_panning = True
1380
+ self._pan_start_pos = event.pos()
1381
+ self.setCursor(Qt.CursorShape.SizeAllCursor)
1382
+ event.accept()
1383
+ else:
1384
+ super().mousePressEvent(event)
1385
+
1386
+ def mouseMoveEvent(self, event):
1387
+ """ パンモード中にマウスを動かしたら、その分だけ視点を移動させる """
1388
+ if self._is_panning:
1389
+ delta = self.mapToScene(event.pos()) - self.mapToScene(self._pan_start_pos)
1390
+ self.translate(delta.x(), delta.y())
1391
+ self._pan_start_pos = event.pos()
1392
+ event.accept()
1393
+ else:
1394
+ super().mouseMoveEvent(event)
1395
+
1396
+ def mouseReleaseEvent(self, event):
1397
+ """ 中ボタンを離したらパンモードを終了 """
1398
+ if event.button() == Qt.MouseButton.MiddleButton:
1399
+ self._is_panning = False
1400
+ # 現在の描画モードに応じたカーソルに戻す
1401
+ current_mode = self.scene().mode if self.scene() else 'select'
1402
+ if current_mode == 'select':
1403
+ self.setCursor(Qt.CursorShape.ArrowCursor)
1404
+ elif current_mode.startswith(('atom', 'bond', 'template')):
1405
+ self.setCursor(Qt.CursorShape.CrossCursor)
1406
+ elif current_mode.startswith(('charge', 'radical')):
1407
+ self.setCursor(Qt.CursorShape.PointingHandCursor)
1408
+ else:
1409
+ self.setCursor(Qt.CursorShape.ArrowCursor)
1410
+ event.accept()
1411
+ else:
1412
+ super().mouseReleaseEvent(event)
1336
1413
 
1337
1414
  class CalculationWorker(QObject):
1338
1415
  # --- 変更点1: 新しいシグナルを追加 ---
@@ -1514,7 +1591,7 @@ class MainWindow(QMainWindow):
1514
1591
  start_calculation = pyqtSignal(str)
1515
1592
  def __init__(self, initial_file=None):
1516
1593
  super().__init__()
1517
- self.setWindowTitle("moleditpy -- Python Molecular Editor by HY"); self.setGeometry(100, 100, 1400, 800)
1594
+ self.setWindowTitle("MoleditPy -- Python Molecular Editor Ver. " + VERSION); self.setGeometry(100, 100, 1400, 800)
1518
1595
  self.data = MolecularData(); self.current_mol = None
1519
1596
  self.current_3d_style = 'ball_and_stick'
1520
1597
  self.show_chiral_labels = False
@@ -1559,7 +1636,7 @@ class MainWindow(QMainWindow):
1559
1636
  self.scene.setSceneRect(-4000,-4000,4000,4000)
1560
1637
  self.scene.setBackgroundBrush(QColor("#FFFFFF"))
1561
1638
 
1562
- self.view_2d=QGraphicsView(self.scene)
1639
+ self.view_2d=ZoomableView(self.scene)
1563
1640
  self.view_2d.setRenderHint(QPainter.RenderHint.Antialiasing)
1564
1641
  self.view_2d.setSizePolicy(
1565
1642
  QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
@@ -1830,7 +1907,7 @@ class MainWindow(QMainWindow):
1830
1907
  menu_bar = self.menuBar()
1831
1908
 
1832
1909
  file_menu = menu_bar.addMenu("&File")
1833
- load_mol_action = QAction("Open MOL/SDF...", self); load_mol_action.triggered.connect(self.load_mol_file)
1910
+ load_mol_action = QAction("Import MOL...", self); load_mol_action.triggered.connect(self.load_mol_file)
1834
1911
  file_menu.addAction(load_mol_action)
1835
1912
  file_menu.addSeparator()
1836
1913
  save_mol_action = QAction("Save 2D as MOL...", self); save_mol_action.triggered.connect(self.save_as_mol)
@@ -1900,6 +1977,29 @@ class MainWindow(QMainWindow):
1900
1977
  clear_all_action.triggered.connect(self.clear_all); edit_menu.addAction(clear_all_action)
1901
1978
 
1902
1979
  view_menu = menu_bar.addMenu("&View")
1980
+
1981
+ zoom_in_action = QAction("Zoom In", self)
1982
+ zoom_in_action.setShortcut(QKeySequence.StandardKey.ZoomIn) # Ctrl +
1983
+ zoom_in_action.triggered.connect(self.zoom_in)
1984
+ view_menu.addAction(zoom_in_action)
1985
+
1986
+ zoom_out_action = QAction("Zoom Out", self)
1987
+ zoom_out_action.setShortcut(QKeySequence.StandardKey.ZoomOut) # Ctrl -
1988
+ zoom_out_action.triggered.connect(self.zoom_out)
1989
+ view_menu.addAction(zoom_out_action)
1990
+
1991
+ reset_zoom_action = QAction("Reset Zoom", self)
1992
+ reset_zoom_action.setShortcut(QKeySequence("Ctrl+0"))
1993
+ reset_zoom_action.triggered.connect(self.reset_zoom)
1994
+ view_menu.addAction(reset_zoom_action)
1995
+
1996
+ fit_action = QAction("Fit to View", self)
1997
+ fit_action.setShortcut(QKeySequence("Ctrl+9"))
1998
+ fit_action.triggered.connect(self.fit_to_view)
1999
+ view_menu.addAction(fit_action)
2000
+
2001
+ view_menu.addSeparator()
2002
+
1903
2003
  self.toggle_chiral_action = QAction("Show Chiral Labels", self, checkable=True)
1904
2004
  self.toggle_chiral_action.setChecked(self.show_chiral_labels)
1905
2005
  self.toggle_chiral_action.triggered.connect(self.toggle_chiral_labels_display)
@@ -2196,6 +2296,8 @@ class MainWindow(QMainWindow):
2196
2296
  for atom_id, data in self.data.atoms.items()}
2197
2297
  bonds = {key: {'order': data['order'], 'stereo': data.get('stereo', 0)} for key, data in self.data.bonds.items()}
2198
2298
  state = {'atoms': atoms, 'bonds': bonds, '_next_atom_id': self.data._next_atom_id}
2299
+
2300
+ state['version'] = VERSION
2199
2301
 
2200
2302
  if self.current_mol: state['mol_3d'] = self.current_mol.ToBinary()
2201
2303
 
@@ -2205,6 +2307,26 @@ class MainWindow(QMainWindow):
2205
2307
  self.clear_2d_editor(push_to_undo=False)
2206
2308
 
2207
2309
  loaded_data = copy.deepcopy(state_data)
2310
+
2311
+ # ファイルのバージョンを取得(存在しない場合は '0.0.0' とする)
2312
+ file_version_str = loaded_data.get('version', '0.0.0')
2313
+
2314
+ try:
2315
+ app_version_parts = tuple(map(int, VERSION.split('.')))
2316
+ file_version_parts = tuple(map(int, file_version_str.split('.')))
2317
+
2318
+ # ファイルのバージョンがアプリケーションのバージョンより新しい場合に警告
2319
+ if file_version_parts > app_version_parts:
2320
+ QMessageBox.warning(
2321
+ self,
2322
+ "Version Mismatch",
2323
+ f"The file you are opening was saved with a newer version of MoleditPy (ver. {file_version_str}).\n\n"
2324
+ f"Your current version is {VERSION}.\n\n"
2325
+ "Some features may not load or work correctly."
2326
+ )
2327
+ except (ValueError, AttributeError):
2328
+ pass
2329
+
2208
2330
  raw_atoms = loaded_data.get('atoms', {})
2209
2331
  raw_bonds = loaded_data.get('bonds', {})
2210
2332
 
@@ -2362,33 +2484,50 @@ class MainWindow(QMainWindow):
2362
2484
  if push_to_undo:
2363
2485
  self.push_undo_state()
2364
2486
 
2365
-
2366
2487
  def load_mol_file(self):
2367
2488
  options = QFileDialog.Option.DontUseNativeDialog
2368
- file_path, _ = QFileDialog.getOpenFileName(self, "Open MOL/SDF File", "", "Chemical Files (*.mol *.sdf);;All Files (*)", options=options)
2489
+ file_path, _ = QFileDialog.getOpenFileName(self, "Import MOL File", "", "Chemical Files (*.mol *.sdf);;All Files (*)", options=options)
2369
2490
  if not file_path: return
2370
2491
  try:
2371
- suppl = Chem.SDMolSupplier(file_path, removeHs=False)
2492
+ suppl = Chem.SDMolSupplier(file_path, removeHs=True)
2372
2493
  mol = next(suppl, None)
2373
2494
  if mol is None: raise ValueError("Failed to read molecule from file.")
2495
+
2496
+ Chem.Kekulize(mol)
2497
+
2374
2498
  self.clear_2d_editor(push_to_undo=False)
2375
2499
  self.current_mol = None; self.plotter.clear(); self.analysis_action.setEnabled(False)
2376
2500
 
2377
- if mol.GetNumConformers() == 0: AllChem.Compute2DCoords(mol)
2501
+ if mol.GetNumConformers() == 0:
2502
+ AllChem.Compute2DCoords(mol)
2378
2503
 
2379
2504
  conf = mol.GetConformer(); SCALE_FACTOR = 50.0
2380
- rdkit_idx_to_my_id = {}
2505
+
2506
+ view_center = self.view_2d.mapToScene(self.view_2d.viewport().rect().center())
2507
+
2381
2508
  positions = [conf.GetAtomPosition(i) for i in range(mol.GetNumAtoms())]
2382
- center_x = sum(p.x for p in positions)/len(positions) if positions else 0
2383
- center_y = sum(p.y for p in positions)/len(positions) if positions else 0
2509
+ if positions:
2510
+ mol_center_x = sum(p.x for p in positions) / len(positions)
2511
+ mol_center_y = sum(p.y for p in positions) / len(positions)
2512
+ else:
2513
+ mol_center_x, mol_center_y = 0.0, 0.0
2514
+
2515
+ rdkit_idx_to_my_id = {}
2384
2516
  for i in range(mol.GetNumAtoms()):
2385
- atom = mol.GetAtomWithIdx(i); pos = conf.GetAtomPosition(i)
2517
+ atom = mol.GetAtomWithIdx(i)
2518
+ pos = conf.GetAtomPosition(i)
2386
2519
  charge = atom.GetFormalCharge()
2387
- scene_x=(pos.x-center_x)*SCALE_FACTOR; scene_y=-(pos.y-center_y)*SCALE_FACTOR
2520
+
2521
+ relative_x = pos.x - mol_center_x
2522
+ relative_y = pos.y - mol_center_y
2523
+
2524
+ scene_x = (relative_x * SCALE_FACTOR) + view_center.x()
2525
+ scene_y = (-relative_y * SCALE_FACTOR) + view_center.y()
2526
+
2388
2527
  atom_id = self.scene.create_atom(atom.GetSymbol(), QPointF(scene_x, scene_y), charge=charge)
2389
2528
  rdkit_idx_to_my_id[i] = atom_id
2529
+
2390
2530
  for bond in mol.GetBonds():
2391
- # (以降のBond読み込み部分は変更なし)
2392
2531
  b_idx,e_idx=bond.GetBeginAtomIdx(),bond.GetEndAtomIdx()
2393
2532
  b_type = bond.GetBondTypeAsDouble(); b_dir = bond.GetBondDir()
2394
2533
  stereo = 0
@@ -2465,7 +2604,7 @@ class MainWindow(QMainWindow):
2465
2604
  try:
2466
2605
  conf=self.current_mol.GetConformer(); num_atoms=self.current_mol.GetNumAtoms()
2467
2606
  xyz_lines=[str(num_atoms)]; smiles=Chem.MolToSmiles(Chem.RemoveHs(self.current_mol))
2468
- xyz_lines.append(f"Generated by moleditpy. SMILES: {smiles}")
2607
+ xyz_lines.append(f"Generated by MoleditPy Ver. {VERSION}. SMILES: {smiles}")
2469
2608
  for i in range(num_atoms):
2470
2609
  pos=conf.GetAtomPosition(i); symbol=self.current_mol.GetAtomWithIdx(i).GetSymbol()
2471
2610
  xyz_lines.append(f"{symbol} {pos.x:.6f} {pos.y:.6f} {pos.z:.6f}")
@@ -2716,8 +2855,11 @@ class MainWindow(QMainWindow):
2716
2855
  edge_color = '#505050'
2717
2856
  self.plotter.add_mesh(glyphs, scalars='colors', rgb=True, smooth_shading=True, show_edges=True, edge_color=edge_color, edge_opacity=0.3, line_width=0.1)
2718
2857
 
2719
- # Ball & Stick スタイルの時のみ結合を描画
2858
+
2720
2859
  if self.current_3d_style == 'ball_and_stick':
2860
+ # 結合メッシュを一時的に格納するリストを作成
2861
+ bond_meshes = []
2862
+
2721
2863
  for bond in mol.GetBonds():
2722
2864
  sp = np.array(conf.GetAtomPosition(bond.GetBeginAtomIdx()))
2723
2865
  ep = np.array(conf.GetAtomPosition(bond.GetEndAtomIdx()))
@@ -2726,11 +2868,13 @@ class MainWindow(QMainWindow):
2726
2868
  d = ep - sp
2727
2869
  h = np.linalg.norm(d)
2728
2870
  if h == 0: continue
2729
- color = [0.5, 0.5, 0.5]
2871
+
2872
+ # この時点では add_mesh しない
2730
2873
  if bt == Chem.rdchem.BondType.SINGLE or bt == Chem.rdchem.BondType.AROMATIC:
2731
2874
  cyl = pv.Cylinder(center=c, direction=d, radius=0.1, height=h)
2732
- self.plotter.add_mesh(cyl, color=color, smooth_shading=True, show_edges=True, edge_color=edge_color, edge_opacity=0.3)
2875
+ bond_meshes.append(cyl)
2733
2876
  else:
2877
+ # (二重結合・三重結合のロジックも同様に、生成したメッシュをリストに追加する)
2734
2878
  v1 = d / h
2735
2879
  v_arb = np.array([0, 0, 1])
2736
2880
  if np.allclose(np.abs(np.dot(v1, v_arb)), 1.0): v_arb = np.array([0, 1, 0])
@@ -2741,19 +2885,28 @@ class MainWindow(QMainWindow):
2741
2885
  s = 0.20
2742
2886
  c1 = c + off_dir * (s / 2)
2743
2887
  c2 = c - off_dir * (s / 2)
2744
- cyl1 = pv.Cylinder(center=c1, direction=d, radius=r, height=h)
2745
- cyl2 = pv.Cylinder(center=c2, direction=d, radius=r, height=h)
2746
- self.plotter.add_mesh(cyl1, color=color, smooth_shading=True, show_edges=True, edge_color=edge_color, edge_opacity=0.3)
2747
- self.plotter.add_mesh(cyl2, color=color, smooth_shading=True, show_edges=True, edge_color=edge_color, edge_opacity=0.3)
2888
+ bond_meshes.append(pv.Cylinder(center=c1, direction=d, radius=r, height=h))
2889
+ bond_meshes.append(pv.Cylinder(center=c2, direction=d, radius=r, height=h))
2748
2890
  elif bt == Chem.rdchem.BondType.TRIPLE:
2749
2891
  r = 0.08
2750
2892
  s = 0.20
2751
- cc = pv.Cylinder(center=c, direction=d, radius=r, height=h)
2752
- c1 = pv.Cylinder(center=c + off_dir * s, direction=d, radius=r, height=h)
2753
- c2 = pv.Cylinder(center=c - off_dir * s, direction=d, radius=r, height=h)
2754
- self.plotter.add_mesh(cc, color=color, smooth_shading=True, show_edges=True, edge_color=edge_color, edge_opacity=0.3)
2755
- self.plotter.add_mesh(c1, color=color, smooth_shading=True, show_edges=True, edge_color=edge_color, edge_opacity=0.3)
2756
- self.plotter.add_mesh(c2, color=color, smooth_shading=True, show_edges=True, edge_color=edge_color, edge_opacity=0.3)
2893
+ bond_meshes.append(pv.Cylinder(center=c, direction=d, radius=r, height=h))
2894
+ bond_meshes.append(pv.Cylinder(center=c + off_dir * s, direction=d, radius=r, height=h))
2895
+ bond_meshes.append(pv.Cylinder(center=c - off_dir * s, direction=d, radius=r, height=h))
2896
+
2897
+ # ループが終わった後、リストに溜めた全メッシュを1つに統合
2898
+ if bond_meshes:
2899
+ combined_bonds = pv.merge(bond_meshes)
2900
+ # 最後に一度だけ add_mesh を呼び出す
2901
+ self.plotter.add_meshedge_color = '#505050'
2902
+ self.plotter.add_mesh(
2903
+ combined_bonds,
2904
+ color=[0.5, 0.5, 0.5],
2905
+ smooth_shading=True,
2906
+ show_edges=True,
2907
+ edge_color=edge_color,
2908
+ edge_opacity=0.3
2909
+ )
2757
2910
 
2758
2911
  # chiral labels: 3D コンフォマーを持つ場合はここで R/S を表示
2759
2912
  if getattr(self, 'show_chiral_labels', False):
@@ -2896,6 +3049,43 @@ class MainWindow(QMainWindow):
2896
3049
  else:
2897
3050
  event.ignore()
2898
3051
 
3052
+ def zoom_in(self):
3053
+ """ ビューを 20% 拡大する """
3054
+ self.view_2d.scale(1.2, 1.2)
3055
+
3056
+ def zoom_out(self):
3057
+ """ ビューを 20% 縮小する """
3058
+ self.view_2d.scale(1/1.2, 1/1.2)
3059
+
3060
+ def reset_zoom(self):
3061
+ """ ビューの拡大率をデフォルト (50%) にリセットする """
3062
+ transform = QTransform()
3063
+ transform.scale(0.5, 0.5)
3064
+ self.view_2d.setTransform(transform)
3065
+
3066
+ def fit_to_view(self):
3067
+ """ シーン上のすべてのアイテムがビューに収まるように調整する """
3068
+ if not self.scene.items():
3069
+ self.reset_zoom()
3070
+ return
3071
+
3072
+ bounds = self.scene.itemsBoundingRect()
3073
+ visible_items_rect = QRectF()
3074
+ for item in self.scene.items():
3075
+ if item.isVisible() and not isinstance(item, TemplatePreviewItem):
3076
+ if visible_items_rect.isEmpty():
3077
+ visible_items_rect = item.sceneBoundingRect()
3078
+ else:
3079
+ visible_items_rect = visible_items_rect.united(item.sceneBoundingRect())
3080
+
3081
+ if not visible_items_rect.isEmpty():
3082
+ self.view_2d.fitInView(visible_items_rect, Qt.AspectRatioMode.KeepAspectRatio)
3083
+ self.view_2d.scale(0.6, 0.6)
3084
+ else:
3085
+ self.reset_zoom()
3086
+
3087
+
2899
3088
  # --- Application Execution ---
2900
3089
  if __name__ == '__main__':
2901
3090
  main()
3091
+
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moleditpy
3
- Version: 0.3.5
4
- Summary: A program to edit molecular structures using Python.
3
+ Version: 0.3.7
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
7
7
  Project-URL: Issues, https://github.com/HiroYokoyama/python_molecular_editor/issues
@@ -22,7 +22,11 @@ Requires-Dist: numpy
22
22
  Requires-Dist: pyvista
23
23
  Requires-Dist: pyvistaqt
24
24
 
25
- # moleditpy -- Python Molecular Editor
25
+ # MoleditPy -- Python Molecular Editor
26
+
27
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.17268533.svg)](https://doi.org/10.5281/zenodo.17268533)
28
+ [![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/)
29
+
26
30
 
27
31
  Pythonで構築された、クロスプラットフォームかつシンプルで直感的な分子構造エディターです。2Dでの分子描画と、3D構造可視化ができます。DFT計算ソフト用のインプット用に構造ファイルのエクスポートをサポートします。
28
32
 
@@ -115,7 +119,7 @@ moleditpy
115
119
 
116
120
  -----
117
121
 
118
- ## Windows ショートカットとファイル関連付けガイド
122
+ ## Windows: ショートカットとファイル関連付けガイド
119
123
 
120
124
  Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリックで開いたり、アイコン付きのショートカットからアプリケーションを起動したりするための詳細な設定手順です。
121
125
 
@@ -146,7 +150,7 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
146
150
 
147
151
  1. **新規ショートカットの作成:** デスクトップなどの任意の場所を右クリックし、「新規作成」→\*\*「ショートカット」\*\*を選択します。
148
152
  2. **実行ファイルのパスを入力:** 「項目の場所を入力してください」に、上記の実行ファイルのパスを入力し、「次へ」をクリックします。
149
- 3. **名前を指定:** ショートカットに任意の名前(例: `moleditpy`)を付けて、「完了」をクリックします。
153
+ 3. **名前を指定:** ショートカットに任意の名前(例: `MoleditPy`)を付けて、「完了」をクリックします。
150
154
  4. **アイコンの変更:**
151
155
  * 作成されたショートカットを右クリックし、\*\*「プロパティ」\*\*を選択します。
152
156
  * 「ショートカット」タブの\*\*「アイコンの変更...」\*\*ボタンをクリックします。
@@ -160,12 +164,12 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
160
164
  1. **スタートメニューフォルダのパスを確認:** エクスプローラーのアドレスバーに以下のいずれかのパスを入力し、Enterキーを押します。
161
165
  * **現在のユーザー専用 (推奨):** `%APPDATA%\Microsoft\Windows\Start Menu\Programs`
162
166
  * **すべてのユーザー共通 (管理者権限が必要):** `%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs`
163
- 2. **ショートカットのコピーまたは移動:** 上記で開いたスタートメニューフォルダに、作成したショートカット(例: デスクトップ上の `moleditpy` ショートカット)を**コピーまたは移動**します。(Ctrlキーを押しながらドラッグするとコピーされます。)
167
+ 2. **ショートカットのコピーまたは移動:** 上記で開いたスタートメニューフォルダに、作成したショートカット(例: デスクトップ上の `MoleditPy` ショートカット)を**コピーまたは移動**します。(Ctrlキーを押しながらドラッグするとコピーされます。)
164
168
  3. **確認:** スタートボタンをクリックし、プログラム一覧からショートカットに付けた名前が追加されていることを確認してください。
165
169
 
166
170
  -----
167
171
 
168
- ## macOS向け:Python CLIアプリをAutomator経由で.app化する方法(moleditpy)
172
+ ## macOS: Python CLIアプリをAutomator経由で.app化する方法
169
173
 
170
174
  この手順では、`pip install moleditpy` でインストールしたPythonアプリをmacOS上で\*\*.appアプリケーション\*\*として使えるようにし、独自アイコンを設定します。
171
175
 
@@ -195,7 +199,7 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
195
199
  ### 4\. アプリとして保存
196
200
 
197
201
  1. メニューから\*\*「ファイル」\*\* → **「保存」** を選択します。
198
- 2. 名前を **`moleditpy.app`** にします。
202
+ 2. 名前を **`MoleditPy.app`** にします。
199
203
  3. 保存場所は一時的にデスクトップなど任意で構いません。
200
204
 
201
205
  ### 5\. アプリケーションフォルダーへ移動
@@ -204,14 +208,14 @@ Windows環境でプロジェクトファイル(`.pmeraw`)をダブルクリ
204
208
 
205
209
  **Finderからコピーする場合**
206
210
 
207
- 1. Finderで `moleditpy.app` を選択し、`⌘ + C` でコピーします。
211
+ 1. Finderで `MoleditPy.app` を選択し、`⌘ + C` でコピーします。
208
212
  2. Finderメニューの「移動」→\*\*「アプリケーション」\*\* を開きます。
209
213
  3. `⌘ + V` で貼り付けます。
210
214
 
211
215
  **ターミナルからコピーする場合**
212
216
 
213
217
  ```bash
214
- sudo cp -R ~/Desktop/moleditpy.app /Applications/
218
+ sudo cp -R ~/Desktop/MoleditPy.app /Applications/
215
219
  ```
216
220
 
217
221
  ### 6\. アイコンを設定
@@ -226,7 +230,7 @@ sudo cp -R ~/Desktop/moleditpy.app /Applications/
226
230
 
227
231
  1. Finderで上記の `icon.png` を開きます。
228
232
  2. 「プレビュー」で画像を開いた状態で `⌘ + A` で**全選択**、`⌘ + C` で**コピー**します。
229
- 3. Finderで `/Applications` フォルダー内の `moleditpy.app` を選択 → `⌘ + I`(情報を見る)を押します。
233
+ 3. Finderで `/Applications` フォルダー内の `MoleditPy.app` を選択 → `⌘ + I`(情報を見る)を押します。
230
234
  4. 左上の小さいアイコンをクリック(枠が出る) → `⌘ + V` で**貼り付け**ます。
231
235
  これでアプリのアイコンが変更されます。
232
236
 
@@ -240,7 +244,7 @@ chmod +x /Users/<username>/Library/Python/<python_version>/bin/moleditpy
240
244
 
241
245
  ### 8\. 完了
242
246
 
243
- これで `/Applications` にインストールされた **`moleditpy.app`** をダブルクリックするだけでPythonアプリが実行できます。
247
+ これで `/Applications` にインストールされた **`MoleditPy.app`** をダブルクリックするだけでPythonアプリが実行できます。
244
248
 
245
249
  -----
246
250
 
File without changes