1. 開発した3Dビューア
今回は特定の分子ボタンをクリックすると3Dで観察可能な分子模型が表示可能なUIを構築した。マウス等で分子をドラッグ&ドロップする事で回転して観察可能となる。今後スケッチから分子をモデルして3D化する等の改良を加える。
2. 使用した環境
プログラムの検証するにあたって使用した環境は以下の通りである。
使用PC
MacBook Pro 3-inch, 2017, Four Thunderbolt 3 Ports
Pythonの環境
Python 3.11.9
Pythonのライブラリ
GUIアプリケーションを構築するために使用。
QApplication, QMainWindowなどのクラスでウィンドウやレイアウトを構築。
QWebEngineViewでHTMLコンテンツを表示。
Chemモジュールを使用して分子をSMILES形式から読み込む。
AllChemで分子の3D構造を生成し、エネルギー最適化を行う。
分子構造をPDB形式で解析するためのライブラリ。
ファイル操作や文字列の入出力に使用。
その他使用したもの
3DレンダリングのためのJavaScriptライブラリ「3Dmol.js」を活用した。これは3D分子モデルの可視化を簡単に行うために設計されたオープンソースのJavaScriptライブラリ。分子シミュレーションの可視化や3Dレンダリングに利用される。主な特徴について以下に詳細を示す。
-
主な機能
- (a) 分子の可視化:
PDBやSDF、XYZファイルなどの化学データフォーマットをサポート。
表示スタイルの選択(リボン、ボール&スティック、空間充填モデルなど)。 - (b) インタラクティブな操作: 回転、ズーム、パン操作に対応。
選択された部分のハイライト表示や詳細表示。 - (c) カスタマイズ可能:
ライティングや色、透明度の調整。
独自のスクリプトを使用して表示内容を動的に変更可能。 - (d) WebGLを活用:
GPUのパフォーマンスを最大限に活用し、高速でリアルなレンダリングを実現。
3. コードの解説
1. 必要なライブラリのインポート
from rdkit import Chem
from rdkit.Chem import AllChem
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QHBoxLayout
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl
from Bio.PDB import PDBParser
from io import StringIO
import os
2. アプリケーションのメインウィンドウクラス
class MoleculeViewer(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("3D Molecule Viewer")
self.setGeometry(100, 100, 800, 600)
-
MoleculeViewer:
PyQt5のQMainWindowを継承して作成したカスタムクラス。
アプリのメインウィンドウとして機能。 -
setWindowTitleとsetGeometry:
ウィンドウのタイトルを設定し、位置とサイズを指定。
3. Webエンジンビューとレイアウトの設定
self.browser = QWebEngineView()
layout = QVBoxLayout()
-
QWebEngineView:
HTMLやJavaScriptを表示できるPyQt5のウィジェット。
このアプリケーションでは、3Dmol.jsを使った分子モデルの可視化に利用。 -
QVBoxLayout:
垂直方向にウィジェットを配置するレイアウト。
4. ボタンの追加
button_layout = QHBoxLayout()
self.buttons = {
"メタン": "C",
"ベンゼン": "C1=CC=CC=C1",
"シクロヘキサン": "C1CCCCC1",
"ブタジエン": "C=CC=C",
"アンモニア": "N",
"フェニルアラニン": "C1=CC=C(C=C1)CC(C(=O)O)N",
}
for label, smiles in self.buttons.items():
button = QPushButton(label)
button.clicked.connect(lambda _, s=smiles: self.load_html(s))
button_layout.addWidget(button)
-
QPushButton
各分子のボタンを作成。
ボタンを押すとself.load_html(smiles)が呼ばれるように設定。 -
SMILES文字列
3D表示したい分子モデルをSMILES文字列(分子構造の表記法)を辞書で管理。
5. レイアウトの組み立て
layout.addLayout(button_layout)
layout.addWidget(self.browser)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
# 初期分子の表示
self.load_html(self.buttons["ベンゼン"])
6. PDBデータをXYZ形式に変換
def pdb_to_xyz_string(self, pdb_content):
parser = PDBParser(QUIET=True)
structure = parser.get_structure('structure', StringIO(pdb_content))
atoms = []
for model in structure:
for chain in model:
for residue in chain:
for atom in residue:
atoms.append((atom.element, atom.coord))
xyz_lines = [f"{len(atoms)}", "Converted from PDB content"]
for element, coord in atoms:
x, y, z = coord
xyz_lines.append(f"{element} {x:.3f} {y:.3f} {z:.3f}")
return "\n".join(xyz_lines)
-
PDBパース:
Bio.PDB.PDBParserでPDB形式のデータを解析し、原子の座標を抽出。 -
XYZ形式変換:
原子数と座標情報をXYZ形式(3Dmol.jsがサポート)に整形。
PDBだと水素原子が消える問題が生じたためxyzに変換した。
7. 分子データの生成とHTMLの読み込み
def load_html(self, smiles):
def generate_3d_pdb(smiles):
mol = Chem.MolFromSmiles(smiles)
mol = Chem.AddHs(mol, addCoords=True)
AllChem.EmbedMolecule(mol)
AllChem.MMFFOptimizeMolecule(mol)
pdb_data = Chem.MolToPDBBlock(mol)
return pdb_data
pdb_data = generate_3d_pdb(smiles)
pdb_data = self.pdb_to_xyz_string(pdb_data)
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<script src="3Dmol-min.js"></script>
</head>
<body>
<div id="viewer" style="width: 800px; height: 600px;"></div>
<script>
let viewer = $3Dmol.createViewer("viewer", {{ backgroundColor: "black" }});
viewer.addModel(`{pdb_data}`, "xyz");
viewer.setStyle({{}}, {{stick: {{radius: 0.2, hydrogen: true}}, sphere: {{scale: 0.3}}}});
viewer.zoomTo();
viewer.render();
</script>
</body>
</html>
"""
html_file = "molecule.html"
with open(html_file, "w", encoding="utf-8") as f:
f.write(html_content)
full_path = os.path.abspath(html_file)
self.browser.setUrl(QUrl.fromLocalFile(full_path))
-
SMILESからPDBへの変換:
RDKitを使ってSMILESから3D構造を生成。
最適化後にPDB形式で出力。 -
3Dmol-min.jsのダウンロードと配置
公式サイトまたはGitHubリポジトリから3Dmol-min.jsをダウンロードする。
ダウンロードした3Dmol-min.jsを、Pythonスクリプト(例:molecule_viewer.py)と同じディレクトリに配置すること。
ディレクトリ構成の例:
project_directory/
├── molecule_viewer.py
├── 3Dmol-min.js -
HTML生成:
生成したXYZデータを埋め込んだHTMLファイルを作成。
3Dmol.jsを使用して分子を可視化。
8. アプリケーションのエントリーポイント
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
viewer = MoleculeViewer()
viewer.show()
sys.exit(app.exec())
4. 全体プログラム
以下にプログラムの全体のコードを添付する。
from rdkit import Chem
from rdkit.Chem import AllChem
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QHBoxLayout
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl
from Bio.PDB import PDBParser
from io import StringIO
import os
class MoleculeViewer(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("3D Molecule Viewer")
self.setGeometry(100, 100, 800, 600)
# Webエンジンビューを作成
self.browser = QWebEngineView()
# レイアウト設定
layout = QVBoxLayout()
# ボタンを追加
button_layout = QHBoxLayout()
self.buttons = {
"メタン": "C",
"ベンゼン": "C1=CC=CC=C1",
"シクロヘキサン": "C1CCCCC1",
"ブタジエン": "C=CC=C",
"アンモニア": "N",
"フェニルアラニン": "C1=CC=C(C=C1)CC(C(=O)O)N",
}
for label, smiles in self.buttons.items():
button = QPushButton(label)
button.clicked.connect(lambda _, s=smiles: self.load_html(s))
button_layout.addWidget(button)
layout.addLayout(button_layout)
layout.addWidget(self.browser)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
# 初期分子の表示
self.load_html(self.buttons["ベンゼン"])
def pdb_to_xyz_string(self, pdb_content):
parser = PDBParser(QUIET=True)
structure = parser.get_structure('structure', StringIO(pdb_content))
atoms = []
for model in structure:
for chain in model:
for residue in chain:
for atom in residue:
atoms.append((atom.element, atom.coord))
xyz_lines = [f"{len(atoms)}", "Converted from PDB content"]
for element, coord in atoms:
x, y, z = coord
xyz_lines.append(f"{element} {x:.3f} {y:.3f} {z:.3f}")
return "\n".join(xyz_lines)
def load_html(self, smiles):
def generate_3d_pdb(smiles):
mol = Chem.MolFromSmiles(smiles) # 分子をSMILESから生成
mol = Chem.AddHs(mol, addCoords=True) # 水素を追加
AllChem.EmbedMolecule(mol) # 3D構造を生成
AllChem.MMFFOptimizeMolecule(mol) # エネルギー最適化
pdb_data = Chem.MolToPDBBlock(mol) # PDB形式でエクスポート
return pdb_data
pdb_data = generate_3d_pdb(smiles) # 3D構造を生成
pdb_data = self.pdb_to_xyz_string(pdb_data)
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<script src="3Dmol-min.js"></script>
</head>
<body>
<div id="viewer" style="width: 800px; height: 600px;"></div>
<script>
let viewer = $3Dmol.createViewer("viewer", {{ backgroundColor: "black" }});
viewer.addModel({pdb_data}, "xyz");
viewer.setStyle({{}}, {{stick: {{radius: 0.2, hydrogen: true}}, sphere: {{scale: 0.3}}}});
viewer.zoomTo();
viewer.render();
</script>
</body>
</html>
"""
html_file = "molecule.html"
with open(html_file, "w", encoding="utf-8") as f:
f.write(html_content)
full_path = os.path.abspath(html_file)
self.browser.setUrl(QUrl.fromLocalFile(full_path))
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
viewer = MoleculeViewer()
viewer.show()
sys.exit(app.exec())