import tkinter as tk
from tkinter import Button, Listbox, Toplevel, Label, messagebox, Frame, Scrollbar
import subprocess
import cv2
from PIL import Image, ImageTk
import modules.util.const as const
import threading
class CameraSelector(Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("カメラの選択")
self.geometry("800x400")
self.cap = None
self.stop_thread = True
self.cameras = self.get_cameras()
self.setup_gui()
self.protocol("WM_DELETE_WINDOW", self.on_close)
def setup_gui(self):
"""GUIのセットアップ"""
self.setup_camera_frame()
self.setup_control_frame()
def setup_camera_frame(self):
"""カメラフレームのセットアップ"""
self.camera_frame = Frame(self)
self.camera_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.feed_label = Label(self.camera_frame)
self.feed_label.pack(pady=20, padx=20, fill=tk.BOTH, expand=True)
def setup_control_frame(self):
"""コントロールフレームのセットアップ"""
self.control_frame = Frame(self)
self.control_frame.pack(side=tk.RIGHT, fill=tk.BOTH, padx=10, pady=10)
scrollbar = Scrollbar(self.control_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.listbox = Listbox(self.control_frame, yscrollcommand=scrollbar.set)
for cam_id, name in self.cameras:
self.listbox.insert(tk.END, f"[{cam_id}] {name}")
self.listbox.pack(pady=20, padx=20, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.listbox.yview)
self.listbox.bind("<Double-Button-1>", self.on_select_camera_id)
self.preview_button = Button(self.control_frame, text="カメラ画像確認", command=self.on_preview_camera)
self.preview_button.pack(pady=10)
self.select_id_button = Button(self.control_frame, text="カメラID確定", command=self.finalize_selection)
self.select_id_button.pack(pady=10)
def get_cameras(self):
"""CameraFinder.exeを使用して利用可能なカメラのIDと名前を取得する"""
result = subprocess.run([const.CAMERA_FINDER_PATH], capture_output=True, text=True)
lines = result.stdout.splitlines()
cameras = []
for line in lines:
if "[" in line and "]" in line:
idx = line.index("[")
id_end = line.index("]")
cam_id = int(line[idx + 1 : id_end])
cam_name = line[id_end + 2 :].strip()
cameras.append((cam_id, cam_name))
return cameras
def on_select_camera_id(self, event=None):
selected_idx = self.listbox.curselection()
if not selected_idx:
return
self.selected_cam_id = self.cameras[selected_idx[0]][0]
self.listbox.selection_set(selected_idx)
def on_preview_camera(self):
if not hasattr(self, "selected_cam_id"):
messagebox.showinfo("情報", "カメラIDを選択してください。")
return
if self.cap and self.cap.isOpened():
self.cap.release()
self.stop_thread = False
self.thread = threading.Thread(target=self.show_camera_feed, args=(self.selected_cam_id,))
self.thread.start()
def show_camera_feed(self, cam_id):
self.cap = cv2.VideoCapture(cam_id)
if not self.cap.isOpened():
messagebox.showerror("エラー", "カメラを開けませんでした。")
return
while not self.stop_thread:
ret, frame = self.cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# アスペクト比を保ったままリサイズ
h, w, _ = frame.shape
aspect_ratio = w / h
new_width = int(self.feed_label.winfo_height() * aspect_ratio)
frame = cv2.resize(frame, (new_width, self.feed_label.winfo_height()))
image = Image.fromarray(frame)
photo = ImageTk.PhotoImage(image=image)
self.feed_label.config(image=photo)
self.feed_label.image = photo
self.cap.release()
def get_selected_camera_id(self):
"""選択されたカメラIDを返す"""
if hasattr(self, "selected_cam_id"):
return self.selected_cam_id
return None
def finalize_selection(self):
if not hasattr(self, "selected_cam_id"):
messagebox.showinfo("情報", "カメラIDを選択してください。")
return
self.selected_camera_id = self.get_selected_camera_id()
self.master.camera_id = self.selected_camera_id
messagebox.showinfo("情報", f"カメラID {self.selected_camera_id} が確定されました。")
self.destroy()
def on_close(self):
self.stop_thread = True # スレッドを停止
if hasattr(self, "thread"):
self.thread.join() # スレッドが終了するのを待つ
if self.cap and self.cap.isOpened():
self.cap.release()
self.destroy() # ウィンドウを終了
@staticmethod
def get_default_camera_id():
"""デフォルトで "VGA Camera" という名前のカメラIDを返す"""
result = subprocess.run([const.CAMERA_FINDER_PATH], capture_output=True, text=True)
lines = result.stdout.splitlines()
for line in lines:
if "VGA Camera" in line:
idx = line.index("[")
id_end = line.index("]")
cam_id = int(line[idx + 1 : id_end])
return cam_id
return None