質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Q&A

解決済

2回答

12730閲覧

Python3 Tkinter 画像の拡大縮小について

person

総合スコア223

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

0グッド

0クリップ

投稿2020/04/14 23:33

編集2020/04/16 04:29

画像の拡大縮小を行いたいです。

調べたところ canvas.scale というのを使えば実現できそうなのですが、実際に画像に反映されません。

自分的にはcanvas.scaleに設定するIDが間違っていると思うのですが、実際のところ何が間違っているのでしょうか?

回答よろしくお願いします。

該当ソース

Python

1from decimal import Decimal 2import tkinter as tk 3 4class App: 5 def __init__(self, win): 6 self.win = win 7 self.win.geometry("500x500") 8 self.img_disp_flag = False 9 10 def view_create(self): 11 self.win.rowconfigure(0, weight=1) 12 self.win.columnconfigure(0, weight=1) 13 14 self.frame_viewer = tk.Frame(win) 15 self.frame_viewer.grid(row=0, column=0, sticky="nsew") 16 self.frame_button = tk.Frame(win) 17 self.frame_button.grid(row=1, column=0, sticky="nsew") 18 19 self.frame_viewer.rowconfigure(0, weight=1) 20 self.frame_viewer.columnconfigure(0, weight=1) 21 22 self.canvas = tk.Canvas(self.frame_viewer) 23 self.canvas.grid(row=0, column=0, sticky="nsew") 24 25 self.ysb = tk.Scrollbar(self.frame_viewer, width=20, orient="vertical", command=self.canvas.yview) 26 self.canvas.configure(yscrollcommand=self.ysb.set) 27 self.ysb.grid(row=0, column=1, sticky="ns") 28 29 self.xsb = tk.Scrollbar(self.frame_viewer, width=20, orient="horizontal", command=self.canvas.xview) 30 self.canvas.configure(xscrollcommand=self.xsb.set) 31 self.xsb.grid(row=1, column=0, sticky="ew") 32 33 self.scale_up_button = tk.Button(self.frame_button, text="+", width=2) 34 self.scale_up_button.grid(row=0, column=0, padx=5, pady=5) 35 36 self.scale_down_button = tk.Button(self.frame_button, text="-", width=2) 37 self.scale_down_button.grid(row=0, column=1, padx=5, pady=5) 38 39 self.scale_default_button = tk.Button(self.frame_button, text="±0", width=2) 40 self.scale_default_button.grid(row=0, column=2, padx=5, pady=5) 41 42 self.img_disp_button = tk.Button(self.frame_button, text="img_disp") 43 self.img_disp_button.grid(row=0, column=3, padx=5, pady=5) 44 45 self.img_hide_button = tk.Button(self.frame_button, text="img_hide") 46 self.img_hide_button.grid(row=0, column=4, padx=5, pady=5) 47 48 self.scale = Decimal("1.0") 49 self.canvas.scale("all", 0, 0, self.scale, self.scale) 50 51 # バインド 52 def bind_event(self): 53 self.canvas.bind("<MouseWheel>", lambda e:self.wheel_rotated(e, app)) 54 self.scale_up_button.bind("<ButtonRelease>", lambda e:self.zoom_up(e, app)) 55 self.scale_down_button.bind("<ButtonRelease>", lambda e:self.zoom_down(e, app)) 56 self.scale_default_button.bind("<ButtonRelease>", lambda e:self.zoom_default(e, app)) 57 self.img_disp_button.bind("<ButtonRelease>", lambda e:self.img_disp(e, app)) 58 self.img_hide_button.bind("<ButtonRelease>", lambda e:self.img_hide(e, app)) 59 60 # 画像上でマウスホイールを動かしたとき 61 def wheel_rotated(self, e, app): 62 # 垂直スクロールバーのみマウスホイールに対応(水平には対応できないか・・・) 63 # https://stackoverflow.com/questions/17355902/python-tkinter-binding-mousewheel-to-scrollbar 64 self.canvas.yview_scroll(int(-1*(e.delta/120)), "units") 65 66 # 画像の拡大 67 def zoom_up(self, e, app): 68 self.scale += Decimal("0.1") 69 self.canvas.scale("current", 0, 0, self.scale, self.scale) 70 print(self.scale) 71 72 # 画像の縮小 73 def zoom_down(self, e, app): 74 self.scale -= Decimal("0.1") 75 self.canvas.scale("current", 0, 0, self.scale, self.scale) 76 print(self.scale) 77 78 # 倍率をデフォルトに戻す 79 def zoom_default(self, e, app): 80 self.scale = Decimal("1.0") 81 self.canvas.scale("current", 0, 0, self.scale, self.scale) 82 print(self.scale) 83 84 """ 85 canvas.scale(ID, x0, y0, rx, ry) 86 ID オブジェクトのID 87 x0 基準となるx座標 88 y0 基準となるy座標 89 rx x座標の拡大率 90 ry y座標の拡大率 91 """ 92 93 # 画像の表示 94 def img_disp(self, e, app): 95 if self.img_disp_flag == False: 96 self.imgfile = "c:\Users\ユーザ名\Desktop\scale_test\test.png" 97 self.img = tk.PhotoImage(file=self.imgfile) 98 self.disp_image = self.canvas.create_image(0, 0, image=self.img, anchor=tk.NW) 99 self.canvas.config(scrollregion=app.canvas.bbox("all")) 100 self.img_disp_flag = True 101 else: 102 print("Already image displayed!") 103 104 # 画像の非表示 105 def img_hide(self, e, app): 106 if self.img_disp_flag == True: 107 self.canvas.delete(self.disp_image) 108 self.img_disp_flag = False 109 else: 110 print("Already image hidden!") 111 112 113if __name__ == "__main__": 114 win = tk.Tk() 115 app = App(win) 116 app.view_create() 117 app.bind_event() 118 win.mainloop()

