D2MatE Top
this page
tkCGIApplicaitonの使い方
CGIスクリプト: atom_info.py
- Applicationインスタンス作成 (Flask互換)
app = tkCGIApplication(__name__, static_folder = '.', template_folder = 'templates')
- appの設定
app.configure(
security_level = 1, # HTMLにscript, CSSを埋め込むことを許可する
log_path = data_dir + f"log/{app.script_filebody}_cgi.log",
# logのパス。HTMLツリー外に作成
)
- API endpointの設定 (API requestを使う場合)
script_url = app.script_name # app.script_name にはスクリプトのURLのサーバより下の部分が入っている
- 表示 route関数の例
@app.route('/version')
def show_version():
app.init_html() #
出力をする関数では、最初に init_html()を実行する。引数が無い場合は
HTMLを出力する
app.print(f"<H1>{program_name} Ver. {version}</H1>")
app.end_html() # tkCGIApplicaiton.run()でもend_html()を実行するので、無くてもよい
- 表示 route関数の例
@app.route('/')
@app.logging
def index():
app.init_html(target = "html") #
出力フォーマットを明示的に"html"指定
--cut--
template_path = os.path.join(app.templates_dir, "index.html")
# index.htmlのtemplateパス
template_str = read_file(template_path)
# template読み込み
params = { # templateの置換辞書型変数
'script_url': script_url,
'next_action': '/atom_info',
'atom_name' : 'Na',
}
html = app.render_template(template_str, params) #
template処理。Jinja2書式。Flask互換
# app.print_original(html) # HTML文字列あるいはtkHTMLElement.outer_htmlを出力する
return html
# あるいは、HTML文字列あるいはtkHTMLElement.outer_htmlをreturnする。
# この場合、tkCGIApplication.run()でwrite()される
- API route関数の例
@app.route('/atom_info')
def atom_info():
app.init_html(target = "json", charset = "utf-8") # JSONを返す場合
--cut--
atom = tkAtomType()
inf = atom.get_atom_information(atom_name)
json_string = json.dumps(inf) # 辞書型変数をJSON文字列に変換。utf-8もascci化する
# json_string = json.dumps(inf, ensure_ascii=False) #
utf-8文字列をそのまま埋め込む。推奨しない
print(json_string) # JSON文字列をprint()すると、クライアントへの応答になる
# return json_string # あるいは、JSON文字列をreturnしてもいい。この場合、tkCGIApplication.run()でwrite()される
- 実行エントリポイント
def main():
app.run(redirect = True)
if __name__ == '__main__':
main()
Templateの例
- Jinja2書式
- app.render_template()では、<body>だけを抽出して置換処理を行う。
<head>にCSSやscriptを入れても削除されるので、<body>に入れる
- HTML標準の<form>要素とJavascriptは相性が悪いので、
<form>はFormData()で変数を一括取得するために使うと割り切ったほうがいい
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>原子情報取得アプリ</title>
</head>
<body>
<script>
const script_url = "{{ script_url }}";
const next_action = "{{ next_action }}";
async function fetchAtomInfo(event) {
event.preventDefault(); // フォームのデフォルト送信を防ぐ
const formData = new FormData(event.target);
const params = new URLSearchParams(formData);
params.append("action", `${next_action}`);
try {
const response = await fetch(script_url, {
method: 'POST',
body: params
});
if (response.ok) {
const jsonData = await response.json();
let formattedData = "";
for (const [key, value] of Object.entries(jsonData)) {
formattedData += `${key}: ${value}\n`;
}
document.getElementById('response').value = formattedData;
} else {
document.getElementById('response').value = "エラー: サーバーからの応答が正しくありません";
}
} catch (error) {
document.getElementById('response').value = `エラー: ${error.message}`;
}
}
</script>
<h1>原子情報取得アプリ atom_info.py</h1>
<form onsubmit="fetchAtomInfo(event)">
<label for="atom_name">atom name:</label>
<input type="text" id="atom_name" name="atom_name"
placeholder="input atom name/number"
value="Na" required size="20">
<br><br>
<button type="submit">Show atom info</button>
</form>
<br>
<label for="response">Response:</label><br>
<textarea id="response" rows="20" cols="80" readonly></textarea>
</body>
</html>