增加讀檔及log事件

This commit is contained in:
JEFF 2025-03-10 21:06:08 +08:00
parent b2312d0758
commit d91bbd1003
9 changed files with 215 additions and 114 deletions

View File

@ -14,44 +14,44 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object): class Ui_MainWindow(object):
def setupUi(self, MainWindow): def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow") MainWindow.setObjectName("MainWindow")
MainWindow.resize(2024, 1118) MainWindow.resize(2182, 1118)
self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget") self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.bt_KeepShot = QtWidgets.QPushButton(self.centralwidget)
self.bt_KeepShot.setMinimumSize(QtCore.QSize(100, 50))
self.bt_KeepShot.setMaximumSize(QtCore.QSize(100, 50))
self.bt_KeepShot.setObjectName("bt_KeepShot")
self.horizontalLayout.addWidget(self.bt_KeepShot)
self.bt_StopKeepShot = QtWidgets.QPushButton(self.centralwidget)
self.bt_StopKeepShot.setMinimumSize(QtCore.QSize(100, 50))
self.bt_StopKeepShot.setMaximumSize(QtCore.QSize(100, 50))
self.bt_StopKeepShot.setObjectName("bt_StopKeepShot")
self.horizontalLayout.addWidget(self.bt_StopKeepShot)
self.bt_detection = QtWidgets.QPushButton(self.centralwidget)
self.bt_detection.setMinimumSize(QtCore.QSize(100, 50))
self.bt_detection.setMaximumSize(QtCore.QSize(100, 50))
self.bt_detection.setObjectName("bt_detection")
self.horizontalLayout.addWidget(self.bt_detection)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
self.view_origin = QtWidgets.QLabel(self.centralwidget) self.view_origin = QtWidgets.QLabel(self.centralwidget)
self.view_origin.setMinimumSize(QtCore.QSize(1000, 1000)) self.view_origin.setMinimumSize(QtCore.QSize(1000, 1000))
self.view_origin.setMaximumSize(QtCore.QSize(1000, 1000)) self.view_origin.setMaximumSize(QtCore.QSize(1000, 1000))
self.view_origin.setObjectName("view_origin") self.view_origin.setObjectName("view_origin")
self.gridLayout.addWidget(self.view_origin, 1, 0, 1, 1) self.gridLayout.addWidget(self.view_origin, 2, 0, 1, 1)
self.view_predict = QtWidgets.QLabel(self.centralwidget) self.view_predict = QtWidgets.QLabel(self.centralwidget)
self.view_predict.setMinimumSize(QtCore.QSize(1000, 1000)) self.view_predict.setMinimumSize(QtCore.QSize(1000, 1000))
self.view_predict.setMaximumSize(QtCore.QSize(1000, 1000)) self.view_predict.setMaximumSize(QtCore.QSize(1000, 1000))
self.view_predict.setObjectName("view_predict") self.view_predict.setObjectName("view_predict")
self.gridLayout.addWidget(self.view_predict, 1, 1, 1, 1) self.gridLayout.addWidget(self.view_predict, 2, 1, 1, 1)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.bt_KeepShot = QtWidgets.QPushButton(self.centralwidget)
self.bt_KeepShot.setMinimumSize(QtCore.QSize(150, 50))
self.bt_KeepShot.setMaximumSize(QtCore.QSize(100, 50))
self.bt_KeepShot.setObjectName("bt_KeepShot")
self.verticalLayout.addWidget(self.bt_KeepShot)
self.bt_StopKeepShot = QtWidgets.QPushButton(self.centralwidget)
self.bt_StopKeepShot.setMinimumSize(QtCore.QSize(150, 50))
self.bt_StopKeepShot.setMaximumSize(QtCore.QSize(100, 50))
self.bt_StopKeepShot.setObjectName("bt_StopKeepShot")
self.verticalLayout.addWidget(self.bt_StopKeepShot)
self.bt_detection = QtWidgets.QPushButton(self.centralwidget)
self.bt_detection.setMinimumSize(QtCore.QSize(150, 50))
self.bt_detection.setMaximumSize(QtCore.QSize(100, 50))
self.bt_detection.setObjectName("bt_detection")
self.verticalLayout.addWidget(self.bt_detection)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.gridLayout.addLayout(self.verticalLayout, 2, 2, 1, 1)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 2024, 22)) self.menubar.setGeometry(QtCore.QRect(0, 0, 2182, 22))
self.menubar.setObjectName("menubar") self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar) MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar = QtWidgets.QStatusBar(MainWindow)
@ -64,8 +64,8 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow): def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.view_origin.setText(_translate("MainWindow", "原圖影像"))
self.view_predict.setText(_translate("MainWindow", "檢測影像"))
self.bt_KeepShot.setText(_translate("MainWindow", "開始連續取像")) self.bt_KeepShot.setText(_translate("MainWindow", "開始連續取像"))
self.bt_StopKeepShot.setText(_translate("MainWindow", "停止連續取像")) self.bt_StopKeepShot.setText(_translate("MainWindow", "停止連續取像"))
self.bt_detection.setText(_translate("MainWindow", "檢測")) self.bt_detection.setText(_translate("MainWindow", "檢測"))
self.view_origin.setText(_translate("MainWindow", "原圖影像"))
self.view_predict.setText(_translate("MainWindow", "檢測影像"))

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>2024</width> <width>2182</width>
<height>1118</height> <height>1118</height>
</rect> </rect>
</property> </property>
@ -15,81 +15,7 @@
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="bt_KeepShot">
<property name="minimumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>開始連續取像</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bt_StopKeepShot">
<property name="minimumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>停止連續取像</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bt_detection">
<property name="minimumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>檢測</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="view_origin"> <widget class="QLabel" name="view_origin">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
@ -108,7 +34,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="2" column="1">
<widget class="QLabel" name="view_predict"> <widget class="QLabel" name="view_predict">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
@ -127,6 +53,80 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="bt_KeepShot">
<property name="minimumSize">
<size>
<width>150</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>開始連續取像</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bt_StopKeepShot">
<property name="minimumSize">
<size>
<width>150</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>停止連續取像</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bt_detection">
<property name="minimumSize">
<size>
<width>150</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>檢測</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">
@ -134,7 +134,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>2024</width> <width>2182</width>
<height>22</height> <height>22</height>
</rect> </rect>
</property> </property>

View File

@ -4,12 +4,15 @@ import cv2
import multiprocessing import multiprocessing
from pypylon import pylon from pypylon import pylon
from read_ini import ConfigReader
class CameraProcess(multiprocessing.Process): class CameraProcess(multiprocessing.Process):
""" 相機擷取獨立進程 """ """ 相機擷取獨立進程 """
def __init__(self, image_queue, exposure_time=20000): def __init__(self, image_queue, exposure_time=20000):
super(CameraProcess, self).__init__() super(CameraProcess, self).__init__()
self.image_queue = image_queue self.image_queue = image_queue
self.exposure_time = exposure_time self.config_reader = ConfigReader() # ✅ 創建 `ConfigReader` 物件
self.exposure_time = exposure_time if exposure_time is not None else self.config_reader.get_exposure_time()
self.running = multiprocessing.Value('b', True) # 控制進程是否運行 self.running = multiprocessing.Value('b', True) # 控制進程是否運行
self.camera = None self.camera = None
@ -19,7 +22,7 @@ class CameraProcess(multiprocessing.Process):
self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice()) self.camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
self.camera.Open() self.camera.Open()
self.camera.ExposureTime.SetValue(float(self.exposure_time)) self.camera.ExposureTime.SetValue(float(self.exposure_time))
print("相機連線成功") print(f"相機連線成功,曝光時間: {self.exposure_time} 微秒")
return True return True
except Exception as e: except Exception as e:
print(f"相機連線失敗: {e}") print(f"相機連線失敗: {e}")
@ -33,7 +36,7 @@ class CameraProcess(multiprocessing.Process):
self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly) self.camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
while self.running.value and self.camera.IsGrabbing(): while self.running.value and self.camera.IsGrabbing():
grab_result = self.camera.RetrieveResult(20000, pylon.TimeoutHandling_ThrowException) grab_result = self.camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
if grab_result.GrabSucceeded(): if grab_result.GrabSucceeded():
image = grab_result.Array # 取得影像數據 image = grab_result.Array # 取得影像數據
if not self.image_queue.full(): if not self.image_queue.full():
@ -46,7 +49,6 @@ class CameraProcess(multiprocessing.Process):
def stop(self): def stop(self):
""" 停止相機擷取進程 """ """ 停止相機擷取進程 """
print("正在停止相機擷取...")
self.running.value = False # 設定為 False讓 while 迴圈停止 self.running.value = False # 設定為 False讓 while 迴圈停止
self.terminate() # 強制終止進程 self.terminate() # 強制終止進程
print("相機擷取進程已終止") print("相機擷取進程已終止")

View File

@ -4,18 +4,29 @@ import numpy as np
import multiprocessing import multiprocessing
from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import QTimer from PyQt5.QtCore import QTimer
from Detection_window import Ui_MainWindow from Detection_window import Ui_MainWindow
from camera.camera_process import CameraProcess from camera.camera_process import CameraProcess
from read_ini import ConfigReader
from log_handler import LogHandler
class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow): class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self): def __init__(self):
super(DetectionApp, self).__init__() super(DetectionApp, self).__init__()
self.setupUi(self) self.setupUi(self)
self.log_handler = LogHandler() # ✅ 建立 LogHandler 物件
self.log_handler.write_log("程式啟動") # ✅ 寫入 log: 程式啟動
# ✅ 讀取 `exposure_time`
self.config_reader = ConfigReader()
self.exposure_time = self.config_reader.get_exposure_time()
# ✅ 先初始化 image_queue再啟動相機擷取 # ✅ 先初始化 image_queue再啟動相機擷取
self.image_queue = multiprocessing.Queue(maxsize=1) self.image_queue = multiprocessing.Queue(maxsize=1)
self.camera_process = None # 相機進程尚未啟動 self.camera_process = None # 相機進程尚未啟動
# ✅ 連接按鈕事件
self.bt_KeepShot.clicked.connect(self.KeepShot) self.bt_KeepShot.clicked.connect(self.KeepShot)
self.bt_StopKeepShot.clicked.connect(self.StopKeepShot) self.bt_StopKeepShot.clicked.connect(self.StopKeepShot)
@ -32,9 +43,11 @@ class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow):
# ✅ 重新建立 Queue避免殘留影像影響新擷取 # ✅ 重新建立 Queue避免殘留影像影響新擷取
self.image_queue = multiprocessing.Queue(maxsize=1) self.image_queue = multiprocessing.Queue(maxsize=1)
# ✅ 確保 `CameraProcess` 是新的 # ✅ 使用 `exposure_time` 啟動相機
self.camera_process = CameraProcess(self.image_queue) self.camera_process = CameraProcess(self.image_queue, exposure_time=self.exposure_time)
self.camera_process.start() self.camera_process.start()
self.log_handler.write_log("相機啟動") # ✅ 寫入 log: 相機啟動
self.statusbar.showMessage(f"開始擷取影像 (曝光時間: {self.exposure_time} 微秒)")
else: else:
print("相機已經在擷取") print("相機已經在擷取")
@ -45,6 +58,7 @@ class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow):
self.camera_process.join() # 等待進程完全結束 self.camera_process.join() # 等待進程完全結束
self.camera_process = None self.camera_process = None
print("已停止影像擷取") print("已停止影像擷取")
self.log_handler.write_log("相機停止") # ✅ 寫入 log: 相機停止
# ✅ 清空 Queue確保新擷取不會讀取到舊影像 # ✅ 清空 Queue確保新擷取不會讀取到舊影像
while not self.image_queue.empty(): while not self.image_queue.empty():
@ -69,12 +83,20 @@ class DetectionApp(QtWidgets.QMainWindow, Ui_MainWindow):
self.view_origin.setPixmap(pixmap) self.view_origin.setPixmap(pixmap)
def closeEvent(self, event): def closeEvent(self, event):
""" 確保程式關閉時正確停止相機擷取進程 """ reply = QtWidgets.QMessageBox.question(
self.StopKeepShot() # 確保關閉程式時停止相機擷取 self, "確認", "確定要關閉應用程式嗎?",
event.accept() # ✅ 允許視窗關閉 QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No
)
if reply == QtWidgets.QMessageBox.Yes:
self.StopKeepShot() # 停止相機
self.log_handler.write_log("程式關閉") # ✅ 寫入 log: 程式關閉
event.accept() # 允許視窗關閉
else:
event.ignore() # 阻止視窗關閉
if __name__ == "__main__": if __name__ == "__main__":
multiprocessing.freeze_support() # 在 Windows 上執行 multiprocessing 需要這行
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
window = DetectionApp() window = DetectionApp()
window.show() window.show()

