Exemplo n.º 1
0
class CameraUI(QWidget):

    sig_camera_connected = pyqtSignal(bool)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        #----------------------------------------------------------------------
        #----------------------------------GUI---------------------------------
        #----------------------------------------------------------------------
        self.setWindowTitle("Camera")

        #------------------------------Camera ui-------------------------------
        cameraimageContainer = QGroupBox("Camera View")
        cameraimageLayout = QGridLayout()

        #-------------------------Camera Operation Buttons---------------------
        self.pictureButton = QPushButton("Snap picture")
        #This is the button for taking a single picture and saving it
        self.pictureButton.clicked.connect(lambda: self.takepicture())

        self.singleframeButton = QPushButton("Single Frame")
        #This is the button for displaying a single frame for ROI selection
        self.singleframeButton.clicked.connect(lambda: self.singleframe())

        self.recordButton = QPushButton()
        #Button for recording for undefined number of frames!
        self.recordButton.setText("Record")
        self.recordButton.setCheckable(True)
        self.recordButton.clicked.connect(lambda: self.record_enable())

        self.timedButton = QPushButton("Timed video")
        #This is the option of recording for a preset time period/number of frames
        self.timedButton.clicked.connect(lambda: self.timedvideo())
        self.duration = QLineEdit()

        self.liveButton = QPushButton()
        #Button for showing a live feed
        self.liveButton.setCheckable(True)
        self.liveButton.setText("Live")
        self.liveButton.clicked.connect(self.live_enabled)
        self.liveButton.clicked.connect(self.live_feed_enable)

        self.roi_selectionButton = QPushButton()
        self.roi_selectionButton.setText("ROI selection")
        self.roi_selectionButton.setCheckable(True)
        self.roi_selectionButton.clicked.connect(self.roi_selection_pressed)

        #------------------------------Settings---------------------------
        camerasettingsContainer = QGroupBox("Settings")
        camerasettingsLayout = QGridLayout()
        '''
        Adding comboboxes for all camera settings. Most are specific for 
        the hamamatsu orca flash 4 and will not work when used with other 
        cameras (such as the democam). In the future maybe better to
        generate these settings based on available micromanager setting instead
        of hardcoding!
        '''

        self.camBox = QComboBox()
        '''
        For connecting to a camera! 
        '''
        self.camBox.setGeometry(QRect(40, 40, 491, 31))
        self.camBox.setObjectName(("Camera"))
        self.camBox.addItem("Hamamatsu")
        self.camBox.addItem("Democam")
        self.camBox.activated[str].connect(self.set_cam)
        camerasettingsLayout.addWidget(self.camBox, 0, 0)

        self.disconnectButton = QPushButton()
        '''
        Disconnect from the camera using the close_cam function in the backend 
        '''
        self.disconnectButton.setText("Disconnect")
        self.disconnectButton.clicked.connect(lambda: self.disconnect())
        camerasettingsLayout.addWidget(self.disconnectButton, 1, 0)

        self.trigBox = QComboBox()
        '''
        Internal is the camera free running mode,
        external is external triggering mode. I do not know what software means
        exactly, it might be a start trigger mode (trigger into free running)
        but I'll have to see
        '''
        self.trigBox.setGeometry(QRect(40, 40, 491, 31))
        self.trigBox.setObjectName(("Trigger Source"))
        self.trigBox.addItem("INTERNAL")
        self.trigBox.addItem("EXTERNAL")
        self.trigBox.addItem("SOFTWARE")
        self.trigBox.activated[str].connect(self.set_trigger_method)
        camerasettingsLayout.addWidget(self.trigBox, 0, 1)

        self.trig_activeBox = QComboBox()
        '''
        This is for selecting what type of triggering method to use.
        '''
        self.trig_activeBox.setGeometry(QRect(40, 40, 491, 31))
        self.trig_activeBox.setObjectName(("Trigger Active Edge"))
        self.trig_activeBox.addItem("SYNCREADOUT")
        self.trig_activeBox.addItem("EDGE")
        self.trig_activeBox.addItem("LEVEL")
        self.trig_activeBox.activated[str].connect(self.set_trigger_active)
        camerasettingsLayout.addWidget(self.trig_activeBox, 1, 1)

        self.binBox = QComboBox()
        '''
        Binning effectively lowers the resolution while keeping sensitity high.
        Could be usefull in some scenarios.
        '''
        self.binBox.setGeometry(QRect(40, 40, 491, 31))
        self.binBox.setObjectName(("Binning"))
        self.binBox.addItem("1x1")
        self.binBox.addItem("2x2")
        self.binBox.addItem("4x4")
        self.binBox.activated[str].connect(self.set_binning)
        camerasettingsLayout.addWidget(self.binBox, 0, 2)

        self.circBufferSize = QLineEdit()
        '''
        Small widget for setting the circular buffer size! Sets size in GB. 
        Data stream size depends on the ROI size. 
        '''
        self.dblval = QDoubleValidator()
        self.dblval.setRange(0, 80)
        self.dblval.setDecimals(1)
        self.circBufferSize.setValidator(self.dblval)
        self.circBufferSize.setFixedWidth(30)
        self.circBufferSize.returnPressed.connect(lambda: \
            self.set_circ_buffersize(float(self.circBufferSize.text())))
        camerasettingsLayout.addWidget(self.circBufferSize, 1, 2)

        self.exposureBox = QGroupBox("Exposure Time")
        '''
        Small widget for setting the exposure time in miliseconds.
        '''
        self.exposureLayout = QGridLayout()
        self.exposure_line = QLineEdit(self)
        self.exposure_line.setFixedWidth(60)
        self.exposure_line.returnPressed.connect(
            lambda: self.exposure_changed())
        self.exposureLayout.addWidget(QLabel("Exposure time (ms)"), 0, 0)
        self.exposureLayout.addWidget(self.exposure_line, 0, 1)
        self.exposureBox.setMaximumWidth(300)
        self.exposureBox.setLayout(self.exposureLayout)

        camerasettingsLayout.addWidget(self.exposureBox, 0, 3, 0, 2)
        camerasettingsContainer.setMaximumHeight(100)
        camerasettingsContainer.setLayout(camerasettingsLayout)

        #------------------------------Monitoring------------------------------
        monitorContainer = QGroupBox()
        monitorLayout = QGridLayout()

        self.framerate_monitor = QLabel()
        monitorLayout.addWidget(self.framerate_monitor, 0, 1)
        monitorLayout.addWidget(QLabel("Framerate (Hz):"), 0, 0)

        self.buffer_free_capacity_monitor = QLabel()
        monitorLayout.addWidget(self.buffer_free_capacity_monitor, 1, 1)
        monitorLayout.addWidget(QLabel("Buffer Capacity (ims):"), 1, 0)

        monitorContainer.setLayout(monitorLayout)
        monitorContainer.setMaximumWidth(200)

        self.monitor_timer = QTimer()
        self.monitor_timer.timeout.connect(lambda: self.update_monitor())
        '''
        I don't start the monitor_timer during initialisation, because this will
        create an error. I start it when connecting to a camera!
        '''

        #------------------------------Videoscreen-----------------------------
        '''
        Initiating an imageview object for the main videoscreen. Hiding the pre
        existing ROI and menubuttons.
        '''

        self.videoWidget = pg.ImageView()
        self.video_item = self.videoWidget.getImageItem()
        self.video_item.setAutoDownsample(True)
        self.videoWidget.ui.roiBtn.hide()
        self.videoWidget.ui.menuBtn.hide()
        self.videoWidget.ui.normGroup.hide()
        self.videoWidget.ui.roiPlot.hide()
        self.videoWidget.autoLevels()
        '''
        The update timer is build for setting the framerate of the imageviewer 
        for the main screen! The .start function determines the framerate 
        (30 ms = 33 Hz).
        '''

        self.update_timer_live = QTimer()
        self.update_timer_live.timeout.connect(lambda: self.update_view())
        '''
        I toggle the live feed on and off by starting/stopping the 
        update_timer_live!
        '''
        #--------------------------setting the camera viewer layout------------

        cameraimageLayout.addWidget(self.videoWidget)
        cameraimageContainer.setLayout(cameraimageLayout)

        #--------------------------master for the camera operations----------

        camopContainer = QGroupBox("Operations")

        camopLayout = QVBoxLayout()
        camopContainer.setMaximumWidth(200)

        camopLayout.addWidget(self.pictureButton, 0)
        camopLayout.addWidget(self.recordButton, 1)
        camopLayout.addWidget(self.timedButton, 2)
        camopLayout.addWidget(self.liveButton, 3)
        camopLayout.addWidget(self.singleframeButton, 4)
        camopLayout.addWidget(self.roi_selectionButton, 5)
        camopContainer.setLayout(camopLayout)

        #------------------------------Master----------------------------------

        master = QGridLayout()
        master.addWidget(camerasettingsContainer, 0, 0)
        master.addWidget(cameraimageContainer, 1, 0)
        master.addWidget(monitorContainer, 0, 1)
        master.addWidget(camopContainer, 1, 1)

        self.setLayout(master)

        #-----------------------------Startup----------------------------------

        self.no_cam_initiated()


