4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

tkinter で画像処理系の GUI を作るメモ

Last updated at Posted at 2022-01-09

画像系の処理の 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 も扱えない)

Screenshot from 2022-01-08 22-42-12.png

Windows では OK.

Screenshot from 2022-01-08 22-46-23.png

conda では tcl ライブラリ関連が freetype 有効でビルドされないようです.

とりあえずは stackoverflow にあるように .so コピーで対応でしょうか...
(LD_LIBRARY_PATH あたりで特定の .so だけ検索順序を変えるとかできるかな?)

Ubuntu system の tcl/tk 使えばいろいろ解決します!

Screenshot from 2022-01-09 22-30-08.png

Livereload?

juriggedreloading である程度 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= でアサインが必要.

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 がうまく取得できないので注意です!

レイアウト

  1. 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 時点ではこのバージョン以上になっています).

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?