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

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

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

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

Python

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

Q&A

解決済

1回答

1116閲覧

python 、Tkinter、画像表示、sleep関数

sib

総合スコア6

Tkinter

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

Python

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

0グッド

0クリップ

投稿2021/06/01 10:27

前提・実現したいこと

時間が立つと画像が増えていくシステム
listという変数に複数の要素を入れ、その要素を座標として扱い、複数の場所に画像表示まで成功。

sleep関数を使用し画像を色んな場所に表示したいです。
どうかよろしくお願いいたします。

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

sleep関数で時間を置いて別の座標に画像を貼り付けが失敗。
sleepを使用すると、画像が表示される前に黒い画面(コンソール?)でprint文がsleepしながら実行されました。
前回張り付けた画像は残したままでいいです。

該当のソースコード

import tkinter import time # ウィンドウ作成 root = tkinter.Tk() root.title("王様をいっぱい表示") root.minsize(1000, 480) # キャンバス配置と画像読み込み canvas = tkinter.Canvas(root,width=1000,height=800) canvas.pack() img = tkinter.PhotoImage(file = "img4/chap4-1-1.png") # 画像表示 def hyouji(): canvas.create_image(list[i], list[i+1], image=img) # リスト list=[700,15,400,90,1000,600,700,800] #繰り返し処理 for i in range(len(list)-1): print(len(list)) canvas.delete("img") hyouji() #time.sleep(1) print(i) root.update() root.mainloop()

試したこと

sleepを使用すると、画像が表示される前に黒い画面(コンソール?)でprint文がsleepしながら実行されました。

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

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答1

0

ベストアンサー

GUIプログラミングでは、描画やマウス・キーボード等のイベント処理を行う必要があるので、
プログラムの動作を止める time.sleep は(同一スレッド内では) 使ってはいけません。

sleep 相当の処理を行いたい場合は、
GUIライブラリの提供するタイマー機能(tkinter では after 関数) を使います。

コードは、指定時間後に関数を実行となるので、forループではなく、
コールバック形式に書き換えが必要です。

書き換え例。GUIでは良くないコード

python

1for n in range(10): 2 print(n) 3 time.sleep(1) 4 root.update()

タイマー&コールバック形式に書き換え

python

1def onload(): 2 def update(n): 3 if n < 10: 4 print(n) 5 root.after(1000, update, n+1) # 1秒後に引数を変えて実行 6 update(n=0) 7 8root.after_idle(onload)

ジェネレーターを使った書き換え

python

1import gentimer # 要ライブラリ: pip install gentimer 2 3def onload(): 4 for n in range(10): 5 print(n) 6 yield 1 7 8root.after_idle(gentimer.tk, onload())

追記: 前回の画像を残す場合、canvas.delete は省けます。

他の改善点:

zip 関数を使うと、ループの記述をより簡潔に書けます。

python

1pos = [700,15,400,90,1000,600,700,800] 2for x, y in zip(pos, pos[1:]): 3 print(x, y) 4 5## x,y のループ 6# 700, 15 7# 15, 400 8# 400, 90

変数 i はグローバル変数にするのではなく、関数の引数にして渡したほうが良いです。
理由: スコープが広過ぎる短い名前の変数は、衝突が起こりやすく問題になる為。

投稿2021/06/01 10:51

編集2021/06/01 11:00
teamikl

総合スコア8664

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

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

sib

2021/06/01 12:44

ご丁寧ご回答ありがとうございます。 余りプログラミングは詳しくないので質問させてください。 タイマー&コールバック形式に書き換えの所で質問です。 root.after(1000, update, n+1) # 1秒後に引数を変えて実行  このupdateの意味がわからないです。 update(n=0) root.after_idle(onload)   このafter_idle(onload)でどういったことになるのでしょうか。               after_idleは他の処理が無い時に実行される分であるというのは、調べて                    分かりました。               関数の中に関数を入れており、まだ私にはわからなくて教えてください。
teamikl

2021/06/02 00:26 編集

質問のコードでは、mainloop 前に書かれてますが root.after_idle(onload) とすることで、 root.mainloop() 内から呼びだされます。 (GUIの「表示後」に実行される) そして、GUIのイベントループが稼働するので root.update() 呼び出しは不要になります。 update() は onload() 内に定義した関数です。(関数名は何でも良い) 1秒(1000ms) 後にupdate 関数を呼び出し 引数 n=0 1秒(1000ms) 後にupdate 関数を呼び出し 引数 n=1 1秒(1000ms) 後にupdate 関数を呼び出し 引数 n=2 : といった挙動で、n == 10 になるとそれ以上 root.after を呼ばないので そこでタイマーの実行が止まります。 関数内で定義してる理由は、他で使う必要が無い為です。 その場でしか使わない為、内部で定義することで短い適当な名前で済ませてます。 外で定義する場合は、他と被らないようにきちんと もう少し長い関数名を考えた方が良いです。 for 文のブロック内で実行していたことを別関数にして、 タイマーで sleep したい時間後に呼び出す、といった ループ文の置き換えです。 ※ ループ文の置き換えが必要な理由は、イベントループ内で実行される為
teamikl

2021/06/02 00:57 編集

>タイマー&コールバック形式に書き換え に関して GUI の部分を省いて考えると(※ コメント内の為インデントは全角) # 元のコード for ループ def func():  for n in range(10):   print(n) # while ループに置き換え def func(n=0):  while n < 10:   print(n)   n += 1 # 「再帰関数」に置き換え def func(n=1):  if n < 10:   print(n)   func(n+1) ここまで出来れば、再帰呼び出しになってる部分を タイマーでの呼び出し (root.afterで登録) にして完了です。 ループ中に変化する値: 変数 n ループ中に実行したい式: print(n) と考えると解りやすいのではないでしょうか。 因みに、タイマー&コールバック形式では 複雑になってくると処理が追いにくくなるという問題があります。 その場合はジェネレータの導入を検討してください。 関連: https://teratail.com/questions/327275 仕組みの説明は難しいですが、使うだけなら sleep したいところを yield にして、呼び出し方法を変えるだけです。 関連2: https://teratail.com/questions/339555 回答に、「よくない例」として root.update() time.sleep を使った実装の 実際の問題点を提示してます。
sib

2021/06/04 09:10

返信が遅くなり申し訳ありません。 分かりやすい説明、補足ありがとうありがとうございます。理解出来ました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問