#------------------------------------------------------------------------------
#-----------------------------FUNCTIONS----------------------------------------
#------------------------------------------------------------------------------

#-----------------------Button disabling-----------------------------------

    """
    Here I disable the buttons and functions that are not to be used simultanuously  
    """
    def live_enabled(self):
        if self.liveButton.isChecked():
            self.singleframeButton.setEnabled(False)
            self.roi_selectionButton.setEnabled(False)
            self.disconnectButton.setEnabled(False)
            ''' 
            and now for the roi selection buttons. I put in the try so it 
            doesn't give an error when no ROI window is yet created. 
            '''
            try:
                '''
                Make sure the ROI window is closed to optimise performance
                '''

                self.roi_selectionButton.setChecked(False)
                self.close_roi_selection()
            except:
                pass
            '''
            Also making sure the settings are not changed during live acquisition
            '''
            self.camBox.setEnabled(False)
            self.trigBox.setEnabled(False)
            self.trig_activeBox.setEnabled(False)
            self.binBox.setEnabled(False)
            self.circBufferSize.setEnabled(False)

            self.exposure_line.setEnabled(False)

        else:
            self.singleframeButton.setEnabled(True)
            self.disconnectButton.setEnabled(True)
            self.roi_selectionButton.setEnabled(True)
            self.camBox.setEnabled(True)
            self.trigBox.setEnabled(True)
            self.trig_activeBox.setEnabled(True)
            self.binBox.setEnabled(True)
            self.circBufferSize.setEnabled(True)

            self.exposure_line.setEnabled(True)

    def no_cam_initiated(self):
        self.timedButton.setEnabled(False)
        self.liveButton.setEnabled(False)
        self.singleframeButton.setEnabled(False)
        self.roi_selectionButton.setEnabled(False)
        self.pictureButton.setEnabled(False)
        self.recordButton.setEnabled(False)
        self.disconnectButton.setEnabled(False)

        self.trigBox.setEnabled(False)
        self.trig_activeBox.setEnabled(False)
        self.binBox.setEnabled(False)
        self.circBufferSize.setEnabled(False)

        self.exposure_line.setEnabled(False)

        self.monitor_timer.stop()
        self.framerate_flag = False

        self.cam_in = False
        self.sig_camera_connected.emit(False)

    def cam_initiated(self):
        self.timedButton.setEnabled(True)
        self.liveButton.setEnabled(True)
        self.singleframeButton.setEnabled(True)
        self.roi_selectionButton.setEnabled(True)
        self.pictureButton.setEnabled(True)
        self.recordButton.setEnabled(True)
        self.disconnectButton.setEnabled(True)

        self.trigBox.setEnabled(True)
        self.trig_activeBox.setEnabled(True)
        self.binBox.setEnabled(True)
        self.circBufferSize.setEnabled(True)

        self.exposure_line.setEnabled(True)

        self.monitor_timer.start(100)
        self.exposure_line.setText(str(int(self.cam.mmc.getExposure())))
        self.circBufferSize.setText(
            str(self.cam.mmc.getCircularBufferMemoryFootprint() / 1000))
        self.framerate_flag = False

        self.cam.timed_filming_done.connect(self.writing_done)

        self.cam_in = True
        self.sig_camera_connected.emit(True)

    def record_enabled(self):
        self.liveButton.setChecked(False)
        self.roi_selectionButton.setChecked(False)

        self.timedButton.setEnabled(False)
        self.liveButton.setEnabled(False)
        self.singleframeButton.setEnabled(False)
        self.roi_selectionButton.setEnabled(False)
        self.pictureButton.setEnabled(False)

        self.camBox.setEnabled(False)
        self.trigBox.setEnabled(False)
        self.trig_activeBox.setEnabled(False)
        self.binBox.setEnabled(False)
        self.circBufferSize.setEnabled(False)
        self.disconnectButton.setEnabled(False)

        self.exposure_line.setEnabled(False)

        self.recordButton.setChecked(True)

    def record_disabled(self):
        self.timedButton.setEnabled(True)
        self.liveButton.setEnabled(True)
        self.singleframeButton.setEnabled(True)
        self.roi_selectionButton.setEnabled(True)
        self.pictureButton.setEnabled(True)
        self.disconnectButton.setEnabled(True)

        self.binBox.setEnabled(True)
        self.camBox.setEnabled(True)
        self.trigBox.setEnabled(True)
        self.trig_activeBox.setEnabled(True)
        self.circBufferSize.setEnabled(True)

        self.exposure_line.setEnabled(True)

        self.recordButton.setChecked(False)

    def writing_done(self, done):
        if done:
            self.record_disabled()
        else:
            self.record_enabled()

    #------------------------------Setting Camera Properties-------------------
    def set_cam(self, cam):
        if cam == "Democam":
            try:
                self.cam = Camera(['Camera', 'DemoCamera', 'DCam'])
                print("{}".format(cam) + " camera loaded.")
                self.cam_initiated()
            except:
                print("{}".format(cam) + " camera not found.")
                self.no_cam_initiated()
        elif cam == "Thorcam":
            try:
                self.cam = Camera(['ThorCam', 'ThorlabsUSBCamera', 'ThorCam'])
                self.cam_initiated()
                print("{}".format(cam) + " camera loaded.")

            except:
                print("{}".format(cam) + " camera not found.")
                self.no_cam_initiated()
        elif cam == "Hamamatsu":
            try:
                self.cam = Camera(
                    ['Camera', 'HamamatsuHam', 'HamamatsuHam_DCAM'])
                self.cam_initiated()
                print("{}".format(cam) + " camera loaded.")

                #specifically for the Hamcam, not necesarry for the DCAM
                self.set_trigger_active("SYNCREADOUT")

            except:
                print("{}".format(cam) + " camera not found.")
                self.no_cam_initiated()

    def set_trigger_method(self, method):
        self.cam.mmc.setProperty(self.cam.name, 'TRIGGER SOURCE', method)

    def set_trigger_active(self, edge):
        self.cam.mmc.setProperty(self.cam.name, 'TRIGGER ACTIVE', edge)

    def set_binning(self, binning):
        self.cam.mmc.setProperty(self.cam.name, 'Binning', binning)

    def set_circ_buffersize(self, size):
        mbsize = 1000 * size
        self.cam.set_buf_size(int(mbsize))  #size in GB

    def exposure_changed(self):
        set_time = float(self.exposure_line.text())
        self.cam.set_exposure_time(set_time)
        actual_exp_time = self.cam.get_exposure_time()
        if not set_time == round(actual_exp_time, 1):
            self.exposure_line.setText(str(round(actual_exp_time, 1)))

    def update_monitor(self):
        if self.cam_in:
            FPS = self.cam.get_framerate()
            self.framerate_monitor.setText(str(FPS))

        if self.recordButton.isChecked():
            ims_in_buffer = self.cam.mmc.getRemainingImageCount()
            buffercap = self.cam.mmc.getBufferTotalCapacity()
            self.buffer_free_capacity_monitor.setText(
                str(ims_in_buffer) + "/" + str(buffercap))

    #-----------------------Data Acquisition Functions-------------------------

    def takepicture(self):
        self.cam.snap()

    def update_view(self):
        self.cam.get_frame()
        self.set_view()

    def set_view(self):
        self.videoWidget.setImage(self.cam.image)

    def timedvideo(self):
        self.timedWindow = TimedPopup(self.cam)
        self.timedWindow.setGeometry(QRect(500, 500, 200, 90))
        self.timedWindow.show()

    def live_feed_enable(self):
        if self.liveButton.isChecked():
            self.cam.startseqacq()
            self.update_timer_live.start(30)  #ms timeout period
        else:
            self.update_timer_live.stop()
            self.cam.stopseqacq()

    def timed_record(self):
        self.cam.start_recording()
        self.recordButton.setChecked(
            True)  #so a manual stop can be implemented
        self.record_enabled()

    def record_enable(self):
        if self.recordButton.isChecked():
            if self.update_timer_live.isActive():
                self.update_timer_live.clicked()
            self.cam.start_recording()

        else:
            self.cam.stop_recording()

    def check_recording(self):
        if self.recordButton.isChecked():
            self.recordButton.click()

    def singleframe(self):
        self.cam.get_single_frame()
        self.set_view()

    #----------------------ROI Selection---------------------------------------
    def roi_selection_pressed(self):

        if self.roi_selectionButton.isChecked():
            self.open_roi_selection()
        else:
            self.close_roi_selection()

    def close_roi_selection(self):
        self.roiWindow.close()
        self.roiWindow.roiWidget.close()

    def open_roi_selection(self):
        self.roiWindow = CameraROI(self.cam)
        self.roiWindow.setGeometry(QRect(100, 100, 600, 600))
        self.roiWindow.show()

    #---------------------Closing the camera app-------------------------------
    def disconnect(self):
        self.cam.close_camera()
        self.no_cam_initiated()
        self.update_timer_live.stop()
        self.monitor_timer.stop()
        self.stopRecording()

    def stopRecording(self):
        """close the camera."""
        try:  #Try so it doesn't give an error when no cam is initiated.
            self.cam.stop_recording()
        except:
            pass

    def closeEvent(self, event):
        """Making sure the camera closes upon closing the application."""
        self.no_cam_initiated()
        self.update_timer_live.stop()
        self.monitor_timer.stop()
        self.stopRecording()

        try:
            self.cam.close_camera()
            self.roiWindow.close()
        except:
            pass