Python(turtleモジュールでお絵かきツール )

turtleモジュールについては前回投稿した
Python(turtleモジュールで描画)
を確認してください。

turtleモジュールのキーイベント

turtleモジュールではマウスやキーボードの入力を取得して、独自に設定したコールバック関数を呼び出すことで、処理を行うことができます。

from turtle import *
Screen()
onscreenclick(goto) # 返り値は中心(0,0)とするスクリーン座標のタプル
pencolor("green")
onkeypress(penup,"Up")
onkeypress(pendown,"Down")
onkeypress(lambda :forward(50),"Right")
onkeypress(lambda :backward(50),"Left")
listen() # onkeyイベントを有効化(TurtleScreen にフォーカス)
turtle.onclick(fun, btn=1, add=None)
・fun -- 2引数の関数でキャンバスのクリックされた点の座標を引数として呼び出されるコールバック関数(無引数なので注意)
・btn -- マウスボタンの番号、デフォルトは 1 (左マウスボタン)
・add -- True または False -- True ならば、 新しい束縛が追加されますが、そうでなければ、 以前の束縛を置き換えます。
fun が None ならば、既存の束縛が取り除かれ、以前に設定した処理を無効にできます。また、addは通常Noneなので、再度onclickを定義した場合は、以前設定した処理は上書きされます。
turtle.onkeypress(fun, key=None)¶
・fun -- 引数なしの関数または None
・key -- 文字列: キー (例 "a") またはキー・シンボル (例 "space")
turtle.listen(xdummy=None, ydummy=None)
onkey メソッドを設定する場合、listen() を書かないと処理されないので注意!

キーの文字列について公式リファレンスに一覧がないので分かりにくいのですが、以下のサイトのキー文字列一覧がturtleのkeyにも使用できました。また、一覧にないのですが、テンキーの”0”、”1”も利用可能です。

Tkinter 8.5リファレンス:Python用のGUI

イベントを設定してお絵かきツール作成

ではキーイベントを利用してお絵かきツールを簡単に作ると以下のコードで実装できます。

from turtle import *

def move(x,y):
    nowX,nowY = position()
    goto(nowX + x, nowY + y)

def reset():
    penup()
    setpos(0,0)
    pendown()

Screen()
shape('turtle')
onkeypress(lambda :move(0,-20), "Down")
onkeypress(lambda :move(0,20), "Up")
onkeypress(lambda :move(-20,0), "Left")
onkeypress(lambda :move(20,0), "Right")
onkeypress(reset, "z")
onkeypress(clear, "c")
onkeypress(lambda :pencolor("red"), "r")
onkeypress(lambda :pencolor("blue"), "b")
onkeypress(lambda :pencolor("green"), "g")
onkeypress(penup, "u")
onkeypress(pendown, "d")
listen()
done()
マジックナンバーを無くし、高階関数を利用してメソッドを増やしたver
from turtle import *

Screen()
d=5 # 一回のキー操作で進む距離(ピクセル数)

# カーソルを移動させるコールバック関数を生成する高階関数
def mover(cx,cy):
    def callback():
        x,y=position()
        goto(x+cx*d,y+cy*d)
    return callback

onkeypress(mover(0,-1), "Down")
onkeypress(mover(0,1), "Up")
onkeypress(mover(-1,0), "Left")
onkeypress(mover(1,0), "Right")
del mover # 後々使わない関数は名前を削除して外部からアクセスできなくするとよい

onkeypress(clear, "c")
onkeypress(penup, "u")
onkeypress(pendown, "d")

onkeypress(lambda :pencolor("red"), "r")
onkeypress(lambda :pencolor("green"), "g")
onkeypress(lambda :pencolor("blue"), "b")

def reset_position():
    penup()
    setpos(0,0)
    pendown()

onkeypress(reset_position, "z")
del reset_position

def draw_by_drag(a,b):
    print("ondrag:",a,b) # デバッグ出力
    tracer(0) # 描画の遅延を一旦0にしてから
    goto(a,b) # 線を描画し、
    tracer(1) # その後、通常の描画モードに戻す

ondrag(draw_by_drag)
onscreenclick(setpos)

