【Tkinter】図形を回転させる!自動で回転・ドラッグで回転

Tkinterで図形を回転

この記事の内容
  • 【結論】Tkinterで図形は回転できる!
  • 図形を自動回転させる方法
  • 図形をマウス操作で回転させる方法

こんにちは、Youta(@youta_blog)です。

今回は、Tkinterにおける図形の回転のお話です。

すずか
すずか

Tkinterってさ、図形の回転ってできる?

先生
先生

結論を言うと、Tkinterに図形を回転させる機能はありません。

ただ、そうした機能を自作できますよ。

現状、Tkinterでは図形の回転がサポートされていません。

では私たちは諦めるしかないのでしょうか?

いいえ、機能がないなら自分で作れば良いのです。

実際、Tkinterで図形を回転させるプログラムを作ってみました。

そう、できないことはないのです。

本記事では、解説・確認用コード・実行結果の3点をご用意しております。

また必要に応じてコードの解説をしていますので、初心者でも安心です。

記事の信頼性は、確認用コードと実行結果が保証します。

少しでも疑問に感じたら、是非ともコードをコピペしてPCで確かめてみてください。

それでは、Tkinterで図形を回転させる手法を見ていきましょう。

前準備

TkinterのCanvasクラスに描画された図形を回転させます。

前準備として、次の\(3\)つを行いましょう。

Canvasを用意する

まずはTkinterのCanvasのインスタンスを作りましょう。


import tkinter as tk


if __name__ == "__main__":
    root = tk.Tk()

    canvas = tk.Canvas(root, width=500, height=500)
    canvas.pack()

    root.mainloop()

コードを実行すると、下記のキャンバスが出現します。

簡単ですね。

Tkinterのキャンバス

回転させる図形を多角形として描く

Canvasクラスのcreate_polygonメソッドで、回転させたい図形を描きます。

create_polygonメソッドは、Canvasクラスにある、多角形を描画するメソッドです。

引数に渡した座標同士を繋ぎ、多角形を描きます。

例えば、座標を\(4\)つ渡して四角形を描きましょう。


import tkinter as tk


root = tk.Tk()

canvas = tk.Canvas(root)
canvas.pack()

canvas.create_polygon(
    x_1, y_1,  # 座標1
    x_2, y_2,  # 座標2
    x_3, y_3,  # 座標3
    x_4, y_4,  # 座標4
)

root.mainloop()

こんなイメージです。

すずか
すずか

四角形を回転させたいんだけど、create_rectangle使っちゃダメ?

先生
先生

ダメです。

長方形や楕円などを描画するメソッドは、Tkinterで用意されています。

create_rectanglecreate_ovalメソッドなどがそうですね。

しかし、図形を回転させるのであれば上記のメソッドを使ってはいけません。

必ずcreate_polygonメソッドで描画してください。

とはいえ、どうすればいいかわからないですよね。

そこで、基本的な図形の描画をcreate_polygonメソッドで行う方法をまとめました。

create_polygonメソッドで図形を描く

長方形

長方形をcreate_polygonメソッドで描きます。

一筆書きの順序で、頂点を\(4\)つcreate_polygonメソッドに渡せばOKです。

例えば、①左上→②右上→③右下→④左下の順に頂点を取ることを考えます。

長方形の4隅の座標

上記の順に、座標をcreate_polygonメソッドの引数として渡すだけです。

一筆書きの順序であれば、開始点がどこでも良いですし、反時計回りでも構いません。

rectangle = canvas.create_polygon(
    x_1, y_1,  # 左上の座標
    x_2, y_2,  # 右上の座標
    x_3, y_3,  # 右下の座標
    x_4, y_4,  # 左下の座標
)

座標を具体的な数値で与えてやれば、例えば下記のように長方形ができます。

長方形

円・楕円

円・楕円をcreate_polygonメソッドで描きます。

長方形のときとは違い、座標を求めるのに一工夫が必要です。

すずか
すずか

まさか、円周の座標を\(1\)個ずつ指定しなきゃいけないの?

ご安心ください。

円・楕円周上の座標を生成する関数は、別記事にて既に作ってあります。

