2022/03/25(金) [n年前の日記]
#1 [python] tkinterとpycairoで矩形分割処理の結果を表示
昨日、Python を使って矩形を分割していく処理を書き直したけれど。
_矩形を分割していく処理について実験中
_アルゴリズムについて
配列をずらずらと出力されても、それらしく分割されているのかさっぱり分からないので、tkinter と pycairo を使って結果を表示するようにしてみたい。
動作確認環境は以下。
とりあえず、それらしいスクリプトが書けたかなと…。
_04_pycairo_in_tkinter_with_rect2.py
_dividerectangle.py
04_pycairo_in_tkinter_with_rect2.py と dividerectangle.py を同じ場所に置いて、python 04_pycairo_in_tkinter_with_rect2.py を実行すれば動くかなと。
動作には pycairo と Pillow が必要。
Python 3.9.11 64bit は、以下で pycairo と Pillow がインストールできた。
Python 2.7.18 32bit は、Pillow については pip install Pillow でインストールできる。pycairo は、pygtk-all-in-one-2.24.2.win32-py2.7.msi を入手してインストールした。
_Index of /binaries/win32/pygtk/2.24/
_矩形を分割していく処理について実験中
_アルゴリズムについて
配列をずらずらと出力されても、それらしく分割されているのかさっぱり分からないので、tkinter と pycairo を使って結果を表示するようにしてみたい。
動作確認環境は以下。
- Windows10 x64 21H2 + Python 2.7.18 32bit + Pillow 6.2.2 + pycairo 1.8.10
- Windows10 x64 21H2 + Python 3.9.11 64bit + Pillow 9.0.1 + pycairo 1.21.0
とりあえず、それらしいスクリプトが書けたかなと…。
_04_pycairo_in_tkinter_with_rect2.py
_dividerectangle.py
04_pycairo_in_tkinter_with_rect2.py と dividerectangle.py を同じ場所に置いて、python 04_pycairo_in_tkinter_with_rect2.py を実行すれば動くかなと。
動作には pycairo と Pillow が必要。
Python 3.9.11 64bit は、以下で pycairo と Pillow がインストールできた。
pip install Pillow pip install pycairo
Python 2.7.18 32bit は、Pillow については pip install Pillow でインストールできる。pycairo は、pygtk-all-in-one-2.24.2.win32-py2.7.msi を入手してインストールした。
_Index of /binaries/win32/pygtk/2.24/
◎ 少し解説。 :
pycairo の描画結果(surface)を tkinter で表示できるように変換するあたりは Pillow を使う。RGBA と BGRA の違いがあるので注意。
_pycairoの描画結果をtkinterで表示
pycairo を使って角丸矩形を描画する処理は以前書いたものをコピペして使った。
_pycairoを勉強中
以下のページが参考になる。
_rounded rectangle
pycairo の使い方に関しては、以下のチュートリアル記事が参考になるはず。英語だけど、ソースとスクリーンショットを眺めれば、なんとなく記述の仕方が分かるのではないかと…。ちなみに、pycairo + PyGTK (PyGObject) を使って解説されてる。
_PyCairo tutorial
_pycairoの描画結果をtkinterで表示
pycairo を使って角丸矩形を描画する処理は以前書いたものをコピペして使った。
_pycairoを勉強中
以下のページが参考になる。
_rounded rectangle
pycairo の使い方に関しては、以下のチュートリアル記事が参考になるはず。英語だけど、ソースとスクリーンショットを眺めれば、なんとなく記述の仕方が分かるのではないかと…。ちなみに、pycairo + PyGTK (PyGObject) を使って解説されてる。
_PyCairo tutorial
[ ツッコむ ]
#2 [python] pycairoのクリッピングとマスキングについて勉強中
Python + pycairo を使って、角丸矩形の中にだけ何かしらを描画してみたい。
GIMP + Python-Fu なら、GIMP の選択範囲機能を使って角丸矩形の選択範囲を用意して、GIMPの描画機能で選択範囲の中だけに色々描いていく、という処理ができる。でも、同じことを pycairo だけで実現するにはどうしたらいいのだろう。
ググっていたら、pycairo にはクリッピング、あるいはマスキングという機能があるそうで。それらを使えば似たような処理ができるのではないかと思えてきた。
_Clipping and masking in PyCairo
動作確認環境は以下。
GIMP + Python-Fu なら、GIMP の選択範囲機能を使って角丸矩形の選択範囲を用意して、GIMPの描画機能で選択範囲の中だけに色々描いていく、という処理ができる。でも、同じことを pycairo だけで実現するにはどうしたらいいのだろう。
ググっていたら、pycairo にはクリッピング、あるいはマスキングという機能があるそうで。それらを使えば似たような処理ができるのではないかと思えてきた。
_Clipping and masking in PyCairo
動作確認環境は以下。
- Windows10 x64 21H2 + Python 2.7.18 32bit + Pillow 6.2.2 + pycairo 1.8.10
- Windows10 x64 21H2 + Python 3.9.11 64bit + Pillow 9.0.1 + pycairo 1.21.0
◎ マスキングを試す。 :
まずはマスキングについて試してみる。
マスキングは、pycairo の surface に含まれているアルファチャンネルだけをマスク情報として利用して、描画先の surface に対してマスク情報を反映しつつ描画する、という説明であっているのだろうか…。
とりあえず、以下のような感じになった。
_02_masking.py
実行すると、3つのpng画像を出力する。
1つは、ベースとなる描画結果。ソース内では、bsae に入ってる内容。
もう1つは、マスクとなる描画結果。角丸矩形の周辺は透明になっている。ソース内では、mask に入ってる内容。
最後の1つは、ベース画像を描画ソースにして、マスクを指定しつつ描画した結果。角丸矩形の中にだけ、ベース画像の中身が描かれていることが分かる。
最後の画像を求めている部分は以下。
各種サンプルでは、直後に c.fill() を呼んでる事例が多いように見受けられたけど、どうやら .mask_surface() 自体が描画を指定する命令だそうで…。.mask_surface() を呼んだ時点で、マスク情報を反映しつつ描画せよ、と指示しているらしい。
ひとまずこれで、GIMP の選択範囲機能に近い感じの処理ができたような気がする。
マスキングは、pycairo の surface に含まれているアルファチャンネルだけをマスク情報として利用して、描画先の surface に対してマスク情報を反映しつつ描画する、という説明であっているのだろうか…。
とりあえず、以下のような感じになった。
_02_masking.py
import cairo import math def draw_rounder_rectangle(ctx, x, y, w, h, ra): """Set sub path rounded rectangle.""" deg = math.pi / 180.0 ctx.new_sub_path() ctx.arc(x + w - ra, y + ra, ra, -90 * deg, 0 * deg) ctx.arc(x + w - ra, y + h - ra, ra, 0 * deg, 90 * deg) ctx.arc(x + ra, y + h - ra, ra, 90 * deg, 180 * deg) ctx.arc(x + ra, y + ra, ra, 180 * deg, 270 * deg) ctx.close_path() def main(): w, h = 256, 256 # draw base surface base = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) c = cairo.Context(base) # fill rectangle c.set_source_rgb(0.5, 0.5, 0.5) c.rectangle(0, 0, w, h) c.fill() # draw lines c.set_source_rgb(0, 1.0, 0) c.set_line_width(6) for y in range(0, h, 32): c.move_to(0, y) c.line_to(w, y) c.stroke() # save surface as png image base.write_to_png("02_masking_base.png") # draw mask surface mask = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) c = cairo.Context(mask) s = 16 draw_rounder_rectangle(c, s, s, w - s * 2, h - s * 2, 32) c.set_source_rgb(0, 0, 0) c.fill() mask.write_to_png("02_masking_mask.png") ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) c = cairo.Context(ims) c.set_source_surface(base, 0, 0) c.mask_surface(mask, 0, 0) # c.fill() # save surface as png image ims.write_to_png("02_masking_result.png") if __name__ == '__main__': main()
実行すると、3つのpng画像を出力する。
1つは、ベースとなる描画結果。ソース内では、bsae に入ってる内容。
もう1つは、マスクとなる描画結果。角丸矩形の周辺は透明になっている。ソース内では、mask に入ってる内容。
最後の1つは、ベース画像を描画ソースにして、マスクを指定しつつ描画した結果。角丸矩形の中にだけ、ベース画像の中身が描かれていることが分かる。
最後の画像を求めている部分は以下。
ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) c = cairo.Context(ims) c.set_source_surface(base, 0, 0) c.mask_surface(mask, 0, 0) # c.fill()
- base には、描画したい内容が入ってる。
- mask には、マスク情報として使う内容(角丸矩形)が入っている。
- c.set_source_surface(base, 0, 0) で、base を描画ソースとして指定している。
- c.mask_surface(mask, 0, 0) で、マスク情報として使う surface を渡している。
各種サンプルでは、直後に c.fill() を呼んでる事例が多いように見受けられたけど、どうやら .mask_surface() 自体が描画を指定する命令だそうで…。.mask_surface() を呼んだ時点で、マスク情報を反映しつつ描画せよ、と指示しているらしい。
ひとまずこれで、GIMP の選択範囲機能に近い感じの処理ができたような気がする。
◎ クリッピングを試す。 :
せっかくだからクリッピングも試す。
pycairo のクリッピングは、事前に何かしらのパスを指定してから .clip() を呼ぶと、直前に指定されていたパスをクリップ範囲として扱うようになる、という機能らしい。
_01_clipping.py
実行すると以下の画像が出力される。このスクリプトでも、角丸矩形の中にだけ何かしらを描くことができている。
以下が、クリッピングの指定をしている部分。
ということで、マスキングを使っても、クリッピングを使っても、GIMPの選択範囲内にのみ描画、みたいな処理ができそうだなと。
ところで、マスキングとクリッピングのどちらを使うべきだろう…?
クリッピングは、クリップ範囲をパスで指定するので、パスで表現できる程度に簡単な形の領域を指定する場合は使える、ということになるのだろうか。
逆に、マスキングは、パスで表現するのが難しい、複雑な図形で領域指定する場合には使える、ということになるのかもしれない。たぶん。自信無いけど。
pycairo のクリッピングは、事前に何かしらのパスを指定してから .clip() を呼ぶと、直前に指定されていたパスをクリップ範囲として扱うようになる、という機能らしい。
_01_clipping.py
import cairo import math def draw_rounder_rectangle(ctx, x, y, w, h, ra): """Set sub path rounded rectangle.""" deg = math.pi / 180.0 ctx.new_sub_path() ctx.arc(x + w - ra, y + ra, ra, -90 * deg, 0 * deg) ctx.arc(x + w - ra, y + h - ra, ra, 0 * deg, 90 * deg) ctx.arc(x + ra, y + h - ra, ra, 90 * deg, 180 * deg) ctx.arc(x + ra, y + ra, ra, 180 * deg, 270 * deg) ctx.close_path() def main(): w, h = 256, 256 # draw base surface base = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) c = cairo.Context(base) # fill background c.set_source_rgb(0.5, 0.5, 0.5) c.rectangle(0, 0, w, h) c.fill() # draw lines c.set_source_rgb(0, 1.0, 0) c.set_line_width(6) for y in range(0, h, 32): c.move_to(0, y) c.line_to(w, y) c.stroke() ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) c = cairo.Context(ims) c.set_source_surface(base, 0, 0) # set clipping path s = 16 draw_rounder_rectangle(c, s, s, w - s * 2, h - s * 2, 32) # clipping c.clip() c.paint() # save surface as png image ims.write_to_png("01_clipping_result.png") if __name__ == '__main__': main()
実行すると以下の画像が出力される。このスクリプトでも、角丸矩形の中にだけ何かしらを描くことができている。
以下が、クリッピングの指定をしている部分。
c.set_source_surface(base, 0, 0) # set clipping path s = 16 draw_rounder_rectangle(c, s, s, w - s * 2, h - s * 2, 32) # clipping c.clip() c.paint()
- base に、描画したい内容を描いておく。
- .set_source_surface(base, 0, 0) で、base を描画ソースとして指定。
- draw_rounder_rectangle() で、角丸矩形のパスを指定。
- .clip() を呼んで、直前まで指定されていたパスを ―― この場合は角丸矩形をクリップ範囲として設定する。
- .paint() で、描画ソースの内容を描画先 surface に描画。クリップ範囲が適用された状態で描画される。
ということで、マスキングを使っても、クリッピングを使っても、GIMPの選択範囲内にのみ描画、みたいな処理ができそうだなと。
ところで、マスキングとクリッピングのどちらを使うべきだろう…?
クリッピングは、クリップ範囲をパスで指定するので、パスで表現できる程度に簡単な形の領域を指定する場合は使える、ということになるのだろうか。
逆に、マスキングは、パスで表現するのが難しい、複雑な図形で領域指定する場合には使える、ということになるのかもしれない。たぶん。自信無いけど。
[ ツッコむ ]
以上、1 日分です。