LoginSignup
mckie
@mckie

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

tkinterでmainloopは記述しているが、ウィンドウが一瞬で消えてしまう

解決したいこと

tkinterを使い、画像と文字をウィンドウに表示させようとしていますが、下記のエラーが出てしまい、画面が一瞬で消えてしまいます。mainloop()は使っているので一瞬で消える原因がわかりません。

プログラムの詳細

openCVを使って、カメラ画像を取得し、その画像から色検出、物体検出をするプログラムを
作っています。
snapshotを撮影すると、webカメラのウィンドウは消えてsnapnumに1が入り、色検出、物体検出して、画像と"OK","NG"判断のウィンドウを表示させます。
一回目のスナップショットのウィンドウは正しく表示され、正しく終了できますが
二回目のウィンドウ(色検出、物体検出後のOK、NG表示)が一瞬で消えてしまいます。
mainloopは使っていますが、表示できません。
エラーを調べると、disp_imageが定義されていないときに呼ばれているとの事らしいですが、解決方法がわかりませんでした。どなたかわかる方がいらしたらご教授お願いします。

発生している問題・エラー

invalid command name "2343488254208disp_image"
    while executing
"2343488254208disp_image"
    ("after" script)
#"disp_image"の前の数字はプログラムを動かす度に変化します。

該当するソースコード

import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk, ImageOps  # 画像データ用
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np
from tkinter import font
import time

import cv2

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)
        self.pack()

        fonts = ("", 14)
        

        self.master.title("OpenCVの動画表示")      # ウィンドウタイトル
        self.master.geometry("1000x500")     # ウィンドウサイズ(幅x高さ)



        frame1 = tk.Frame(self.master, width=400,height=800)
        frame1.pack(expand = True,fill=tk.BOTH)


        self.button = tk.Button(frame1,text="Snapshot",width=20, height=5, font=fonts,bg="beige" ,command=self.snapshot)
        self.button.pack(side=tk.BOTTOM,fill=tk.X)
        
        # Canvasの作成
        self.canvas = tk.Canvas(frame1)
        # Canvasにマウスイベント(左ボタンクリック)の追加
        self.canvas.bind('<Button-1>', self.canvas_click)
        # Canvasを配置
        self.canvas.pack(expand = True, fill = tk.BOTH)

        # カメラをオープンする
        self.capture = cv2.VideoCapture(0)

        self.disp_id = None

   
        

    def canvas_click(self, event):
        '''Canvasのマウスクリックイベント'''

        if self.disp_id is None:
            # 動画を表示
            self.disp_image()
        else:
            # 動画を停止
            self.after_cancel(self.disp_id)
            self.disp_id = None

    def disp_image(self):
        '''画像をCanvasに表示する'''

        # フレーム画像の取得
        ret, self.frame22 = self.capture.read()
    
        # BGR→RGB変換
        cv_image = cv2.cvtColor(self.frame22, cv2.COLOR_BGR2RGB)
        # NumPyのndarrayからPillowのImageへ変換
        pil_image = Image.fromarray(cv_image)

        # キャンバスのサイズを取得
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()

        # 画像のアスペクト比(縦横比)を崩さずに指定したサイズ(キャンバスのサイズ)全体に画像をリサイズする
        pil_image = ImageOps.pad(pil_image, (canvas_width, canvas_height))

        # PIL.ImageからPhotoImageへ変換する
        self.photo_image = ImageTk.PhotoImage(image=pil_image)

        # 画像の描画
        self.canvas.create_image(
                canvas_width / 2,       # 画像表示位置(Canvasの中心)
                canvas_height / 2,                   
                image=self.photo_image  # 表示画像データ
                )


        # disp_image()を10msec後に実行する
        self.disp_id = self.after(10, self.disp_image)


    def snapshot(self):
        filename = "C:/Users/Pictures/match.png"
        self.snapnum = 1
        
        cv2.imwrite( filename,self.frame22)
        print(filename)
        self.master.destroy()
        self.capture.release()
        #time.sleep(5)
        #match_suc:match_suc
        cv2.destroyAllWindows()
        time.sleep(5)
        self.snapnum = 1




