ご注文はリード化合物ですか?〜医薬化学録にわ〜

自分の勉強や備忘録などを兼ねて好き勝手なことを書いていくブログです。

グラフィカルユーザーインターフェース(GUI)の作り方入門

ケモインフォマティクスと聞くと、機械学習的なアプローチを思い浮かべる人も多いかと思いますが、伝統的には、解析ツールを化学者に提供する、というのも重要な役割です。例えば Gaussian や GAMESS は有用な量子化学計算ソフトウェアですが、そのグラフィカルユーザーインターフェースGUI)が使いやすいことも、広く普及した要因の一つだと思います。
論文で発表された解析用のツールなどはよく GitHub などに上がっているので、ケモインフォマティシャンなどはすぐに使える場合も多いですが、実験化学者の方々全員がプログラミングに精通しているわけではありません。解析ツールが現場で気軽に使われるためには、優れた GUI を提供する必要があります。

構造式とその分子量を表示する GUI を作ってみました。テキストボックスに SMILES を入力し、ボタンを押すと分子量と構造式が出る GUI です。

import numpy as np
import pandas as pd
from rdkit import Chem
from rdkit.Chem.Descriptors import MolWt
from rdkit.Chem.Draw import IPythonConsole, MolToImage
import tkinter as tk
from PIL import Image, ImageTk

# 結果を表示するウィンドウの設定
def createNewWindow():
    newWindow = tk.Toplevel(root)
    newWindow.title("Result")
    newWindow.geometry("700x700")
    
    # テキストボックスに入力した SMILES を取り出して分子量を表示
    mol = Chem.MolFromSmiles(textbox.get())
    result = MolWt(mol)
    weight = tk.Label(newWindow, text = str(result), foreground = "dodgerblue")
    weight.grid()
    
    # 構造式の表示
    img = ImageTk.PhotoImage(MolToImage(mol))
    
    # 構造式を表示する領域を指定
    canvas = tk.Canvas(newWindow, bg = "black", width = 400, height = 400)
    canvas.place(x = 100, y = 100)
    canvas.create_image(200, 200, image = img, anchor = tk.CENTER)
    
    # 結果のウィンドウの表示
    newWindow.mainloop()
    
# メインのウィンドウの設定
root = tk.Tk()

# ウィンドウ名
root.title("Molecule viewer")

# ウィンドウのサイズ
root.geometry("500x500")

# ウィンドウ中の文章
label = tk.Label(root, text = "Input SMILES", foreground = "crimson")
label.grid(row = 0, column = 1)

# テキストボックスの設定
textbox = tk.Entry(root)
textbox.insert(tk.END, "CCO")
textbox.grid(row = 1, column = 1)

# ボタンの設定
button = tk.Button(root, text = "Show", command = createNewWindow)
button.grid(row = 1, column = 0)

# 実行
root.mainloop()

GUI 作成用のライブラリとして、tkinter を用いました。tkinter はデフォルトで入っているので、別途インストールする必要はありません。
GUI ライブラリとしては、wxPython を本当は使ってみたかったのですが、別途インストールが必要なのと、Jupyter Notebook で使おうとすると、カーネルを設定する必要があったので、今回は使用を見送りました。

最初に、メインのウィンドウのインスタンスを tk.Tk() で作ります。ウィンドウの名前やサイズなどの設定を、このインスタンスに入力していくイメージです。
geometry で初期ウィンドウのサイズを決められます。縦x横を文字列として入力します。真ん中の x は普通に小文字のアルファベットの x です。
tk.Label() で、ウィンドウ内に文章を挿入できます。foreground で色を設定できますが、matplotlib と同様に、名前で指定することもできます。

matplotlib.org

テキストボックス(検索欄など、自由に文字を入力できる枠)は、tk.Entry() で設定できます。引数を何も設定しなくても今回は動きましたが、より複雑になるとエラーを起こしそうなので、インスタンスを指定した方が良い気がします。insert でデフォルトの入力を用意することもできますが、無くても問題ありません。tk.END はデフォルトの文字をどう枠内に置くか、の設定で、とりあえずは”おまじない”で良いです。grid でパーツをウィンドウ内のどの部分に置くかを設定できます。場所の設定関数は、grid、place、pack の 3 種類がありますが、grid と pack を併用したらエラーが出たので、統一した方がいい気がします。row と column で大体の位置を指定できますが、より細かく位置設定をしたい場合は、他の方法を使う必要がありそうです。

ボタンも tk.Button() を使って、テキストボックスと似た感じで設定できます。大切なのは command という引数で、ボタンを押すと、指定したオブジェクトを実行してくれます。今回は、新しい画面を出力する自作関数を指定しています。

結果のウィンドウは、メインのウィンドウとは別に用意します。tk.Toplevel(root) で新しいインスタンスを作ります。親のインスタンスを指定することで、対応関係を作ります。結果のウィンドウについても、メインと同様に要素を追加していきます。分子量は float 型として出てくるので、str 型に変換して、文字列として出力できるようにします。

構造式の表示は、mol オブジェクトを MolToImage(mol) で png に変換し、ImageTk.PhotoImage(png) で GUI で表示できるオブジェクトに変換します。
GUI 側は、tk.Canvas() で画像を表示できる領域を用意します。bg は background のことで、画像を貼り付ける領域の色です。プリントなどを貼り付けるボードの色、というのがイメージしやすいでしょうか。anchor で画像の位置を指定できますが、任意の位置に来るようにするには、canvas のパラメータも関連するため、少しコツが必要です。

mainloop() で GUI を表示することができます。逆に、Jupyter Notebook で変数などを確認したい場合は、mainloop() を除くとやりやすいです。mainloop() は各ウィンドウに対して用意します。

上記のコードを Jupyter Notebook で実行して出てきた画面は以下の通りです。結果は default のエタノールではなく、フェノールにしてあります。

f:id:imedchem:20210117170338p:plain
GUI の実行結果

分子量(青字)と構造式が結果の画面に示されています。ウィンドウの左上には、ちゃんと拡大や終了ボタンもあります。
赤いボタンを押してメインのウィンドウを閉じれば、このコードも終了します。逆に、閉じないと、Jupyter Notebook ではこのコードのあるセルを実行し続けているので、他のことはできなくなってしまいます。

今回はとりあえず動くものを作ってみましたが、工夫次第で、多機能かつデザイン性にも優れた GUI を作ることもできそうです。
少しシステムデザインよりの話ですが、この辺りをやるケモインフォマティクスの人が増えてもいいのかな、とも思います。