前提・実現したいこと
tkinterを用いたGUIのアプリケーションを作っているのですが、実行ボタンが押された際に関数を実行して目的の動作を行わせ、停止ボタンが押された際に停止させようと思っています。ボタンを押した際の動作はうまくいったのですが、停止ボタンを押そうとしてもstartの関数内でループしてしまい停止ボタンを押すことができません、どうすればよいでしょうか
該当のソースコード
from mutagen.mp3 import MP3 as mp3
import numpy as np
import cv2
import pygame
import time
import threading
import sys
import tkinter
root =tkinter.Tk()
root.title(u"app")
root.geometry("600x700")
def end1():
t=1
def getCircle1(frame, lower_color, upper_color):
MIN_RADIUS = 25
# HSVによる画像情報に変換
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# ガウシアンぼかしを適用して、認識精度を上げる
blur = cv2.GaussianBlur(hsv, (9, 9), 0)
# 指定した色範囲のみを抽出する
color = cv2.inRange(blur, lower_color, upper_color)
# オープニング・クロージングによるノイズ除去
element8 = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], np.uint8)
oc = cv2.morphologyEx(color, cv2.MORPH_OPEN, element8)
oc = cv2.morphologyEx(oc, cv2.MORPH_CLOSE, element8)
# 輪郭抽出
img, contours, hierarchy = cv2.findContours(oc, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print("{} contours.".format(len(contours)))
if len(contours) > 0:
# 一番大きい赤色領域を指定する
contours.sort(key=cv2.contourArea, reverse=True)
cnt = contours[0]
# 最小外接円を用いて円を検出する
(x, y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
# 円が小さすぎたら円を検出していないとみなす
if radius < MIN_RADIUS:
return None
else:
return center, radius
else:
return None
def getCircle2(frame, lower_color, upper_color):
MIN_RADIUS = 25
# HSVによる画像情報に変換
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# ガウシアンぼかしを適用して、認識精度を上げる
blur = cv2.GaussianBlur(hsv, (9, 9), 0)
# 指定した色範囲のみを抽出する
color = cv2.inRange(blur, lower_color, upper_color)
# オープニング・クロージングによるノイズ除去
element8 = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], np.uint8)
oc = cv2.morphologyEx(color, cv2.MORPH_OPEN, element8)
oc = cv2.morphologyEx(oc, cv2.MORPH_CLOSE, element8)
# 輪郭抽出
img, contours, hierarchy = cv2.findContours(oc, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print("{} contours.".format(len(contours)))
if len(contours) > 0:
# 一番大きい赤色領域を指定する
contours.sort(key=cv2.contourArea, reverse=True)
cnt = contours[0]
# 最小外接円を用いて円を検出する
(x, y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
# 円が小さすぎたら円を検出していないとみなす
if radius < MIN_RADIUS:
return None
else:
return center, radius
else:
return None
def start():
# 内蔵カメラを起動(カメラが一つしか繋がっていない場合は、引数に0を渡せば良い)
cap = cv2.VideoCapture(1)
flgb1=0
flgt1=0
flgb2=0
flgt2=0
while True:
# 赤色の円を抽出する
frame = cap.read()[1]
getframe1 = getCircle1(frame, np.array([130,80,80]), np.array([200,255,255]))
#黄色の円を検出
getframe2 = getCircle2(frame, np.array([30,50,50]), np.array([80,255,255]))
#赤が見つかったら
if getframe1 is not None:
# 見つかった円の上に青い円を描画
# getframe[0]:中心座標、getframe[1]:半径
cv2.circle(frame, getframe1[0], getframe1[1], (255, 0, 0), 2)
if flgb1==0:#flgb1が0の時
filename = 'ファイルパス' #再生したいmp3ファイル
pygame.mixer.init()
pygame.mixer.music.load(filename) #音源を読み込み
mp3_length = mp3(filename).info.length #音源の長さ取得
pygame.mixer.music.play(1) #再生開始。1の部分を変えるとn回再生(その場合は次の行の秒数も×nすること)
time.sleep(mp3_length + 0.25) #再生開始後、音源の長さだけ待つ(0.25待つのは誤差解消)
pygame.mixer.music.stop() #音源の長さ待ったら再生停止
flgb1=1#flgb1に1を代入し一回だけの処理
flgt1=0#flgt1に0を代入し時間処理を可能にする
at1=time.time()+5#今の時間+5秒
#黄色が見つかったら
if getframe2 is not None:
# 見つかった円の上に青い円を描画
# getframe[0]:中心座標、getframe[1]:半径
cv2.circle(frame, getframe2[0], getframe2[1], (255, 0, 0), 2)
if flgb2==0:#flgb2が0の時
filename = 'ファイルパス' #再生したいmp3ファイル
pygame.mixer.init()
pygame.mixer.music.load(filename) #音源を読み込み
mp3_length = mp3(filename).info.length #音源の長さ取得
pygame.mixer.music.play(1) #再生開始。1の部分を変えるとn回再生(その場合は次の行の秒数も×nすること)
time.sleep(mp3_length + 0.25) #再生開始後、音源の長さだけ待つ(0.25待つのは誤差解消)
pygame.mixer.music.stop() #音源の長さ待ったら再生停止
flgb2=1#flgb2に1を代入し一回だけの処理
flgt2=0#flgt2に0を代入し時間処理を可能にする
at2=time.time()+5#今の時間+5秒
nt1=time.time()
nt2=time.time()
if flgb1==1 and flgt1==0 and at1<=nt1:
flgb1=0
flgt1=1
if flgb2==1 and flgt2==0 and at2<=nt2:
flgb2=0
flgg2=1
# 検出結果とともに映像を表示
cv2.imshow('Circle Detect', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 終了時にカメラを解放
cap.release()
cv2.destroyAllWindows()
btn1=tkinter.Button(root, text='aモード 開始',font=(" ",20),bg='#f0e68c',fg='#ff0000',height=5,width=14,command=start)
btn1.place(x=50,y=170)
btn2=tkinter.Button(root, text='aモード 停止',font=(" ",20),bg='#f0e68c',fg='#ff0000',height=5,width=14,command=end1)
btn2.place(x=340,y=170)
root.mainloop()
試したこと
ボタンが押された際にstartの関数を呼び出し、動作させることに成功した。
停止ボタンが押された際にstart内の動作を停止させたい。
補足情報(FW/ツールのバージョンなど)
python3.7
opencv-python(3.4.3.18)
numpy(1.15.1)
pygame(1.9.4)
muatagen(1.41.1)
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
今回のように連続的な処理を行う場合は、その処理を Buttonからのイベント関数内で行うのではなく別スレッドにて行うようにし、Buttonからのイベント関数では単にスレッドの開始処理、停止処理のみを行うようにします。
質問に挙げていただいたアプリを動作させる環境が整わなかったので、とりあえず雛形となるスレッドを動作させる部分のみのサンプルを提示しておきますので参考にしてください
import numpy as np
import time
import threading
import tkinter
root =tkinter.Tk()
root.title(u"app")
root.geometry("600x700")
thread = None
running = False
def DoSomething():
global running
# ここ以下メインとなる処理を書く
# Stopボタンが押された場合, end関数内で running を Falseにするので
# 速やかにこの関数内の処理を終了するようにしてください
running = True
print("Thread Start")
while running:
print("MainLoop")
time.sleep(1)
print("Thread Stop")
def start():
global thread
# スレッドが既に停止している場合は無視
if thread is not None:
return
# DoSomething関数を別スレッドで実行する
thread = threading.Thread(target=DoSomething)
thread.start()
def end():
global running,thread
# スレッドが既に停止している場合は無視
if thread is None:
return
# runningフラグを落とす。DoSomething関数が終了するハズ
running = False
# スレッドが停止するまで待つ
thread.join()
thread = None
btn1=tkinter.Button(root, text='aモード 開始',font=(" ",20),bg='#f0e68c',fg='#ff0000',height=5,width=14,command=start)
btn1.place(x=50,y=170)
btn2=tkinter.Button(root, text='aモード 停止',font=(" ",20),bg='#f0e68c',fg='#ff0000',height=5,width=14,command=end)
btn2.place(x=340,y=170)
root.mainloop()
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.33%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/12/06 17:13
何とか無事に完成させることができました!