if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()
    if app.snapnum == 1:
        window = tk.Tk()
        img = cv2.imread("C:/Users/Pictures/match.png")  # 画像を読み込む。

        # 色基準で2値化する。
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)  # HSV 色空間に変換

        red = cv2.inRange(hsv, np.array([145, 70, 0]), np.array([180, 255, 255]))
        yellow = cv2.inRange(hsv, np.array([10, 80, 0]), np.array([50, 255, 255]))
        green = cv2.inRange(hsv, np.array([30, 190, 0]), np.array([90, 255, 255]))
        blue = cv2.inRange(hsv, np.array([90, 70, 90]), np.array([210, 255, 255]))
        #white = cv2.inRange(hsv, np.array([108, 21, 0]), np.array([255, 70, 255]))

        # 白だけゴミがあるので、収縮演算
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
        #white = cv2.erode(white, kernel)


        bin_imgs = {'red': red, 'yellow': yellow, 'green': green,
                    'blue': blue, }

       




        fig, ax = plt.subplots(figsize=(8, 5))
        ax.axis('off')

        ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        count1 = {'red':0,'yellow':0,'green':0,'blue':0}
        # 輪郭検出し、数を求める。
        ##############################################
        for label, bin_img in bin_imgs.items():
            contours,hierarchy = cv2.findContours(
                bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            # 輪郭を構成する頂点数で誤検出を除く。
            contours = list(filter(lambda cnt: len(cnt) > 30, contours))
            count = len(contours)
            count1[label] = count
            print('color: {}, conunt: {}'.format(label, count))

            # 描画する。
            for cnt in contours:
                cnt = np.squeeze(cnt, axis=1)  # (N, 1, 2) -> (N, 2)
                ax.add_patch(Polygon(cnt, fill=None, lw=2., color=label))
            plt.savefig("C:/Users/Pictures/matchafter.png")    
            #plt.show()

        

        fonts = ("", 14)
        

        window.title("処理結果") # ウィンドウタイトル
        window.geometry("1200x700")     # ウィンドウサイズ(幅x高さ)
       
        

        
        frame1 = tk.Frame(window,bg="beige",width=1200,height=500)
        frame1.pack(expand = True,fill=tk.BOTH)

       

        
        
        # Canvasの作成
        canvas = tk.Canvas(frame1,bg="beige",width=1200,height=500)
    
        # Canvasを配置
        canvas.pack(expand = True, fill = tk.BOTH)
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
        # フレーム画像の取得
        img = ImageTk.PhotoImage(file = "C:/Users/Pictures/matchafter.png")
        w = img.width # 横幅を取得                                            
        h = img.height # 縦幅を取得  

        #img = img.resize(( int(w * (800/w)), int(h * (800/w)) ))


        

        # キャンバスのサイズを取得
        canvas_width = canvas.winfo_width()
        canvas_height = canvas.winfo_height()
       
       
        RedAnswer = 5
        BlueAnswer = 5
        YellowAnswer = 5
        GreenAnswer = 5

        if (count1['red']) >= RedAnswer and \
            (count1['yellow']) >= YellowAnswer and\
             (count1['green']) >= GreenAnswer and \
              (count1['blue']) >= BlueAnswer :
            font1 = font.Font(family='Helvetica', size=110, weight='bold')
            label2 = tk.Label(window, text="OK",font=font1,fg="green")  #文字ラベル設定
            label2.pack(side = "bottom")
            canvas.create_image(
                600,       # 画像表示位置(Canvasの中心)
                250,                   
                image=img,
                anchor = tk.CENTER
                 # 表示画像データ
                )

        else:
            rednotenough = RedAnswer-(count1['red'])
            yellowNotEnough = YellowAnswer-(count1['yellow'])
            greenNotEnough = GreenAnswer-(count1['green'])
            blueNotEnough = BlueAnswer-(count1['blue'])

            #0以下は0にする
            rednotenough = max(0,rednotenough)
            yellowNotEnough = max(0,yellowNotEnough)
            greenNotEnough = max(0,greenNotEnough)
            blueNotEnough = max(0,blueNotEnough)

            font0 = font.Font(family='Helvetica', size=20, weight='bold')          
            text0 = "赤色が"+ str(rednotenough) + "個\n"\
                    "黄色が"+ str(yellowNotEnough) + "個\n"\
                    "緑が"+ str(greenNotEnough) + "個\n"\
                    "青が"+ str(blueNotEnough) + "個\n"\
                    "足りません"
                

                        
            label0 = tk.Label(window, text=text0,font=font0,fg="red")
            label0 .place(relx=0.85,rely=0.75)


            font1 = font.Font(family='Helvetica', size=110, weight='bold')
            label1 = tk.Label(window, text="NG",font=font1,fg="red")  #文字ラベル設定
            label1.pack(side = "bottom")

            canvas.create_image(
                600,       # 画像表示位置(Canvasの中心)
                250,                   
                image=img,
                anchor = tk.CENTER
                 # 表示画像データ
                )
        
        
    window.mainloop()

自分で試したこと

2回目のウィンドウ表示である、window.mainloopをifの外や中に移動してみましたが変わりませんでした

0

1Answer

import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk, ImageOps  # 画像データ用
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np
from tkinter import font
import time

import cv2

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)
        self.pack()

        fonts = ("", 14)
        

        self.master.title("OpenCVの動画表示")      # ウィンドウタイトル
        self.master.geometry("1000x500")     # ウィンドウサイズ(幅x高さ)



        frame1 = tk.Frame(self.master, width=400,height=800)
        frame1.pack(expand = True,fill=tk.BOTH)


        self.button = tk.Button(frame1,text="Snapshot",width=20, height=5, font=fonts,bg="beige" ,command=self.snapshot)
        self.button.pack(side=tk.BOTTOM,fill=tk.X)
        
        # Canvasの作成
        self.canvas = tk.Canvas(frame1)
        # Canvasにマウスイベント(左ボタンクリック)の追加
        self.canvas.bind('<Button-1>', self.canvas_click)
        # Canvasを配置
        self.canvas.pack(expand = True, fill = tk.BOTH)

        # カメラをオープンする
        self.capture = cv2.VideoCapture(0)

        self.disp_id = None

   
        

    def canvas_click(self, event):
        '''Canvasのマウスクリックイベント'''

        if self.disp_id is None:
            # 動画を表示
            self.disp_image()
        else:
            # 動画を停止
            self.after_cancel(self.disp_id)
            self.disp_id = None

    def disp_image(self):
        '''画像をCanvasに表示する'''

        # フレーム画像の取得
        ret, self.frame22 = self.capture.read()
    
        # BGR→RGB変換
        cv_image = cv2.cvtColor(self.frame22, cv2.COLOR_BGR2RGB)
        # NumPyのndarrayからPillowのImageへ変換
        pil_image = Image.fromarray(cv_image)

        # キャンバスのサイズを取得
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()

        # 画像のアスペクト比(縦横比)を崩さずに指定したサイズ(キャンバスのサイズ)全体に画像をリサイズする
        pil_image = ImageOps.pad(pil_image, (canvas_width, canvas_height))

        # PIL.ImageからPhotoImageへ変換する
        self.photo_image = ImageTk.PhotoImage(image=pil_image)

        # 画像の描画
        self.canvas.create_image(
                canvas_width / 2,       # 画像表示位置(Canvasの中心)
                canvas_height / 2,                   
                image=self.photo_image  # 表示画像データ
                )


        # disp_image()を10msec後に実行する
        self.disp_id = self.after(10, self.disp_image)


    def snapshot(self):
        filename = "C:/Users/Pictures/match.png"
        self.snapnum = 1
        
        cv2.imwrite( filename,self.frame22)
        print(filename)
        self.master.destroy()
        self.capture.release()
        #time.sleep(5)
        #match_suc:match_suc
        cv2.destroyAllWindows()
        time.sleep(5)
        self.snapnum = 1