listen()
クラスを定義して自前でループの更新処理を設定し、幅調整やスタンプ機能を実装したver
import turtle

class Square:    
    def __init__(self, x, y, size):
        self.size = size
        self.x = x
        self.y = y

    def drawself(self):
        turtle.goto(self.x - self.size // 2, self.y - self.size // 2)
        turtle.fillcolor(turtle.pencolor())
        turtle.begin_fill()
        for _ in range(4):
            turtle.forward(self.size)
            turtle.left(90)
        turtle.end_fill()

class Circle:    
    def __init__(self, x, y, size):
        self.size = size*0.5
        self.x = x
        self.y = y

    def drawself(self):
        turtle.goto(self.x, self.y - self.size // 2)
        turtle.fillcolor(turtle.pencolor())
        turtle.begin_fill()
        turtle.circle(self.size)
        turtle.end_fill()

class Draw:
    def __init__(self):
        self.__speed = 10
        self.__pensize = 1
        self.__stampsize = 20
        turtle.Screen()
        turtle.shape("circle")
        turtle.turtlesize(0.1,0.1,self.__pensize)
        turtle.tracer(0)
        turtle.listen()
        turtle.onkeypress(self.move(0,-1), "Down")
        turtle.onkeypress(self.move(0,1), "Up")
        turtle.onkeypress(self.move(-1,0), "Left")
        turtle.onkeypress(self.move(1,0), "Right")
        turtle.onkeypress(self.reset(), "z")
        turtle.onkeypress(turtle.clear, "c")
        turtle.onkeypress(lambda :turtle.pencolor("red"), "r")
        turtle.onkeypress(lambda :turtle.pencolor("blue"), "b")
        turtle.onkeypress(lambda :turtle.pencolor("green"), "g")
        turtle.onkeypress(turtle.penup, "u")
        turtle.onkeypress(turtle.pendown, "d")
        turtle.onkeypress(self.setspeed(-1), "Shift_L")
        turtle.onkeypress(self.setspeed(1), "Shift_R")
        turtle.onkeypress(self.setsize(-1), "Control_L")
        turtle.onkeypress(self.setsize(1), "Control_R")
        turtle.onkeypress(self.setstampsize(-1), "Alt_L")
        turtle.onkeypress(self.setstampsize(1), "Alt_R")
        turtle.onkeypress(self.stamp(0), "0")
        turtle.onkeypress(self.stamp(1), "1")
        turtle.ondrag(turtle.goto)
        turtle.onscreenclick(turtle.setpos)

    def move(self,x,y):
        def f():
            nowX,nowY = turtle.position()
            turtle.goto(nowX+x*self.__speed, nowY+y*self.__speed)
        return f

    def reset(self):
        def f():
            turtle.penup()
            turtle.goto(0,0)
            turtle.pendown()
            self.__pensize = 1
            self.__speed = 10
            turtle.pensize(self.__pensize)
            turtle.turtlesize(0.1,0.1,self.__pensize)
        return f       

    def nextFrame(self):
        turtle.update()
        turtle.ontimer(lambda: self.nextFrame(), 100)

    def setspeed(self,n):
        def f():
            if self.__speed == 1 and n < 0:
                return None
            self.__speed += n
            print("speed:",self.__speed)
        return f

    def setsize(self,n):
        def f():
            if self.__pensize == 1 and n < 0:
                return None
            self.__pensize += n
            print("pen:",self.__pensize)
            turtle.pensize(self.__pensize)
            turtle.turtlesize(outline=self.__pensize)
        return f

    def setstampsize(self,n):
        def f():
            if self.__stampsize == 1 and n < 0:
                return None
            self.__stampsize += n
            print("stamp:",self.__stampsize)
        return f

    def stamp(self,shape):
        def f():
            if shape == 0:
                x,y = turtle.position()
                s = Square(x,y,self.__stampsize)
                s.drawself()
            elif shape == 1:
                x,y = turtle.position()
                s = Circle(x,y,self.__stampsize)
                s.drawself()
        return f

draw = Draw()
draw.nextFrame()

turtleモジュールのリファレンスに各メソッドの説明があるので、使いやすいように自分でカスタマイズするのも勉強になります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください