From ce369e2fa3086789124a9f6f874debbf9ab3b558 Mon Sep 17 00:00:00 2001 From: "ry.yamafuji" Date: Thu, 11 Sep 2025 23:02:16 +0900 Subject: [PATCH] =?UTF-8?q?GUI=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 ++- docs/images/pyside6.png | Bin 0 -> 12996 bytes docs/images/tkinter.png | Bin 0 -> 7160 bytes docs/outline.md | 51 +++++++ docs/sample/sample.jpg | Bin 0 -> 6788 bytes examples/example_gui_pyside6.py | 61 +++++++++ examples/example_gui_tkinter.py | 41 ++++++ requirements.txt | 2 + src/video_player.py | 229 ++++++++++++++++++++++++++++++++ 9 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 docs/images/pyside6.png create mode 100644 docs/images/tkinter.png create mode 100644 docs/outline.md create mode 100644 docs/sample/sample.jpg create mode 100644 examples/example_gui_pyside6.py create mode 100644 examples/example_gui_tkinter.py create mode 100644 requirements.txt create mode 100644 src/video_player.py diff --git a/README.md b/README.md index 8e3210d..2639fc7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,23 @@ # image-analyzer-lab -画像認識及び検知、セクションなどの視覚研究ラボ \ No newline at end of file +画像認識及び検知、セクションなどの視覚研究ラボ + +## Exec + +```bat +python src\video_player.py +``` + +## Setup + +```sh +python -m venv venv +# windows +venv\Scripts\activate +# package +pip install -r requirements.txt +``` + +```sh +python -m pip install --upgrade --extra-index-url https://PySimpleGUI.net/install PySimpleGUI +``` diff --git a/docs/images/pyside6.png b/docs/images/pyside6.png new file mode 100644 index 0000000000000000000000000000000000000000..74f043a7f8eafe386e5b688b2d070c560770553a GIT binary patch literal 12996 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

