[python3](/questions/tagged/python3 "'python3'
のタグが付いた質問を表示")と[tkinter](/questions/tagged/tkinter "'tkinter'
のタグが付いた質問を表示")を用いてGUIを作成しようとしたのですが、わからない点があったので質問させて頂きます。

### やりたいこと

①：「startボタン」と「stopボタン」の２つのボタンが配置されているメインウィンドウの作成  
②：「startボタン」を押すと、接続しているUSBカメラの映像をメインウィンドウ内に表示  
③：「stopボタン」を押すと、②で表示されたUSBカメラの映像を消す（メインウィンドウは残したまま）

### 困っている点

①と②は色々なサイトを参考にして作成できたのですが、③の処理のところで  
USBカメラの映像を消すことが出来なくて困っています。

現状「stopボタン」を押すとUSBカメラの映像はそのまま変化なしで、以下のエラーメッセージが出ます。

```

    Exception in thread Thread-8:
    Traceback (most recent call last):
    File "C:\Users\usr\Anaconda3\lib\threading.py", line 916, in _bootstrap_inner self.run()
    File "C:\Users\usr\Anaconda3\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
    TypeError: destroy() missing 1 required positional argument: 'panel'
    
```

### コード

```

    import cv2
    from PIL import Image
    from PIL import ImageTk
    import threading
    import tkinter as tk
    
    
    def button1_clicked():
        thread = threading.Thread(target=videoLoop, args=())
        thread.start()
        
    def button2_clicked():
        thread = threading.Thread(target=destroy, args=())
        thread.start()
    
    def destroy(panel):
        panel.destroy()
    
    def videoLoop(mirror=False):
        No=0
        cap = cv2.VideoCapture(No)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 800)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 600)
    
        while True:
            ret, to_draw = cap.read()
            if mirror is True:
                to_draw = to_draw[:,::-1]
            
            image = cv2.cvtColor(to_draw, cv2.COLOR_BGR2RGB)
            image = Image.fromarray(image)
            image = ImageTk.PhotoImage(image)
            panel = tk.Label(image=image)
            panel.image = image
            panel.place(x=50, y=50)
            
        return panel
    
                
    root = tk.Tk()
    root.geometry("1920x1080+0+0")
    
    button1 = tk.Button(root, text="start", bg="#fff", font=("",50), command=button1_clicked)
    button1.place(x=1000, y=100, width=400, height=250)
    
    button2 = tk.Button(root, text="stop", bg="#fff", font=("",50), command=button2_clicked)
    button2.place(x=1000, y=360, width=400, height=250)
    
    root.mainloop()
    
```
CC BY-SA 4.0
2018-10-01T04:04:23.017
48862
2022-06-12T10:03:05.367
2021-03-15T00:08:49.223
3060
30306
question
python python3 opencv tkinter
Python3とTkinterを用いてカメラ映像をon/off出来るボタン付きGUIを作成したい
私はそのカメラ映像自身に変わるものを持っておりませんので、上記のエラー状況だけを見て解答を書かせていただきます。

エラーの内容は、`destroy()`という関数が、`panel`という引数を要求しているのにも関わらず、その`panel`という引数の値には、何も入れ込まれていませんよ。という感じのエラーです。

```

    def destroy(panel):
        panel.destroy()
    
```

で定められている通り、`destroy`関数の引数として`panel`が設定されていますね。この関数を使う時には、`panel`というものに何らかの値を入れてやらなくてはなりません。

`panel`という変数は、提示していただいたコードのいろいろな箇所に見られますが、このコードが意味を持つ箇所は、

```

    def button2_clicked():
        thread = threading.Thread(target=destroy, args=())
        thread.start()
    
```

ですね。`Thread`の引数を見ていただくと、`destroy`という関数を、`args`=()引数なしで動かすという意味になります。

前述のとおり、ここでは、少なくとも引数なしで動かすというと、エラーになります。

蛇足かも知れませんが、

```

    def button1_clicked():
        thread = threading.Thread(target=videoLoop, args=())
        thread.start()
    
```

このボタン1の方はなぜエラーにならないのかというと、

```

    def videoLoop(mirror=False):
    
```

という形で、引数の値が初期設定されているからです。だから、引数なしで使うという場合、自然と`mirror`の値が`False`になります。  
下では`True`の場合の処理もなさっているようなのですが、これだと常に`False`になりますよ。

という訳で、まずは`destroy`関数の引数に、ちゃんとした値を入れる事を考えて見られてはいかがでしょうか。

**追記**  
コメントをいただき、全体のコードを見て、気になったところを加えてみたいと思います。参考にしてください。  
`panel.destroy()`という形を見るように、この`panel`という引数は、`Tkinter`の`Label`型であることは、`videoloop`関数の戻り値から見ると間違いないようです。

すると、この戻り値である`panel`を受け取って、何とか引数として、`destroy(panel)`で入れ込んでやらなくてはなりませんが、戻り値を取得するには、`something_variable
=
videoloop()`として、`something_variable`に、`panel`型を入れ込み、その型を、`destroy(something_variable)`として使う事が、現段階での解決の道筋だと思います。  
この時,`panel`から`something_variable`に名前が変化しているところは、わかっていただけますね。  
例を示すと、

```

    def somethin_func():
        return "a"
    
    b = "b"
    b = something_func()
    print(b)
    "a"
    
```

となるように、この"a"が簡単に言えば、`Tkinter`の`Label`型になっただけだという事です。  
`something_variable`の代わりに、ご自身でなんでもいいから名前をつけてください。

しかし、`destroy(panel)`は、`button2_clicked()`関数の中で（ローカルで）利用されているため、引数を、その場所へ届かせるのは、若干距離が遠いように感じます。

全体のコードを見て、もう少しコードとコードの関係性を設計しなおすことをお勧めしますが、無理に通そうとすれば、

```

    def button2_clicked(panel):
        #button2_clickedの引数に何もなければ未定義のエラー
        thread = threading.Thread(target=destroy, args=(panel))
    　　　　        thread.start()
    
```

という形で、`button2_clicked(panel)`として、値を内部の関数に渡すようにする。  
すると、必然的に、

```

    button2 = tk.Button(root, text="stop", bg="#fff", font=("",50), command=button2_clicked(panel))#ここ！
    button2.place(x=1000, y=360, width=400, height=250)
    
```

という形で、ここにも引数を入れてやらなくてはならなくなります。

`videoloop`関数は、`button1_clicked`が実行されて、初めて実行される関数なので、その`panel`を戻り値として返すにしても、それをどこで受取、そしてどうやって`destroy`の引数として入れ込むかという処理を考えなくては行けなくなり、引数一つを何度もいろいろな関数をまたがってバトンパスしていかなくてはいけないため、後で見返した時にコードの理解が出来なくなる可能性があり、また、これからコードを増やしていく際に、この`panel`が色々なところで、引っ掛かりを見せてくる可能性が考えられます。

また、`while`ループなのですが、常に条件式が`True`となっており、ループから抜け出す処理がありません。ですので、せっかく`panel`変数に値を入れ込んでも、永久に`panel`が戻り値として帰ってくることはなさそうです。

最初は`panel`を引数として入れ込むことを考えることを提案したのですが、もう少しコードの位置関係を考え直してみられると良いかと思われます。

`panel`は本当に引数で渡さなくてはならないでしょうか。  
`while`ループ内では、どのような事が意図されていますか？いつループを止め、`panel`を戻り値として返さなくてはならないですか？  
`threading`は本当に必要ですか？

気になったところ、というだけでまだあるかもしれません。私は全てを理解して提案をしているわけではありませんが、大体のところで外れはないと思います。コードだけを見て、気になったところです。おそらくは、ビデオの画素的な情報を取得され、それを、[pillow](/questions/tagged/pillow
"'pillow'
のタグが付いた質問を表示")で、画像データとして変換し、色を付けて、`panel`という変数でラベルを作成し、その絵の変化を動画として画面に表示されたいのだろうと思います。ループ処理は、その画像の変化を常に更新するためでしょうか。そしてボタン2を押せば、その画面自体が消えてほしい。  
違うかもしれませんが、本当に自分のされたいことが、コードとして表現されているのかをじっくりと考えて見られる事をお勧めします。もちろん基本書と`Tkinter`の基本を見直してみたりすること、細かい簡単なコードで実験を繰り返すことも有用です。コードを何度も見返して、頭の中で処理を反芻するか、紙に書いていただくと、もっといいコーディングが出来るかもしれません。もっと別のメソッドを使って、同じようなことができるかもしれません。（例えば、`destroy()`するのではなくて、単に`lower()`で見えなくすればいいのではないかとか。そうして今の処理にあったメソッドを調べて使ってみるといいかもしれません。  
また何かあれば更新させていただきます。長くなってしまい申し訳ありません。
CC BY-SA 4.0
2018-10-01T06:25:33.717
48864
2018-10-02T01:40:13.430
2018-10-02T01:40:13.430
24284
24284
48862
answer