追記(subsample()による縮小、scaleが0以下だと拡大が意図した動作とは違う動作になる)

def zoom_up(self, e, app): if self.img_disp_flag == True: self.canvas.delete(self.disp_image) self.scale -= 1 self.img_zoom_up = self.img.subsample(self.scale, self.scale) #self.img_zoom_up = self.img.zoom(x=self.scale, y=self.scale) self.disp_image = self.canvas.create_image(0, 0, image=self.img_zoom_up, anchor=tk.NW) self.img_x_zoom_up = self.img_zoom_up.width() self.img_y_zoom_up = self.img_zoom_up.height() print("size:", self.img_x_zoom_up, self.img_y_zoom_up) print("scale:", self.scale) else: print("Not displaying an image yet!") def zoom_down(self, e, app): if self.img_disp_flag == True: self.canvas.delete(self.disp_image) self.scale += 1 self.img_zoom_down = self.img.subsample(self.scale, self.scale) self.disp_image = self.canvas.create_image(0, 0, image=self.img_zoom_down, anchor=tk.NW) self.img_x_zoom_down = self.img_zoom_down.width() self.img_y_zoom_down = self.img_zoom_down.height() print("size:", self.img_x_zoom_down, self.img_y_zoom_down) print("scale:", self.scale) else: print("Not displaying an image yet!") def zoom_default(self, e, app): if self.img_disp_flag == True: self.canvas.delete(self.disp_image) self.scale = 1 self.img_zoom_default = self.img.subsample(self.scale, self.scale) self.disp_image = self.canvas.create_image(0, 0, image=self.img_zoom_default, anchor=tk.NW) print("size:", self.img_x, self.img_y) print("scale:", self.scale) else: print("Not displaying an image yet!")

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

PIL(Pillow)を使った拡大縮小のサンプルです。

zoom/subsample では決まった倍率にしか変更できなかったので、
用途に合わなかったかもしれません。整数値しか指定できませんでした。

イメージ説明

表示位置は左上に固定してますが、create_image では
指定した座標が画像の中心に来るようになってるので、
必要に応じて、適宜調整してください。

python

