示例#1
0
def main():
    import numpy as np
    options = parse_options()
    os.makedirs(options.dir, exist_ok=True)
    config_logger(options.dir)

    with open(options.config, 'r') as stream:
        try:
            opt = yaml.safe_load(stream)
        except yaml.YAMLError as exc:
            print(exc)
            logging.exception(exc)
            exit(1)

    gt_dir = f"{options.dir}/original"
    data_dir = f"{options.dir}/spoiled"
    export_dir = f"{options.dir}/export"
    os.makedirs(data_dir, exist_ok=True)
    os.makedirs(gt_dir, exist_ok=True)
    os.makedirs(export_dir, exist_ok=True)

    seed = opt.get('seed', None)
    if not seed:
        seed = random.randint(0, 2**32 - 1)
        opt['seed'] = seed
        opt['n_workers'] = options.workers
    logging.info(f"Starting generation with seed {seed} ")
    n_workers = opt.get('n_workers', None)
    n_workers = n_workers or options.workers  # ensure determinism

    filler = [None for _ in range(n_workers - (options.size % n_workers))]
    vals = np.concatenate([np.arange(options.size),
                           filler]).reshape(-1, n_workers).transpose()
    vals = [[
        f'{options.out_name}_{val:04}' for val in values if val is not None
    ] for values in vals]
    processes = []

    random.seed(seed)
    pbar = ProgressBar(options.size)

    def update_pbar(item):
        pbar.update(f"Generated {item}")

    for i in range(n_workers):
        seed = random.randint(0, 2**32 - 1)
        generator = Generator(opt)
        spoiler = Spoiler()
        exporters = from_options(opt, export_dir)
        visitors = [spoiler] + exporters
        p = Process(target=gen_image_pool,
                    args=(generator, visitors, vals[i], options, seed,
                          update_pbar))
        p.start()
        processes.append(p)
    for p in processes:
        p.join()

    with open(f"{options.dir}/config.yml", 'w') as f:
        yaml.dump(opt, f)
    def parseInput(self, filename, nStars, player):
        self.nStars = nStars
        red = []
        blue = []
        fh = open(filename)
        file_contents = fh.readlines()

        adding_red = True

        for line in file_contents:
            fields = []
            line = line.rstrip('\r\n')

            if line == "Red dancer positions (start at 0)":
                continue

            if line == "Blue dancer positions (start at 0)":
                adding_red = False
                continue
            if line == "    ":
                continue

            fields = line.split(' ')

            if adding_red:
                red.append([int(fields[0]), int(fields[1])])
            else:
                blue.append([int(fields[0]), int(fields[1])])

        self.red = red
        self.blue = blue
        self.setupBoard(self.boardSize, red, blue)

        if player == 'choreographer':
            self.playerName = "choreo"
            self.player = Choreo(self)
        else:
            self.playerName = "spoiler"
            self.player = Spoiler(self)
class IO:
    def __init__(self, host, port, boardSize):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.s.connect((host, int(port)))
        self.boardSize = boardSize

    def parseInput(self, filename, nStars, player):
        self.nStars = nStars
        red = []
        blue = []
        fh = open(filename)
        file_contents = fh.readlines()

        adding_red = True

        for line in file_contents:
            fields = []
            line = line.rstrip('\r\n')

            if line == "Red dancer positions (start at 0)":
                continue

            if line == "Blue dancer positions (start at 0)":
                adding_red = False
                continue
            if line == "    ":
                continue

            fields = line.split(' ')

            if adding_red:
                red.append([int(fields[0]), int(fields[1])])
            else:
                blue.append([int(fields[0]), int(fields[1])])

        self.red = red
        self.blue = blue
        self.setupBoard(self.boardSize, red, blue)

        if player == 'choreographer':
            self.playerName = "choreo"
            self.player = Choreo(self)
        else:
            self.playerName = "spoiler"
            self.player = Spoiler(self)

    def setupBoard(self, size, red, blue):
        board = [['.' for i in range(size)] for j in range(size)]
        tupleBoard = [[0 for i in range(size)] for j in range(size)]

        for i in range(0, len(red)):
            r = red[i]
            board[r[0]][r[1]] = 'R'
            tupleBoard[r[0]][r[1]] = (i, 'R')

        for i in range(0, len(blue)):
            b = blue[i]
            board[b[0]][b[1]] = 'B'
            tupleBoard[b[0]][b[1]] = (i, 'B')

        self.board = board
        self.tupleBoard = tupleBoard

    def begin(self):
        data = ""
        flag = 0
        while 1:
            data += self.s.recv(1024)

            if "$" in data:
                break

            if "#" in data and flag == 0:
                starData = self.getStarConfiguration(data)
                flag = 1

            if "^" in data and flag == 0:
                self.sendOutput(self.player.move())

            if "#" in data and flag == 1:
                self.sendOutput(self.player.move())
                data = ""

        self.s.close()

    def sendOutput(self, moves):
        print moves
        self.s.sendall('{}'.format(moves))

    def getStarConfiguration(self, data):
        data = data.split('#')[0].strip()

        data = data.split(' ')

        stars = []

        for i in range(0, len(data), 2):
            stars.append((int(data[i]), int(data[i + 1])))
            self.board[int(data[i])][int(data[i + 1])] = 'S'

        self.stars = stars

        return stars
