質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Anaconda

Anacondaは、Python本体とPythonで利用されるライブラリを一括でインストールできるパッケージです。環境構築が容易になるため、Python開発者間ではよく利用されており、商用目的としても利用できます。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1839閲覧

tkinterの処理を軽くしたい

ppap

総合スコア68

Anaconda

Anacondaは、Python本体とPythonで利用されるライブラリを一括でインストールできるパッケージです。環境構築が容易になるため、Python開発者間ではよく利用されており、商用目的としても利用できます。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2022/09/06 07:50

編集2022/09/06 12:40

前提

pythonのtkinterでクリックにより画像を回転させ、再度クリックすることで回転が止まるアニメーションを作成しています。

afterメソッドで無限に画像を回転させる処理を行ったうえで、後から回転をストップさせるイベントを呼び出し、アニメーションの操作を実現しています。

実現したいこと

画像を任意の角速度で回転させたいです。
現在、5[deg/s]や1[deg/s]で回転させようとしても、処理が追い付かずに想定よりゆっくりな回転速度になってしまいます。

しかし、現在備わっている
・左クリックで左回転スタート
・右クリックで右回転スタート
・左右いづれかのクリックで回転をストップ
という機能は保持したままにしたいという課題があります。

発生している問題・エラーメッセージ

エラーメッセージはありません。

該当のソースコード

python

1import tkinter as tk 2from PIL import Image, ImageTk 3import datetime 4 5 6#新規ウィンドウを作成 7root = tk.Tk() 8root.title("SVV測定") 9root.attributes("-fullscreen", True) 10 11 12#画面サイズの指定 13WIDTH = root.winfo_screenwidth() 14HEIGHT = root.winfo_screenheight() 15 16 17#ウィンドウ上にフレームを作成 18frame = tk.Frame(root) 19frame.place(x = -5, y = -5, width = WIDTH + 10, height = HEIGHT + 10) 20 21 22#フレーム上に、キャンバスを作成 23canvas = tk.Canvas(frame, width = WIDTH, height = HEIGHT, bg = "black") 24canvas.place(x = 0, y = 0, width = WIDTH + 10, height = HEIGHT + 10) 25 26 27#画像のプリセット角を"cs"として定義 28cs = 0 29 30 31#キャンバスにSVV画像を貼り付け 32img = Image.open("D:\EXPERIMENT\programming\Python\SVV\svv_bar.png") 33img = img.rotate(cs) 34tkimg = ImageTk.PhotoImage(img, width = WIDTH, height = HEIGHT) 35canvas.create_image(WIDTH/2, HEIGHT/2, image=tkimg, anchor="center", tags="img") 36 37 38#キャンバスに、透明な画像を貼り付け 39img_snow = Image.open("D:\EXPERIMENT\programming\Python\SVV\svv_snow.png") 40tkimg_snow = ImageTk.PhotoImage(img_snow, width = WIDTH, height = HEIGHT) 41canvas.create_image(WIDTH/2, HEIGHT/2, image=tkimg_snow, anchor="center", tags="img_snow") 42 43 44#"c"の初期値を定義 45c = 0 46 47 48#角速度の指定 49interval = datetime.timedelta(milliseconds = 20) 50 51 52#イベントが発生したときの処理 53def press_L(event): 54 global interval 55 next_time = datetime.datetime.now() + interval 56 #左クリックの回数によって、左回転させる 57 global img 58 global tkimg 59 global c 60 c = c + 0.1 61 canvas.delete("all") 62 IMG = img.rotate(c, resample = Image.BICUBIC) 63 tkimg = ImageTk.PhotoImage(IMG) 64 canvas.create_image(WIDTH/2, HEIGHT/2, image = tkimg, anchor = "center", tags = "img") 65 canvas.create_image(WIDTH/2, HEIGHT/2, image=tkimg_snow, anchor = "center", tags="img_snow") 66 now = datetime.datetime.now() 67 delay_ms = int((next_time - now).total_seconds() * 1000) 68 id = root.after(delay_ms, press_L, None) 69 def stop_L(event): 70 #左クリックで、左回転をストップする 71 root.after_cancel(id) 72 canvas.tag_bind("img_snow", "<ButtonPress-1>", press_L) 73 canvas.tag_bind("img_snow", "<ButtonPress-3>", press_R) 74 def stop_L_by_R(event): 75 #右クリックでも、左回転をストップする 76 root.after_cancel(id) 77 canvas.tag_bind("img_snow", "<ButtonPress-3>", press_R) 78 canvas.tag_bind("img_snow", "<ButtonPress-1>", stop_L) 79 canvas.tag_bind("img_snow", "<ButtonPress-3>", stop_L_by_R) 80 81 82def press_R(event): 83 global interval 84 next_time = datetime.datetime.now() + interval 85 #右クリックの回数によって、右回転させる 86 global img 87 global tkimg 88 global c 89 c = c - 0.1 90 canvas.delete("all") 91 IMG = img.rotate(c, resample = Image.BICUBIC) 92 tkimg = ImageTk.PhotoImage(IMG) 93 canvas.create_image(WIDTH/2, HEIGHT/2, image = tkimg, anchor = "center", tags = "img") 94 canvas.create_image(WIDTH/2, HEIGHT/2, image=tkimg_snow, anchor = "center", tags="img_snow") 95 now = datetime.datetime.now() 96 delay_ms = int((next_time - now).total_seconds() * 1000) 97 id = root.after(delay_ms, press_R, None) 98 def stop_R(event): 99 #右クリックで、右回転をストップする 100 root.after_cancel(id) 101 canvas.tag_bind("img_snow", "<ButtonPress-3>", press_R) 102 canvas.tag_bind("img_snow", "<ButtonPress-1>", press_L) 103 def stop_R_by_L(event): 104 #左クリックでも、右回転をストップする 105 root.after_cancel(id) 106 canvas.tag_bind("img_snow", "<ButtonPress-1>", press_L) 107 canvas.tag_bind("img_snow", "<ButtonPress-3>", stop_R) 108 canvas.tag_bind("img_snow", "<ButtonPress-1>", stop_R_by_L) 109 110 111def finish(event): 112 #Enterキーを押すことで全画面表示を終了し、回転角度を取得する 113 root.destroy() 114 print(-c) 115 116 117#左クリックで呼び出すイベント 118canvas.tag_bind("img_snow", "<ButtonPress-1>", press_L) 119 120 121#右クリックで呼び出すイベント 122canvas.tag_bind("img_snow", "<ButtonPress-3>", press_R) 123 124 125#Enterキーで呼び出すイベント 126root.bind("<Return>", finish) 127 128 129#メインループ 130root.mainloop() 131