34
log_handler.py Normal file
View File

@ -0,0 +1,34 @@
import os
import datetime
class LogHandler:
""" 日誌處理類別,負責寫入 log 檔案 """
def __init__(self, log_dir="logging"):
""" 初始化 log 系統 """
self.log_dir = log_dir
self.ensure_log_directory_exists()
self.log_file = self.get_log_filename()
def ensure_log_directory_exists(self):
""" 確保 logging 資料夾存在,如果不存在則建立 """
if not os.path.exists(self.log_dir):
os.makedirs(self.log_dir)
def get_log_filename(self):
""" 取得今日的 log 檔案名稱YYYY-MM-DD.txt """
today = datetime.datetime.now().strftime("%Y-%m-%d")
return os.path.join(self.log_dir, f"{today}.txt")
def write_log(self, event):
""" 寫入 log 訊息 """
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_message = f"[{timestamp}] {event}\n"
# 確保 log 檔案是最新的(每天更新)
self.log_file = self.get_log_filename()
with open(self.log_file, "a", encoding="utf-8") as file:
file.write(log_message)
print(f"📄 [LOG] {log_message.strip()}") # 也在 console 顯示 log

18
logging/2025-03-10.txt Normal file
View File

@ -0,0 +1,18 @@
[2025-03-10 17:50:57] 程式啟動
[2025-03-10 17:51:04] 相機啟動
[2025-03-10 17:51:22] 相機停止
[2025-03-10 17:51:33] 相機啟動
[2025-03-10 17:51:57] 相機停止
[2025-03-10 17:51:58] 相機啟動
[2025-03-10 17:52:04] 相機停止
[2025-03-10 17:52:13] 程式關閉
[2025-03-10 21:02:35] 程式啟動
[2025-03-10 21:02:44] 相機啟動
[2025-03-10 21:03:24] 相機停止
[2025-03-10 21:03:27] 程式關閉
[2025-03-10 21:04:15] 程式啟動
[2025-03-10 21:04:18] 相機啟動
[2025-03-10 21:04:29] 相機停止
[2025-03-10 21:04:32] 相機啟動
[2025-03-10 21:04:37] 相機停止
[2025-03-10 21:04:44] 程式關閉

BIN
model/best.pt Normal file

Binary file not shown.

View File

@ -1,2 +1,2 @@
[Camera_Setting] [Camera_Setting]
exposuretime_edge = 18122 exposuretime = 30000

25
read_ini.py Normal file
View File

@ -0,0 +1,25 @@
import configparser
class ConfigReader:
""" 用於讀取 parameter.ini 設定檔的類別 """
def __init__(self, ini_file="parameter.ini"):
""" 初始化並讀取 ini 檔案 """
self.ini_file = ini_file
self.config = configparser.ConfigParser()
self.read_config()
def read_config(self):
""" 讀取 ini 檔案內容 """
self.config.read(self.ini_file)
def get_exposure_time(self):
""" 取得曝光時間,如果讀取失敗則回傳預設值 5000 """
try:
exposure_time = int(self.config["Camera_Setting"]["exposuretime"])
print(f"讀取曝光時間: {exposure_time} 微秒")
return exposure_time
except Exception as e:
print(f"讀取 `parameter.ini` 失敗: {e}")
return 5000 # 預設曝光時間