つきすけ の コーディング記

細かいところで嫌にならないように、小さいことでも解説していくブログ。たまに関係ないことも書く。

Python の Tkinter で 半透明の色を重ねて表示する (PIL使用)

半透明の白色とかを重ねたインターフェースを考えがち。今回はTkinterでそれを実現したいが一筋縄ではいかない。
とりあえず単純にcanvasを透明にするだとか、半透明のpngを重ねるだとかやってみたができそうにない。そういうわけで、PILを駆使して『背景に使用するの画像を一部抜き取って白透過を重ねたような画像を作り出し背景画像とする』という力技を行うことにした。もっとスマートな方法を知っている方がいたらどうか教えてください。

なお、画像の表示方法は例によってTkinter8.5仕様、8.6以上の方はもっと簡単にいろいろできるかもしれぬ。

※python3系で書かれています。2系の場合はコード中のtkinterをすべてTkinterにすればできるはず。

目標

今回は複雑なので先にどんなインターフェースを作るかをご紹介。下図の通りだが、画面サイズは400x400。背景画像は画面いっぱい(少しはみ出し気味)に映し出し、300x300の透過率50%の白色を上に重ねたような背景を作る。

背景画像を用意する

焦らずとりあえず下準備。背景画像を埋め込む。今回は縦長の画像をお借りしているのでリサイズ・位置調整で表示。この辺の細かいお話は前の記事にて。
tsukisuke.hateblo.jp

import tkinter
from PIL import Image, ImageTk

window = tkinter.Tk()
window.geometry("400x400")
window.title("translucent")

canvas_bg = tkinter.Canvas(background="blue", width=400, height=400)
canvas_bg.place(x=200, y=200, anchor=tkinter.CENTER)

# 画像を指定・リサイズ・位置調整 
img = Image.open('bg.jpg')
w = img.width # 横幅を取得                                                                                                                                                                       
h = img.height # 縦幅を取得                                                                                                                                                                      
img = img.resize(( int(w * (500/w)), int(h * (500/w)) ))
# 画像表示
img = ImageTk.PhotoImage(img)
canvas_bg.create_image(200, 200, image=img, anchor=tkinter.CENTER)                                                                                                                                                                     

window.mainloop()

写真そのまま入れただけ

Photo by Kenrick Mills on Unsplash
Kenrick Mills (@kenrickmills) | Unsplash Photo Community

全体に半透明の白を重ねる

先に述べた通り、単純にcanvasを重ねるや、pngを重ねるだけではうまく行かない。よってコード上で背景画像を透過白背景を重ねた状態に編集し、背景画像とする。

import tkinter
from PIL import Image, ImageTk

window = tkinter.Tk()
window.geometry("400x400")
window.title("translucent")

canvas_bg = tkinter.Canvas(background="blue", width=400, height=400)
canvas_bg.place(x=200, y=200, anchor=tkinter.CENTER)

# 画像を指定・リサイズ・位置調整                                                
img = Image.open('bg.jpg')
w = img.width # 横幅を取得                                                      
h = img.height # 縦幅を取得                                                     
img = img.resize(( int(w * (500/w)), int(h * (500/w)) ))

# 白色を作成する                               
img_white = Image.new('RGB', img.size, (255, 255, 255))

# 30%透過して重ねる                                  
img = Image.blend(img, img_white, 0.3)

# 画像表示                                                                      
img = ImageTk.PhotoImage(img)
canvas_bg.create_image(200, 200, image=img, anchor=tkinter.CENTER)

window.mainloop()

全体に透過白30%を重ねた画像

一部に半透明の白色を重ねる

さきほど使った Image.bledn()や、調べると出てくるImage.alpha_composite()などは同じ大きさの画像の合成にしか使うことができない。工夫してこれらで重ねようとおもったがとことんうまく行かない。よって、重ねる部分を書き換えていく方法をとった。

400x400 のキャンバス上に、300x300 の白透過画像を真ん中に乗せたような画像を作成したいが、元画像が中央寄せのためひとまず元画像の中央を計算。そこから300 / 2 = 150 の範囲無内にあるピクセルに適当に数値を足し、白っぽくする。

import tkinter
from PIL import Image, ImageTk

window = tkinter.Tk()
window.geometry("400x400")
window.title("translucent")

canvas_bg = tkinter.Canvas(background="blue", width=400, height=400)
canvas_bg.place(x=200, y=200, anchor=tkinter.CENTER)

# 画像を指定・リサイズ                                                          
img = Image.open('bg.jpg') # jpegをopen                                         
w = img.width # 横幅を取得                                                      
h = img.height # 縦幅を取得                                                     
img = img.resize(( int(w * (500/w)), int(h * (500/w)) ))

# 中央を取得                                                        
center = (int(img.width/2), int(img.height/2))
# 以下のfor分のwidth, height に取得したw, h を使うとindexエラーが出ます注意     
for y in range(img.height):
    for x in range(img.width):
        if abs(center[0] - x) < 150:
            if abs(center[1] - y) < 150:
                p = img.getpixel((x, y))
                putpixel = (int(p[0] + 100), int(p[1] + 100), int(p[2] + 100))
                img.putpixel((x, y), putpixel)


# 位置調整・画像表示                                                            
img = ImageTk.PhotoImage(img)
canvas_bg.create_image(200, 200, image=img, anchor=tkinter.CENTER)

window.mainloop()

実行結果


お借りした画像

今回お借りした月の画像と出展

Photo by Kenrick Mills on Unsplash
Kenrick Mills : Kenrick Mills (@kenrickmills) | Unsplash Photo Community
Unsplash : Beautiful Free Images & Pictures | Unsplash