>>【Python】円周・楕円周上の座標を求める方法を徹底解説!プログラム付き!

get_arc_coords関数で生成した楕円の座標をcreate_polygonメソッドに渡します。

get_arc_coords関数に関しては上記の記事をご覧ください。

center = (x, y)  # 中心座標 
size = (width, height)  # 横×縦
angle_range = (0, 360)  # 角度の範囲

coords = get_arc_coords(
    center,
    size,
    angle_range,
)

oval = canvas.create_polygon(coords)

例えば、中心座標\((250,\ 250)\)・縦\(×\)横が\(400×300\)だと、次のような楕円になります。

楕円

扇形

扇形をcreate_polygonメソッドで描きます。

こちらも、先ほどのget_arc_coords関数を利用します。

角度の範囲は扇形の形に合わせ、お好きな数値にしてください。

center = (x, y)  # 中心座標 
size = (width, height)  # 横×縦
angle_range = (start, end)  # 角度の範囲

coords = get_arc_coords(
    center,
    size,
    angle_range,
)
coords.append(center)

sector = canvas.create_polygon(coords)

例として、先ほどの楕円の設定はそのままに、角度の範囲を\((0, 90)\)としましょう。

左下だけが残りましたね。

扇形

回転の中心を決める

どこを中心に図形を回転させるかを決めま

「この点の周りで図形を回転させたいんだ!」という強い意志があれば、回転の中心はその点で決まりです。

そうでなければ、無難に図形の重心\(=\)回転の中心としましょう。

すずか
すずか

はいはい、重心も計算しないといけないんでしょ。

ご安心ください。

任意の多角形の重心を求める関数は、別記事にて既に作ってあります。

>>【Python】図形の重心の求め方!凹多角形・凸多角形に対応!

重心を取得するget_centroid関数は、多角形の座標が引数です。

試しに、get_arc_coords関数で求めた楕円周上の座標から、get_centroid関数でその中心を取得できるか見てみます。

center = (250, 250)  # 中心座標 
size = (400, 300)  # 横×縦
angle_range = (0, 360)  # 角度の範囲

coords = get_arc_coords(
    center,
    size,
    angle_range,
)

centroid = get_centroid(coords)

実は、上記のコードの\(1\)行目のcenterが重心なのですが、一応テストしておきます。

print(centroid)
# [249.99999999999994, 250.0]

centerが\((250, 250)\)だったので、問題ないですね。

図形を自動で回転させる方法

図形を自動回転させる方法を、プログラムと共にご紹介します。

手順は次の\(2\)つです。

図形を自動的に回転させる手順

\(1\)つずつ見ていきましょう。

図形を回転させる関数を作成

まずは、図形を回転させる関数を用意します。

rotate_shaperotate_pointという\(2\)つの関数を作りました。


import math


def rotate_shape(canvas, tagOrId, center, angle, is_radian=False):
    # 図形の現在の座標を取得
    unrotated_coords = canvas.coords(tagOrId)

    # 回転した座標を計算する
    rotated_coords = rotate_point(unrotated_coords, center, angle, is_radian)

    # 回転後の座標を図形に設定する
    canvas.coords(tagOrId, rotated_coords)


def rotate_point(point, center, angle, is_radian=False):
    if not is_radian:
        angle = math.radians(angle)

    sin_angle = math.sin(angle)
    cos_angle = math.cos(angle)

    rotated_points = []

    for i in range(0, len(point), 2):
        x, y = point[i] - center[0], point[i + 1] - center[1]

        rotated_x = x * cos_angle - y * sin_angle + center[0]
        rotated_y = y * cos_angle + x * sin_angle + center[1]

        rotated_points.extend((rotated_x, rotated_y))

    return rotated_points

ここでは、図形を回転させるrotate_shape関数を紹介します。

rotate_shapeは、対象の図形を指定座標の周りに指定角度分回転させる関数です。

