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

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

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

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

Tkinter

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

受付中

画像を複数表示しそれぞれ拡大縮小・移動ができるようにしたい

MSA
MSA

総合スコア0

Python 3.x

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

Tkinter

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

1回答

0評価

0クリップ

347閲覧

投稿2022/01/17 10:45

複数の画像を表示し、それぞれ拡大縮小・移動ができるようにしたいです。

現在、一つの画像に対して拡大縮小移動はできるのですが、画像を選択しなおすとそれまで表示されていた画像が消え新たに選択された画像に置き換わります。これを複数の画像を随時選択して表示し、さらにそれぞれ拡大縮小・移動ができるようにしたいです。

python初心者で恥ずかしながらこのコードも下記のサイトからほぼコピペさせてもらいました。そこから必要でない箇所を省いた次第です。色々調べやってみたのですがうまくいきません。どなたか教えてください。

参考にしたサイト

python

import tkinter as tk # ウィンドウ作成用 from tkinter import filedialog # ファイルを開くダイアログ用 from PIL import Image, ImageTk # 画像データ用 import numpy as np # アフィン変換行列演算用 import os # ディレクトリ操作用 class Application(tk.Frame): def __init__(self, master=None): super().__init__(master) self.pack() self.pil_image = None # 表示する画像データ self.my_title = "Image Viewer" # タイトル self.back_color = "#008B8B" # 背景色 # ウィンドウの設定 self.master.title(self.my_title) # タイトル self.master.geometry("500x400") # サイズ self.create_menu() # メニューの作成 self.create_widget() # ウィジェットの作成 def menu_open_clicked(self, event=None): # ファイル→開く filename = tk.filedialog.askopenfilename( filetypes = [("Image file", ".bmp .png .jpg .tif"), ("Bitmap", ".bmp"), ("PNG", ".png"), ("JPEG", ".jpg"), ("Tiff", ".tif") ], # ファイルフィルタ initialdir = os.getcwd() # カレントディレクトリ ) # 画像ファイルを設定する self.set_image(filename) def menu_quit_clicked(self): # ウィンドウを閉じる self.master.destroy() # create_menuメソッドを定義 def create_menu(self): self.menu_bar = tk.Menu(self) # Menuクラスからmenu_barインスタンスを生成 self.file_menu = tk.Menu(self.menu_bar, tearoff = tk.OFF) self.menu_bar.add_cascade(label="File", menu=self.file_menu) self.file_menu.add_command(label="Open", command = self.menu_open_clicked, accelerator="Ctrl+O") self.file_menu.add_separator() # セパレーターを追加 self.file_menu.add_command(label="Exit", command = self.menu_quit_clicked) self.menu_bar.bind_all("<Control-o>", self.menu_open_clicked) # ファイルを開くのショートカット(Ctrol-Oボタン) self.master.config(menu=self.menu_bar) # メニューバーの配置 def create_widget(self): '''ウィジェットの作成''' # Canvas self.canvas = tk.Canvas(self.master, background= self.back_color) self.canvas.pack(expand=True, fill=tk.BOTH) # この両方でDock.Fillと同じ # マウスイベント self.master.bind("<Motion>", self.mouse_move) # MouseMove self.master.bind("<B1-Motion>", self.mouse_move_left) # MouseMove(左ボタンを押しながら移動) self.master.bind("<Button-1>", self.mouse_down_left) # MouseDown(左ボタン) self.master.bind("<Double-Button-1>", self.mouse_double_click_left) # MouseDoubleClick(左ボタン) self.master.bind("<MouseWheel>", self.mouse_wheel) # MouseWheel def set_image(self, filename): ''' 画像ファイルを開く ''' if not filename: return # PIL.Imageで開く self.pil_image = Image.open(filename) # 画像全体に表示するようにアフィン変換行列を設定 self.zoom_fit(self.pil_image.width, self.pil_image.height) # 画像の表示 self.draw_image(self.pil_image) # カレントディレクトリの設定 os.chdir(os.path.dirname(filename)) # ------------------------------------------------------------------------------- # マウスイベント # ------------------------------------------------------------------------------- def mouse_move(self, event): ''' マウスの移動時 ''' if self.pil_image == None: return # 画像座標 mouse_posi = np.array([event.x, event.y, 1]) # マウス座標(numpyのベクトル) mat_inv = np.linalg.inv(self.mat_affine) # 逆行列(画像→Cancasの変換からCanvas→画像の変換へ) image_posi = np.dot(mat_inv, mouse_posi) # 座標のアフィン変換 x = int(np.floor(image_posi[0])) y = int(np.floor(image_posi[1])) if x >= 0 and x < self.pil_image.width and y >= 0 and y < self.pil_image.height: # 輝度値の取得 value = self.pil_image.getpixel((x, y)) def mouse_move_left(self, event): ''' マウスの左ボタンをドラッグ ''' if self.pil_image == None: return self.translate(event.x - self.__old_event.x, event.y - self.__old_event.y) self.redraw_image() # 再描画 self.__old_event = event def mouse_down_left(self, event): ''' マウスの左ボタンを押した ''' self.__old_event = event def mouse_double_click_left(self, event): ''' マウスの左ボタンをダブルクリック ''' if self.pil_image == None: return self.zoom_fit(self.pil_image.width, self.pil_image.height) self.redraw_image() # 再描画 def mouse_wheel(self, event): ''' マウスホイールを回した ''' if self.pil_image == None: return if (event.delta < 0): # 上に回転の場合、縮小 self.scale_at(0.8, event.x, event.y) else: # 下に回転の場合、拡大 self.scale_at(1.25, event.x, event.y) self.redraw_image() # 再描画 # ------------------------------------------------------------------------------- # 画像表示用アフィン変換 # ------------------------------------------------------------------------------- def reset_transform(self): '''アフィン変換を初期化(スケール1、移動なし)に戻す''' self.mat_affine = np.eye(3) # 3x3の単位行列 def translate(self, offset_x, offset_y): ''' 平行移動 ''' mat = np.eye(3) # 3x3の単位行列 mat[0, 2] = float(offset_x) mat[1, 2] = float(offset_y) self.mat_affine = np.dot(mat, self.mat_affine) def scale(self, scale:float): ''' 拡大縮小 ''' mat = np.eye(3) # 単位行列 mat[0, 0] = scale mat[1, 1] = scale self.mat_affine = np.dot(mat, self.mat_affine) def scale_at(self, scale:float, cx:float, cy:float): ''' 座標(cx, cy)を中心に拡大縮小 ''' # 原点へ移動 self.translate(-cx, -cy) # 拡大縮小 self.scale(scale) # 元に戻す self.translate(cx, cy) def zoom_fit(self, image_width, image_height): '''画像をウィジェット全体に表示させる''' # キャンバスのサイズ canvas_width = self.canvas.winfo_width() canvas_height = self.canvas.winfo_height() if (image_width * image_height <= 0) or (canvas_width * canvas_height <= 0): return # アフィン変換の初期化 self.reset_transform() scale = 1.0 offsetx = 0.0 offsety = 0.0 if (canvas_width * image_height) > (image_width * canvas_height): # ウィジェットが横長(画像を縦に合わせる) scale = canvas_height / image_height # あまり部分の半分を中央に寄せる offsetx = (canvas_width - image_width * scale) / 2 else: # ウィジェットが縦長(画像を横に合わせる) scale = canvas_width / image_width # あまり部分の半分を中央に寄せる offsety = (canvas_height - image_height * scale) / 2 # 拡大縮小 self.scale(scale) # あまり部分を中央に寄せる self.translate(offsetx, offsety) # ------------------------------------------------------------------------------- # 描画 # ------------------------------------------------------------------------------- def draw_image(self, pil_image): if pil_image == None: return # キャンバスのサイズ canvas_width = self.canvas.winfo_width() canvas_height = self.canvas.winfo_height() # キャンバスから画像データへのアフィン変換行列を求める #(表示用アフィン変換行列の逆行列を求める) mat_inv = np.linalg.inv(self.mat_affine) # PILの画像データをアフィン変換する dst = pil_image.transform( (canvas_width, canvas_height), # 出力サイズ Image.AFFINE, # アフィン変換 tuple(mat_inv.flatten()), # アフィン変換行列(出力→入力への変換行列)を一次元のタプルへ変換 Image.NEAREST, # 補間方法、ニアレストネイバー fillcolor= self.back_color ) # 表示用画像を保持 self.image = ImageTk.PhotoImage(image=dst) # 画像の描画 item = self.canvas.create_image( 0, 0, # 画像表示位置(左上の座標) anchor='nw', # アンカー、左上が原点 image=self.image # 表示画像データ ) def redraw_image(self): ''' 画像の再描画 ''' if self.pil_image == None: return self.draw_image(self.pil_image) if __name__ == "__main__": root = tk.Tk() app = Application(master=root) app.mainloop()

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

まだ回答がついていません

会員登録して回答してみよう

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

ただいまの回答率
87.20%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Python 3.x

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

Tkinter

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