1import tkinter as tk 2from tkinter import ttk 3from PIL import Image, ImageTk 4 5 6class PILImage: 7 def __init__(self, filepath, canvas, pos): 8 self.pos = pos 9 self.filepath = filepath 10 self.canvas = canvas 11 self.src_image = Image.open(filepath) 12 self.image = None 13 self.imageId = None 14 self.scale = 1.0 15 self.update() 16 17 def update(self): 18 from math import floor 19 20 # ローカル変数宣言 (self.が大量にあり読みにくい場合) 21 imageId = self.imageId 22 src_image = self.src_image 23 canvas = self.canvas 24 scale = self.scale 25 x, y = self.pos 26 w, h = self.src_image.size 27 28 # 以前の画像を消去 29 if imageId: 30 canvas.delete(imageId) 31 32 # スケール変更後のサイズを計算 33 w2, h2 = floor(w*scale), floor(h*scale) 34 35 # リサイズ 36 img = src_image.resize((w2, h2)) 37 38 # 表示位置調整(中心 → 左上の座標) 39 x2, y2 = x+w2//2, y+h2//2 40 41 # 描画更新 42 self.image = image = ImageTk.PhotoImage(img) 43 self.imageId = canvas.create_image(x2, y2, image=image) 44 45 def setScale(self, scale): 46 self.scale = scale 47 self.update() 48 49 50def main(): 51 root = tk.Tk() 52 root.title("PIL resize demo") 53 root.geometry("600x600+100+100") 54 55 frame = ttk.Frame(root) 56 frame.pack(fill=tk.BOTH, expand=1) 57 58 canvas = tk.Canvas(frame) 59 canvas.pack(fill=tk.BOTH, expand=1) 60 61 image = PILImage("test.png", canvas, pos=(0, 0)) 62 63 scale = 1.0 64 def zoomIn(): 65 nonlocal scale 66 scale += 0.1 67 image.setScale(scale) 68 69 def zoomOut(): 70 nonlocal scale 71 scale -= 0.1 72 image.setScale(scale) 73 74 ttk.Button(frame, text="+", command=zoomIn).pack(fill=tk.BOTH, expand=0) 75 ttk.Button(frame, text="-", command=zoomOut).pack(fill=tk.BOTH, expand=0) 76 77 78 root.mainloop() 79 80if __name__ == '__main__': 81 main()

投稿2020/04/16 10:40

teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

IDを修正するなら、self.img_disp もしくは "all"ですが、
テキストのフォントサイズやラスター画像の中身には影響しないので、
scaleでは、期待する動作にはならないです。

PhotoImage の zeem(), subsample() 辺りを使っての画像のリサイズが必要です。

画面全体の表示位置等の割合を保ったまま拡大縮小を行う場合に、
画像のリサイズと合わせてスケールも呼び出します。

投稿2020/04/15 06:13

teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

person

2020/04/16 04:36

質問に追記しましたが、subsample()で縮小はできました、 しかし、scaleが0以下のときはsubsample()での拡大が意図した動作になりません。(画像が垂直方向に反転し、小さくなっていきます。) 一応zoom()を試したところ拡大できましたが、subsample()と比べscaleの増減が反対になってしまいます。 (subsample()はscaleを大きくすると縮小、zoom()はscaleを大きくすると拡大) そうなるとscaleの増減にif文などを付けないと意図した動作になりませんが、他に簡単な方法はありますか?
teamikl

2020/04/16 05:29

追記したところ以外に変更点はありますか? 画像のパスと、追記分を反映させて実行してみたところ、 こちらでは +- を押したときに TclError: expected interger but got "0.0" となり、確認できません。 >scaleが0以下 値の単位齟齬がありそうです。例えば subsample に 2 を指定すると 2ピクセル毎の画素から1ピクセル取り出したイメージを返すので、1/2 割合に換算すると 50%です。zoomはその逆。 ドキュメントより - zoom の取る値は0より大きい値 - subsample は負の値を指定した時には反転します Negative values will cause the image to be flipped about the Y or X axes https://www.tcl.tk/man/tcl8.4/TkCmd/photo.htm#M22 他の方法、PIL(Pillow)で resize でしょうか。 扱える画像フォーマットが増える他、 拡大縮小のアルゴリズムも選べるので画質向上も期待できます。
person

2020/04/16 05:33

追記したところ以外の変更点 view_create()内の self.scale = Decimal("1.0") self.canvas.scale("all", 0, 0, self.scale, self.scale) を削除しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問