引数
変数名データ型必須 / 任意説明
canvastkinterのCanvasクラス必須図形が存在するCanvasオブジェクト
tagOrId文字列(タグ)・整数(ID)必須回転させる図形のタグ又はID
centerリスト・タプル必須回転の中心座標\((x,\ y)\)
座標は座標平面内に限る。
angle整数・浮動小数点必須回転角度
is_radian真偽値任意
デフォルトはFalse
angleの単位
Trueで弧度法に、Falseで度数法に切り替える。
rotate_shape関数の引数

なお、座標を回転移動させるrotate_point関数の詳細は、下記記事をご覧ください。

>> 任意点周りの回転移動を2通り解説!プログラムで自動化!

繰り返し処理を設定

Canvasクラスのafterメソッドを使い、回転処理を繰り返させます。

先ほどのrotate_shape関数は、図形を1回だけ回転させて終わってしまいます。

しかし、afterメソッドと組み合わせると、指定時間ごとに自動でrotate_shape関数を呼び出してくれます。

それにより、あたかも図形が自動回転しているように見えるのです。

以上を踏まえてコードを書いてみます。

def auto_rotate(canvas, tagOrId, center, angle, is_radian=False):
    # 図形を回転させる
    rotate_shape(canvas, tagOrId, center, angle, is_radian)

    # 10ミリ秒後に再び自分自身(auto_rotate)を呼び出す
    canvas.after(10, auto_rotate, canvas, tagOrId, center, angle, is_radian)

auto_rotate関数は図形を自動回転させます。

引数はrotate_shape関数と全く同じなのでご安心ください。

引数
変数名データ型必須 / 任意説明
canvastkinterのCanvasクラス必須図形が存在するCanvasオブジェクト
tagOrId文字列(タグ)・整数(ID)必須回転させる図形のタグ又はID
centerリスト・タプル必須回転の中心座標\((x,\ y)\)
座標は座標平面内に限る。
angle整数・浮動小数点必須回転角度
is_radian真偽値任意
デフォルトはFalse
angleの単位
Trueで弧度法に、Falseで度数法に切り替える。
auto_rotate関数の引数

では、下準備をして実際にauto_rotate関数を呼んでみましょう。

# ウィンドウの作成
root = tk.Tk()

# キャンバスの作成
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack()

# 図形の座標リストと重心座標の取得
shape_coords = [
    100, 100,
    400, 100,
    400, 400,
    100, 400,
]
center = get_centroid(shape_coords)

# 図形の描画
shape = canvas.create_polygon(shape_coords)

# 図形を自動的に回転させる
angle = 1  # 回転角度
auto_rotate(canvas, shape, center, angle)

root.mainloop()

実行結果です。

先生
先生

これは回転角度が\(1^{\circ}\)ですが、数値を上げれば回る速度も上がりますよ。

図形をマウス操作で回転させる方法

図形をクリック&ドラッグにより回転させる術をご紹介します。

すずか
すずか

マウスの動きとか検知しなきゃ、難しそう・・・。

先生
先生

bindメソッドでイベント設定をするだけですよ!

すずか
すずか

じゃあ、ドラッグ中の角度の計算はどうするん?

まったく問題ありません。

下記記事にて、任意の\(3\)点から角度を求めるプログラムはすでに用意しております。

>> 3点の座標から角度と回転方向を求める方法を解説!プログラムで自動化!

では、早速プログラムを見てみましょう。

画面内の任意の場所からマウス操作可能

画面のどこをドラッグしても図形を回転できるようにします。

①クリック時・②ドラッグ時・③リリース時の、計\(3\)つのイベントを拾います。

def on_click(event):
    global clicked_coords
    global centroid

    # クリックされた座標を記録
    clicked_coords= (event.x, event.y)

    # 重心を計算
    centroid = get_centroid(canvas.coords(tagOrId))

    # ドラッグとリリースのイベントをバインド
    root.bind('<B1-Motion>', on_drag)
    root.bind('<ButtonRelease-1>', on_release)


def on_drag(event):
    global clicked_coords
    global centroid

    # 前回の座標・ドラッグ中の座標・重心から角度を計算
    current_coords = (event.x, event.y)
    angle = get_signed_angle(clicked_coords, current_coords, centroid)

    # 図形を回転
    rotate_shape(canvas, tagOrId, center, angle)

    # 座標を更新
    clicked_coords = current_coords


