功能;
pyqt5實現一個界面,上面設置一個畫面,一個按鈕
通過按鈕文字和狀態切換來控制攝像頭的開啟與關閉
完整代碼如下:
python
import sys
import cv2
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget
class CameraThread(QThread):
change_pixmap = pyqtSignal(QImage)
stopped = pyqtSignal() # 通知主線程線程已停止
def run(self):
cap = cv2.VideoCapture(0)
if not cap.isOpened():
self.stopped.emit()
return
while not self.isInterruptionRequested():
ret, frame = cap.read()
if not ret:
break
# ✅ 水平翻轉鏡像(左右翻轉)
frame = cv2.flip(frame, 1)
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_frame.shape
bytes_per_line = ch * w
qt_image = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
self.change_pixmap.emit(qt_image.copy())
self.msleep(10)
cap.release()
self.stopped.emit()
def stop(self):
self.requestInterruption()
self.wait()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("攝像頭實時顯示")
self.resize(640, 480)
# 圖像顯示標籤
self.image_label = QLabel("攝像頭畫面")
self.image_label.setScaledContents(True)
self.image_label.setStyleSheet("border: 1px solid #888;")
self.image_label.setAlignment(Qt.AlignCenter) # 文字居中
# ✅ 只有一個按鈕
self.toggle_btn = QPushButton("啟動攝像頭")
self.toggle_btn.setStyleSheet("""
QPushButton {
background-color: #4CAF50;
color: white;
font-size: 14px;
font-weight: bold;
padding: 10px;
border-radius: 5px;
}
QPushButton:hover {
background-color: #45a049;
}
QPushButton:disabled {
background-color: #cccccc;
}
""")
self.toggle_btn.setFixedHeight(40)
# 佈局
layout = QVBoxLayout()
layout.addWidget(self.image_label)
layout.addWidget(self.toggle_btn)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
# 攝像頭線程
self.camera_thread = CameraThread(self)
self.camera_thread.change_pixmap.connect(self.update_image)
self.camera_thread.stopped.connect(self.on_camera_stopped)
# ✅ 按鈕點擊切換狀態
self.toggle_btn.clicked.connect(self.toggle_camera)
def toggle_camera(self):
"""切換攝像頭狀態:啟動 / 停止"""
if not self.camera_thread.isRunning():
# 啟動攝像頭
self.image_label.setText("加載中...")
self.toggle_btn.setEnabled(False) # 短暫禁用防止重複點擊
self.camera_thread.start()
# 線程啟動後更新按鈕狀態
self.toggle_btn.setText("停止攝像頭")
self.toggle_btn.setStyleSheet("""
QPushButton {
background-color: #f44336;
color: white;
font-size: 14px;
font-weight: bold;
padding: 10px;
border-radius: 5px;
}
QPushButton:hover {
background-color: #da190b;
}
""")
self.toggle_btn.setEnabled(True)
else:
# 停止攝像頭
self.camera_thread.stop()
def update_image(self, qt_image):
"""更新畫面"""
self.image_label.setPixmap(QPixmap.fromImage(qt_image))
def on_camera_stopped(self):
"""攝像頭停止後的回調"""
self.image_label.clear()
self.image_label.setText("攝像頭已停止")
# ✅ 恢復按鈕為「啟動」狀態
self.toggle_btn.setText("啟動攝像頭")
self.toggle_btn.setStyleSheet("""
QPushButton {
background-color: #4CAF50;
color: white;
font-size: 14px;
font-weight: bold;
padding: 10px;
border-radius: 5px;
}
QPushButton:hover {
background-color: #45a049;
}
""")
def closeEvent(self, event):
"""窗口關閉時確保線程停止"""
if self.camera_thread.isRunning():
self.camera_thread.stop()
super().closeEvent(event)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())