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

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

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

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

2447閲覧

画像の拡大プレビューを表示したい

shu-san

総合スコア5

Tkinter

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/08/04 00:22

前提・実現したいこと

下記イメージはZOZOTOWNの画像拡大プレビューの様子
画像左にある原画像内にマウスで移動させると、マウス周辺の薄い白枠部分が拡大画像として右側に表示される。
また、マウスの動きに追従してプレビュー画像も変化し、マウスが原画像上から外れると拡大画像は消える。

イメージ説明

作りたいもの

イメージの様に、画像を読み込んで、

  • 画像内の座標にマウスがある場合、隣にマウス位置周辺の拡大画像を2つ(カラーとモノクロ)表示させる
  • 画像内でマウスの座標が変化すると、拡大画像も動的に変化する。
  • 画像内の座標からマウスが抜けると、拡大画像が消える

ようなシステムを作りたいと考えています。

単純に画像の拡大画像を隣に表示させることはできたのですが、
マウス座標との関連付けや、動的な変化のコードの作成方法がイメージできず
アルゴリズムや、参考コードなどわかる方がいらっしゃればアドバイス・ご教示をぜひお願いします。

↓今のところできている部分
イメージ説明

試したこと(今のところできている部分のコード)

Python3

1import tkinter 2from PIL import Image, ImageTk 3from tkinter import font 4 5# windowを描画 6window = tkinter.Tk() 7# windowサイズを変更 8window.geometry("1500x700") 9# windowタイトルを設定 10window.title("Image Viewer") 11# 画像を表示するための準備 12img = Image.open('任意画像') 13img = img.resize((608, 456)) 14img2 = img.convert('L') 15im_crop = img.crop((100, 50, 200, 150)) 16im_crop = im_crop.resize((200, 200)) 17im_crop2 = img2.crop((100, 50, 200, 150)) 18im_crop2 = im_crop2.resize((200, 200)) 19 20img = ImageTk.PhotoImage(img) 21img2 = ImageTk.PhotoImage(im_crop) 22img3 = ImageTk.PhotoImage(im_crop2) 23 24# 画像を表示するためのキャンバス作成 25canvas = tkinter.Canvas( bg = "gray", width=1500, height=700) 26canvas.place(x=50, y=25) 27 28# キャンバスに画像と文字を表示 29canvas.pack() 30canvas.create_image(50, 100, image=img, anchor=tkinter.NW) 31canvas.create_image(758, 100, image=img2, anchor=tkinter.NW) 32canvas.create_image(1058, 100, image=img3, anchor=tkinter.NW) 33 34font = font.Font(family='Arial', size=16, weight='bold') 35image_title = tkinter.Label(text='Original Image', bg = "gray", font=font) 36canvas.create_text(50, 60, text='Original Image', anchor=tkinter.NW, font=font) 37image_title2 = tkinter.Label(text='Enlarged Image', bg = "gray", font=font) 38canvas.create_text(758, 60, text='Enlarged Image', anchor=tkinter.NW, font=font) 39image_title3 = tkinter.Label(text='Enlarged Image (monochrome)', bg = "gray", font=font) 40canvas.create_text(1058, 60, text='Enlarged Image (monochrome)', anchor=tkinter.NW, font=font) 41 42window.mainloop()

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

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

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

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

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

guest

回答2

0

ポイントは1つのcanvasに複数の画像を配置するのではなく
canvasをそれぞれに分ける事で、各キャンバス毎に変化させる事ができる様になります。

他にもっと適切なコーディングの仕方があるかもしれませんが、ご参考にでもしてください。

python

1import tkinter as tk 2from PIL import Image, ImageTk 3from tkinter import font 4 5#各フレームの縦横のサイズを決めておく 6 7_squarelength = 300 8_framelength = 100 9 10class MainApplication(tk.Frame): 11 def __init__(self, master): 12 super().__init__(master) 13 self.master = master 14 self.master.geometry('1500x700') 15 self.master.title("Image Viewer") 16 17 self.image = Image.open('IMG_20200630 164118.jpg') 18 self.resize_image = self.image.resize((608, 456)) 19 self.main_image = ImageTk.PhotoImage(self.resize_image) 20 21 self.create_widget() 22 23 def create_widget(self): 24 self.frame = tk.Frame(self.master, background='gray', width=1500, height=700) 25 self.frame.propagate(False) 26 self.frame.pack() 27 28 self.canvas1 = tk.Canvas(self.frame, width=self.resize_image.width, height=self.resize_image.height) 29 self.canvas2 = tk.Canvas(self.frame, width=_squarelength, height=_squarelength) 30 self.canvas3 = tk.Canvas(self.frame, width=_squarelength, height=_squarelength) 31 32 self.canvas1.place(x=50, y=100) 33 self.canvas2.place(x=758, y=100) 34 self.canvas3.place(x=1058, y=100) 35 36 self.canvas1.create_image(0, 0, image=self.main_image, anchor=tk.NW) 37 38 # canvas1にマウスが乗った場合、離れた場合のイベントをセット。 39 self.canvas1.bind('<Motion>', self.mouse_motion) 40 self.canvas1.bind('<Leave>', self.mouse_leave) 41 42 43 44 font = tk.font.Font(family='Arial', size=16, weight='bold') 45 image_title = tk.Label(text='Original Image', bg = "gray", font=font) 46 image_title2 = tk.Label(text='Enlarged Image', bg = "gray", font=font) 47 image_title3 = tk.Label(text='Enlarged Image (monochrome)', bg = "gray", font=font) 48 49 image_title.place(x=50, y=60, anchor=tk.NW) 50 image_title2.place(x=758, y=60, anchor=tk.NW) 51 image_title3.place(x=1058, y=60, anchor=tk.NW) 52 53 54 def mouse_motion(self, event): 55 # マウス位置の座標を取得, 写真から切り出す座標を定義 56 x = event.x 57 y = event.y 58 59 self.frame_rect(x, y) 60 self.canvas_set(x, y) 61 62 def frame_rect(self, x, y): 63 # 過去に枠線が描画されている場合はそれを削除し、メイン画像内マウス位置に枠線を描画 64 self.frame_refresh() 65 self.crop_frame = (x-_framelength/2, y-_framelength/2, x+_framelength/2, y+_framelength/2) 66 self.rectframe = self.canvas1.create_rectangle(self.crop_frame, outline='#AAA', width=2, tag='rect') 67 68 def frame_refresh(self): 69 try: 70 self.canvas1.delete('rect') 71 except: 72 pass 73 74 def canvas_set(self, x, y): 75 # 枠線内をクロップし、ズームする 76 zoom_mag = _squarelength / _framelength 77 croped = self.resize_image.crop(self.crop_frame) 78 zoom_image = croped.resize((int(croped.width*zoom_mag), int(croped.height*zoom_mag))) 79 80 # ズームした画像を右側canvasに当てはめる 81 self.image_refresh() 82 self.sub_image1 = ImageTk.PhotoImage(zoom_image) 83 self.sub_image2 = ImageTk.PhotoImage(zoom_image.convert('L')) 84 85 self.sub_cv1 = self.canvas2.create_image(0, 0, image=self.sub_image1, anchor=tk.NW, tag='cv1') 86 self.sub_cv2 = self.canvas3.create_image(0, 0, image=self.sub_image2, anchor=tk.NW, tag='cv2') 87 88 def image_refresh(self): 89 try: 90 self.canvas2.delete('cv1') 91 self.canvas3.delete('cv2') 92 except: 93 pass 94 95 def mouse_leave(self, event): 96 try: 97 self.canvas2.delete('cv1') 98 self.canvas3.delete('cv2') 99 self.canvas1.delete('rect') 100 except: 101 pass 102 103 104 105if __name__ == '__main__': 106 root = tk.Tk() 107 app = MainApplication(master = root) 108 app.mainloop()

投稿2020/08/04 13:07

nto

総合スコア1438

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

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

nto

2020/08/04 13:09

電車の中でに必死に回答を書いてたらteamiklさんが先に回答をされていたので そちらをご参考下さい><
teamikl

2020/08/04 22:13

ほぼ同じ趣旨でした。> canvas3枚 <Motion> <Leave> 唯一、懸念だった、crop/resizeによる処理も実際動かしてみると、 それ程動作に支障でることもありませんね。 高解像度でサイズも巨大だったら影響あるかもしれないけど、 全然許容範囲内でした。 注意点として書き残しておくと <Motion>のような頻繁に呼ばれるイベント内で、 時間のかかる処理をすると、イベント処理が停滞して、 動作が重たく感じる事があります。 改善ポイントではありますが、動作に支障がでてない場合は 対応しても目に見える変化はないので、必須ではない、 優先度: 低 といった所です。
shu-san

2020/08/04 23:11

コードをご教授いただきありがとうございます。こちらでも動作確認することができました。 canvasをそれぞれに分ける部分が非常に参考になりました。
guest

0

ベストアンサー

イメージ説明

選択範囲の座標は未調整ですが、このような感じでしょうか。
内容が多岐に渡るので、一度に説明するのは難しいですが、

  1. キャンバスは3枚用意します。

プレビュー用、オリジナル用、グレースケール用。

  1. crop で画像を切り出してますが、

動的に行う場合に毎回切り出していては動作が重たくなるので、
オリジナルサイズの画像をキャンバスに描画しておき、
表示部分を絞りスクロールさせることで対応します。

  1. マウスの追従は canvas.bind("<Motion>", event_handle)
  2. マウスが画像(キャンバス)から抜けるのは <Leave> イベントで捕捉
  3. tk のキャンバス描画APIは透過色に対応していないので、

PILを使って透過画像を作成することで「マウス周辺の薄い白枠部分」を実装します。
参考URL: how-to-make-a-tkinter-canvas-rectangle-transparent


参考: tkinter canvas

取り急ぎキーワードのみ。

四角形の移動: <Motion> イベント内でマウスの位置に moveto
範囲スクロール: scrollregion, bbox("all"), xview_moveto, yview_moveto

投稿2020/08/04 12:32

teamikl

総合スコア8664

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

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

shu-san

2020/08/04 23:09

アイデアおよび関連URLのご教授ありがとうございます。非常に参考になりました。これを元に、コードを作成してみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問