def main():
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()
    if app.snapnum == 1:
        window = tk.Tk()
        img = cv2.imread("C:/Users/Pictures/match.png")  # 画像を読み込む。

        # 色基準で2値化する。
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)  # HSV 色空間に変換

        red = cv2.inRange(hsv, np.array([145, 70, 0]), np.array([180, 255, 255]))
        yellow = cv2.inRange(hsv, np.array([10, 80, 0]), np.array([50, 255, 255]))
        green = cv2.inRange(hsv, np.array([30, 190, 0]), np.array([90, 255, 255]))
        blue = cv2.inRange(hsv, np.array([90, 70, 90]), np.array([210, 255, 255]))
        #white = cv2.inRange(hsv, np.array([108, 21, 0]), np.array([255, 70, 255]))

        # 白だけゴミがあるので、収縮演算
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
        #white = cv2.erode(white, kernel)


        bin_imgs = {'red': red, 'yellow': yellow, 'green': green,
                    'blue': blue, }

       




        fig, ax = plt.subplots(figsize=(8, 5))
        ax.axis('off')

        ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        count1 = {'red':0,'yellow':0,'green':0,'blue':0}
        # 輪郭検出し、数を求める。
        ##############################################
        for label, bin_img in bin_imgs.items():
            contours,hierarchy = cv2.findContours(
                bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            # 輪郭を構成する頂点数で誤検出を除く。
            contours = list(filter(lambda cnt: len(cnt) > 30, contours))
            count = len(contours)
            count1[label] = count
            print('color: {}, conunt: {}'.format(label, count))

            # 描画する。
            for cnt in contours:
                cnt = np.squeeze(cnt, axis=1)  # (N, 1, 2) -> (N, 2)
                ax.add_patch(Polygon(cnt, fill=None, lw=2., color=label))
            plt.savefig("C:/Users/Pictures/matchafter.png")    
            #plt.show()

        

        fonts = ("", 14)
        

        window.title("処理結果") # ウィンドウタイトル
        window.geometry("1200x700")     # ウィンドウサイズ(幅x高さ)
       
        

        
        frame1 = tk.Frame(window,bg="beige",width=1200,height=500)
        frame1.pack(expand = True,fill=tk.BOTH)

       

        
        
        # Canvasの作成
        canvas = tk.Canvas(frame1,bg="beige",width=1200,height=500)
    
        # Canvasを配置
        canvas.pack(expand = True, fill = tk.BOTH)
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
        # フレーム画像の取得
        img = ImageTk.PhotoImage(file = "C:/Users/Pictures/matchafter.png")
        w = img.width # 横幅を取得                                            
        h = img.height # 縦幅を取得  

        #img = img.resize(( int(w * (800/w)), int(h * (800/w)) ))


        

        # キャンバスのサイズを取得
        canvas_width = canvas.winfo_width()
        canvas_height = canvas.winfo_height()
       
       
        RedAnswer = 5
        BlueAnswer = 5
        YellowAnswer = 5
        GreenAnswer = 5

        if (count1['red']) >= RedAnswer and \
            (count1['yellow']) >= YellowAnswer and\
             (count1['green']) >= GreenAnswer and \
              (count1['blue']) >= BlueAnswer :
            font1 = font.Font(family='Helvetica', size=110, weight='bold')
            label2 = tk.Label(window, text="OK",font=font1,fg="green")  #文字ラベル設定
            label2.pack(side = "bottom")
            canvas.create_image(
                600,       # 画像表示位置(Canvasの中心)
                250,                   
                image=img,
                anchor = tk.CENTER
                 # 表示画像データ
                )

        else:
            rednotenough = RedAnswer-(count1['red'])
            yellowNotEnough = YellowAnswer-(count1['yellow'])
            greenNotEnough = GreenAnswer-(count1['green'])
            blueNotEnough = BlueAnswer-(count1['blue'])

            #0以下は0にする
            rednotenough = max(0,rednotenough)
            yellowNotEnough = max(0,yellowNotEnough)
            greenNotEnough = max(0,greenNotEnough)
            blueNotEnough = max(0,blueNotEnough)

            font0 = font.Font(family='Helvetica', size=20, weight='bold')          
            text0 = "赤色が"+ str(rednotenough) + "個\n"\
                    "黄色が"+ str(yellowNotEnough) + "個\n"\
                    "緑が"+ str(greenNotEnough) + "個\n"\
                    "青が"+ str(blueNotEnough) + "個\n"\
                    "足りません"
                

                        
            label0 = tk.Label(window, text=text0,font=font0,fg="red")
            label0 .place(relx=0.85,rely=0.75)


            font1 = font.Font(family='Helvetica', size=110, weight='bold')
            label1 = tk.Label(window, text="NG",font=font1,fg="red")  #文字ラベル設定
            label1.pack(side = "bottom")

            canvas.create_image(
                600,       # 画像表示位置(Canvasの中心)
                250,                   
                image=img,
                anchor = tk.CENTER
                 # 表示画像データ
                )
        
        
    window.mainloop()

if __name__ == "__main__":
    main()

試してないので合ってるわからないのですが、これでいかがでしょうか?

0

Comments

  1. @mckie

    Questioner
    @yutaka_mさん
    ご回答ありがとうございます。試してみましたが、結果は同じでした。

Your answer might help someone💌