- 【結論】Tkinterで図形は回転できる!
- 図形を自動回転させる方法
- 図形をマウス操作で回転させる方法
こんにちは、Youta(@youta_blog)です。
今回は、Tkinterにおける図形の回転のお話です。
Tkinterってさ、図形の回転ってできる?
結論を言うと、Tkinterに図形を回転させる機能はありません。
ただ、そうした機能を自作できますよ。
現状、Tkinterでは図形の回転がサポートされていません。
では私たちは諦めるしかないのでしょうか?
いいえ、機能がないなら自分で作れば良いのです。
実際、Tkinterで図形を回転させるプログラムを作ってみました。
そう、できないことはないのです。
本記事では、解説・確認用コード・実行結果の3点をご用意しております。
また必要に応じてコードの解説をしていますので、初心者でも安心です。
記事の信頼性は、確認用コードと実行結果が保証します。
少しでも疑問に感じたら、是非ともコードをコピペしてPCで確かめてみてください。
それでは、Tkinterで図形を回転させる手法を見ていきましょう。
Contents
前準備
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()
コードを実行すると、下記のキャンバスが出現します。
簡単ですね。
回転させる図形を多角形として描く
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_rectangle
・create_oval
メソッドなどがそうですね。
しかし、図形を回転させるのであれば上記のメソッドを使ってはいけません。
必ずcreate_polygon
メソッドで描画してください。
とはいえ、どうすればいいかわからないですよね。
そこで、基本的な図形の描画をcreate_polygon
メソッドで行う方法をまとめました。
長方形をcreate_polygon
メソッドで描きます。
一筆書きの順序で、頂点を\(4\)つcreate_polygon
メソッドに渡せばOKです。
例えば、①左上→②右上→③右下→④左下の順に頂点を取ることを考えます。
上記の順に、座標を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_shape
とrotate_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
は、対象の図形を指定座標の周りに指定角度分回転させる関数です。
変数名 | データ型 | 必須 / 任意 | 説明 |
---|---|---|---|
canvas | tkinterの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
関数と全く同じなのでご安心ください。
変数名 | データ型 | 必須 / 任意 | 説明 |
---|---|---|---|
canvas | tkinterの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_click
・on_drag
・on_release
というハンドラーがあります。
イベントとハンドラーの対応関係は下表にまとめました。
イベント | 関数(ハンドラー) | 機能 |
---|---|---|
<ButtonPress-1> | on_click | クリック時に座標を記録し、重心を計算。また、ドラッグとリリースのイベントをバインド。 |
<B1-Motion> | on_drag | ドラッグ時に回転角度を計算し、図形を回転。 |
| 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】角丸長方形の作り方を解説!角の丸みも変更可能!最後までお付き合いいただき、ありがとうございました。