上記の処理に使用した画像を作成するコード

python

1#---SVV測定に使用する画像の作成--- 2 3from PIL import Image, ImageDraw 4 5#背景色が黒色のキャンパスを作成。 6im = Image.new('RGB', (500, 500), (0, 0, 0)) 7draw = ImageDraw.Draw(im) 8 9 10#中央に線を描画 11draw.line((50, 250, 450, 250), fill=(255, 255, 255), width=10) 12 13 14#同じサイズの画像を作成 15im_clear = Image.new("RGBA", im.size, (0, 0, 0, 0)) 16width = im.size[0] 17height = im.size[1] 18 19 20#色ごとに処理 21for x in range(width): 22 for y in range(height): 23 pixel = im.getpixel( (x, y) ) 24 25 # 黒なら処理しない 26 if pixel[0] == 0 and pixel[1] == 0 and pixel[2] == 0: 27 continue 28 29 # 黒以外なら、用意した画像にピクセルを書き込み 30 im_clear.putpixel( (x, y), pixel ) 31 32 33#SVVで表示させるバーをpng形式で保存 34im_clear.save("D:\EXPERIMENT\programming\Python\SVV\svv_bar.png", quality=100) 35 36 37 38#---透明なpng画像の作成--- 39 40#画像サイズの指定 41WIDTH = 1600 42HEIGHT = 1000 43 44 45#新しい画像を作成 46im_snow = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0)) 47 48 49#新しい画像をもう一つ作成 50im_snow2 = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0)) 51 52 53#色ごとに処理 54for x in range(width): 55 for y in range(height): 56 pixel = im_snow2.getpixel( (x, y) ) 57 58 # 黒なら処理しない 59 if pixel[0] == 0 and pixel[1] == 0 and pixel[2] == 0: 60 continue 61 62 # 黒以外なら、用意した画像にピクセルを書き込み 63 im_snow.putpixel( (x, y), pixel ) 64 65 66#透明な画像をpng形式で保存 67im_snow.save("D:\EXPERIMENT\programming\Python\SVV\svv_snow.png", quality=100) 68

試したこと

先ず、アニメーションの更新速度を遅くする方法を試しました。
結果として、更新速度を200ミリ秒、1回の更新で回転する角度を1度まで解像度を下げても、想定より遅い角速度になってしまいました。

次に、扱っている画像データを軽くしようと試みました。
①"svv_bar"と名付けている棒が描画されたpng画像に関して400x400 ~ 600x600 の範囲で様々な数値を試したところ、500x500での処理速度が最も早くなりました。
※400x200のような長方形だと、画像が回転した際に見切れてしまいました。

②"svv_snow"と名付けている何も描画されていないpng画像のサイズを変更しました。
こちらは画面全体を覆うだけの広さが必要なので、画面以上の大きさで何パターンか試したところ、1600x1000において処理速度が最速となりました。

③そもそも画像を2種類用いていることが処理が重くなる原因かと考え、"svv_bar"のサイズを大きくして動作を確認しました。
"svv_bar"のサイズが大きくなるほど処理が重くなっていったので、こちらの方法は良くなさそうです。

