2022/04/02(土) [n年前の日記]
#1 [python] tkinterのScrollbarについて勉強
Python + tkinter で、以下のようなことをしたい。
動作確認環境は以下。
以下のページを参考にして実験してみた。
_Tkinterの使い方: スクロールバー(Scrollbar)の使い方 | だえうホームページ
_python - Tkinter button expand using grid - Stack Overflow
_python tkinter キャンバスのスクロールバーについて
たぶん、できた、ような気がする。こんな感じになった。
_02_scrollbar_canvas.py
動作に必要なpng画像は以下。
_tex_1024.png
py 02_scrollbar_canvas.py と打って実行。こうなった。
スクロールバーを動かすとキャンバス内の画像の表示位置も変わっているし、ウインドウサイズを変更するとキャンバスのサイズも追従して変化している。更に、キャンバス内をマウスの左ボタンでドラッグしてスクロールができるようにもなった。これで目的は果たせそう。
- キャンバス( Canvas() ) にスクロールバー( .Scrollbar() )をつけたい。
- ウインドウサイズを変更したらキャンバスのサイズも追従して変わるようにしたい。
動作確認環境は以下。
- Windows10 x64 21H2 + Python 2.7.18 32bit + Tkinter + Pillow 6.2.2
- Windows10 x64 21H2 + Python 3.9.12 64bit + tkinter + Pillow 9.0.1
以下のページを参考にして実験してみた。
_Tkinterの使い方: スクロールバー(Scrollbar)の使い方 | だえうホームページ
_python - Tkinter button expand using grid - Stack Overflow
_python tkinter キャンバスのスクロールバーについて
たぶん、できた、ような気がする。こんな感じになった。
_02_scrollbar_canvas.py
try:
# Python 2.7
import Tkinter as tk
# import ttk
except Exception:
# Python 3.x
import tkinter as tk
# from tkinter import ttk
from PIL import Image
from PIL import ImageTk
imgfile = "tex_1024.png"
app = tk.Tk()
frame = tk.Frame(app, bg="green")
frame.pack(expand=1, fill=tk.BOTH)
frame.columnconfigure(0, weight=1)
frame.rowconfigure(0, weight=1)
# create Canvas
canvas = tk.Canvas(frame, bg="#888888")
canvas.grid(row=0, column=0, sticky=tk.N + tk.S + tk.W + tk.E)
# create Scrollbar
xbar = tk.Scrollbar(frame, orient=tk.HORIZONTAL, command=canvas.xview)
ybar = tk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview)
xbar.grid(row=1, column=0, sticky=tk.W + tk.E)
ybar.grid(row=0, column=1, sticky=tk.N + tk.S)
canvas.config(xscrollcommand=xbar.set)
canvas.config(yscrollcommand=ybar.set)
# load png image, set to canvas
im = Image.open(imgfile)
photo_image = ImageTk.PhotoImage(im)
canvas.create_image(16, 16, anchor=tk.NW, image=photo_image)
# set scroll region
iw = photo_image.width()
ih = photo_image.height()
region = (0, 0, iw + 32, ih + 32)
canvas.config(scrollregion=region)
# set drag scroll
canvas.bind("<ButtonPress-1>", lambda e: canvas.scan_mark(e.x, e.y))
canvas.bind("<B1-Motion>", lambda e: canvas.scan_dragto(e.x, e.y, gain=1))
app.mainloop()
動作に必要なpng画像は以下。
_tex_1024.png
py 02_scrollbar_canvas.py と打って実行。こうなった。
スクロールバーを動かすとキャンバス内の画像の表示位置も変わっているし、ウインドウサイズを変更するとキャンバスのサイズも追従して変化している。更に、キャンバス内をマウスの左ボタンでドラッグしてスクロールができるようにもなった。これで目的は果たせそう。
◎ 少し解説。 :
ウインドウサイズを変更した際に、.grid() でレイアウトしたウィジェットのサイズを追従させて変化させるには、weight= を使う。
上記の指定の場合、.grid() で配置した (0,0) のウィジェットのサイズが、親に追従するようになる。
ちなみに、.pack() でレイアウトしたウィジェットのサイズを親に追従させたい場合は、expand=1, fill=tk.BOTH を指定することになる。
マウスボタン押しのドラッグでキャンバス内をPANさせたい場合は、以下のような記述でいいらしい。
余談。tkinter のキャンバスに画像を表示したい場合、Pillow (PIL) を使って画像を読み込んで、ImageTk.PhotoImage() で tkinter が利用できる状態に変換するのがヨサゲ。と言うのも、Python 2.7.18 32bit + tkinter (Tkinter) の機能で png画像を読み込もうとしたらエラーになってしまったので…。Pillow を経由すれば、どんな画像フォーマットも読み込んで表示できるようになるはず。
frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1)
上記の指定の場合、.grid() で配置した (0,0) のウィジェットのサイズが、親に追従するようになる。
ちなみに、.pack() でレイアウトしたウィジェットのサイズを親に追従させたい場合は、expand=1, fill=tk.BOTH を指定することになる。
frame.pack(expand=1, fill=tk.BOTH)
マウスボタン押しのドラッグでキャンバス内をPANさせたい場合は、以下のような記述でいいらしい。
canvas.bind("<ButtonPress-1>", lambda e: canvas.scan_mark(e.x, e.y))
canvas.bind("<B1-Motion>", lambda e: canvas.scan_dragto(e.x, e.y, gain=1))
余談。tkinter のキャンバスに画像を表示したい場合、Pillow (PIL) を使って画像を読み込んで、ImageTk.PhotoImage() で tkinter が利用できる状態に変換するのがヨサゲ。と言うのも、Python 2.7.18 32bit + tkinter (Tkinter) の機能で png画像を読み込もうとしたらエラーになってしまったので…。Pillow を経由すれば、どんな画像フォーマットも読み込んで表示できるようになるはず。
im = Image.open(imgfile) photo_image = ImageTk.PhotoImage(im) canvas.create_image(16, 16, anchor=tk.NW, image=photo_image)
[ ツッコむ ]
以上、1 日分です。