示例#4
0
    def init_calib_grp(self):
        # load fit settings if they exist
        if self.settings.contains('calibrated'):
            self.calibrated = self.settings.value('calibrated')
        else:
            self.calibrated = False
        if self.settings.contains('a'):
            self.a = self.settings.value('a')
        else:
            self.a = 0
        if self.settings.contains('b'):
            self.b = self.settings.value('b')
        else:
            self.b = 0
        if self.settings.contains('c'):
            self.c = self.settings.value('c')
        else:
            self.c = 0

        # create a group for calibrations
        self.calib_grp = Spoiler(title='Calibration')
        self.calib_layout = QtWidgets.QGridLayout()

        # create calibration textboxes
        self.chan1_txt = QtWidgets.QLineEdit()
        self.chan2_txt = QtWidgets.QLineEdit()
        self.chan3_txt = QtWidgets.QLineEdit()
        self.energy1_txt = QtWidgets.QLineEdit()
        self.energy2_txt = QtWidgets.QLineEdit()
        self.energy3_txt = QtWidgets.QLineEdit()
        self.units_txt = QtWidgets.QLineEdit()
        self.chan1_txt.setValidator(self.int_only)
        self.chan2_txt.setValidator(self.int_only)
        self.chan3_txt.setValidator(self.int_only)
        self.chan1_txt.setAlignment(QtCore.Qt.AlignRight)
        self.chan2_txt.setAlignment(QtCore.Qt.AlignRight)
        self.chan3_txt.setAlignment(QtCore.Qt.AlignRight)
        self.energy1_txt.setValidator(self.float_only)
        self.energy2_txt.setValidator(self.float_only)
        self.energy3_txt.setValidator(self.float_only)
        self.energy1_txt.setAlignment(QtCore.Qt.AlignRight)
        self.energy2_txt.setAlignment(QtCore.Qt.AlignRight)
        self.energy3_txt.setAlignment(QtCore.Qt.AlignRight)
        self.units_txt.setPlaceholderText('keV')
        self.energy1_txt.setMinimumWidth(70)
        self.energy2_txt.setMinimumWidth(70)
        self.energy3_txt.setMinimumWidth(70)

        # load calib settings
        if self.settings.contains('chan1'):
            self.chan1 = self.settings.value('chan1')
            self.chan1_txt.setText(str(self.chan1))
        else:
            self.chan1 = 0
        if self.settings.contains('chan2'):
            self.chan2 = self.settings.value('chan2')
            self.chan2_txt.setText(str(self.chan2))
        else:
            self.chan2 = 0
        if self.settings.contains('chan3'):
            self.chan3 = self.settings.value('chan3')
            self.chan3_txt.setText(str(self.chan3))
        else:
            self.chan3 = 0
        if self.settings.contains('energy1'):
            self.energy1 = self.settings.value('energy1')
            self.energy1_txt.setText('{0:.2f}'.format(self.energy1))
        else:
            self.energy1 = 0
        if self.settings.contains('energy2'):
            self.energy2 = self.settings.value('energy2')
            self.energy2_txt.setText('{0:.2f}'.format(self.energy2))
        else:
            self.energy2 = 0
        if self.settings.contains('energy3'):
            self.energy3 = self.settings.value('energy3')
            self.energy3_txt.setText('{0:.2f}'.format(self.energy3))
        else:
            self.energy3 = 0
        if self.settings.contains('units'):
            self.units = self.settings.value('units')
            self.units_txt.setText(self.settings.value('units'))
        else:
            self.units = 'keV'

        # add response functions to calibration textboxes
        def update_calib():
            calib_pts = np.array([(self.chan1, self.energy1),\
                (self.chan2, self.energy2), (self.chan3, self.energy3)])
            # ignore points where chan = 0 or energy = 0
            valid_pts = calib_pts[np.logical_and(calib_pts[:,0] > 0,\
                calib_pts[:,1] > 0), :]
            npts = valid_pts.shape[0]

            # calculate quadratic fit parameters
            try:
                if npts == 0:
                    self.calibrated = False
                    self.a = 0
                    self.b = 0
                    self.c = 0
                elif npts == 1:
                    self.calibrated = True
                    self.a = 0
                    self.b = valid_pts[0, 1] / valid_pts[0, 0]
                    self.c = 0
                elif npts == 2:
                    self.calibrated = True
                    self.a = 0
                    self.b = (valid_pts[1,1] - valid_pts[0,1]) /\
                        (valid_pts[1,0] - valid_pts[0,0])
                    self.c = valid_pts[0, 1] - self.b * valid_pts[0, 0]
                else:
                    A0 = -valid_pts[0, 0]**2 + valid_pts[1, 0]**2
                    B0 = -valid_pts[0, 0] + valid_pts[1, 0]
                    D0 = -valid_pts[0, 1] + valid_pts[1, 1]
                    A1 = -valid_pts[1, 0]**2 + valid_pts[2, 0]**2
                    B1 = -valid_pts[1, 0] + valid_pts[2, 0]
                    D1 = -valid_pts[1, 1] + valid_pts[2, 1]
                    A2 = -B1 / B0 * A0 + A1
                    D2 = -B1 / B0 * D0 + D1
                    self.calibrated = True
                    self.a = D2 / A2
                    self.b = (D0 - A0 * self.a) / B0
                    self.c = valid_pts[0,1] - self.a*valid_pts[0,0]**2 -\
                        self.b*valid_pts[0,0]
            except:
                self.calibrated = False
                self.a = 0
                self.b = 0
                self.c = 0

            self.settings.setValue('calibrated', self.calibrated)
            self.settings.setValue('a', self.a)
            self.settings.setValue('b', self.b)
            self.settings.setValue('c', self.c)

            # update marker info label
            self.update_marker()

        def chan1_change():
            chan1_str = self.chan1_txt.text()
            if chan1_str == '':
                self.chan1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan1 = 0
                self.settings.remove('chan1')
            elif chan1_str == '-' or int(chan1_str) < 0\
                    or int(chan1_str) >= self.chan_max:
                self.chan1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.chan1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan1 = int(chan1_str)
                self.settings.setValue('chan1', self.chan1)
            update_calib()

        def chan2_change():
            chan2_str = self.chan2_txt.text()
            if chan2_str == '':
                self.chan2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan2 = 0
                self.settings.remove('chan2')
            elif chan2_str == '-' or int(chan2_str) < 0\
                    or int(chan2_str) >= self.chan_max:
                self.chan2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.chan2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan2 = int(chan2_str)
                self.settings.setValue('chan2', self.chan2)
            update_calib()

        def chan3_change():
            chan3_str = self.chan3_txt.text()
            if chan3_str == '':
                self.chan3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan3 = 0
                self.settings.remove('chan3')
            elif chan3_str == '-' or int(chan3_str) < 0\
                    or int(chan3_str) >= self.chan_max:
                self.chan3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.chan3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan3 = int(chan3_str)
                self.settings.setValue('chan3', self.chan3)
            update_calib()

        def energy1_change():
            energy1_str = self.energy1_txt.text()
            if energy1_str == '':
                self.energy1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy1 = 0
                self.settings.remove('energy1')
            elif energy1_str == '-' or float(energy1_str) < 0:
                self.energy1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.energy1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy1 = float(energy1_str)
                self.settings.setValue('energy1', self.energy1)
            update_calib()

        def energy2_change():
            energy2_str = self.energy2_txt.text()
            if energy2_str == '':
                self.energy2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy2 = 0
                self.settings.remove('energy2')
            elif energy2_str == '-' or float(energy2_str) < 0:
                self.energy2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.energy2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy2 = float(energy2_str)
                self.settings.setValue('energy2', self.energy2)
            update_calib()

        def energy3_change():
            energy3_str = self.energy3_txt.text()
            if energy3_str == '':
                self.energy3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy3 = 0
                self.settings.remove('energy3')
            elif energy3_str == '-' or float(energy3_str) < 0:
                self.energy3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.energy3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy3 = float(energy3_str)
                self.settings.setValue('energy3', self.energy3)
            update_calib()

        def units_change():
            units_str = self.units_txt.text()
            if units_str == '':
                self.units = 'keV'
                self.settings.remove('units')
            else:
                self.units = units_str
                self.settings.setValue('units', self.units)

            # update marker info label
            self.update_marker()

        self.chan1_txt.textChanged.connect(chan1_change)
        self.chan2_txt.textChanged.connect(chan2_change)
        self.chan3_txt.textChanged.connect(chan3_change)
        self.energy1_txt.textChanged.connect(energy1_change)
        self.energy2_txt.textChanged.connect(energy2_change)
        self.energy3_txt.textChanged.connect(energy3_change)
        self.units_txt.textChanged.connect(units_change)

        # layout calibration widgets
        self.calib_layout.addWidget(QtWidgets.QLabel('Channel'), 0, 0)
        self.calib_layout.addWidget(QtWidgets.QLabel('Energy/Time'), 0, 2)
        self.calib_layout.addWidget(QtWidgets.QLabel('='), 1, 1)
        self.calib_layout.addWidget(QtWidgets.QLabel('='), 2, 1)
        self.calib_layout.addWidget(QtWidgets.QLabel('='), 3, 1)
        self.calib_layout.addWidget(QtWidgets.QLabel('Units: '), 4, 0)
        self.calib_layout.addWidget(self.chan1_txt, 1, 0)
        self.calib_layout.addWidget(self.chan2_txt, 2, 0)
        self.calib_layout.addWidget(self.chan3_txt, 3, 0)
        self.calib_layout.addWidget(self.energy1_txt, 1, 2)
        self.calib_layout.addWidget(self.energy2_txt, 2, 2)
        self.calib_layout.addWidget(self.energy3_txt, 3, 2)
        self.calib_layout.addWidget(self.units_txt, 4, 1, 1, 2)
        self.calib_grp.setContentLayout(self.calib_layout)
