예제 #1
0
class Ui_MainWindowcamera(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        MainWindow.setIconSize(QtCore.QSize(48, 48))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
        self.tabWidget.setGeometry(QtCore.QRect(130, 10, 651, 561))
        self.tabWidget.setObjectName("tabWidget")
        # self.tabWidget.setCurrentIndex()

        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.groupBox_2 = QtWidgets.QGroupBox(self.tab)
        self.groupBox_2.setGeometry(QtCore.QRect(60, 20, 181, 261))
        self.groupBox_2.setObjectName("groupBox_2")
        self.widget = QCameraViewfinder(self.groupBox_2)
        self.widget.setGeometry(QtCore.QRect(20, 20, 141, 231))
        self.widget.setObjectName("widget")
        self.label = QtWidgets.QLabel(self.tab)
        self.label.setGeometry(QtCore.QRect(10, 10, 621, 441))
        self.label.setText("")
        self.label.setPixmap(
            QtGui.QPixmap("../../aipicture/145940263255235.jpeg"))
        self.label.setScaledContents(True)
        self.label.setObjectName("label")

        self.label_2 = QtWidgets.QLabel(self.tab)
        self.label_2.setGeometry(QtCore.QRect(410, 30, 151, 251))
        self.label_2.setText("")
        self.label_2.setPixmap(QtGui.QPixmap(""))
        self.label_2.setScaledContents(False)
        self.label_2.setObjectName("label_2")
        # self.btn1 = QtWidgets.QPushButton("确定", self)
        # self.btn1.move(20, 120)

        self.plainTextEdit = QtWidgets.QPlainTextEdit(self.tab)
        self.plainTextEdit.setGeometry(QtCore.QRect(140, 320, 171, 31))
        self.plainTextEdit.setObjectName("plainTextEdit")

        self.label_3 = QtWidgets.QLabel(self.tab)
        self.label_3.setGeometry(QtCore.QRect(40, 310, 81, 31))
        self.label_3.setStyleSheet("font: 14pt'楷体';color:rgb(255,255,255);")
        self.label_3.setObjectName("label_3")

        self.label_4 = QtWidgets.QLabel(self.tab)
        self.label_4.setGeometry(QtCore.QRect(40, 382, 81, 21))
        self.label_4.setStyleSheet("font:14pt '楷体';color:rgb(255,255,255);")
        self.label_4.setObjectName("label_4")

        self.plainTextEdit_2 = QtWidgets.QPlainTextEdit(self.tab)
        self.plainTextEdit_2.setGeometry(QtCore.QRect(140, 380, 171, 31))
        self.plainTextEdit_2.setObjectName("plainTextEdit_2")

        self.btnget = QtWidgets.QPushButton(self.tab)
        self.btnget.setGeometry(QtCore.QRect(380, 340, 75, 23))
        self.btnget.setObjectName("pushButton")

        self.label.raise_()
        self.groupBox_2.raise_()
        self.label_2.raise_()
        self.plainTextEdit.raise_()
        self.label_3.raise_()
        self.label_4.raise_()
        self.plainTextEdit_2.raise_()
        self.btnget.raise_()
        self.tabWidget.addTab(self.tab, "")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.label_5 = QtWidgets.QLabel(self.tab_3)
        self.label_5.setGeometry(QtCore.QRect(0, 0, 661, 451))
        self.label_5.setText("")
        self.label_5.setPixmap(QtGui.QPixmap("../../aipicture/searchbg.jpg"))
        self.label_5.setScaledContents(True)
        self.label_5.setObjectName("label_5")
        self.groupBox_3 = QtWidgets.QGroupBox(self.tab_3)
        self.groupBox_3.setGeometry(QtCore.QRect(20, 20, 611, 411))
        self.groupBox_3.setObjectName("groupBox_3")

        self.btncap = QtWidgets.QPushButton(self.groupBox_3)
        self.btncap.setGeometry(QtCore.QRect(150, 30, 72, 23))
        self.btnget.setObjectName("pushButton")

        self.widget_2 = QCameraViewfinder(self.groupBox_3)
        self.widget_2.setGeometry(QtCore.QRect(50, 100, 171, 271))
        self.widget_2.setObjectName("widget_2")
        self.label_6 = QtWidgets.QLabel(self.groupBox_3)
        self.label_6.setGeometry(QtCore.QRect(50, 21, 101, 31))
        self.label_6.setLineWidth(1)
        self.label_6.setTextFormat(QtCore.Qt.AutoText)
        self.label_6.setScaledContents(False)
        self.label_6.setStyleSheet("font:14pt '楷体';color:rgb(255,255,255);")
        self.label_6.setObjectName("label_6")
        self.plainTextEdit_3 = QtWidgets.QPlainTextEdit(self.groupBox_3)
        self.plainTextEdit_3.setGeometry(QtCore.QRect(320, 100, 201, 31))
        self.plainTextEdit_3.setObjectName("plainTextEdit_3")
        self.plainTextEdit_3.setStyleSheet("font:14pt '楷体';")
        self.label_7 = QtWidgets.QLabel(self.groupBox_3)
        self.label_7.setGeometry(QtCore.QRect(230, 100, 80, 19))
        self.label_7.setObjectName("label_7")
        self.label_7.setStyleSheet("font:14pt '楷体';color:rgb(255,255,255);")
        self.label_8 = QtWidgets.QLabel(self.groupBox_3)
        self.label_8.setGeometry(QtCore.QRect(230, 180, 81, 31))
        self.label_8.setObjectName("label_8")
        self.label_8.setStyleSheet("font:14pt '楷体';color:rgb(255,255,255);")
        self.label_9 = QtWidgets.QLabel(self.groupBox_3)
        self.label_9.setGeometry(QtCore.QRect(230, 260, 81, 31))
        self.label_9.setObjectName("label_9")
        self.label_9.setStyleSheet("font:14pt '楷体';color:rgb(255,255,255);")
        self.plainTextEdit_4 = QtWidgets.QPlainTextEdit(self.groupBox_3)
        self.plainTextEdit_4.setGeometry(QtCore.QRect(320, 180, 201, 31))
        self.plainTextEdit_4.setObjectName("plainTextEdit_4")
        self.plainTextEdit_4.setStyleSheet("font:14pt '楷体';")
        self.plainTextEdit_5 = QtWidgets.QPlainTextEdit(self.groupBox_3)
        self.plainTextEdit_5.setGeometry(QtCore.QRect(320, 260, 201, 31))
        self.plainTextEdit_5.setObjectName("plainTextEdit_5")
        self.plainTextEdit_5.setStyleSheet("font:14pt '楷体';")
        self.label_10 = QtWidgets.QLabel(self.groupBox_3)
        self.label_10.setGeometry(QtCore.QRect(250, 350, 61, 21))
        self.label_10.setObjectName("label_10")
        self.label_10.setStyleSheet("font:14pt '楷体';color:rgb(255,255,255);")
        self.plainTextEdit_6 = QtWidgets.QPlainTextEdit(self.groupBox_3)
        self.plainTextEdit_6.setGeometry(QtCore.QRect(320, 340, 201, 31))
        self.plainTextEdit_6.setObjectName("plainTextEdit_6")
        self.plainTextEdit_6.setStyleSheet("font:14pt '楷体';")
        self.tabWidget.addTab(self.tab_3, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")

        self.btndata = QtWidgets.QPushButton(self.tab_2)
        self.btndata.setGeometry(QtCore.QRect(30, 210, 75, 23))
        self.btndata.setObjectName("pushButton")
        self.btndata.raise_()
        self.plainTextEdit_7 = QtWidgets.QPlainTextEdit(self.tab_2)
        self.plainTextEdit_7.setGeometry(QtCore.QRect(130, 60, 431, 331))
        self.plainTextEdit_7.setObjectName("plainTextEdit_7")
        self.plainTextEdit_7.setStyleSheet("font:14pt '楷体';")
        self.label_11 = QtWidgets.QLabel(self.tab_2)
        self.label_11.setGeometry(QtCore.QRect(60, 60, 54, 12))
        self.label_11.setObjectName("label_11")
        self.tabWidget.addTab(self.tab_2, "")
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(0, 10, 121, 471))
        self.groupBox.setObjectName("groupBox")
        self.addnew = QtWidgets.QPushButton(self.groupBox)
        self.addnew.setGeometry(QtCore.QRect(20, 40, 91, 51))
        self.addnew.setObjectName("addnew")
        self.searchinfo = QtWidgets.QPushButton(self.groupBox)
        self.searchinfo.setGeometry(QtCore.QRect(20, 170, 91, 51))
        self.searchinfo.setObjectName("searchinfo")
        self.pushButton_3 = QtWidgets.QPushButton(self.groupBox)
        self.pushButton_3.setGeometry(QtCore.QRect(20, 300, 91, 51))
        self.pushButton_3.setObjectName("pushButton_3")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
        self.menubar.setObjectName("menubar")
        self.menuwtf = QtWidgets.QMenu(self.menubar)
        self.menuwtf.setObjectName("menuwtf")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        self.toolBar.setEnabled(True)
        self.toolBar.setIconSize(QtCore.QSize(60, 60))
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
        self.actionActStartCamera = QtWidgets.QAction(MainWindow)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("E:/aipicture/videocam_video_72px.ico"),
                       QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionActStartCamera.setIcon(icon)
        self.actionActStartCamera.setObjectName("actionActStartCamera")
        self.actionActCloseCamrera = QtWidgets.QAction(MainWindow)
        icon1 = QtGui.QIcon()
        icon1.addPixmap(
            QtGui.QPixmap("E:/aipicture/videocam_off_video_72px.ico"),
            QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionActCloseCamrera.setIcon(icon1)
        self.actionActCloseCamrera.setObjectName("actionActCloseCamrera")
        self.actionActCaputure = QtWidgets.QAction(MainWindow)
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap("E:/aipicture/camcorder_72px.ico"),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionActCaputure.setIcon(icon2)
        self.actionActCaputure.setObjectName("actionActCaputure")
        self.actionActExit = QtWidgets.QAction(MainWindow)
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap("E:/aipicture/power_exit_72px.ico"),
                        QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.actionActExit.setIcon(icon3)
        self.actionActExit.setObjectName("actionActExit")
        self.menuwtf.addAction(self.actionActStartCamera)
        self.menuwtf.addSeparator()
        self.menuwtf.addAction(self.actionActCloseCamrera)
        self.menuwtf.addSeparator()
        self.menuwtf.addAction(self.actionActCaputure)
        self.menuwtf.addSeparator()
        self.menuwtf.addAction(self.actionActExit)
        self.menuwtf.addSeparator()
        self.menubar.addAction(self.menuwtf.menuAction())

        self.toolBar.addAction(self.actionActStartCamera)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.actionActCloseCamrera)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.actionActCaputure)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.actionActExit)
        self.toolBar.addSeparator()

        self.retranslateUi(MainWindow)
        # self.tabWidget.setCurrentIndex(0)
        # self.addnew.clicked['bool'].connect(self.addnew.click)
        # self.searchinfo.clicked.connect(self.searchinfo.click)
        # self.pushButton_3.clicked['bool'].connect(self.pushButton_3.click)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.groupBox_2.setTitle(_translate("MainWindow", "GroupBox"))
        self.label_3.setText(_translate("MainWindow", "学生姓名"))
        self.label_4.setText(_translate("MainWindow", "学生成绩"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab),
                                  _translate("MainWindow", "拍照增加"))
        self.groupBox_3.setTitle(_translate("MainWindow", "GroupBox"))
        self.label_6.setText(_translate("MainWindow", "照片查找"))
        self.label_7.setText(_translate("MainWindow", "学生姓名"))
        self.label_8.setText(_translate("MainWindow", "学生性别"))
        self.label_9.setText(_translate("MainWindow", "学生成绩"))
        self.label_10.setText(_translate("MainWindow", "颜值"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3),
                                  _translate("MainWindow", "查找"))
        self.label_11.setText(_translate("MainWindow", "数据库"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2),
                                  _translate("MainWindow", "查看"))
        self.groupBox.setTitle(_translate("MainWindow", "shiftscene"))
        self.addnew.setText(_translate("MainWindow", "拍照增加"))
        self.searchinfo.setText(_translate("MainWindow", "查找"))
        self.pushButton_3.setText(_translate("MainWindow", "查看"))
        self.btncap.setText(_translate("MainWindow", "搜索"))
        self.btnget.setText(_translate("MainWindow", "提交"))
        self.btndata.setText(_translate("MainWindow", "查看"))
        self.menuwtf.setTitle(_translate("MainWindow", "开始"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.actionActStartCamera.setText(
            _translate("MainWindow", "ActStartCamera"))
        self.actionActStartCamera.setToolTip(
            _translate("MainWindow", "使用摄像头(ctrl+s)"))
        self.actionActStartCamera.setShortcut(
            _translate("MainWindow", "Ctrl+S"))
        self.actionActCloseCamrera.setText(
            _translate("MainWindow", "ActCloseCamrera"))
        self.actionActCloseCamrera.setToolTip(
            _translate("MainWindow", "关闭摄像头(ctrl+c)"))
        self.actionActCloseCamrera.setShortcut(
            _translate("MainWindow", "Ctrl+C"))
        self.actionActCaputure.setText(_translate("MainWindow", "ActCaputure"))
        self.actionActCaputure.setToolTip(
            _translate("MainWindow", "拍照(ctrl+d)"))
        self.actionActCaputure.setShortcut(_translate("MainWindow", "Ctrl+D"))
        self.actionActExit.setText(_translate("MainWindow", "ActExit"))
        self.actionActExit.setToolTip(_translate("MainWindow", "退出(esc)"))
        self.actionActExit.setShortcut(_translate("MainWindow", "Esc"))
예제 #2
0
class MyWindow(Window, Form):
    __slots__ = [
        # widgets
        'cameraViewfinder',
        'statCanvas0',
        'statCanvas1',
        'statCanvas2',
        # commons
        'threadpool',
        # camera
        'camera',
        'capture',
        'capture_timer',
        # photo
        'photo_timestamp',
        'photo_pixmap',
        # setting
        'warn_notifier',
        'warn_notifier_timer',
        'stat_notifier',
        'stat_notifier_timer',
    ]

    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # widgets
        self.cameraViewfinder = QCameraViewfinder(self.cameraWidget)
        self.cameraViewfinder.setGeometry(QRect(-1, 52, 641, 481))
        self.cameraViewfinder.setObjectName('cameraViewfinder')
        self.statCanvas0 = StatCanvas(self.statWidget0, 'statCanvas0', 640,
                                      480)
        self.statCanvas1 = StatCanvas(self.statWidget1, 'statCanvas1', 640,
                                      480)
        self.statCanvas2 = StatCanvas(self.statWidget2, 'statCanvas2', 640,
                                      480)

        # common
        self.threadpool = QThreadPool()
        # camera
        self.camera = None
        self.capture = None
        self.init_camera_and_capture()
        self.capture_timer = self.create_timer(5_000, self.capture_safe)
        # photo
        self.photo_timestamp = None
        self.photo_pixmap = QPixmap()
        # setting
        self.warn_notifier = Notifier(['확인', '무시'])
        self.warn_notifier_timer = self.create_timer(60_000,
                                                     self.notify_warn_if_bad)
        self.stat_notifier = Notifier(['확인', '무시'])
        self.stat_notifier_timer = self.create_timer(
            self.get_setting_stat_interval(), self.notify_stat)

        # connect: camera
        self.cameraButton.toggled.connect(
            lambda toggle: self.cameraButton.setText('정지' if toggle else '시작'))
        self.cameraButton.toggled.connect(self.toggle_all_timers)
        # connect: stat, photo
        self.statTabWidget.tabBarClicked.connect(self.load_stat)
        self.tabWidget.tabBarClicked.connect(lambda index: self.load_photo(
        ) if index == 1 else self.load_stat(self.statTabWidget.currentIndex())
                                             if index == 2 else None)
        self.photoLeftButton.clicked.connect(
            lambda: self.load_photo(left=True))
        self.photoRightButton.clicked.connect(
            lambda: self.load_photo(right=True))
        # connect: setting
        self.settingWarnButton.toggled.connect(self.toggle_warn_timer)
        self.connect_setting_stat_cycle_selected(self.settingStatCycleOff)
        self.connect_setting_stat_cycle_selected(self.settingStatCycle1)
        self.connect_setting_stat_cycle_selected(self.settingStatCycle2)
        self.connect_setting_stat_cycle_selected(self.settingStatCycle3)
        self.connect_setting_stat_cycle_selected(self.settingStatCycle6)
        self.settingStatTestButton.pressed.connect(
            lambda: self.settingStatTestButton.setIcon(
                QIcon('resource/test_off.png')))
        self.settingStatTestButton.released.connect(
            lambda: self.settingStatTestButton.setIcon(
                QIcon('resource/test_on.png')))
        self.settingStatTestButton.clicked.connect(self.notify_stat)

    def connect_setting_stat_cycle_selected(self, cycle: QPushButton) -> None:
        def selected(toggle: bool) -> None:
            checkeds = [x for x in others if x.isChecked()]
            if not checkeds:
                cycle.setChecked(True)
            elif toggle:
                checkeds[0].setChecked(False)
                self.toggle_stat_timer(cycle is not self.settingStatCycleOff)

        others: List[QPushButton] = [
            self.settingStatCycle1,
            self.settingStatCycle2,
            self.settingStatCycle3,
            self.settingStatCycle6,
            self.settingStatCycleOff,
        ]
        others.remove(cycle)
        cycle.toggled.connect(selected)

    def init_camera_and_capture(self) -> bool:
        available_cameras = QCameraInfo.availableCameras()
        if not available_cameras:
            return False
        self.camera = self.create_camera(available_cameras, 0,
                                         self.cameraViewfinder,
                                         self.error_camera)
        self.capture = self.create_capture(self.camera,
                                           self.process_image_on_threadpool,
                                           self.error_camera)
        if self.camera.isAvailable():
            self.camera.start()
        return True

    def check_camera(self) -> bool:
        if (self.camera is None or not self.camera.isAvailable()
            ) and not self.init_camera_and_capture():
            return False
        if not self.capture.isReadyForCapture():
            self.camera.start()
        return True

    def start_capture_timer_safe(self) -> None:
        if self.check_camera():
            self.capture_timer.start()
        else:
            self.error_camera()

    def capture_safe(self) -> None:
        if self.check_camera():
            self.capture.capture()
        else:
            self.error_camera()

    def error_camera(self, *args) -> None:
        self.cameraButton.setChecked(False)

    def get_setting_stat_interval(self) -> int:
        cycles: List[QPushButton] = [
            self.settingStatCycle1,
            self.settingStatCycle2,
            self.settingStatCycle3,
            self.settingStatCycle6,
        ]
        checkeds = [x for x in cycles if x.isChecked()]
        text = checkeds[0].objectName().replace('settingStatCycle', '',
                                                1) if checkeds else 1
        msec = int(text) * 3_600_000
        return msec

    def process_image_on_threadpool(self, id: int, qimg: QImage) -> None:
        def display_result(record: RecordType) -> None:
            if record['success']:
                self.display_ihunch_status(self.cameraStatusBar,
                                           record['human'], record['ihunch'])

        worker = Worker(upload_image_save_record, qimg)
        worker.signals.result.connect(display_result)
        self.threadpool.start(worker)

    def load_stat(self, index: int = 0) -> None:
        statCanvas = getattr(self, f'statCanvas{index}')
        if index == 0:
            statCanvas.plot_today_vs_yesterday()
        elif index == 1:
            statCanvas.plot_week_to_day()
        else:
            statCanvas.plot_week_to_percent()
        statCanvas.draw()

    def load_photo(self, left=False, right=False) -> None:
        beg, end = None, None
        index = -1
        if left:
            end = self.photo_timestamp
        elif right:
            beg = self.photo_timestamp
            index = 0
        records = RecordsDriver.load(beg, end)
        if records:
            record = records[index]
            timestamp = record['timestamp']
            self.photo_timestamp = datetime.strptime(timestamp,
                                                     DATETIME_FORMAT)
            self.photo_pixmap.load(str(PHOTOS_DIR / f'{timestamp}.jpg'))
            self.photoImage.setPixmap(self.photo_pixmap)
            self.display_ihunch_status(self.photoStatusBar, record['human'],
                                       record['ihunch'])

    def toggle_all_timers(self, toggle: bool) -> None:
        if toggle:
            self.start_capture_timer_safe()
        else:
            self.capture_timer.stop()
        self.toggle_warn_timer(toggle)
        self.toggle_stat_timer(toggle)

    def toggle_warn_timer(self, toggle: bool) -> None:
        if toggle and self.cameraButton.isChecked():
            self.warn_notifier_timer.start()
        else:
            self.warn_notifier_timer.stop()

    def toggle_stat_timer(self, toggle: bool) -> None:
        if toggle and self.cameraButton.isChecked():
            self.stat_notifier_timer.setInterval(
                self.get_setting_stat_interval())
            self.stat_notifier_timer.start()
        else:
            self.stat_notifier_timer.stop()

    def notify_warn_if_bad(self) -> None:
        minutes = 5
        beg = datetime.now() - timedelta(minutes=minutes)
        records = RecordsDriver.load(beg)
        stats = [x['ihunch'] > 0.5 for x in records if x['human']]
        if not stats:
            return
        ihunch_percent = sum(stats) / len(stats) * 100
        if ihunch_percent < 90:
            return
        self.warn_notifier.notify(
            '거북목 경고',
            f'최근 {minutes}분 중에 거북목 자세 비중이 {ihunch_percent:.2f}%를 차지합니다!',
            '거북목 탈출 넘버원',
            on_action=lambda nid, index: self.settingWarnButton.setChecked(
                False) if index == 1 else None)

    def notify_stat(self) -> None:
        hours = self.stat_notifier_timer.interval() // 3_600_000
        beg = datetime.now() - timedelta(hours=hours)
        records = RecordsDriver.load(beg)
        stats = [x['ihunch'] > 0.5 for x in records if x['human']]
        if not stats:
            return
        ihunch_percent = sum(stats) / len(stats) * 100
        self.stat_notifier.notify(
            '최근 거북목 통계',
            f'최근 {hours}시간 중에 거북목 자세 비중이 {ihunch_percent:.2f}%를 차지했습니다.',
            '거북목 탈출 넘버원',
            on_action=lambda nid, index: self.settingStatCycleOff.setChecked(
                True) if index == 1 else None)

    @staticmethod
    def create_camera(cameras: list, index: int, viewfinder: QCameraViewfinder,
                      error: Callable) -> QCamera:
        camera = QCamera(cameras[index])
        camera.setViewfinder(viewfinder)
        camera.setCaptureMode(QCamera.CaptureStillImage)
        camera.error.connect(error)
        return camera

    @staticmethod
    def create_capture(camera: QCamera, callback: Callable,
                       error: Callable) -> QCameraImageCapture:
        capture = QCameraImageCapture(camera)
        capture.setCaptureDestination(QCameraImageCapture.CaptureToBuffer)
        capture.imageCaptured.connect(callback)
        capture.error.connect(error)
        return capture

    @staticmethod
    def create_timer(interval: int, callback: Callable) -> QTimer:
        timer = QTimer()
        timer.setInterval(interval)
        timer.timeout.connect(callback)
        return timer

    @staticmethod
    def display_ihunch_status(statusbar: QLabel, human: bool,
                              ihunch: float) -> None:
        if human:
            if ihunch > 0.5:
                text, color = '거북목', 'FF0000'
            else:
                text, color = '정상', '23F200'
            text += f' ({ihunch:.4f})'
        else:
            text, color = '자리비움', 'FF971E'
        statusbar.setText(text)
        statusbar.setAlignment(Qt.AlignCenter)
        statusbar.setStyleSheet(f'color: #{color};')