5rDS 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 S_W;lpJA3;4DU8|GyZ;Aj7Lca^ literal 0 HcmV?d00001 diff --git a/docs/images/tkinter.png b/docs/images/tkinter.png new file mode 100644 index 0000000000000000000000000000000000000000..2f14d6c9486b147d6c7573911ccfa9185b05755e GIT binary patch literal 7160 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 H+|B(D3dP@v literal 0 HcmV?d00001 diff --git a/examples/example_gui_pyside6.py b/examples/example_gui_pyside6.py new file mode 100644 index 0000000..ce97119 --- /dev/null +++ b/examples/example_gui_pyside6.py @@ -0,0 +1,61 @@ +import sys +from PySide6.QtWidgets import ( + QApplication, QWidget, QLabel, QLineEdit, QCheckBox, QListWidget, + QPushButton, QVBoxLayout, QHBoxLayout, QMessageBox +) + + +class MainWindow(QWidget): + def __init__(self): + super().__init__() + self.setWindowTitle("PySide6 GUI サンプル") + self.resize(500, 300) + + # --- 左側ウィジェット --- + label = QLabel("名前を入力してください:") + self.entry = QLineEdit() + self.chk = QCheckBox("同意します") + + left_layout = QVBoxLayout() + left_layout.addWidget(label) + left_layout.addWidget(self.entry) + left_layout.addWidget(self.chk) + left_layout.addStretch() + + # --- 右側ウィジェット --- + self.listbox = QListWidget() + for item in ["りんご", "バナナ", "みかん", "ぶどう"]: + self.listbox.addItem(item) + + btn_exec = QPushButton("実行") + btn_exec.clicked.connect(self.on_exec) + + btn_quit = QPushButton("終了") + btn_quit.clicked.connect(self.close) + + right_layout = QVBoxLayout() + right_layout.addWidget(self.listbox) + right_layout.addWidget(btn_exec) + right_layout.addWidget(btn_quit) + right_layout.addStretch() + + # --- メインレイアウト(横並び) --- + main_layout = QHBoxLayout() + main_layout.addLayout(left_layout, 1) + main_layout.addLayout(right_layout, 1) + + self.setLayout(main_layout) + + def on_exec(self): + name = self.entry.text() + checked = "はい" if self.chk.isChecked() else "いいえ" + item = self.listbox.currentItem().text() if self.listbox.currentItem() else "未選択" + msg = f"こんにちは {name} さん\nチェック: {checked}\n選択: {item}" + QMessageBox.information(self, "挨拶", msg) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) diff --git a/examples/example_gui_tkinter.py b/examples/example_gui_tkinter.py new file mode 100644 index 0000000..1acfd7b --- /dev/null +++ b/examples/example_gui_tkinter.py @@ -0,0 +1,41 @@ +import tkinter as tk +from tkinter import messagebox + +def on_click(): + name = entry.get() + checked = var_chk.get() + msg = f"こんにちは {name} さん!\nチェック状態: {checked}" + messagebox.showinfo("挨拶", msg) + +root = tk.Tk() +root.title("Tkinter GUI サンプル") +root.geometry("400x300") # 幅×高さ + +# ラベル +label = tk.Label(root, text="名前を入力してください:", font=("Arial", 12)) +label.pack(pady=5) + +# 入力欄 +entry = tk.Entry(root, width=30) +entry.pack(pady=5) + +# チェックボックス +var_chk = tk.BooleanVar() +chk = tk.Checkbutton(root, text="同意します", variable=var_chk) +chk.pack(pady=5) + +# リストボックス +listbox = tk.Listbox(root, height=4) +for item in ["りんご", "バナナ", "みかん", "ぶどう"]: + listbox.insert(tk.END, item) +listbox.pack(pady=5) + +# ボタン +btn = tk.Button(root, text="実行", command=on_click) +btn.pack(pady=10) + +# 終了ボタン +btn_quit = tk.Button(root, text="終了", command=root.quit) +btn_quit.pack(pady=5) + +root.mainloop() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e741782 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +opencv-python +PySide6 \ No newline at end of file diff --git a/src/video_player.py b/src/video_player.py new file mode 100644 index 0000000..7d1c40b --- /dev/null +++ b/src/video_player.py @@ -0,0 +1,229 @@ +import sys, os, cv2, numpy as np +from pathlib import Path +from PySide6.QtCore import Qt, QTimer +from PySide6.QtGui import QAction, QImage, QPixmap, QDragEnterEvent, QDropEvent +from PySide6.QtWidgets import ( + QApplication, QMainWindow, QWidget, QLabel, QPushButton, QFileDialog, + QVBoxLayout, QHBoxLayout, QSplitter, QTabWidget, QFormLayout, QSpinBox, QCheckBox +) + +IMAGE_EXT = {".jpg", ".jpeg", ".png", ".bmp", ".gif"} +VIDEO_EXT = {".mp4", ".mov", ".avi", ".mkv", ".m4v"} + +def bgr_to_qpixmap(bgr): + rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB) + h, w, ch = rgb.shape + qimg = QImage(rgb.data, w, h, ch*w, QImage.Format.Format_RGB888) + return QPixmap.fromImage(qimg) + +class MediaViewer(QWidget): + """画像と動画を共通で扱う左側ビューワ""" + def __init__(self, parent=None): + super().__init__(parent) + self.label = QLabel("Drop files here\n(画像/動画)") + self.label.setAlignment(Qt.AlignCenter) + self.label.setStyleSheet("background:#222;color:#aaa;border:1px solid #444;") + self.label.setMinimumSize(640, 360) + + self.btn_bar = QHBoxLayout() + self.btn_open = QPushButton("Open") + self.btn_play = QPushButton("Play") + self.btn_pause = QPushButton("Pause") + self.btn_stop = QPushButton("Stop") + for b in (self.btn_open, self.btn_play, self.btn_pause, self.btn_stop): + self.btn_bar.addWidget(b) + self.btn_bar.addStretch() + + lay = QVBoxLayout(self) + lay.addWidget(self.label, 1) + lay.addLayout(self.btn_bar) + + # 動画用 + self.timer = QTimer(self) + self.timer.timeout.connect(self._next_frame) + self.cap = None + self.current_path = None + self._is_video = False + + # イベント + self.btn_open.clicked.connect(self.open_file) + self.btn_play.clicked.connect(self.play) + self.btn_pause.clicked.connect(self.pause) + self.btn_stop.clicked.connect(self.stop) + + # D&D 有効化 + self.setAcceptDrops(True) + + # ---- Public API ---- + def load(self, path: str): + self.stop() + self.current_path = path + suffix = Path(path).suffix.lower() + + if suffix in IMAGE_EXT: + self._is_video = False + data = np.fromfile(path, dtype=np.uint8) + img = cv2.imdecode(data, cv2.IMREAD_COLOR) + if img is None: + self.label.setText("画像を開けませんでした") + return + self.label.setPixmap(bgr_to_qpixmap(img).scaled( + self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)) + elif suffix in VIDEO_EXT: + self._is_video = True + self.cap = cv2.VideoCapture(path) + if not self.cap.isOpened(): + self._is_video = False + self.label.setText("動画を開けませんでした") + return + self.play() + else: + self.label.setText("未対応の形式です") + + def open_file(self): + path, _ = QFileDialog.getOpenFileName(self, "Open media", "", + "Media (*.jpg *.jpeg *.png *.bmp *.gif *.mp4 *.mov *.avi *.mkv *.m4v)") + if path: + self.load(path) + + def play(self): + if not self._is_video: + return + if self.cap is None: + return + # 30fps程度で更新(必要に応じて調整) + self.timer.start(33) + + def pause(self): + self.timer.stop() + + def stop(self): + self.timer.stop() + if self.cap is not None: + self.cap.release() + self.cap = None + + # ---- Internal ---- + def _next_frame(self): + if self.cap is None: + return + ok, frame = self.cap.read() + if not ok: + self.pause() + return + self.label.setPixmap(bgr_to_qpixmap(frame).scaled( + self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)) + + # ---- D&D ---- + def dragEnterEvent(self, e: QDragEnterEvent): + if e.mimeData().hasUrls(): + e.acceptProposedAction() + + def dropEvent(self, e: QDropEvent): + urls = e.mimeData().urls() + if not urls: + return + path = urls[0].toLocalFile() + if os.path.isfile(path): + self.load(path) + + # リサイズ時もアスペクト保持で表示更新 + def resizeEvent(self, ev): + super().resizeEvent(ev) + if self.current_path and not self._is_video and self.label.pixmap(): + # 静止画のみスケールし直し + self.open_image_again() + + def open_image_again(self): + if not self.current_path or self._is_video: + return + data = np.fromfile(self.current_path, dtype=np.uint8) + img = cv2.imdecode(data, cv2.IMREAD_COLOR) + if img is None: + return + self.label.setPixmap(bgr_to_qpixmap(img).scaled( + self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)) + + +class RightPanel(QWidget): + """右側:プレビュー/設定のタブ""" + def __init__(self, parent=None): + super().__init__(parent) + tabs = QTabWidget() + + # Analyser タブ(解析) + self.analyser = QWidget() + a_lay = QVBoxLayout(self.analyser) + # ボタンを追加 + a_lay.addWidget(QPushButton("Run Analysis")) + a_lay.addStretch() + + # Preview タブ(分析結果の簡易表示など) + self.preview = QWidget() + pv_lay = QVBoxLayout(self.preview) + pv_lay.addWidget(QLabel("分析プレビュー(ここに結果サムネ/メトリクス等)")) + pv_lay.addStretch() + + # Settings タブ(しきい値や切替など) + self.settings = QWidget() + st_form = QFormLayout(self.settings) + self.spin_conf = QSpinBox(); self.spin_conf.setRange(10, 90); self.spin_conf.setValue(35) + self.chk_boxes = QCheckBox("検知枠を表示"); self.chk_boxes.setChecked(True) + st_form.addRow("信頼度(%)", self.spin_conf) + st_form.addRow(self.chk_boxes) + + tabs.addTab(self.analyser, "Analyser") + tabs.addTab(self.settings, "Settings") + tabs.addTab(self.preview, "Preview") + + lay = QVBoxLayout(self) + lay.addWidget(tabs) + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("CV Studio (Prototype)") + self.resize(1200, 700) + + self.viewer = MediaViewer() + self.right = RightPanel() + + splitter = QSplitter() + splitter.addWidget(self.viewer) + splitter.addWidget(self.right) + # 半々に分割 + splitter.setSizes([self.width()//2, self.width()//2]) + + # 2.1 + # splitter.setStretchFactor(0, 3) + # splitter.setStretchFactor(1, 2) + + self.setCentralWidget(splitter) + + self._build_menu() + self.setAcceptDrops(True) + + def _build_menu(self): + open_act = QAction("Open...", self) + open_act.triggered.connect(self.viewer.open_file) + self.menuBar().addMenu("File").addAction(open_act) + + # ルートウィンドウにもD&D(フォルダから直接落とせるように) + def dragEnterEvent(self, e: QDragEnterEvent): + if e.mimeData().hasUrls(): + e.acceptProposedAction() + + def dropEvent(self, e: QDropEvent): + urls = e.mimeData().urls() + if urls: + path = urls[0].toLocalFile() + if os.path.isfile(path): + self.viewer.load(path) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + w = MainWindow() + w.show() + sys.exit(app.exec())