最後に、全てのcanvas.tag_bindをroot.bindに変更し、透明な画像を用いなくても動作するようにしてみました。
用いる画像が少なくなる分、処理が軽くなるかと思ったのですが、大差はありませんでした。

補足情報(FW/ツールのバージョンなど)

AnacondaでPythonを使用しています。
・Anacondaのバージョン:conda 4.12.0
・Pythonのバージョン:Python 3.9.12
・Windowsのバージョン:version 21H2

デバイスの仕様
・プロセッサ:11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz 2.80 GHz
・実装RAM:16.0 GB

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shiracamus

2022/09/06 08:49 編集

1枚の画像処理にどれくらいの時間がかかっているのですか? その処理時間から1秒間に何枚表示できるか決まると思うのですが。 事前に回転させた画像を生成しておいて、単純に描画するだけのループ処理にはできないのですか?
ppap

2022/09/06 12:42 編集

>1枚の画像処理にどれくらいの時間がかかっているのですか? 毎回一定ではありませんが、現在の手順だと20~30msほど時間がかかっています。 時間がかかりすぎていることにより、目標の角速度に到達できていないものと思われるため、処理時間を短縮できる別の処理に関して、アイデアを募集しています。 ■「試したこと」にも記載しているように、200ミリ秒まで間隔を広げても角速度が遅くなってしまうことから、原因は他の箇所にもあると思われます。 >事前に回転させた画像を生成しておいて、単純に描画するだけのループ処理にはできないのですか? 0.1deg刻みの画像を表示させたく、そのような画像をまとめて作成したところ、たまにスキップされてしまう角度があったため、予め作成する方法をとっていません。
ppap

2022/09/06 15:34

>事前に回転させた画像を生成しておいて、単純に描画するだけのループ処理にはできないのですか? 不足なく回転させた画像を生成することができたため、単純に描画するだけのコードを試してみましたが、処理速度は同等でした。
shiracamus

2022/09/06 22:53 編集

1秒に1枚の表示なら絶対に処理が間に合うでしょうから、1秒間隔で5度の回転した図形を360度分表示してみて、72秒きっかりで終わるか確認してみてはいかがしょうか?
ppap

2022/09/07 02:03

1秒に1枚であっても、72秒きっかりでは終わりませんでした。 72秒で355度の回転でした。
shiracamus

2022/09/07 02:33

なので、絶対時刻の設定が間違っています。 もう一つの方の質問の方にもコメントしました。
ppap

2022/09/07 02:38

アドバイスをありがとうございます。 もう一つの方の質問に対するコメントも拝見いたしました。
guest

回答2

0

root.after で渡している時間(delay_ms) が 0 になって描画が行われてないのではないでしょうか。delay_ms が負数になることも考慮して、例えば、delay_ms が 0以下なら 1 にするといった処理を入れてはどうでしょう。
(自分の環境ではそれで回るようになりました。)
(最初の回答から修正)

投稿2022/09/06 23:19

編集2022/09/06 23:40
sigsegv

総合スコア895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ppap

2022/09/07 02:08

回答をありがとうございます。 元々のコードでも回りはするのですが、上記の処理を行うことで5[deg/s]の回転が可能になったということでしょうか? (現在20ミリ秒に0.1deg回転させるコードにしているため、5[deg/s]で回転したかが気になっています) 回答で頂いたアドバイスを基に、press_L中の " delay_ms = int((next_time - now).total_seconds() * 1000)" の下に、 if delay_ms > 0: id = root.after(delay_ms, press_L, None) else: delay_ms = 1 id = root.after(delay_ms, press_L, None) を追記しましたが、私の環境では5[deg/s]で回転しませんでした。 sigsegvさんが試してくださったコードとは、どのように異なっているでしょうか?
sigsegv

2022/09/08 01:17

私のPC環境で質問のコードを実行すると、「画面が変わらない、たまに一定時間後の画面に飛んでいる」という現象でした。その原因は、回転した図形の計算にほぼ 20ms 前後掛かっていて、回答に書いたように root.after で渡す時間が 0ms になっていたためでした。 修正後に 5 deg/sec で回転するかどうかは確認していません。 というか、私の環境では 20ms でタイマーを繰り返すことができなかったのです。 なので、求めている回答になってないのかもしれません…
ppap

2022/09/08 11:18

お忙しいところ返信を頂き、ありがとうございます。 環境によって動作の状況がかなり異なるのですね。 「root.after で渡す時間が 0ms になっていた」というアドバイスのおかげで他の方法を思いつき、解決に至りました。 ありがとうございました。
guest

0

自己解決

クリックによって"press_L"を呼び出す前に別の関数を呼び出し、そこで基準となる時刻を読み込んでからpress_Lを無限ループさせるという方法で上手く動作しました。

投稿2022/09/08 11:20

ppap

総合スコア68

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問