示例#5
0
    def init_plot_grp(self):
        self.mode = 'Auto'

        # create a group for plot settings
        self.plot_grp = Spoiler(title='Plot Settings')
        self.plot_layout = QtWidgets.QGridLayout()

        # create plot mode buttons
        self.log_btn = QtWidgets.QPushButton('Log')
        self.auto_btn = QtWidgets.QPushButton('Auto')
        self.log_btn.setMinimumWidth(20)
        self.auto_btn.setMinimumWidth(20)
        self.disable_btn(self.auto_btn)

        # add response functions for buttons
        def log_click():
            self.mode = 'Log'
            self.disable_btn(self.log_btn)
            self.enable_btn(self.auto_btn)
            self.plot.update(self.chans, self.counts, self.roi_mask, self.mode)
            self.popts = self.plot.fit_roi(self.get_roi(), self.calibrated,\
                self.a, self.b, self.c)

        def auto_click():
            self.mode = 'Auto'
            self.enable_btn(self.log_btn)
            self.disable_btn(self.auto_btn)
            self.plot.update(self.chans, self.counts, self.roi_mask, self.mode)
            self.popts = self.plot.fit_roi(self.get_roi(), self.calibrated,\
                self.a, self.b, self.c)

        self.log_btn.clicked.connect(log_click)
        self.auto_btn.clicked.connect(auto_click)

        # create rebinning dropdown menu
        self.chan_box = QtWidgets.QComboBox()
        self.chan_box.setEditable(True)
        self.chan_box.lineEdit().setReadOnly(True)
        self.chan_box.lineEdit().setAlignment(QtCore.Qt.AlignRight)
        chans = self.chan_max
        n = 0
        while chans >= self.chan_min:
            self.chan_box.addItem(str(int(chans)))
            self.chan_box.setItemData(n, QtCore.Qt.AlignRight,\
                QtCore.Qt.TextAlignmentRole)
            chans /= 2
            n += 1

        # add response function for rebinning menu
        def chan_change():
            self.chans = int(self.chan_max /
                             (1 << self.chan_box.currentIndex()))
            self.plot.update(self.chans, self.counts, self.roi_mask, self.mode)
            self.popts = self.plot.fit_roi(self.get_roi(), self.calibrated,\
                self.a, self.b, self.c)

        self.chan_box.currentIndexChanged.connect(chan_change)

        # layout plot widgets
        self.plot_layout.addWidget(QtWidgets.QLabel('Plot Mode: '), 0, 0, 1, 2)
        self.plot_layout.addWidget(QtWidgets.QLabel('Channels: '), 2, 0)
        self.plot_layout.addWidget(self.log_btn, 1, 0)
        self.plot_layout.addWidget(self.auto_btn, 1, 1)
        self.plot_layout.addWidget(self.chan_box, 2, 1)
        self.plot_grp.setContentLayout(self.plot_layout)
示例#6
0
    def init_adc_grp(self):
        self.gate = self.get_gate()
        self.lld = self.get_lld()
        self.uld = self.get_uld()

        # create a group for ADC settings
        self.adc_grp = Spoiler(title='ADC Settings')
        self.adc_layout = QtWidgets.QGridLayout()

        # create gate dropdown menu
        self.gate_box = QtWidgets.QComboBox()
        for option in ['Off', 'Coincidence', 'Anticoincidence']:
            self.gate_box.addItem(option)
        self.gate_box.setCurrentIndex(self.gate)

        # add response function for gate menu
        def gate_change():
            self.gate = self.gate_box.currentIndex()
            self.set_gate(self.gate)

        self.gate_box.currentIndexChanged.connect(gate_change)

        # create discriminator textboxes
        self.lld_txt = QtWidgets.QLineEdit(str(self.lld))
        self.uld_txt = QtWidgets.QLineEdit(str(self.uld))
        self.lld_txt.setValidator(self.int_only)
        self.uld_txt.setValidator(self.int_only)
        self.lld_txt.setAlignment(QtCore.Qt.AlignRight)
        self.uld_txt.setAlignment(QtCore.Qt.AlignRight)
        self.lld_txt.setMinimumWidth(5)
        self.uld_txt.setMinimumWidth(5)

        # add response functions for discriminator textboxes
        def lld_change():
            lld_str = self.lld_txt.text()
            if lld_str == '':
                self.lld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.lld = 0
                self.set_lld(self.lld)
            elif lld_str == '-' or int(lld_str) < 0\
                    or int(lld_str) >= self.chan_max:
                self.lld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.lld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.lld = int(lld_str)
                self.set_lld(self.lld)

        def uld_change():
            uld_str = self.uld_txt.text()
            if uld_str == '':
                self.uld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.uld = 0
                self.set_uld(self.uld)
            elif uld_str == '-' or int(uld_str) < 0\
                    or int(uld_str) >= self.chan_max:
                self.uld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.uld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.uld = int(uld_str)
                self.set_uld(self.uld)

        self.lld_txt.textChanged.connect(lld_change)
        self.uld_txt.textChanged.connect(uld_change)

        # layout ADC widgets
        self.adc_layout.addWidget(QtWidgets.QLabel('Gate: '), 0, 0, 1, 2)
        self.adc_layout.addWidget(QtWidgets.QLabel('Lower Disc: '), 2, 0)
        self.adc_layout.addWidget(QtWidgets.QLabel('Upper Disc: '), 3, 0)
        self.adc_layout.addWidget(self.gate_box, 1, 0, 1, 2)
        self.adc_layout.addWidget(self.lld_txt, 2, 1)
        self.adc_layout.addWidget(self.uld_txt, 3, 1)
        self.adc_grp.setContentLayout(self.adc_layout)
示例#7
0
    def init_preset_grp(self):
        self.rpre = self.get_real_preset()
        if self.rpre > 0:
            self.rpre_str = '{0:.2f}'.format(self.rpre / 1000)
        else:
            self.rpre_str = ''
        self.lpre = self.get_live_preset()
        if self.lpre > 0:
            self.lpre_str = '{0:.2f}'.format(self.lpre / 1000)
        else:
            self.lpre_str = ''

            # create a group for preset limits
        self.preset_grp = Spoiler(title='Preset Limits')
        self.preset_layout = QtWidgets.QGridLayout()

        # create preset textboxes
        self.rpre_txt = QtWidgets.QLineEdit(self.rpre_str)
        self.lpre_txt = QtWidgets.QLineEdit(self.lpre_str)
        self.rpre_txt.setValidator(self.float_only)
        self.lpre_txt.setValidator(self.float_only)
        self.rpre_txt.setAlignment(QtCore.Qt.AlignRight)
        self.lpre_txt.setAlignment(QtCore.Qt.AlignRight)
        self.rpre_txt.setMinimumWidth(50)
        self.lpre_txt.setMinimumWidth(50)

        # add response functions to preset textboxes
        def rpre_changed():
            if not self.active:
                self.rpre_str = self.rpre_txt.text()
                if self.rpre_str == '':
                    self.rpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.white))
                    self.rpre = 0
                    self.set_real_preset(self.rpre)
                elif self.rpre_str == '-' or float(self.rpre_str) < 0:
                    self.rpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.red))
                else:
                    self.rpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.white))
                    self.rpre = int(float(self.rpre_str) * 1000)
                    self.set_real_preset(self.rpre)

        def lpre_changed():
            if not self.active:
                self.lpre_str = self.lpre_txt.text()
                if self.lpre_str == '':
                    self.lpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.white))
                    self.lpre = 0
                    self.set_live_preset(self.lpre)
                elif self.lpre_str == '-' or float(self.lpre_str) < 0:
                    self.lpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.red))
                else:
                    self.lpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.white))
                    self.lpre = int(float(self.lpre_str) * 1000)
                    self.set_live_preset(self.lpre)

        self.rpre_txt.textChanged.connect(rpre_changed)
        self.lpre_txt.textChanged.connect(lpre_changed)

        # layout preset widgets
        self.preset_layout.addWidget(QtWidgets.QLabel('Real: '), 0, 0)
        self.preset_layout.addWidget(QtWidgets.QLabel('Live: '), 1, 0)
        self.preset_layout.addWidget(self.rpre_txt, 0, 1)
        self.preset_layout.addWidget(self.lpre_txt, 1, 1)
        self.preset_grp.setContentLayout(self.preset_layout)

        # enable/disable presets
        if self.active:
            self.rpre_txt.setReadOnly(True)
            self.lpre_txt.setReadOnly(True)
        else:
            self.rpre_txt.setReadOnly(False)
            self.lpre_txt.setReadOnly(False)
