画像系の処理の GUI を作りたい(e.g. 機械学習のためのアノテーションや処理結果の表示など)
Qt だとインストールとかバインディングとかめんどいのよね...
(tkinter だと Python にデフォルトで入っている)
あと Qt だと, OSS 版だと LGPL なのでライセンスがちょっと微妙(tkinter だと Python ライセンスで, Tck/Tk 自体も BSD-style license: https://www.tcl.tk/software/tcltk/license.html)
python 3.8 or later を想定します.
テーマ
デフォルトではやや古臭いので, ナウでヤングな見た目になる ttkbootstrap あたり使うのがいいでしょう.
きちんとメンテされていて安心して使えます!
ただ, pip で入るリリース版はやや古いかもしれません. 不都合あってもだいたい github の最新では治っている印象があります.
custom font, font antialias
Linux だと tcl/tk が freetype 有効でビルドされているかなどの状況次第になります.
Conda では antialias + external TTF フォント対応できない.
Ubuntu 20.04 + conda python 3.8 ではアンチエイジングされませんでした... (また, custom font も扱えない)
Windows では OK.
conda では tcl ライブラリ関連が freetype 有効でビルドされないようです.
とりあえずは stackoverflow にあるように .so コピーで対応でしょうか...
(LD_LIBRARY_PATH
あたりで特定の .so だけ検索順序を変えるとかできるかな?)
Ubuntu system の tcl/tk 使えばいろいろ解決します!
Livereload?
jurigged
と reloading
である程度 livereload できます.
root = tk.Tk()
b1 = ttk.Button(root, text="Button 2", bootstyle=SUCCESS)
b1.pack(side=LEFT, padx=15, pady=100)
b2 = ttk.Button(root, text="Button 1", bootstyle=(INFO, OUTLINE))
b2.pack(side=LEFT, padx=115, pady=10)
b3 = ttk.Button(root, text="Button 3", bootstyle=(INFO, OUTLINE))
b3.pack(side=LEFT, padx=115, pady=10)
@reloading
def fn():
root.update()
root.mainloop()
ただ, これだと padx などは反映できますが, text などは反映できません.
TTF font?
Windows 以外では, 直接カスタムフォントを読む方法はありません.
Linux の場合 tkextafont
があります.
ただ, ttf ファイルに fontFamily 情報がない場合エラーがでますので注意ください.
File "/home/syoyo/miniconda3/lib/python3.8/site-packages/tkextrafont/__init__.py", line 92, in __init__
root.tk.call("extrafont::load", file)
_tkinter.TclError: key "fontFamily" not known in dictionary
ESC で Window と閉じたい
新規 Window の title を設定する.
.title()
で設定する.
root = tkinter.Toplevel()
root.title("My title")
ttkbootstrap
Theme builder
python -m ttkcreator
フォントの大きさを変える.
全体のフォントサイズを変える
style.configure(".",font=("",14))
は ttkbootstrap では使えない.
ttkbootstrap では TButton など個別にフォント設定できるのはあるが, Treeview は個別フォント設定できない.
"TkDefaultFont" の設定を使っている
したがって ttkbootstrap で全体のフォントサイズを変えるなら
from tkinter import font
default_font = font.nametofont("TkDefaultFont")
default_font.configure(size=32)
とする.
widget ごとの場合,
style.configure
で新しくスタイルを作り,
widget に style=
でアサインが必要.
- treeview : https://memopy.hatenadiary.jp/entry/2017/06/04/093310
(ただしこれは ttkbootstrap では使えないかもしれません)
Label の文字列の変更
Label の文字列などは, configure でパラメータを変えることができます.
label = ttk.Label(text="Hello")
label.configure(text="muda")
treeview でセルや columnごとに色を変える
row ごとはできますが, セルごとや column ごとは簡単にはできません.
あたいで頑張ればいけるかもしれません.
ウィンドウをスクロールする.
ttkbootstrap では ScrolledFrame があります.
素の tkinter の場合は以下.
【Python/Tkinter】スクロール可能なFrameを作成するクラス
https://qiita.com/shinno1993/items/3ea14ffd7f17d8214961
ありがとうございます.
Widgets
他の記事や検索を参照ください. e.g.
PythonのTkinterを使ってみる
https://qiita.com/nnahito/items/ad1428a30738b3d93762
Tkinter ファイルを開くためのダイアログを表示する (tkinter.filedialog)
http://hanairosoft.skr.jp/programming/tkinter-open-file-dialog.html
Checkbutton
Checkbutton では, command
でチェック on/off されたときのコールバックを指定できます.
.bind
でクリックを拾うやりかただと, チェックの state がうまく取得できないので注意です!
レイアウト
- Widget を配置しよう
https://www.shido.info/py/tkinter2.html
【tkinter】grid()を使ったWidgetの配置の方法
https://www.pytry3g.com/entry/grid-widget
ありがとうございます.
ImGui みたいに, 「ここで Widget を改行させる/改行させない」, みたいなレイアウト指示はできません.
うまく整列させたい場合, grid を使うことになるでしょう. grid では内部で row, column 数の最大値を管理しているようですので, 「3x3 のグリッドを作ります」みたいな事前定義はしないです(のでちょっとややこしい)
また, grid を使う場合, 関連する複数 Frame では pack
ではなく grid
を使います(混在できない. 混在しているとエラーが出る)
よりフレキシブルな配置をしたい場合, 上記 URL にあるように place
を使うことになるでしょう.
grid レイアウト
セパレータやラベルなど
セパレータやラベル(内容が変わらない)であれば直接インスタンスに対して pack()
, grid()
でいけます.
ImGui っぽいレイアウト
定義順で上から下に並べていく感じのものです.
基本は side="top"
をすべての widget で指定すればいけるはず?
右詰めしたい場合は anchor="w"
とします.
ImGui::SameLine()
のように改行せず横に配置していきたいときは
でしょうか. ただ, この場合は一度 frame を作るのがよいかもしれません.
パディング, スペーシング
padx, pady である程度調整できます. スカラー値だと, 両方にパディングが入ります.
padx=(0, 100)
などと指定することで片方にパディングすることができます.
widget を作らずにスペース作りたい場合, 空の frame をつくることになるでしょうか.
他の widget も frame にすることがありちょっとめんどいです. 1x1
画像を入れるというのも手かもしれません.
Treeview に scrollbar をつける
縦スクロールバーの場合.
treeview = ...
treeview.pack(side="left", fill="both", expand="yes")
vscrollbar = ttk.Scrollbar(frame, orient="vertical", command=treeview.yview)
treeview.configure(yscrollcommand=vscrollbar.set)
view_vscrollbar.pack(side="right", fill="y")
treeview には fill="both"
と expand="yes"
を指定することで, treeview のウィジェットが scrollbar 抜いたぶん最大で配置されるようになります.
padx
などでスペースは調整して.
よりレイアウト細かく指定したいときは, grid
or place
を使ったほうがよいでしょう.
Treeview cell で text wrap する.
直接的な方法は無いようです.
テキストを固定文字で wrap するなりので対応します.
Treeview row の高さを変える
style を作って rowheight を指定します.
2D, 3D の plot
matplotlib と組み合わせるといいでしょうか.
matplotlib の pyplot もデフォルトでは tkinter を使うっぽい.
matplotlib を別ウィンドウで pop up で出す.
matplotlib でプロット画面を出したい場合,
matplotlib の backend が tkinter の場合, そのままではメインウィンドウ(GUI)の tkinter のコンテキストとまざってうまくうごきません.
を参考にして, 以下のような感じにします.
root = tk.Toplevel() # ttk.Toplevel() when you use ttkbootstrap
fig = plt.Figure()
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1.0)
ax = fig.add_subplot(111)
# test data
x = [1, 2, 3, 4, 5]
y = [0.3, 0.5, 0.8, 1.2, 5.3]
ax.plot(x,y)
canvas.draw() # pop-ups another window
FigureCanvasTkAgg.show()
は deprecated でエラーになります. draw
を使います.
図形の描画
canvas で行う.
tkinterの使い方:Canvasクラスで図形を描画する
https://daeudaeu.com/tkinter_canvas_draw/
画像の表示
tkinter.PhotoImage
自体では PNG など一部サポートとなる.
jpg を読む場合は PIL などと組み合わせる.
canvas 自体は create_image
で与えられた画像でよろしくリサイズはしてくれないので, 画像を表示したい場合 Canvas を作る時点で大きさの指定が必要になりますので注意ください.
PhotoImage で画像の大きさを取得する場合, width()
, height()
メソッドを使う.
Canvas では画像は中心基準で配置されるため, Canvas == 画像サイズにする場合, x, y は画像のサイズの半分を指定しないといけないので注意です!
また, Frame などをクラスで定義している場合, image のデータは保持しておかないと, 関数から抜けたときにガベコレで消されてしまい表示されないので注意
from PIL import Image, ImageTk
= Label(window, image=img).pack()
playsound("song.mp3",block=False)
label.dra
def MyFrame(ttk.Frame):
...
def build_widget(self):
pil_img = Image.open(open(image_filename, 'rb'))
self.img = ImageTk.PhotoImage(pil_img) # <-
canvas = ttk.Canvas(root, width=self.img.width(), height=self.img.height())
canvas.create_image(self.img.width//2, self.img.height//2, image=self.img)
# canvas も self.canvas なりで保存しておくのが望ましいかも
numpy 配列からの画像データ生成
Image.fromarray
でいけます!
Pixel 値の取得
たとえばマウスポインタ上の画像の色を取得したいときがあります.
tk.Canvas
自体には pixel 値を取得する方法はないようです.
画像の場合は, PhotoImage あたりと同じ大きさの Canvas を作り, PhotoImage のピクセル値を取得で対応になるでしょうか.
Canvas の大きさの変更
configure(width=..., height=...)
でいけます.
tkinter では, widget の設定を変えるときはだいたい configure()
でいけるようです.
画像の入れ替え
create_image で id が返りますので, これを使って itemconfigure()
で入れ替えをします.
(canvas.configure(image=...)
だと -image
が無いエラーになる(canvas は複数の描画要素をもつため, image
だけでは判断できない(前のバージョンだとできるかも?))
image_id = canvas.create_image(..., image=...)
...
canvas.itemconfigure(image_id, image=new_img)
他の canvas 要素(create_rectangle
など)も同様です.
OpenGL
pip で入る pyopengltk がいいでしょうか.
ただ pyopengltk が依存する PyOpenGL は Windows だとビルドが走るため, MSVC などインストールしておく必要があります.
MSVC インストールとかめんどい場合は prebuilt を使うか,
conda で環境を作っている場合は conda install pyopengl
で conda ビルドの PyOpenGL をインストールしておくとよいでしょう.
DLL からシンボルを直接読み込むという手もあります.
GUI コードの live reload
やや複雑な GUI だったりレイアウト調整とか, スクリプト書き直して再実行... ではめんどいですよね.
exec()
あたりで動的実行するといいかもです!
Table で cell edit できるようにする.
クリックしたら Entry widget を動的に作って対応します.
ただ, ttkbootstrap 使っているとレイアウトが崩れますので微調整で対応しましょう(bbox の値とかにオフセットを与えたりなど)
undo/redo 機能
自前でがんばります!
Python tkinter で undo/redo をやりたいメモ
https://qiita.com/syoyo/items/f1191ebdac2031d67537
アプリ化
pyinstaller でいけます.
ただ, ttkbootstrap を含める場合, ttkbootstrap v1.7.0 以前では, Windows で空白があると起動できない issue があります.
v1.7.1 以上を使いましょう(2022/03/01 時点ではこのバージョン以上になっています).