def on_release(event):
    global clicked_coords
    global centroid

    # 変数をリセット
    clicked_coords = None
    centroid = None

    # ドラッグとリリースのイベントをアンバインド
    root.unbind('<B1-Motion>')
    root.unbind('<ButtonRelease-1>')


# ウィンドウの作成
root = tk.Tk()

# キャンバスの作成
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack()

# 図形の座標リストと重心座標の取得
shape_coords = [
    100, 100,
    400, 100,
    400, 400,
    100, 400,
]
center = get_centroid(shape_coords)

# 図形の描画
tagOrId = canvas.create_polygon(shape_coords)

# イベント設定
root.bind('<ButtonPress-1>', on_click)

root.mainloop()

①〜③の各イベントには、それぞれon_clickon_dragon_releaseというハンドラーがあります。

イベントとハンドラーの対応関係は下表にまとめました。

イベントと関数(ハンドラー)
イベント関数(ハンドラー)機能
<ButtonPress-1>on_clickクリック時に座標を記録し、重心を計算。また、ドラッグとリリースのイベントをバインド。
<B1-Motion>on_dragドラッグ時に回転角度を計算し、図形を回転。
<ButtonRelease-1>on_releaseリリース時に変数をリセットし、バインドされたイベントをアンバインド。
検知するイベントとそのハンドラー

では、コードを実行して確かめてみましょう。

なお、動画では回転中のカーソルの形状を変えています。

図形内からのみマウス操作可能

図形内にカーソルがないと回転できないようにします。

コードはとても簡単。

というのも、基本的には先ほどのroot.bind(event, func)canvas.tag_bind(tagOrId, event, func)に変えるだけですからね。

def on_click(event):
    global clicked_coords
    global centroid

    # クリックされた座標を記録
    clicked_coords= (event.x, event.y)

    # 重心を計算
    centroid = get_centroid(canvas.coords(tagOrId))

    # ドラッグとリリースのイベントをバインド
    canvas.tag_bind(tagOrId, '<B1-Motion>', on_drag)
    canvas.tag_bind(tagOrId, '<ButtonRelease-1>', on_release)


def on_drag(event):
    global clicked_coords
    global centroid

    # 前回の座標・ドラッグ中の座標・重心から角度を計算
    current_coords = (event.x, event.y)
    angle = get_signed_angle(clicked_coords, current_coords, centroid)

    # 図形を回転
    rotate_shape(canvas, tagOrId, center, angle)

    # 座標を更新
    clicked_coords = current_coords


def on_release(event):
    global clicked_coords
    global centroid

    # 変数をリセット
    clicked_coords = None
    centroid = None

    # ドラッグとリリースのイベントをアンバインド
    canvas.tag_unbind(tagOrId, '<B1-Motion>')
    canvas.tag_unbind(tagOrId, '<ButtonRelease-1>')


# ウィンドウの作成
root = tk.Tk()

# キャンバスの作成
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack()

# 図形の座標リストと重心座標の取得
shape_coords = [
    100, 100,
    400, 100,
    400, 400,
    100, 400,
]
center = get_centroid(shape_coords)

# 図形の描画
tagOrId = canvas.create_polygon(shape_coords)

# イベント設定
canvas.tag_bind(tagOrId, '<ButtonPress-1>', on_click)
canvas.tag_bind(tagOrId, '<Leave>', on_release)  # マウスが図形外に出たら操作不可

root.mainloop()

実行してみましょう。

わかりづらいかも知れませんが、図形内のクリックのみ有効となっています。

まとめ

今回は、Tkinterで図形の回転を実現する方法を解説しました。

図形の自動回転マウス操作による回転のプログラムを実装しましたので、参考にしていただければ幸いです。

本記事では正方形を回転させる例しか示しませんでしたが、以下のように多種多様な図形に応用可能です。

こうした特殊な図形の描き方も、徐々に発信していければと思います。

Tkinterで角丸長方形を作る方法のアイキャッチ 【Tkinter】角丸長方形の作り方を解説!角の丸みも変更可能!

最後までお付き合いいただき、ありがとうございました。

コメントを残す

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

CAPTCHA