示例#8
0
class MCBWidget(QtWidgets.QGroupBox):
    white = '#ffffff'
    red = '#ff0000'
    gray = '#cccccc'

    gate_index = {'OFF': 0, 'COIN': 1, 'ANTI': 2}

    def __init__(self, mcb_driver, ndet, **kwargs):
        super().__init__(**kwargs)
        self.setObjectName('MCBBox')
        self.setStyleSheet('QGroupBox#MCBBox{' +\
            'padding-top:15px; margin-top:-15px}')
        self.layout = QtWidgets.QHBoxLayout()
        self.setLayout(self.layout)

        # create QValidator objects
        self.int_only = QtGui.QIntValidator()
        self.float_only = QtGui.QDoubleValidator()

        # establish connection with MCB and get info from it
        self.driver = mcb_driver
        self.hdet = self.driver.open_detector(ndet)
        self.name, self.id = self.driver.get_config_name(ndet)
        self.chan_max = self.driver.get_det_length(self.hdet)
        self.chan_min = 8
        self.active = self.is_active()
        self.start_datetime = datetime.fromtimestamp(\
            self.driver.get_start_time(self.hdet))
        self.start_time_str = self.start_datetime.strftime('%I:%M:%S %p')
        self.start_date_str = self.start_datetime.strftime('%m/%d/%Y')

        # create label displaying MCB ID and name
        self.title = '{0:04d} {1}'.format(self.id, self.name)
        self.label = QtWidgets.QLabel(self.title)
        self.sample = QtWidgets.QLineEdit()
        self.sample.setPlaceholderText('Sample Description')

        # setup settings for storing memory
        self.settings = QtCore.QSettings('pystro', self.title)

        # load sample description settings
        if self.settings.contains('sample'):
            self.sample.setText(self.settings.value('sample'))

        # add response function for sample textbox
        def sample_change():
            if self.sample.text() == '':
                self.settings.remove('sample')
            else:
                self.settings.setValue('sample', self.sample.text())

        self.sample.textChanged.connect(sample_change)

        # get neutral button color
        self.get_neutral_color()

        # initialize sections of MCBWidget
        self.init_data_grp()
        self.init_time_grp()
        self.init_preset_grp()
        self.init_adc_grp()
        self.init_plot_grp()
        self.init_calib_grp()
        self.init_plotwidget()

        # layout widgets
        self.left_layout = QtWidgets.QVBoxLayout()
        self.right_layout = QtWidgets.QVBoxLayout()

        self.plot_layout = QtWidgets.QGridLayout()
        self.marker_layout = QtWidgets.QHBoxLayout()
        self.fit_layout = QtWidgets.QHBoxLayout()

        self.layout.addLayout(self.left_layout, 20)
        self.layout.addLayout(self.right_layout, 1)

        self.left_layout.addLayout(self.plot_layout)
        self.left_layout.addLayout(self.marker_layout)
        self.left_layout.addLayout(self.fit_layout)
        self.plot_layout.addWidget(self.label, 0, 0)
        self.plot_layout.addWidget(self.sample, 0, 1)
        self.plot_layout.addWidget(self.plot, 1, 0, 1, 2)
        self.marker_layout.addWidget(QtWidgets.QLabel('Marker: '))
        self.marker_layout.addWidget(self.chan_lbl)
        self.marker_layout.addWidget(QtWidgets.QLabel(' ('))
        self.marker_layout.addWidget(self.calib_lbl)
        self.marker_layout.addWidget(QtWidgets.QLabel(') = '))
        self.marker_layout.addWidget(self.count_lbl)
        self.marker_layout.addWidget(QtWidgets.QLabel(' Counts'))
        self.marker_layout.addWidget(QtWidgets.QWidget(), 10)
        self.fit_layout.addWidget(QtWidgets.QLabel('ROI Fit:  μ = '))
        self.fit_layout.addWidget(self.mu_chan_lbl)
        self.fit_layout.addWidget(QtWidgets.QLabel(' ('))
        self.fit_layout.addWidget(self.mu_energy_lbl)
        self.fit_layout.addWidget(QtWidgets.QLabel('),  σ = '))
        self.fit_layout.addWidget(self.sig_chan_lbl)
        self.fit_layout.addWidget(QtWidgets.QLabel(' ('))
        self.fit_layout.addWidget(self.sig_energy_lbl)
        self.fit_layout.addWidget(QtWidgets.QLabel(')'))
        self.fit_layout.addWidget(QtWidgets.QWidget(), 10)

        self.right_layout.addWidget(self.data_grp)
        self.right_layout.addWidget(self.time_grp)
        self.right_layout.addWidget(self.preset_grp)
        self.right_layout.addWidget(self.adc_grp)
        self.right_layout.addWidget(self.plot_grp)
        self.right_layout.addWidget(self.calib_grp)
        self.right_layout.addWidget(QtWidgets.QWidget(), 10)

    def get_neutral_color(self):
        # get neutral button color
        btn_color = QtWidgets.QPushButton().palette().color(\
            QtGui.QPalette.Background)
        self.neutral = '#{0:02x}{0:02x}{0:02x}'.format(\
            btn_color.red(), btn_color.green(), btn_color.blue())

    def init_plotwidget(self):
        self.counts, self.roi_mask = self.get_data()
        self.chans = self.chan_max

        # create MCB plot widget (with initial histogram and markers)
        self.plot = MCBPlot(self.chans, self.counts, self.roi_mask,\
            enableMenu=False)

        # create line info labels
        self.chan_lbl = QtWidgets.QLabel()
        self.count_lbl = QtWidgets.QLabel()
        self.calib_lbl = QtWidgets.QLabel()
        self.chan_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.count_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.calib_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.chan_lbl.setMinimumWidth(40)
        self.count_lbl.setMinimumWidth(70)
        self.calib_lbl.setMinimumWidth(80)

        # create ROI fit info labels
        self.mu_chan_lbl = QtWidgets.QLabel()
        self.mu_energy_lbl = QtWidgets.QLabel()
        self.sig_chan_lbl = QtWidgets.QLabel()
        self.sig_energy_lbl = QtWidgets.QLabel()
        self.mu_chan_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.mu_energy_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.sig_chan_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.sig_energy_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.mu_chan_lbl.setMinimumWidth(100)
        self.mu_energy_lbl.setMinimumWidth(130)
        self.sig_chan_lbl.setMinimumWidth(100)
        self.sig_energy_lbl.setMinimumWidth(130)

        # update marker and ROI fit
        self.update_marker()

        # add response function for line position change
        self.plot.line().sigPositionChanged.connect(self.update_marker)

        # add response function for ROI menu actions
        def roi_mark():
            pos = self.plot.box().pos()
            size = self.plot.box().size()
            x0 = max(int(pos.x() * self.chan_max / self.chans), 0)
            x1 = min(int((pos.x() + size.x()) * self.chan_max / self.chans),\
                self.chan_max-1)

            self.set_roi(x0, x1 - x0 + 1)

        def roi_clear():
            pos = self.plot.box().pos()
            size = self.plot.box().size()
            x0 = max(int(pos.x() * self.chan_max / self.chans), 0)
            x1 = min(int((pos.x() + size.x()) * self.chan_max / self.chans),\
                self.chan_max-1)

            self.clear_roi(x0, x1 - x0 + 1)

        self.plot.box().sigMark.connect(roi_mark)
        self.plot.box().sigClear.connect(roi_clear)

    def init_data_grp(self):
        # create a group for data acq buttons
        self.data_grp = QtWidgets.QGroupBox('Data Acquisition')
        self.data_layout = QtWidgets.QHBoxLayout()
        self.data_grp.setLayout(self.data_layout)

        # create data acq buttons
        self.start_btn = QtWidgets.QPushButton('')
        self.stop_btn = QtWidgets.QPushButton('')
        self.clear_btn = QtWidgets.QPushButton('')

        # add icons to data acq buttons
        self.start_btn.setIcon(QtGui.QIcon('icons/start.png'))
        self.stop_btn.setIcon(QtGui.QIcon('icons/stop.png'))
        self.clear_btn.setIcon(QtGui.QIcon('icons/clear.png'))
        self.start_btn.setIconSize(QtCore.QSize(20, 20))
        self.stop_btn.setIconSize(QtCore.QSize(20, 20))
        self.clear_btn.setIconSize(QtCore.QSize(20, 20))

        # add response functions for data acq buttons
        self.start_btn.clicked.connect(self.start)
        self.stop_btn.clicked.connect(self.stop)
        self.clear_btn.clicked.connect(self.clear)

        # layout data acq buttons
        self.data_layout.addWidget(self.start_btn)
        self.data_layout.addWidget(self.stop_btn)
        self.data_layout.addWidget(self.clear_btn)

        # enable/disable buttons
        if self.active:
            self.disable_btn(self.start_btn)
            self.enable_btn(self.stop_btn)
        else:
            self.enable_btn(self.start_btn)
            self.disable_btn(self.stop_btn)

    def init_time_grp(self):
        self.real = self.get_real()
        self.real_str = '{0:.2f}'.format(self.real / 1000)
        self.live = self.get_live()
        self.live_str = '{0:.2f}'.format(self.live / 1000)
        self.dead = 0
        self.dead_str = '%'
        self.dreals = deque([0] * 4)
        self.dlives = deque([0] * 4)

        # create a group for timing information
        self.time_grp = QtWidgets.QGroupBox('Timing')
        self.time_layout = QtWidgets.QGridLayout()
        self.time_grp.setLayout(self.time_layout)

        # create timing labels
        self.start_time_lbl = QtWidgets.QLabel(self.start_time_str)
        self.start_date_lbl = QtWidgets.QLabel(self.start_date_str)
        self.real_lbl = QtWidgets.QLabel(self.real_str)
        self.live_lbl = QtWidgets.QLabel(self.live_str)
        self.dead_lbl = QtWidgets.QLabel(self.dead_str)
        self.start_time_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.start_date_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.real_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.live_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.dead_lbl.setAlignment(QtCore.Qt.AlignRight)
        self.start_time_lbl.setMinimumWidth(50)
        self.start_date_lbl.setMinimumWidth(50)
        self.real_lbl.setMinimumWidth(50)
        self.live_lbl.setMinimumWidth(50)
        self.dead_lbl.setMinimumWidth(50)

        # layout timing labels
        self.time_layout.addWidget(QtWidgets.QLabel('Start: '), 0, 0)
        self.time_layout.addWidget(QtWidgets.QWidget(), 1, 0)
        self.time_layout.addWidget(QtWidgets.QLabel('Real: '), 2, 0)
        self.time_layout.addWidget(QtWidgets.QLabel('Live: '), 3, 0)
        self.time_layout.addWidget(QtWidgets.QLabel('Dead: '), 4, 0)
        self.time_layout.addWidget(self.start_time_lbl, 0, 1)
        self.time_layout.addWidget(self.start_date_lbl, 1, 1)
        self.time_layout.addWidget(self.real_lbl, 2, 1)
        self.time_layout.addWidget(self.live_lbl, 3, 1)
        self.time_layout.addWidget(self.dead_lbl, 4, 1)

    def init_preset_grp(self):
        self.rpre = self.get_real_preset()
        if self.rpre > 0:
            self.rpre_str = '{0:.2f}'.format(self.rpre / 1000)
        else:
            self.rpre_str = ''
        self.lpre = self.get_live_preset()
        if self.lpre > 0:
            self.lpre_str = '{0:.2f}'.format(self.lpre / 1000)
        else:
            self.lpre_str = ''

            # create a group for preset limits
        self.preset_grp = Spoiler(title='Preset Limits')
        self.preset_layout = QtWidgets.QGridLayout()

        # create preset textboxes
        self.rpre_txt = QtWidgets.QLineEdit(self.rpre_str)
        self.lpre_txt = QtWidgets.QLineEdit(self.lpre_str)
        self.rpre_txt.setValidator(self.float_only)
        self.lpre_txt.setValidator(self.float_only)
        self.rpre_txt.setAlignment(QtCore.Qt.AlignRight)
        self.lpre_txt.setAlignment(QtCore.Qt.AlignRight)
        self.rpre_txt.setMinimumWidth(50)
        self.lpre_txt.setMinimumWidth(50)

        # add response functions to preset textboxes
        def rpre_changed():
            if not self.active:
                self.rpre_str = self.rpre_txt.text()
                if self.rpre_str == '':
                    self.rpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.white))
                    self.rpre = 0
                    self.set_real_preset(self.rpre)
                elif self.rpre_str == '-' or float(self.rpre_str) < 0:
                    self.rpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.red))
                else:
                    self.rpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.white))
                    self.rpre = int(float(self.rpre_str) * 1000)
                    self.set_real_preset(self.rpre)

        def lpre_changed():
            if not self.active:
                self.lpre_str = self.lpre_txt.text()
                if self.lpre_str == '':
                    self.lpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.white))
                    self.lpre = 0
                    self.set_live_preset(self.lpre)
                elif self.lpre_str == '-' or float(self.lpre_str) < 0:
                    self.lpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.red))
                else:
                    self.lpre_txt.setStyleSheet(\
                        'background-color: {0}'.format(self.white))
                    self.lpre = int(float(self.lpre_str) * 1000)
                    self.set_live_preset(self.lpre)

        self.rpre_txt.textChanged.connect(rpre_changed)
        self.lpre_txt.textChanged.connect(lpre_changed)

        # layout preset widgets
        self.preset_layout.addWidget(QtWidgets.QLabel('Real: '), 0, 0)
        self.preset_layout.addWidget(QtWidgets.QLabel('Live: '), 1, 0)
        self.preset_layout.addWidget(self.rpre_txt, 0, 1)
        self.preset_layout.addWidget(self.lpre_txt, 1, 1)
        self.preset_grp.setContentLayout(self.preset_layout)

        # enable/disable presets
        if self.active:
            self.rpre_txt.setReadOnly(True)
            self.lpre_txt.setReadOnly(True)
        else:
            self.rpre_txt.setReadOnly(False)
            self.lpre_txt.setReadOnly(False)

    def init_adc_grp(self):
        self.gate = self.get_gate()
        self.lld = self.get_lld()
        self.uld = self.get_uld()

        # create a group for ADC settings
        self.adc_grp = Spoiler(title='ADC Settings')
        self.adc_layout = QtWidgets.QGridLayout()

        # create gate dropdown menu
        self.gate_box = QtWidgets.QComboBox()
        for option in ['Off', 'Coincidence', 'Anticoincidence']:
            self.gate_box.addItem(option)
        self.gate_box.setCurrentIndex(self.gate)

        # add response function for gate menu
        def gate_change():
            self.gate = self.gate_box.currentIndex()
            self.set_gate(self.gate)

        self.gate_box.currentIndexChanged.connect(gate_change)

        # create discriminator textboxes
        self.lld_txt = QtWidgets.QLineEdit(str(self.lld))
        self.uld_txt = QtWidgets.QLineEdit(str(self.uld))
        self.lld_txt.setValidator(self.int_only)
        self.uld_txt.setValidator(self.int_only)
        self.lld_txt.setAlignment(QtCore.Qt.AlignRight)
        self.uld_txt.setAlignment(QtCore.Qt.AlignRight)
        self.lld_txt.setMinimumWidth(5)
        self.uld_txt.setMinimumWidth(5)

        # add response functions for discriminator textboxes
        def lld_change():
            lld_str = self.lld_txt.text()
            if lld_str == '':
                self.lld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.lld = 0
                self.set_lld(self.lld)
            elif lld_str == '-' or int(lld_str) < 0\
                    or int(lld_str) >= self.chan_max:
                self.lld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.lld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.lld = int(lld_str)
                self.set_lld(self.lld)

        def uld_change():
            uld_str = self.uld_txt.text()
            if uld_str == '':
                self.uld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.uld = 0
                self.set_uld(self.uld)
            elif uld_str == '-' or int(uld_str) < 0\
                    or int(uld_str) >= self.chan_max:
                self.uld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.uld_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.uld = int(uld_str)
                self.set_uld(self.uld)

        self.lld_txt.textChanged.connect(lld_change)
        self.uld_txt.textChanged.connect(uld_change)

        # layout ADC widgets
        self.adc_layout.addWidget(QtWidgets.QLabel('Gate: '), 0, 0, 1, 2)
        self.adc_layout.addWidget(QtWidgets.QLabel('Lower Disc: '), 2, 0)
        self.adc_layout.addWidget(QtWidgets.QLabel('Upper Disc: '), 3, 0)
        self.adc_layout.addWidget(self.gate_box, 1, 0, 1, 2)
        self.adc_layout.addWidget(self.lld_txt, 2, 1)
        self.adc_layout.addWidget(self.uld_txt, 3, 1)
        self.adc_grp.setContentLayout(self.adc_layout)

    def init_plot_grp(self):
        self.mode = 'Auto'

        # create a group for plot settings
        self.plot_grp = Spoiler(title='Plot Settings')
        self.plot_layout = QtWidgets.QGridLayout()

        # create plot mode buttons
        self.log_btn = QtWidgets.QPushButton('Log')
        self.auto_btn = QtWidgets.QPushButton('Auto')
        self.log_btn.setMinimumWidth(20)
        self.auto_btn.setMinimumWidth(20)
        self.disable_btn(self.auto_btn)

        # add response functions for buttons
        def log_click():
            self.mode = 'Log'
            self.disable_btn(self.log_btn)
            self.enable_btn(self.auto_btn)
            self.plot.update(self.chans, self.counts, self.roi_mask, self.mode)
            self.popts = self.plot.fit_roi(self.get_roi(), self.calibrated,\
                self.a, self.b, self.c)

        def auto_click():
            self.mode = 'Auto'
            self.enable_btn(self.log_btn)
            self.disable_btn(self.auto_btn)
            self.plot.update(self.chans, self.counts, self.roi_mask, self.mode)
            self.popts = self.plot.fit_roi(self.get_roi(), self.calibrated,\
                self.a, self.b, self.c)

        self.log_btn.clicked.connect(log_click)
        self.auto_btn.clicked.connect(auto_click)

        # create rebinning dropdown menu
        self.chan_box = QtWidgets.QComboBox()
        self.chan_box.setEditable(True)
        self.chan_box.lineEdit().setReadOnly(True)
        self.chan_box.lineEdit().setAlignment(QtCore.Qt.AlignRight)
        chans = self.chan_max
        n = 0
        while chans >= self.chan_min:
            self.chan_box.addItem(str(int(chans)))
            self.chan_box.setItemData(n, QtCore.Qt.AlignRight,\
                QtCore.Qt.TextAlignmentRole)
            chans /= 2
            n += 1

        # add response function for rebinning menu
        def chan_change():
            self.chans = int(self.chan_max /
                             (1 << self.chan_box.currentIndex()))
            self.plot.update(self.chans, self.counts, self.roi_mask, self.mode)
            self.popts = self.plot.fit_roi(self.get_roi(), self.calibrated,\
                self.a, self.b, self.c)

        self.chan_box.currentIndexChanged.connect(chan_change)

        # layout plot widgets
        self.plot_layout.addWidget(QtWidgets.QLabel('Plot Mode: '), 0, 0, 1, 2)
        self.plot_layout.addWidget(QtWidgets.QLabel('Channels: '), 2, 0)
        self.plot_layout.addWidget(self.log_btn, 1, 0)
        self.plot_layout.addWidget(self.auto_btn, 1, 1)
        self.plot_layout.addWidget(self.chan_box, 2, 1)
        self.plot_grp.setContentLayout(self.plot_layout)

    def init_calib_grp(self):
        # load fit settings if they exist
        if self.settings.contains('calibrated'):
            self.calibrated = self.settings.value('calibrated')
        else:
            self.calibrated = False
        if self.settings.contains('a'):
            self.a = self.settings.value('a')
        else:
            self.a = 0
        if self.settings.contains('b'):
            self.b = self.settings.value('b')
        else:
            self.b = 0
        if self.settings.contains('c'):
            self.c = self.settings.value('c')
        else:
            self.c = 0

        # create a group for calibrations
        self.calib_grp = Spoiler(title='Calibration')
        self.calib_layout = QtWidgets.QGridLayout()

        # create calibration textboxes
        self.chan1_txt = QtWidgets.QLineEdit()
        self.chan2_txt = QtWidgets.QLineEdit()
        self.chan3_txt = QtWidgets.QLineEdit()
        self.energy1_txt = QtWidgets.QLineEdit()
        self.energy2_txt = QtWidgets.QLineEdit()
        self.energy3_txt = QtWidgets.QLineEdit()
        self.units_txt = QtWidgets.QLineEdit()
        self.chan1_txt.setValidator(self.int_only)
        self.chan2_txt.setValidator(self.int_only)
        self.chan3_txt.setValidator(self.int_only)
        self.chan1_txt.setAlignment(QtCore.Qt.AlignRight)
        self.chan2_txt.setAlignment(QtCore.Qt.AlignRight)
        self.chan3_txt.setAlignment(QtCore.Qt.AlignRight)
        self.energy1_txt.setValidator(self.float_only)
        self.energy2_txt.setValidator(self.float_only)
        self.energy3_txt.setValidator(self.float_only)
        self.energy1_txt.setAlignment(QtCore.Qt.AlignRight)
        self.energy2_txt.setAlignment(QtCore.Qt.AlignRight)
        self.energy3_txt.setAlignment(QtCore.Qt.AlignRight)
        self.units_txt.setPlaceholderText('keV')
        self.energy1_txt.setMinimumWidth(70)
        self.energy2_txt.setMinimumWidth(70)
        self.energy3_txt.setMinimumWidth(70)

        # load calib settings
        if self.settings.contains('chan1'):
            self.chan1 = self.settings.value('chan1')
            self.chan1_txt.setText(str(self.chan1))
        else:
            self.chan1 = 0
        if self.settings.contains('chan2'):
            self.chan2 = self.settings.value('chan2')
            self.chan2_txt.setText(str(self.chan2))
        else:
            self.chan2 = 0
        if self.settings.contains('chan3'):
            self.chan3 = self.settings.value('chan3')
            self.chan3_txt.setText(str(self.chan3))
        else:
            self.chan3 = 0
        if self.settings.contains('energy1'):
            self.energy1 = self.settings.value('energy1')
            self.energy1_txt.setText('{0:.2f}'.format(self.energy1))
        else:
            self.energy1 = 0
        if self.settings.contains('energy2'):
            self.energy2 = self.settings.value('energy2')
            self.energy2_txt.setText('{0:.2f}'.format(self.energy2))
        else:
            self.energy2 = 0
        if self.settings.contains('energy3'):
            self.energy3 = self.settings.value('energy3')
            self.energy3_txt.setText('{0:.2f}'.format(self.energy3))
        else:
            self.energy3 = 0
        if self.settings.contains('units'):
            self.units = self.settings.value('units')
            self.units_txt.setText(self.settings.value('units'))
        else:
            self.units = 'keV'

        # add response functions to calibration textboxes
        def update_calib():
            calib_pts = np.array([(self.chan1, self.energy1),\
                (self.chan2, self.energy2), (self.chan3, self.energy3)])
            # ignore points where chan = 0 or energy = 0
            valid_pts = calib_pts[np.logical_and(calib_pts[:,0] > 0,\
                calib_pts[:,1] > 0), :]
            npts = valid_pts.shape[0]

            # calculate quadratic fit parameters
            try:
                if npts == 0:
                    self.calibrated = False
                    self.a = 0
                    self.b = 0
                    self.c = 0
                elif npts == 1:
                    self.calibrated = True
                    self.a = 0
                    self.b = valid_pts[0, 1] / valid_pts[0, 0]
                    self.c = 0
                elif npts == 2:
                    self.calibrated = True
                    self.a = 0
                    self.b = (valid_pts[1,1] - valid_pts[0,1]) /\
                        (valid_pts[1,0] - valid_pts[0,0])
                    self.c = valid_pts[0, 1] - self.b * valid_pts[0, 0]
                else:
                    A0 = -valid_pts[0, 0]**2 + valid_pts[1, 0]**2
                    B0 = -valid_pts[0, 0] + valid_pts[1, 0]
                    D0 = -valid_pts[0, 1] + valid_pts[1, 1]
                    A1 = -valid_pts[1, 0]**2 + valid_pts[2, 0]**2
                    B1 = -valid_pts[1, 0] + valid_pts[2, 0]
                    D1 = -valid_pts[1, 1] + valid_pts[2, 1]
                    A2 = -B1 / B0 * A0 + A1
                    D2 = -B1 / B0 * D0 + D1
                    self.calibrated = True
                    self.a = D2 / A2
                    self.b = (D0 - A0 * self.a) / B0
                    self.c = valid_pts[0,1] - self.a*valid_pts[0,0]**2 -\
                        self.b*valid_pts[0,0]
            except:
                self.calibrated = False
                self.a = 0
                self.b = 0
                self.c = 0

            self.settings.setValue('calibrated', self.calibrated)
            self.settings.setValue('a', self.a)
            self.settings.setValue('b', self.b)
            self.settings.setValue('c', self.c)

            # update marker info label
            self.update_marker()

        def chan1_change():
            chan1_str = self.chan1_txt.text()
            if chan1_str == '':
                self.chan1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan1 = 0
                self.settings.remove('chan1')
            elif chan1_str == '-' or int(chan1_str) < 0\
                    or int(chan1_str) >= self.chan_max:
                self.chan1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.chan1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan1 = int(chan1_str)
                self.settings.setValue('chan1', self.chan1)
            update_calib()

        def chan2_change():
            chan2_str = self.chan2_txt.text()
            if chan2_str == '':
                self.chan2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan2 = 0
                self.settings.remove('chan2')
            elif chan2_str == '-' or int(chan2_str) < 0\
                    or int(chan2_str) >= self.chan_max:
                self.chan2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.chan2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan2 = int(chan2_str)
                self.settings.setValue('chan2', self.chan2)
            update_calib()

        def chan3_change():
            chan3_str = self.chan3_txt.text()
            if chan3_str == '':
                self.chan3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan3 = 0
                self.settings.remove('chan3')
            elif chan3_str == '-' or int(chan3_str) < 0\
                    or int(chan3_str) >= self.chan_max:
                self.chan3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.chan3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.chan3 = int(chan3_str)
                self.settings.setValue('chan3', self.chan3)
            update_calib()

        def energy1_change():
            energy1_str = self.energy1_txt.text()
            if energy1_str == '':
                self.energy1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy1 = 0
                self.settings.remove('energy1')
            elif energy1_str == '-' or float(energy1_str) < 0:
                self.energy1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.energy1_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy1 = float(energy1_str)
                self.settings.setValue('energy1', self.energy1)
            update_calib()

        def energy2_change():
            energy2_str = self.energy2_txt.text()
            if energy2_str == '':
                self.energy2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy2 = 0
                self.settings.remove('energy2')
            elif energy2_str == '-' or float(energy2_str) < 0:
                self.energy2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.energy2_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy2 = float(energy2_str)
                self.settings.setValue('energy2', self.energy2)
            update_calib()

        def energy3_change():
            energy3_str = self.energy3_txt.text()
            if energy3_str == '':
                self.energy3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy3 = 0
                self.settings.remove('energy3')
            elif energy3_str == '-' or float(energy3_str) < 0:
                self.energy3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.red))
            else:
                self.energy3_txt.setStyleSheet(\
                    'background-color: {0}'.format(self.white))
                self.energy3 = float(energy3_str)
                self.settings.setValue('energy3', self.energy3)
            update_calib()

        def units_change():
            units_str = self.units_txt.text()
            if units_str == '':
                self.units = 'keV'
                self.settings.remove('units')
            else:
                self.units = units_str
                self.settings.setValue('units', self.units)

            # update marker info label
            self.update_marker()

        self.chan1_txt.textChanged.connect(chan1_change)
        self.chan2_txt.textChanged.connect(chan2_change)
        self.chan3_txt.textChanged.connect(chan3_change)
        self.energy1_txt.textChanged.connect(energy1_change)
        self.energy2_txt.textChanged.connect(energy2_change)
        self.energy3_txt.textChanged.connect(energy3_change)
        self.units_txt.textChanged.connect(units_change)

        # layout calibration widgets
        self.calib_layout.addWidget(QtWidgets.QLabel('Channel'), 0, 0)
        self.calib_layout.addWidget(QtWidgets.QLabel('Energy/Time'), 0, 2)
        self.calib_layout.addWidget(QtWidgets.QLabel('='), 1, 1)
        self.calib_layout.addWidget(QtWidgets.QLabel('='), 2, 1)
        self.calib_layout.addWidget(QtWidgets.QLabel('='), 3, 1)
        self.calib_layout.addWidget(QtWidgets.QLabel('Units: '), 4, 0)
        self.calib_layout.addWidget(self.chan1_txt, 1, 0)
        self.calib_layout.addWidget(self.chan2_txt, 2, 0)
        self.calib_layout.addWidget(self.chan3_txt, 3, 0)
        self.calib_layout.addWidget(self.energy1_txt, 1, 2)
        self.calib_layout.addWidget(self.energy2_txt, 2, 2)
        self.calib_layout.addWidget(self.energy3_txt, 3, 2)
        self.calib_layout.addWidget(self.units_txt, 4, 1, 1, 2)
        self.calib_grp.setContentLayout(self.calib_layout)

    def update_mcb(self):
        self.counts, self.roi_mask = self.get_data()

        # update plot
        self.plot.update(self.chans, self.counts, self.roi_mask, self.mode)

        # fit ROI's
        self.popts = self.plot.fit_roi(self.get_roi(), self.calibrated, self.a,\
            self.b, self.c)

        # enable/disable data buttons and preset boxes
        old_state = self.active
        self.active = self.is_active()
        state_changed = (self.active != old_state)

        if state_changed:
            if self.active:
                self.disable_btn(self.start_btn)
                self.enable_btn(self.stop_btn)

                self.rpre_txt.setReadOnly(True)
                self.lpre_txt.setReadOnly(True)
            else:
                self.enable_btn(self.start_btn)
                self.disable_btn(self.stop_btn)

                self.rpre_txt.setReadOnly(False)
                self.lpre_txt.setReadOnly(False)

        # update timing
        self.start_datetime = datetime.fromtimestamp(\
            self.driver.get_start_time(self.hdet))
        self.start_time_str = self.start_datetime.strftime('%I:%M:%S %p')
        self.start_date_str = self.start_datetime.strftime('%m/%d/%Y')
        old_real = self.real
        self.real = self.get_real()
        self.real_str = '{0:.2f}'.format(self.real / 1000)
        old_live = self.live
        self.live = self.get_live()
        self.live_str = '{0:.2f}'.format(self.live / 1000)

        if self.active:
            self.dreals.append(self.real - old_real)
            self.dreals.popleft()
            dreal = sum(self.dreals)
            self.dlives.append(self.live - old_live)
            self.dlives.popleft()
            dlive = sum(self.dlives)
            if dreal > dlive:
                self.dead = (1 - dlive / dreal) * 100
                self.dead_str = '{0:.2f} %'.format(self.dead)
            else:
                self.dead = 0
                self.dead_str = '%'
        else:
            self.dead = 0
            self.dead_str = '%'

        self.start_time_lbl.setText(self.start_time_str)
        self.start_date_lbl.setText(self.start_date_str)
        self.real_lbl.setText(self.real_str)
        self.live_lbl.setText(self.live_str)
        self.dead_lbl.setText(self.dead_str)

        # update line info label
        self.update_marker()

    def update_marker(self):
        # get marker line channel and counts
        self.line_x = int(self.plot.line().value())
        self.line_y = int(self.plot.rebin[self.line_x])

        # set channel and counts label
        self.chan_lbl.setText(str(self.line_x))
        self.count_lbl.setText(str(self.line_y))

        # if calibrated, set energy label
        if self.calibrated:
            self.calib_lbl.setText('{0:.2f} {1}'.format(self.a*self.line_x**2 +\
                self.b*self.line_x + self.c, self.units))
        else:
            self.calib_lbl.setText('uncalibrated')

        # check if marker line is in an ROI
        chan = self.line_x * self.chan_max / self.chans
        nroi = self.get_nroi(chan)

        # if in an ROI, set fit labels
        if nroi is not None:
            popt = self.popts[nroi]
            try:
                self.mu_chan_lbl.setText('{0:.2f} ± {1:.2f}'\
                    .format(popt['mu_chan_opt'], popt['mu_chan_err']))
                self.sig_chan_lbl.setText('{0:.2f} ± {1:.2f}'\
                    .format(popt['sig_chan_opt'], popt['sig_chan_err']))
            except:
                self.mu_chan_lbl.setText('could not fit')
                self.sig_chan_lbl.setText('could not fit')
            if self.calibrated:
                try:
                    self.mu_energy_lbl.setText('{0:.2f} ± {1:.2f} {2}'\
                        .format(popt['mu_energy_opt'], popt['mu_energy_err'],\
                        self.units))
                    self.sig_energy_lbl.setText('{0:.2f} ± {1:.2f} {2}'\
                        .format(popt['sig_energy_opt'], popt['sig_energy_err'],\
                        self.units))
                except:
                    self.mu_energy_lbl.setText('could not fit')
                    self.sig_energy_lbl.setText('could not fit')
            else:
                self.mu_energy_lbl.setText('uncalibrated')
                self.sig_energy_lbl.setText('uncalibrated')
        else:
            self.mu_chan_lbl.setText('')
            self.mu_energy_lbl.setText('')
            self.sig_chan_lbl.setText('')
            self.sig_energy_lbl.setText('')

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Left:
            new_pos = self.plot.line().value() - 1
            if new_pos >= 0:
                self.plot.line().setValue(new_pos)
            self.plot.box().hide()
        elif event.key() == QtCore.Qt.Key_Right:
            new_pos = self.plot.line().value() + 1
            if new_pos < self.chans:
                self.plot.line().setValue(new_pos)
            self.plot.box().hide()

    def enable_btn(self, btn):
        btn.setEnabled(True)
        btn.setStyleSheet('background-color: {0}'.format(self.neutral))

    def disable_btn(self, btn):
        btn.setEnabled(False)
        btn.setStyleSheet('background-color: {0}'.format(self.gray))

    def get_nroi(self, chan):
        rois = self.get_roi()
        count = 0
        for i in range(len(rois)):
            start_chan, num_chans = rois[i]
            if chan >= start_chan and chan < start_chan + num_chans:
                return i
        return None

    def is_active(self):
        return self.driver.is_active(self.hdet)

    def start(self):
        self.driver.comm(self.hdet, 'START')
        self.start_time = datetime.fromtimestamp(\
            self.driver.get_start_time(self.hdet))
        self.update()

    def stop(self):
        self.driver.comm(self.hdet, 'STOP')
        self.update()

    def clear(self):
        self.driver.comm(self.hdet, 'CLEAR')
        self.update()

    def get_data(self):
        return self.driver.get_data(self.hdet, 0, self.chan_max)

    def get_real(self):
        resp = self.driver.comm(self.hdet, 'SHOW_TRUE')
        msec = int(resp[2:-4]) * 20
        return msec

    def get_live(self):
        resp = self.driver.comm(self.hdet, 'SHOW_LIVE')
        msec = int(resp[2:-4]) * 20
        return msec

    def get_real_preset(self):
        resp = self.driver.comm(self.hdet, 'SHOW_TRUE_PRESET')
        msec = int(resp[2:-4]) * 20
        return msec

    def get_live_preset(self):
        resp = self.driver.comm(self.hdet, 'SHOW_LIVE_PRESET')
        msec = int(resp[2:-4]) * 20
        return msec

    def get_gate(self):
        resp = self.driver.comm(self.hdet, 'SHOW_GATE')
        index = self.gate_index[resp[3:-1]]
        return index

    def get_lld(self):
        resp = self.driver.comm(self.hdet, 'SHOW_LLD')
        lld = int(resp[2:-4])
        return lld

    def get_uld(self):
        resp = self.driver.comm(self.hdet, 'SHOW_ULD')
        uld = int(resp[2:-4])
        return uld

    def get_roi(self):
        rois = []
        resp = self.driver.comm(self.hdet, 'SHOW_ROI')
        while int(resp[7:12]) > 0:
            rois.append((int(resp[2:7]), int(resp[7:12])))
            resp = self.driver.comm(self.hdet, 'SHOW_NEXT')
        return rois

    def set_data(self, start_chan, num_chans=1, value=0):
        self.driver.comm(self.hdet, 'SET_DATA {}, {}, {}'.format(start_chan,\
            num_chans, value))

    def set_real(self, msec):
        ticks = int(msec / 20)
        self.driver.comm(self.hdet, 'SET_TRUE {}'.format(ticks))

    def set_live(self, msec):
        ticks = int(msec / 20)
        self.driver.comm(self.hdet, 'SET_LIVE {}'.format(ticks))

    def set_real_preset(self, msec):
        ticks = int(msec / 20)
        self.driver.comm(self.hdet, 'SET_TRUE_PRESET {}'.format(ticks))

    def set_live_preset(self, msec):
        ticks = int(msec / 20)
        self.driver.comm(self.hdet, 'SET_LIVE_PRESET {}'.format(ticks))

    def set_gate(self, index):
        options = ['OFF', 'COINCIDENT', 'ANTICOINCIDENT']
        self.driver.comm(self.hdet, 'SET_GATE_{}'.format(options[index]))

    def set_lld(self, disc):
        self.driver.comm(self.hdet, 'SET_LLD {}'.format(disc))

    def set_uld(self, disc):
        self.driver.comm(self.hdet, 'SET_ULD {}'.format(disc))

    def set_roi(self, start_chan, num_chans):
        self.driver.comm(self.hdet, 'SET_ROI {}, {}'.format(start_chan,\
            num_chans))

    def clear_roi(self, start_chan, num_chans):
        self.driver.comm(self.hdet, 'SET_WINDOW {}, {}'.format(start_chan,\
            num_chans))
        self.driver.comm(self.hdet, 'CLEAR_ROI')
        self.driver.comm(self.hdet, 'SET_WINDOW')