このブログは、はんなりPython Advent Calendar 2020 - Qiita15日目の記事です。
TkSugarというのは、わたしがつくったモジュールで、YAML形式のファイルを読み込むと、それに準じたTkのウィンドウを生成する というモジュールです。
コントリビューションいただけると非常に助かります。
昨今のOSSの流行りに則り(?)コミットログやコメントは全文英語でやっています(Issueはさすがに無理なので英語)*1。
サンプルはリポジトリのsamplesフォルダに格納されています。tkinterを直接いじるのは結構コードが長くなり、なかなか骨が折れるので、一つの解決策として一つどうでしょうか。
はんなりPython方面では、10月のLT会でお話ししました。動画はSpeakerDeckの詳細ページから。
なんでこんなものを作ったのか
最近はほぼほぼWebアプリ全盛になったし、検証アプリを作るならJupyter NotebookやCUIで事足りるしで、デスクトップで動くようなアプリを作る機会は本当に少なくなった と思います。
ただ、それでもGUIを作らなくちゃいけない という機会は少なからずあり、デスクトップ向けのGUIモジュールを使う機会 というのはやはりある と思っています。
わたしはまさにその少なからずあるGUI要件を扱っており、フリーランスとしての受託開発事業でも、デスクトップアプリを作っています*2。
同種のGUIモジュールはたくさんあるが…。
とはいえこういうGUIのモジュールって、結構たくさんあります。
- Python標準のtkinter
- クロスプラットフォームをウリにしているKivyやPyQt
- HTMLベースのPyWebView
個人的には今までPyWebViewを使っていて、そんなに不満はなかったのですが、開発を進めていくにあたりHTMLは
- 自分で実装したくない機能まで実装してしまう(ファイルのドラッグドロップなど)。
- HTMLなだけあってできることが非常に多く、反面常にセキュリティ上のリスクがつきまとう。
- JavaScriptやCSS(Less)の理解が必須であり、規模が膨らむほどやることが増えてツラい。
- Pythonとの連携をJS APIというかたちで実装することになり、設計力が要求される。
- PyWebView自体がPython 3.8以上に対応していない*3。
ということで、最新のPythonに常に対応していて拡張が割と容易なGUIモジュールがないかと思い、探していました。
標準モジュールの強さ
じゃあKivyやPyQtを使えば良いじゃない と思いきや、サンプルを見てみると結構コードが複雑で、それなりに使うのに独自の知識が必要になります。
また、拡張ができるのかどうか(独自コントロールが作れるのか)などの情報がイマイチ分からず、今後の活用に不安が残る。
そういう点では、なんだかんだ絶対に最新のPythonバージョンで使えることが約束されており(標準モジュールですから・・・)、依存関係を不要に増やさず、拡張性にも優れる、tkinterに敵うものはないな というのが結局分かったことです。
ただtkinterって、使いづらくない?
ただtkinterは、1度これを使ってプロダクトを作って納品してみたのですが、結構GUIを作るのが大変。
DelphiやC#などを使ったことがある方はそれをイメージしてもらえればと思いますが、それら言語でデザイナーが自動生成するのに相当するコードを全部自力で書く必要があり、凝ったUIを作るのはなかなかにツラいものがあります。
その割にほぼ定型的なコードが多く、これはもっと最適化できないか と思ったもの。それで作ったのが今回のTkSugarということです。
TkSugarでやっていること
TkSugarでやっていることは、ほとんどリフレクションを使ったクラスの自動生成と自動設定の組み合わせです*4。
tkinterに依存した処理はあまり多くないため、テストコードの中にはテストデータがTkウィジェットと関係ないただのクラス なんてものもあったりします*5。
またこのおかげで、ttkなどの追加モジュールや、tkcalendarなどの外部モジュール、自作モジュールなどにもガッツリ対応可能です*6。
もちろんたまにうまく動かないクラスもあったりするのですが・・・。それについては、同じ名前のクラスを作って差し替えるようにするなどして対応しています*7。
ジェネレータでありGUIモジュールそのものではない
基本的にはTkSugarはジェネレータであり、GUIモジュールではありません。
これは、今後いつまでも開発を続けなくても、そこそこに使えるモジュールであることを維持する狙いがあります。
こうしておけば、わたしがtkinterを使わなくなったり、メンテナンスが滞ったりしてもとりあえず使えるし、それによるセキュリティリスクも最小限で済みますしね。
ローカライズ支援機能も実装
ローカライズ支援として、辞書となるYAMLファイルを指定しておくと、GUI定義ファイル内のキーワードを置換して出力する なんて機能も実装(0.1.3以降。0.1.2から機能はありますがバグがあります)。結構使えるんじゃないかな と思います。
ここで得た学びなど
このコードを通して、Pythonのリフレクションの強さを学びました。
Pythonのリフレクションはinspect
モジュールやgetattr
などのメソッドを使うのですが、これらのメソッドを使えばメソッドにどんな引数があるか、どんなドキュメントコメントが設定されているか、クラスにはどんなメソッドがあるか、その名前はメソッドかプロパティかなどの情報を容易に取得することができます。
tkinter自体はとにかく情報源が少なく、公式ドキュメントも一見しっかりしているようでいて個々のウィジェットについての解説がほとんどないなどなかなか扱いづらいものですが、ちゃんとまとめている方も多く、頑張れば結構使えるんじゃないかな と。
いちおうtkinterについては調べた情報をZennにまとめてみています*8ので、興味のある方はご覧ください。
*1:Visual Studio CodeのComment Translateという拡張を使うとコメントは日本語で読むこともできるので、英語でもそれほどには困りません。多少は英語を読む機会が増えるため、なんとなく文意は拾えるようになりますし
*2:会社の規模が小さすぎて、サーバを置いてなんとか みたいなことにはならない模様
*3:使ってみればとりあえず動くか と思いきや、3.8以降を使うとエラーでPyInstallerによるパッケージの作成に失敗する(恐らくフックDLLまわり)
*4:Generator.py _instantiate()メソッドあたり
*5:test_generator_methods.pyの多くのテストコードなど
*6:作例はcustomwidget.pyあたり。このReferBoxについては今後どこかでモジュール公開しようかな
*8:個人的にはStack Overflowに「どうやってそれを見つけたの?」『情報は見つからなかったけどTclでTkは使ってたから分かるよ』みたいなことがしれっと書かれていたのには なんというかやばみを感じました(Pythonでは情報は得られないのか…)