Pythonで雨を降らせるプログラムを作る
目次
Pythonで雨を降らせるプログラムを作る
2023年の8月ですが、異常な暑さです。
埼玉の熊谷などは39度を記録しており、過去に類を見ない異常気象になっています。
筆者の仕事部屋にはクーラーがなく、そのため仕事をすると死んでしまいます。
ですので今は仕事部屋から退避してクーラーのある所でこの記事を書いていますが、天気はカンカン晴れです。
雨でも降ってくれれば少しは暑さもマシになるのですが、なかなかそうもいきません。
そこで今回はPythonで雨を降らせるプログラムを作ってみました。
このプログラムで涼めば少しはマシになるかも・・・?
ということでそのプログラムの解説を行いたいと思います。
プログラムの実行風景
今回のプログラムを実行すると以下のような結果になります。
夜の雨って感じで涼しい風景ですね。
ソースコード全文
以下がソースコード全文です。
import tkinter as tk import random class Rain: def __init__(self): self.id = None self.ay = 1 self.length = 1 class App(tk.Tk): def __init__(self): super().__init__() self.title('PyRain') self.width = 800 self.height = 600 self.geometry(f'{self.width}x{self.height}') self.canvas = tk.Canvas(self, bg="black") self.canvas.pack(expand=True, fill=tk.BOTH) self.rains = [] self.nrains = 100 for _ in range(self.nrains): rain = Rain() x = random.randint(0, self.width) y = random.randint(0, self.height) rain.length = random.randint(10, 50) x2 = x y2 = y + rain.length rain.id = self.canvas.create_line(x, y, x2, y2, fill=self.get_color()) rain.ay = random.randint(5, 15) self.rains.append(rain) self.after(33, self.update) def get_color(self): colors = ['white', 'blue', 'cyan'] return random.choice(colors) def update(self): for rain in self.rains: self.canvas.move(rain.id, 0, rain.ay) pos = self.canvas.coords(rain.id) if pos[1] >= self.height: x = random.randint(0, self.width) y = -rain.length self.canvas.moveto(rain.id, x, y) self.after(33, self.update) App().mainloop()
このソースコードの解説をしていきたいと思います。
プログラム全体の構成
プログラムの全体の構成としてはまずTkinterでウィンドウを作ります。
それからCanvas
ウィジェットでウィンドウ全体をキャンバスにします。
そしてCanvas
に雨粒に見立てた線を描画し、その線を上から下に移動させます。
線がウィンドウからはみ出したら線の位置をリセットして繰り返します。
今回はこの方針で実装していきます。
Rainクラスの作成
雨粒を表すRain
クラスを作ります。
class Rain: def __init__(self): self.id = None self.ay = 1 self.length = 1
id ... CanvasのオブジェクトのID
ay ... y座標の移動量
length ... 雨粒の縦の長さ
Appクラスの作成
今回はGUIアプリなのでPythonの標準ライブラリのTkinterを使います。
ですのでtk.Tk
クラスを継承したApp
クラスを作ります。
tk.Tk
クラスはTkinterのウィンドウを表すクラスで、これを継承することでウィンドウを定義できます。
class App(tk.Tk): def __init__(self): super().__init__() ... App().mainloop()
App().mainloop()
としてmainloop()
メソッドを呼び出すことでウィンドウを起動しています。
App.init()の実装
App.__init__()
でプログラムの初期化を行います。
def __init__(self): super().__init__() self.title('PyRain') self.width = 800 self.height = 600 self.geometry(f'{self.width}x{self.height}') ...
まず上記では親のイニシャライザの呼び出しとウィンドウのタイトルの設定をself.title()
で行っています。
それからself.width
にウィンドウの横幅、self.height
にウィンドウの高さを定義します。
そしてself.geometry()
でウィンドウのサイズを設定しています。
self.canvas = tk.Canvas(self, bg="black") self.canvas.pack(expand=True, fill=tk.BOTH) self.rains = [] self.nrains = 100
上記ではself.canvas
にCanvas
ウィジェットを代入しています。
Canvas
のbg
はblack
で、こうするとキャンバス全体が黒色になります。
そしてself.canvas.pack()
でウィジェットをウィンドウ上に配置します。
expand=True
で親ウィジェットに合わせて伸縮する設定に、fill=tk.BOTH
で上下左右にウィジェットが埋められるようになります。
self.rains
はRain
のオブジェクトを保存するリストです。
self.nrains
は雨粒の個数でこれは100個にしています。
for _ in range(self.nrains): rain = Rain() x = random.randint(0, self.width) y = random.randint(0, self.height) rain.length = random.randint(10, 50) x2 = x y2 = y + rain.length rain.id = self.canvas.create_line(x, y, x2, y2, fill=self.get_color()) rain.ay = random.randint(5, 15) self.rains.append(rain)
上記では100個の雨粒を初期化しています。
for _ in range(self.nrains):
でループをself.nrains
の数だけ回します。
それからrain = Rain()
で雨粒のオブジェクトを作成。
x = random.randint(0, self.width)
で0
からself.width
の間でランダムにx
を定義します。y
についても同様です。
それからrain.length
に雨粒の縦の長さをランダムに設定します。
x2
とy2
には線の終端の座標を保存します。x
とy
は線の始点の座標です。
そしてself.canvas.create_line()
で線オブジェクトを作成します。
x
とy
が線の始点の座標、x2
とy2
が線の終点の座標、fill
が線の色です。
線の色はself.get_color()
でランダムに生成しています。
self.canvas.create_line()
はオブジェクトの作成に成功するとオブジェクトを表すIDを返すのでそれをrain.id
に保存します。
rain.ay
にもランダムに線のy
座標の移動量を代入しています。
rain
オブジェクトを初期化したらself.rains
リストにrain
オブジェクトをself.rains.append()
で追加します。
self.after(33, self.update)
self.after()
は指定ミリ秒後に指定の関数を実行するとtk.Tk
のメソッドです。
このメソッドで33ミリ秒後にself.update()
を実行するように設定しています。
こうすると初期化の33ミリ秒後にself.update()
が一回実行されます。
self.update()
のプログラムのループ処理が書いてあるメソッドです。雨粒の移動などをこのメソッドで行います。
App.get_color()の実装
get_color()
メソッドはランダムに雨粒の色を生成するメソッドです。
def get_color(self): colors = ['white', 'blue', 'cyan'] return random.choice(colors)
self.canvas.create_line()
のfill
に設定できる色は16進数のほかwhite
やblue
などの名前でも設定できます。
上記の実装ではcolors
にwhite
, blue
, cyan
の色を格納し、そこからrandom.choice()
でランダムに1つ選択して返しています。
App.update()の実装
update()
メソッドにはループ中の処理を書いていきます。
def update(self): for rain in self.rains: self.canvas.move(rain.id, 0, rain.ay) pos = self.canvas.coords(rain.id) if pos[1] >= self.height: x = random.randint(0, self.width) y = -rain.length self.canvas.moveto(rain.id, x, y) self.after(33, self.update)
まずself.rains
をfor文で回してrain
オブジェクトを取り出します。
self.canvas.move()
でrain.id
を指定してrain.ay
の量だけrain.id
のy
座標を移動させます。
rain.ay
がプラスなのでこの移動は上から下に行われます。
キャンバス上の座標軸は左上がx=0, y=0
で右下がx=self.width, y=self.height
になります。
self.canvas.coords()
でrain.id
のキャンバス上の座標を取り出します。
pos[1]
にオブジェクトのy
座標が入ってますのでこれの値がself.height
以上になったら、つまりy
座標がキャンバス外にはみ出したら、rain.id
の座標を初期化します。
rain.id
の座標はself.canvas.moveto()
で絶対座標で設定します。
self.canvas.move()
は相対的な座標の移動、self.canvas.moveto()
は絶対的な座標の指定になります。
self.update()
が完了したら最後にself.after()
でself.update()
を再帰的に呼び出しておきます。
こうすると再帰的な呼び出しが33ミリ秒ごとに続き、ゲームループのようになります。
おわりに
今回はPythonで雨を降らせてみました。
この記事を書き終わった直後に実際に雨が降ってきました。
これは祈祷の効果がある祈祷プログラムかもしれません。。
なにか参考になれば幸いです。
(^ _ ^) | 祈祷プログラム! |
(・ v ・) | 雨よ降れ! |