def set_calfactor(self):
        """
        Measure filament width, set spinbox to value, 
        then call by clicking calibrate button.
        
        """
        prev_cal = self.ui.cal_factor.value()
        user_w_pxl = self.ui.vline_right.value() - self.ui.vline_left.value()
        width_pxl = np_ave(
            self.cal_width_history)  # self.areas['width'].width_value
        new_cal = round(self.ui.filament_width.value() / float(width_pxl),
                        4)  #mm/pxl
        self.ui.cal_factor.setValue(new_cal)

        append_texteditor(
            self.ui.text_pipe_out,
            'Calibrate: user_W: {} vs detected: {:.4f} pxl'.format(
                user_w_pxl, width_pxl))
        append_texteditor(
            self.ui.text_pipe_out,
            'Calibrate: prev cal_factor:  {:.4f} mm/pxl'.format(prev_cal))

        #update vals
        self.set_cal_values()

        #set cal date
        date_time = QtCore.QDateTime.currentDateTime()
        self.ui.cal_date.setDateTime(date_time)

        #return to main tab
        self.ui.tabWidget.setCurrentIndex(0)
 def save_config(self, config_filename=None):
     """
     """
     self.update_config()
     #write
     config_filename = config_filename or self.config_filename
     with open(config_filename, 'w') as configfile:
         self.config.write(configfile)
     #
     append_texteditor(self.ui.text_pipe_out,
                       'Config updated: {}'.format(config_filename))
 def set_cal_values(self):
     """
     """
     self.convert_width = self.ui.cal_factor.value()
     self.convert_speed = self.convert_width * self.stream_fps
     self.ui.calfactor_inv.setValue(1 / self.convert_width)
     self.frame_result['cal_factor'] = self.convert_width
     #done
     append_texteditor(
         self.ui.text_pipe_out,
         'Calibrate: set cal_factor:  {:.4f} mm/pxl'.format(
             self.ui.cal_factor.value()))
     append_texteditor(
         self.ui.text_pipe_out,
         'Calibrate: set convert_speed:  {:.4f} mm/pxl frame/s'.format(
             self.convert_speed))
 def record_video_save(self):
     """
     stop and save
     """
     self.recording = False
     self.ui.record_video.setChecked(False)
     if self.record_file is not None:
         #release video file - save
         self.record_file.release()
         self.ui.record_state.setText('Saved')
         self.ui.record_start_new.setEnabled(False)
         self.record_file = None
         append_texteditor(self.ui.text_pipe_out,
                           'Video saved: {}'.format(self.record_filename))
         #save config with the same namve
         configname = self.record_filename.split('.avi')[0]
         configname += '.cfg'
         self.save_config(configname)
    def load_video_from_file(self, video_fullname=None):
        """
        Called by pb clicked:
            disconnect current camera if connected
            enalbe controls
            setup frame numbers and slider.
            
        Once video is running, camera view cannot be restored. 
        Only through restarting app.
        """
        self.main_timer.stop()

        if self.stream is not None:
            self.stream.stop()  #stop thread, release capture
            self.stream.new_frame.disconnect()

        if video_fullname is None:
            video_fullname = load_file_name(self,
                                            type_filter='Video File (*.AVI)')

        if video_fullname is not None:
            append_texteditor(self.ui.text_pipe_out,
                              'load video: {}'.format(video_fullname))

            self.ui.frame_next.setEnabled(True)
            self.ui.frame_prev.setEnabled(True)
            self.ui.frame_number.setEnabled(True)
            self.ui.play_video.setEnabled(True)

            if self.video_capture is not None:
                self.video_capture.release()
                self.video_capture = None

            self.video_capture = cv2.VideoCapture(video_fullname)
            retval, frame = self.video_capture.read()
            self.frame_extruder = frame

            filename = os.path.split(video_fullname)[-1]
            self.video_filename = os.path.splitext(filename)[0]
            if retval:

                self.ui.fps.setValue(self.video_capture.get(cv2.CAP_PROP_FPS))
                self.stream_fps = self.ui.fps.value()
                self.time_passed_ms = (1 / self.ui.fps.value()) * 1000
                self.num_frames = int(
                    self.video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
                append_texteditor(
                    self.ui.text_pipe_out,
                    'next_video shape: {}, num frames {}'.format(
                        frame.shape, self.num_frames))

                self.ui.frame_number.setMaximum(self.num_frames)
                #try get config
                file_search = os.path.splitext(video_fullname)[0]
                video_config_filename = get_files_like(
                    folder=None, filename_contains=file_search, ext='cfg')
                try:
                    video_config_filename = video_config_filename[0]
                    append_texteditor(
                        self.ui.text_pipe_out,
                        'load video config: {}'.format(video_config_filename))
                    append_texteditor(
                        self.ui.text_pipe_out,
                        'video config: {}'.format(video_config_filename))
                    self.load_configs(video_config_filename)
                    self.areas_setup()
                    self.set_cal_values()

                except IndexError:
                    append_texteditor(self.ui.text_pipe_out,
                                      'no config file found: {}'.format(
                                          video_config_filename))  #
            else:
                append_texteditor(self.ui.text_pipe_out,
                                  'frame is None: {}'.format(video_fullname))

        #---- end if video_filename is not None

        self.main_timer.start(self.main_timer_rate)
    def __init__(self, main_app, pipe_in=None, pipe_out=None, **kwargs):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.app_settings = kwargs
        self.pipe_in = pipe_in
        self.pipe_out = pipe_out

        self.close_attempts = 0  #complicated exit process

        self.measure_active = False  #when active, do not display cv dwg to increase speed
        self.process_active = True  #run realtime in-processing
        self.new_frame_processed = False  #update display on True
        self.update_areas = False  #True, new/update areas and frame
        self.frame_id = 0  # id used in ROIA for update of points,
        # also the current video frame number
        self.prev_frame = -1
        self.convert_speed = 1.0  #cal factor/fps
        self.convert_width = 1.0  #cal factor
        self.time_passed_ms = (1 / 30.) * 1000
        #        self.missed_frames = 0

        #collect a few filament width estimations over time
        self.cal_collect_width = False  #control
        self.cal_width_history = []  #collected data
        self.cal_w_len = 60  #number to collect

        #result reporting -send dict via pipe
        self.frame_result = {}
        self.frame_result['name'] = 'frame_result'
        self.frame_result['time_stamp'] = ''
        self.frame_result['gear_speed'] = 0  #pxl/frame
        self.frame_result['filament_speed'] = 0  #pxl/frame
        self.frame_result['filament_width'] = 0  #pxl
        self.frame_result['time_elasped'] = 0  #sec
        self.frame_result['cal_factor'] = 1.0  #mm/pxl
        self.frame_result['frame_id'] = 1.0  #

        #setup reporting -send dict via pipe
        self.setup_details = {}
        self.setup_details['name'] = 'setup_details'
        self.setup_details['user_filament_width'] = 2.85  #mm
        self.setup_details['config'] = {}

        #video recording reporting
        self.video_details = {}
        self.video_details['name'] = 'video_filename'
        self.video_details['video_filename'] = None
        self.data_folder = 'data'

        #load *.cfg
        self.load_configs()

        #timers
        self.current_time = QtCore.QTime()
        self.main_timer_rate = 50  #ms
        self.main_timer = QtCore.QTimer(self)
        self.main_timer.timeout.connect(self.main_task_loop)

        #source
        self.video_capture = None  #from a video file
        self.stream = None  #from camera
        self.frame_extruder = None

        #threading
        self.__workers_done = True
        self.__threads = []

        if kwargs.get('load_video_def', False):
            self.video_mode = True
            self.load_video_from_file(
                video_fullname='data\Test_2017_02_17__08_45_46.avi')
            self.ui.load_video.setEnabled(True)

        else:
            self.video_mode = False
            self.init_threads(main_app)

        #video recording settings
        self.recording = False  #busy recording
        self.record_file = None
        self.record_filename_prev = ''
        self.record_fps = self.stream_fps  # fps for video file

        #UI camera display
        self.microscope_display = CameraLabel(self.ui.groupBox_video)
        self.microscope_display.setMaximumSize(QtCore.QSize(640, 480))
        self.microscope_display.setFrameShape(QtWidgets.QFrame.WinPanel)
        self.microscope_display.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.microscope_display.setLineWidth(2)
        self.microscope_display.setScaledContents(True)
        self.microscope_display.setAlignment(QtCore.Qt.AlignCenter)
        self.microscope_display.setObjectName("microscope_display")
        self.ui.verticalLayout_2.addWidget(self.microscope_display)

        if self.frame_extruder is not None:
            self.microscope_display.pixmap = converter(self.frame_extruder)

        #init configs
        self.areas_setup()
        self.set_cal_values()

        #CONNECTIONS
        #UI ROI setup
        self.ui.img_rotation.valueChanged.connect(self.set_update_areas)

        self.ui.vline_left.valueChanged.connect(self.set_update_areas)
        self.ui.vline_right.valueChanged.connect(self.set_update_areas)

        self.ui.w_y2.valueChanged.connect(self.set_update_areas)
        self.ui.w_vline_left_border.valueChanged.connect(self.set_update_areas)
        self.ui.w_vline_right_border.valueChanged.connect(
            self.set_update_areas)

        self.ui.fil_roi_x.valueChanged.connect(self.set_update_areas)
        self.ui.fil_roi_y.valueChanged.connect(self.set_update_areas)
        self.ui.fil_roi_size.valueChanged.connect(self.set_update_areas)

        self.ui.gear_roi_x.valueChanged.connect(self.set_update_areas)
        self.ui.gear_roi_y.valueChanged.connect(self.set_update_areas)
        self.ui.gear_roi_size.valueChanged.connect(self.set_update_areas)

        self.ui.mat_colour.currentIndexChanged.connect(self.set_update_areas)

        self.ui.save_config.clicked.connect(self.save_config)

        #calibration
        #        self.ui.filament_width.valueChanged.connect(self.updated_std_width)
        self.ui.calibrate.clicked.connect(self.start_cal)

        #video playback
        self.ui.load_video.clicked.connect(self.load_video_from_file)
        self.ui.frame_number.valueChanged.connect(self.new_frame_trackbar)
        self.ui.frame_next.clicked.connect(self.new_frame_next)
        self.ui.frame_prev.clicked.connect(self.new_frame_prev)

        #video recording
        self.ui.record_video.clicked.connect(self.record_video_start_stop)
        self.ui.record_save.clicked.connect(self.record_video_save)
        self.ui.record_start_new.clicked.connect(self.record_start_new)

        #---
        qr = self.frameGeometry()
        self.move(qr.topLeft())
        self.setWindowTitle('Vision')
        self.ui.report_results.setChecked(False)

        if self.video_mode:
            self.ui.tabWidget.setCurrentIndex(4)

        #DEBUG
        #profile
        self.busy_profile = kwargs.get('do_profiling',
                                       0)  #0 or number of frames to profile

        self.ping_count = 0

        #START
        current_time_str = self.current_time.currentTime().toString(
            'hh:mm:ss.zzz')
        append_texteditor(self.ui.text_pipe_out,
                          'start time_stamp: {}'.format(current_time_str))

        #start threads if any
        for thread, worker in self.__threads:
            thread.start(
            )  # this will emit 'started' and start thread's event loop

        self.main_timer.start(self.main_timer_rate)
        self.pipe_out.send('vision started')
        self.updated_std_width(self.ui.filament_width.value())
    def load_configs(self, add_config_filename=None):
        """
        """
        self.expected_speed = 2  #mm/s

        default_config_filename = 'config_vision.cfg'
        self.set_config_name = 'vision'

        self.config = configparser.RawConfigParser()
        self.config.optionxform = str
        self.config_filename = default_config_filename

        read_result = self.config.read(self.config_filename)
        append_texteditor(self.ui.text_pipe_out,
                          'config loaded: {}'.format(read_result))
        if add_config_filename is not None:
            self.config_filename = add_config_filename
            read_result = self.config.read(self.config_filename)
            append_texteditor(self.ui.text_pipe_out,
                              'add config loaded: {}'.format(read_result))

        #image processing
        self.ui.width_thresh_inv.setChecked(
            self.config.getboolean(self.set_config_name, 'width_thresh_inv'))

        #vertical alingment line - user sets it to filament left and right edge
        self.ui.vline_left.setValue(
            self.config.getint(self.set_config_name, 'vline_left'))
        self.ui.vline_right.setValue(
            self.config.getint(self.set_config_name, 'vline_right'))

        #whole image rotation
        self.ui.img_rotation.setValue(
            self.config.getfloat(self.set_config_name, 'img_rotation'))

        #ROI - filament width estimation
        self.ui.w_y2.setValue(self.config.getint(self.set_config_name, 'w_y2'))
        self.ui.w_vline_left_border.setValue(
            self.config.getint(self.set_config_name, 'w_vline_left_border'))
        self.ui.w_vline_right_border.setValue(
            self.config.getint(self.set_config_name, 'w_vline_right_border'))

        #ROI - filament speed estimation
        self.ui.fil_roi_x.setValue(
            self.config.getint(self.set_config_name, 'fil_roi_x'))
        self.ui.fil_roi_y.setValue(
            self.config.getint(self.set_config_name, 'fil_roi_y'))
        self.ui.fil_roi_size.setValue(
            self.config.getint(self.set_config_name, 'fil_roi_size'))

        #ROI GEAR
        self.ui.gear_roi_x.setValue(
            self.config.getint(self.set_config_name, 'gear_roi_x'))
        self.ui.gear_roi_y.setValue(
            self.config.getint(self.set_config_name, 'gear_roi_y'))
        self.ui.gear_roi_size.setValue(
            self.config.getint(self.set_config_name, 'gear_roi_size'))

        #CAL
        self.ui.filament_width.setValue(
            self.config.getfloat(self.set_config_name, 'filament_width'))
        self.ui.cal_factor.setValue(
            self.config.getfloat(self.set_config_name, 'cal_factor'))
        self.ui.calfactor_inv.setValue(
            1 / self.config.getfloat(self.set_config_name, 'cal_factor'))
        date_time = self.config.get(self.set_config_name, 'cal_date')
        self.ui.cal_date.setDateTime(QtCore.QDateTime.fromString(date_time))

        #material
        set_combo_index(self.ui.mat_colour,
                        self.config.get(self.set_config_name, 'mat_colour'))
        set_combo_index(self.ui.mat_type,
                        self.config.get(self.set_config_name, 'mat_colour'))
        self.ui.mat_date_id.setText(
            self.config.get(self.set_config_name, 'mat_date_id'))
        self.ui.mat_manufacturer.setText(
            self.config.get(self.set_config_name, 'mat_manufacturer'))

        #pre-process
        self.rot_apply_flag = round(self.ui.img_rotation.value(), 1) != 0

        #camera
        self.ui.cam_model.setText(self.config.get('camera', 'cam_model'))
        self.ui.cam_number.setValue(self.config.getint('camera', 'cam_number'))
        self.ui.cam_fps.setValue(self.config.getint('camera', 'cam_fps'))

        self.ui.fps.setValue(self.config.getint('camera', 'cam_fps'))

        #for writing to configs to results file
        self.setup_details['config'] = make_config_dict(self.config)
    def main_task_loop(self):
        """
        """
        #replay recorded video
        if self.video_mode:
            if self.video_new_frame():
                self.process_new_frame(0, self.frame_id, self.time_passed_ms,
                                       self.frame_extruder)

        #update display
        if self.new_frame_processed:
            self.new_frame_processed = False
            self.post_process()

        elif not (self.video_mode):
            self.ping_count += 1
            if self.ping_count > 30:
                self.pipe_out.send('ping {}'.format(self.ping_count))
                self.ping_count = 0
                date_time = QtCore.QDateTime.currentDateTime()
                msg = date_time.toString()
                self.pipe_out.send(msg)
                self.new_frame_processed = True

        #poll comms
        if self.pipe_in.poll():
            new_task = self.pipe_in.recv()
            append_texteditor(self.ui.text_pipe_in,
                              'New task {}'.format(new_task))
            if isinstance(new_task, dict):
                #process video
                if new_task.get('process_video', False):
                    self.ui.report_results.setChecked(True)
                    self.ui.play_video.setChecked(True)

                #process camera
                else:
                    measure = new_task.get('measure', False)
                    if measure:
                        self.updated_std_width()

                    if new_task.get('record_video', False):
                        self.ui.record_video.setChecked(True)
                        self.record_video_start_stop(new_state=True)

                    if new_task.get('save_video', False):
                        self.record_video_save()

                    self.ui.report_results.setChecked(measure)
                    self.measure_active = measure and not (self.video_mode)
                    self.process_active = not (
                        self.recording
                        and not (self.ui.report_results.isChecked()))

            else:
                #string command or something else
                if new_task == 'exit':
                    self.close()
                elif new_task == 'init_dino_cam':
                    #try re-init cam
                    #                    self.init_dino_cam()
                    pass

        #only execute for limeted number of frames
        if self.busy_profile > 0:
            self.busy_profile -= 1
            if self.busy_profile <= 0:
                self.close()