コード例 #1
0
    def __init__(self, sub_id, proc_id, f_name):
        self.f_name = f_name
        self.reader = BlackrockRawIO(f_name, nsx_to_load=5)

        self.db_wrapper = DBWrapper()

        # signal settings
        self.SR = None
        self.channels = []
        self.depths = []
        self.depth_times = []
        self.rec_start_time = None
        self.prepare_depth_values()

        # settings
        self.subject_settings = {'id': sub_id}
        self.procedure_settings = {'target_name': None,
                                   'type': 'surgical'}
        self.buffer_settings = {'sampling_rate': 30000,
                                'buffer_length': '6.000',
                                'sample_length': '4.000',
                                'delay_buffer': '0.500',
                                'overwrite_depth': True,
                                'run_buffer': False,
                                'electrode_settings': {}}
        self.procedure_name = 'Proc' + str(proc_id)

        for electrode in self.channels:
            self.buffer_settings['electrode_settings'][electrode] = {'threshold': True,
                                                                     'validity': 90.0}
        self.features_settings = {}

        # manage settings and start process
        self.manage_settings()
コード例 #2
0
ファイル: FeaturesGUI.py プロジェクト: SachsLab/NeuroportDBS
    def __init__(self):
        super(FeaturesGUI, self).__init__()
        self.setWindowTitle('FeaturesGUI')
        self.plot_widget = None

        # settings dictionaries
        self.subject_settings = {}
        self.procedure_settings = {}
        self.buffer_settings = {}
        self.features_settings = {}

        # DB wrapper
        self.db_wrapper = DBWrapper()
コード例 #3
0
    def __init__(self, features_settings):
        super(FeaturesWidget, self).__init__()

        # Settings
        self.feature_categories = DBWrapper().all_features.keys()
        self.features_settings = features_settings
        if not self.features_settings:
            self.features_settings['features'] = {}

            # Check if default values are defined
            for cat in self.feature_categories:
                # defaults to true, compute all features
                self.features_settings['features'][cat] = True

        self.features_widgets = {}

        features_layout = QGridLayout(self)

        # Add an option to toggle all features
        self.all_features = QCheckBox('All')
        self.all_features.setChecked(False)
        self.all_features.clicked.connect(self.toggle_all)
        features_layout.addWidget(self.all_features, 0, 0, 1, 1)
        if 'features' in self.features_settings.keys():
            for idx, (label, sett) in enumerate(
                    self.features_settings['features'].items()):
                self.features_widgets[label] = QCheckBox(label)
                self.features_widgets[label].setChecked(sett)
                self.features_widgets[label].clicked.connect(self.toggle)
                features_layout.addWidget(self.features_widgets[label],
                                          idx + 1, 0, 1, 1)
コード例 #4
0
    def __init__(self, subject_settings):
        super(SubjectWidget, self).__init__()

        self.subject_enums = DBWrapper().return_enums('subject')

        subject_layout = QGridLayout(self)
        subject_layout.setColumnMinimumWidth(2, 60)

        subject_layout.addWidget(QLabel("Id: "), 0, 0, 1, 1)
        self.id_combo = QComboBox()
        self.id_combo.setEditable(True)
        self.id_combo.addItem('')
        self.id_combo.addItems(DBWrapper().list_all_subjects())
        self.id_combo.currentIndexChanged.connect(self.load_subject)
        self.id_combo.lineEdit().editingFinished.connect(self.check_subject)

        subject_layout.addWidget(self.id_combo, 0, 1, 1, 4)

        subject_layout.addWidget(QLabel("Name: "), 1, 0, 1, 1)
        self.name_edit = QLineEdit()
        self.name_edit.setMaxLength(135)
        subject_layout.addWidget(self.name_edit, 1, 1, 1, 4)

        subject_layout.addWidget(QLabel("Sex: "), 2, 0, 1, 1)
        self.sex_combo = QComboBox()
        self.sex_combo.addItems(self.subject_enums['sex'] if 'sex' in
                                self.subject_enums.keys() else [])
        self.sex_combo.setCurrentIndex(0)
        subject_layout.addWidget(self.sex_combo, 2, 1, 1, 1)

        subject_layout.addWidget((QLabel("Date of birth: ")), 3, 0, 1, 1)
        self.dob_calendar = QCalendarWidget()
        subject_layout.addWidget(self.dob_calendar, 3, 1, 1, 3)

        subject_layout.addWidget(QLabel("NSP file comment: "), 4, 0, 1, 1)
        self.file_comment = QTextEdit("")
        self.file_comment.setMaximumHeight(150)
        subject_layout.addWidget(self.file_comment, 4, 1, 1, 4)

        # Subject Settings
        self.subject_settings = subject_settings
        if not self.subject_settings:
            self.update_settings_from_db(-1)

        self.update_subject()
コード例 #5
0
 def check_all_procedures(self, subject_id, block):
     self.prev_proc.blockSignals(block)
     self.all_procedures = DBWrapper().list_all_procedures(subject_id)
     self.prev_proc.clear()
     self.prev_proc.addItem('')
     self.prev_proc.addItems([
         ' '.join([
             x.target_name, x.recording_config,
             x.date.strftime('%Y-%m-%d')
         ]) for x in self.all_procedures
     ])
     self.prev_proc.setCurrentIndex(0)
     self.prev_proc.blockSignals(False)
コード例 #6
0
ファイル: FeaturesGUI.py プロジェクト: SachsLab/NeuroportDBS
    def process_settings(self, sub_sett, proc_sett, depth_sett, feat_sett):
        self.subject_settings = dict(sub_sett)
        self.procedure_settings = dict(proc_sett)
        self.depth_settings = dict(depth_sett)
        self.features_settings = dict(feat_sett)

        # validate that we have some data in the electrode_settings. If the NSP is not connected we will have
        # to load the channel names from the DB. Also we want to keep the FeaturesGUI unaware of the DB channels.
        if len(self.depth_settings['electrode_settings']) == 0:
            self.depth_settings['electrode_settings'] = {}
            for lbl in DBWrapper().list_channel_labels():
                self.depth_settings['electrode_settings'][lbl] = DEPTHSETTINGS
            CbSdkConnection().is_simulating = True

        # set new features
        self.feature_select.setCurrentIndex(0)  # Raw
        while self.feature_select.count() > 2:  # Raw and Mapping
            self.feature_select.removeItem(2)
        self.feature_select.addItems(self.features_settings['features'].keys())

        # set new channels
        self.chan_select.setCurrentIndex(0)  # None
        while self.chan_select.count() > 1:
            self.chan_select.removeItem(1)
        self.chan_select.addItems(
            self.depth_settings['electrode_settings'].keys())

        # clear and update stacked widget
        to_delete = [
            self.plot_stack.widget(x) for x in range(self.plot_stack.count())
        ]
        for wid in to_delete:
            self.plot_stack.removeWidget(wid)
            wid.deleteLater()
            wid = None
        self.stack_dict = {}
        self.create_plots()

        self.depth_wrapper.send_settings(self.depth_settings)
        self.features_wrapper.send_settings(self.features_settings)

        if not self.features_process_running:
            self.manage_feature_process(True)

        # self.clear()
        self.read_from_shared_memory()
コード例 #7
0
class NS5OfflinePlayback:
    def __init__(self, sub_id, proc_id, f_name):
        self.f_name = f_name
        self.reader = BlackrockRawIO(f_name, nsx_to_load=5)

        self.db_wrapper = DBWrapper()

        # signal settings
        self.SR = None
        self.channels = []
        self.depths = []
        self.depth_times = []
        self.rec_start_time = None
        self.prepare_depth_values()

        # settings
        self.subject_settings = {'id': sub_id}
        self.procedure_settings = {'target_name': None,
                                   'type': 'surgical'}
        self.buffer_settings = {'sampling_rate': 30000,
                                'buffer_length': '6.000',
                                'sample_length': '4.000',
                                'delay_buffer': '0.500',
                                'overwrite_depth': True,
                                'run_buffer': False,
                                'electrode_settings': {}}
        self.procedure_name = 'Proc' + str(proc_id)

        for electrode in self.channels:
            self.buffer_settings['electrode_settings'][electrode] = {'threshold': True,
                                                                     'validity': 90.0}
        self.features_settings = {}

        # manage settings and start process
        self.manage_settings()

    def prepare_depth_values(self):
        self.reader.parse_header()

        # channels
        self.channels = [x[0] for x in self.reader.header['signal_channels']]
        self.SR = self.reader.header['signal_channels'][0][2]
        self.rec_start_time = self.reader.raw_annotations['blocks'][0]['rec_datetime']

        # comments
        rexp = re.compile(r'[a-zA-Z]*\:?(?P<depth>\-?\d*\.\d*)')
        for com in self.reader.nev_data['Comments'][0]:
            # some comments aren't depth related
            tmp_match = rexp.match(com[5].decode('utf-8'))
            if tmp_match:
                # older files marked depths at regular intervals and not only when it changed
                # we need to keep only new depth values
                d = float(tmp_match.group('depth'))
                if len(self.depths) == 0 or d != self.depths[-1]:
                    self.depth_times.append(com[0])
                    self.depths.append(d)

    def manage_settings(self):
        win = SettingsDialog(self.subject_settings,
                             self.procedure_settings,
                             self.buffer_settings,
                             self.features_settings)

        win.update_settings()
        win.close()

        sub_id = self.db_wrapper.load_or_create_subject(self.subject_settings)

        if sub_id == -1:
            print("Subject not created.")
            return False
        else:
            self.subject_settings['subject_id'] = sub_id
            self.procedure_settings['subject_id'] = sub_id
            self.procedure_settings['target_name'] = self.procedure_name
            proc_id = self.db_wrapper.load_or_create_procedure(self.procedure_settings)

            self.buffer_settings['procedure_id'] = proc_id
            self.features_settings['procedure_id'] = proc_id

        self.process_data()

    def process_data(self):
        # get settings
        buffer_length = int(float(self.buffer_settings['buffer_length']) * self.SR)
        sample_length = int(float(self.buffer_settings['sample_length']) * self.SR)
        valid_thresh = [int(x['validity'] / 100 * sample_length)
                        for x in self.buffer_settings['electrode_settings'].values()]

        bar = QProgressDialog('Processing', 'Stop', 0, len(self.depths), None)
        bar.setWindowModality(Qt.WindowModal)
        bar.show()
        # loop through each comment time and check whether segment is long enough
        for idx, (time, depth) in enumerate(zip(self.depth_times[:-1], self.depths[:-1])):
            bar.setValue(idx)
            bar.setLabelText(self.f_name + "  " + str(depth))
            if bar.wasCanceled():
                break

            if self.depth_times[idx + 1] - time >= sample_length:
                # get sample first, if doesn't pass validation, move forward until it does
                # or until buffer_length is elapsed.
                data = self.reader.get_analogsignal_chunk(i_start=time, i_stop=time + sample_length).T
                valid = self.validate_data_sample(data)
                t_offset = 0
                while not all(np.sum(valid, axis=1) > valid_thresh) and \
                        t_offset + sample_length < buffer_length and \
                        time + t_offset + sample_length < self.depth_times[idx + 1]:
                    t_offset += 300  # roughly a 100 Hz update rate.
                    data = self.reader.get_analogsignal_chunk(i_start=time + t_offset,
                                                              i_stop=time + t_offset + sample_length).T
                    valid = self.validate_data_sample(data)

                # send to db
                self.db_wrapper.save_depth_datum(depth=depth,
                                                 data=data,
                                                 is_good=np.sum(valid, axis=1) > valid_thresh,
                                                 group_info=self.channels,
                                                 start_time=self.rec_start_time +
                                                 datetime.timedelta(seconds=(time + t_offset) / self.SR),
                                                 stop_time=self.rec_start_time +
                                                 datetime.timedelta(seconds=(time + t_offset +
                                                                             sample_length) /
                                                                    self.SR))
        bar.close()

    @staticmethod
    def validate_data_sample(data):
        # TODO: implement other metrics
        # SUPER IMPORTANT: when cbpy returns an int16 value, it can be -32768, however in numpy:
        #     np.abs(-32768) = -32768 for 16 bit integers since +32768 does not exist.
        # We therefore can't use the absolute value for the threshold.
        threshold = 30000  # arbitrarily set for now
        validity = (-threshold < data) & (data < threshold)

        return validity
コード例 #8
0
ファイル: FeaturesGUI.py プロジェクト: SachsLab/NeuroportDBS
    def update(self):
        # Depth process
        output = self.depth_wrapper.worker_status()
        self.status_label.setPixmap(self.status_icons[output])

        if self.depth_wrapper.is_running():
            self.depth_process_btn.setStyleSheet("QPushButton { color: white; "
                                                 "background-color : green; "
                                                 "border-color : green; "
                                                 "border-width: 2px}")
        else:
            self.depth_process_running = False
            self.depth_process_btn.setStyleSheet("QPushButton { color: white; "
                                                 "background-color : red; "
                                                 "border-color : red; "
                                                 "border-width: 2px}")

        if self.features_wrapper.is_running():
            self.features_process_btn.setStyleSheet(
                "QPushButton { color: white; "
                "background-color : green; "
                "border-color : green; "
                "border-width: 2px}")
        else:
            self.features_process_running = False
            self.features_process_btn.setStyleSheet(
                "QPushButton { color: white; "
                "background-color : red; "
                "border-color : red; "
                "border-width: 2px}")

        if self.sweep_control.isChecked():
            self.read_from_shared_memory()

        # features plot
        curr_chan_lbl = self.chan_select.currentText()
        if curr_chan_lbl != 'None':
            curr_feat = self.feature_select.currentText()
            do_hp = self.do_hp.isChecked()

            if do_hp != self.plot_stack.currentWidget().plot_config['do_hp'] or \
                    self.y_range != self.plot_stack.currentWidget().plot_config['y_range']:
                self.plot_stack.currentWidget().clear_plot()
                self.stack_dict[curr_chan_lbl][curr_feat][1] = 0
                self.plot_stack.currentWidget().plot_config['do_hp'] = do_hp
                self.plot_stack.currentWidget(
                ).plot_config['y_range'] = self.y_range

            curr_datum = self.stack_dict[curr_chan_lbl][curr_feat][1]
            if curr_feat == 'Raw':
                all_data = DBWrapper().load_depth_data(chan_lbl=curr_chan_lbl,
                                                       gt=curr_datum,
                                                       do_hp=do_hp,
                                                       return_uV=True)
            elif curr_feat == 'Mapping':
                all_data = DBWrapper().load_mapping_response(
                    chan_lbl=curr_chan_lbl, gt=curr_datum)
            else:
                all_data = DBWrapper().load_features_data(
                    category=curr_feat, chan_lbl=curr_chan_lbl, gt=curr_datum)
            if all_data:
                self.plot_stack.currentWidget().update_plot(dict(all_data))
                self.stack_dict[curr_chan_lbl][curr_feat][1] = max(
                    all_data.keys())
コード例 #9
0
ファイル: FeaturesGUI.py プロジェクト: SachsLab/NeuroportDBS
class FeaturesGUI(CustomGUI):
    def __init__(self):
        super(FeaturesGUI, self).__init__()
        self.setWindowTitle('FeaturesGUI')
        self.plot_widget = None

        # settings dictionaries
        self.subject_settings = {}
        self.procedure_settings = {}
        self.buffer_settings = {}
        self.features_settings = {}

        # DB wrapper
        self.db_wrapper = DBWrapper()

    # Override create actions to call the clean_exit script
    def create_actions(self):
        # Actions
        self.actions = {
            'Connect': QAction("Connect", self),
            'Quit': QAction("Quit", self),
            'AddPlot': QAction("Add Plot", self)
        }
        self.actions['Connect'].triggered.connect(
            self.on_action_connect_triggered)
        self.actions['Quit'].triggered.connect(self.clean_exit)
        self.actions['AddPlot'].triggered.connect(
            self.on_action_add_plot_triggered)

    def clean_exit(self):
        if self.plot_widget:
            self.plot_widget.kill_processes()

            if self.plot_widget.awaiting_close:
                del self.plot_widget

        QApplication.instance().quit()

    # defined in the CustomGUI class, is triggered when the "Add Plot" button
    # is pressed in the default GUI (Connect, Add Plot, Quit)
    def on_action_add_plot_triggered(self):
        # Get all the available information for settings
        # NSP info, None if not connected
        sampling_group_id = SAMPLINGGROUPS.index(str(SAMPLINGRATE))
        self.group_info = self.cbsdk_conn.get_group_config(sampling_group_id)

        # we only need to set the default values for the depth buffer here since it requires electrode
        # information. The rest accepts empty dicts
        self.buffer_settings['sampling_rate'] = SAMPLINGRATE
        self.buffer_settings['sampling_group_id'] = sampling_group_id
        self.buffer_settings['buffer_length'] = '{:.3f}'.format(BUFFERLENGTH)
        self.buffer_settings['sample_length'] = '{:.3f}'.format(SAMPLELENGTH)
        self.buffer_settings['delay_buffer'] = '{:.3f}'.format(DELAYBUFFER)
        self.buffer_settings['overwrite_depth'] = OVERWRITEDEPTH
        self.buffer_settings['electrode_settings'] = {}

        if self.group_info:
            for electrode in self.group_info:
                self.buffer_settings['electrode_settings'][
                    electrode['label'].decode('utf-8')] = DEPTHSETTINGS

        # Open settings dialog and update DBWrapper subject and settings dicts
        if not self.manage_settings():
            return

        # Configure CB SDK connection
        self.cbsdk_conn.cbsdk_config = {
            'reset': True,
            'get_continuous': True,
            'get_events': False,
            'get_comments': True,
            'buffer_parameter': {
                'comment_length': 10
            }
        }

        # NSP Buffer widget
        # this widget handles the process creation that scans depth values, buffers the data and sends it to the DB.
        self.plot_widget = FeaturesPlotWidget(self.group_info)
        self.plot_widget.was_closed.connect(self.clean_exit)
        self.plot_widget.call_manage_settings.connect(self.get_settings)

        # send values to widgets
        self.send_settings()

    # The custom GUI class has an update function, which calls the
    # do_plot_update function. This function then calls the update
    # function of all display widgets.
    def do_plot_update(self):
        # since all widgets have different use, they will each handle their own data collection.
        self.plot_widget.update()

    def manage_settings(self):
        # Open prompt to input subject details
        win = SettingsDialog(self.subject_settings, self.procedure_settings,
                             self.buffer_settings, self.features_settings)
        result = win.exec_()
        if result == QDialog.Accepted:
            win.update_settings()
        else:
            return False

        # Create or load subject
        # Returns subject_id/-1 whether subject is properly created or not
        sub_id = self.db_wrapper.load_or_create_subject(self.subject_settings)

        if sub_id == -1:
            print("Subject not created.")
            return False
        else:
            self.subject_settings['subject_id'] = sub_id
            self.procedure_settings['subject_id'] = sub_id
            proc_id = self.db_wrapper.load_or_create_procedure(
                self.procedure_settings)

            self.buffer_settings['procedure_id'] = proc_id
            self.features_settings['procedure_id'] = proc_id

            return True

    def get_settings(self):
        # open prompt and update values
        if self.manage_settings():
            # update values to widgets
            self.send_settings()

    def send_settings(self):
        self.plot_widget.process_settings(self.subject_settings,
                                          self.procedure_settings,
                                          self.buffer_settings,
                                          self.features_settings)
コード例 #10
0
 def update_settings_from_db(self, idx):
     for key, value in DBWrapper().load_subject_details(idx).items():
         self.subject_settings[key] = value
コード例 #11
0
 def update_settings_from_db(self, idx):
     self.procedure_settings.update(DBWrapper().load_procedure_details(
         idx, exclude=['subject', 'procedure_id']))
コード例 #12
0
class SubjectWidget(QWidget):
    subject_change = Signal(int)

    def __init__(self, subject_settings):
        super(SubjectWidget, self).__init__()

        self.subject_enums = DBWrapper().return_enums('subject')

        subject_layout = QGridLayout(self)
        subject_layout.setColumnMinimumWidth(2, 60)

        subject_layout.addWidget(QLabel("Id: "), 0, 0, 1, 1)
        self.id_combo = QComboBox()
        self.id_combo.setEditable(True)
        self.id_combo.addItem('')
        self.id_combo.addItems(DBWrapper().list_all_subjects())
        self.id_combo.currentIndexChanged.connect(self.load_subject)
        self.id_combo.lineEdit().editingFinished.connect(self.check_subject)

        subject_layout.addWidget(self.id_combo, 0, 1, 1, 4)

        subject_layout.addWidget(QLabel("Name: "), 1, 0, 1, 1)
        self.name_edit = QLineEdit()
        self.name_edit.setMaxLength(135)
        subject_layout.addWidget(self.name_edit, 1, 1, 1, 4)

        subject_layout.addWidget(QLabel("Sex: "), 2, 0, 1, 1)
        self.sex_combo = QComboBox()
        self.sex_combo.addItems(self.subject_enums['sex'] if 'sex' in
                                self.subject_enums.keys() else [])
        self.sex_combo.setCurrentIndex(0)
        subject_layout.addWidget(self.sex_combo, 2, 1, 1, 1)

        subject_layout.addWidget((QLabel("Date of birth: ")), 3, 0, 1, 1)
        self.dob_calendar = QCalendarWidget()
        subject_layout.addWidget(self.dob_calendar, 3, 1, 1, 3)

        subject_layout.addWidget(QLabel("NSP file comment: "), 4, 0, 1, 1)
        self.file_comment = QTextEdit("")
        self.file_comment.setMaximumHeight(150)
        subject_layout.addWidget(self.file_comment, 4, 1, 1, 4)

        # Subject Settings
        self.subject_settings = subject_settings
        if not self.subject_settings:
            self.update_settings_from_db(-1)

        self.update_subject()

    def update_subject(self):
        self.name_edit.setText(
            self.read_dict_value(self.subject_settings, 'name'))
        self.id_combo.setCurrentText(
            self.read_dict_value(self.subject_settings, 'id'))
        self.sex_combo.setCurrentText(
            self.read_dict_value(self.subject_settings, 'sex'))
        dob = self.read_dict_value(self.subject_settings, 'birthday')
        if dob not in [None, '']:
            q_dob = QDate.fromString(dob, 'yyyy-MM-d')
            self.dob_calendar.setSelectedDate(q_dob)
        else:
            self.dob_calendar.setSelectedDate(QDate.currentDate())

    def update_settings_from_db(self, idx):
        for key, value in DBWrapper().load_subject_details(idx).items():
            self.subject_settings[key] = value

    def load_subject(self):
        # id is a unique and mandatory field
        self.check_subject()
        self.update_subject()

    def check_subject(self):
        # when changing the id in the combobox, can be modifying or entering an existing subject id. Check to load data
        # if so.
        curr_id = self.id_combo.currentText()
        if curr_id != '':
            self.update_settings_from_db(curr_id)
            self.subject_change.emit(self.subject_settings['subject_id'])
        else:
            self.update_settings_from_db(-1)
            self.subject_change.emit(-1)

    @staticmethod
    def read_dict_value(dictionary, value):
        return str(dictionary[value]) if value in dictionary.keys() else ''

    def to_dict(self):
        self.subject_settings['id'] = self.id_combo.currentText()
        self.subject_settings['name'] = self.name_edit.text()
        self.subject_settings['sex'] = self.sex_combo.currentText()
        self.subject_settings['birthday'] = self.dob_calendar.selectedDate(
        ).toPyDate()
        self.subject_settings['NSP_comment'] = self.file_comment.toPlainText()
コード例 #13
0
    def __init__(self, procedure_settings):
        super(ProcedureWidget, self).__init__()

        # Settings
        self.procedure_settings = procedure_settings

        # populate with defaults if empty
        if not self.procedure_settings:
            self.update_settings_from_db(-1)

        self.proc_enums = DBWrapper().return_enums('procedure')
        proc_layout = QGridLayout(self)

        row = 0
        proc_layout.addWidget(QLabel("Previous procedures: "), row, 0, 1, 1)
        self.prev_proc = QComboBox()
        self.prev_proc.setEnabled(True)
        self.check_all_procedures(None, False)
        self.prev_proc.currentIndexChanged.connect(
            self.procedure_selection_change)
        proc_layout.addWidget(self.prev_proc, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Target name: "), row, 0, 1, 1)
        self.target_name = QLineEdit("")
        proc_layout.addWidget(self.target_name, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Type: "), row, 0, 1, 1)
        self.type_combo = self.combo_from_enum('type')
        proc_layout.addWidget(self.type_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Recording configuration: "), row, 0, 1,
                              1)
        self.rec_combo = self.combo_from_enum('recording_config')
        proc_layout.addWidget(self.rec_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Electrode configuration: "), row, 0, 1,
                              1)
        self.electrode_combo = self.combo_from_enum('electrode_config')
        proc_layout.addWidget(self.electrode_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Distance to target: "), row, 0, 1, 1)
        self.dist_to_target = self.coord_line_edit()
        proc_layout.addWidget(self.dist_to_target, row, 1, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("Entry (x, y, z): "), row, 0, 1, 1)
        self.entry_x = self.coord_line_edit()
        proc_layout.addWidget(self.entry_x, row, 1, 1, 1)
        self.entry_y = self.coord_line_edit()
        proc_layout.addWidget(self.entry_y, row, 2, 1, 1)
        self.entry_z = self.coord_line_edit()
        proc_layout.addWidget(self.entry_z, row, 3, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("Target (x, y, z): "), row, 0, 1, 1)
        self.target_x = self.coord_line_edit()
        proc_layout.addWidget(self.target_x, row, 1, 1, 1)
        self.target_y = self.coord_line_edit()
        proc_layout.addWidget(self.target_y, row, 2, 1, 1)
        self.target_z = self.coord_line_edit()
        proc_layout.addWidget(self.target_z, row, 3, 1, 1)

        row += 1
        self.comp_dist_to_target = QLabel(
            "Computed distance: 0.000 mm; Difference: 0.000 mm")
        proc_layout.addWidget(self.comp_dist_to_target, row, 0, 1, 2)

        row += 1
        proc_layout.addWidget(QLabel("A (x, y, z): "), row, 0, 1, 1)
        self.a_x = self.coord_line_edit()
        proc_layout.addWidget(self.a_x, row, 1, 1, 1)
        self.a_y = self.coord_line_edit()
        proc_layout.addWidget(self.a_y, row, 2, 1, 1)
        self.a_z = self.coord_line_edit()
        proc_layout.addWidget(self.a_z, row, 3, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("E (x, y, z): "), row, 0, 1, 1)
        self.e_x = self.coord_line_edit()
        proc_layout.addWidget(self.e_x, row, 1, 1, 1)
        self.e_y = self.coord_line_edit()
        proc_layout.addWidget(self.e_y, row, 2, 1, 1)
        self.e_z = self.coord_line_edit()
        proc_layout.addWidget(self.e_z, row, 3, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("Offset direction: "), row, 0, 1, 1)
        self.offset_direction_combo = self.combo_from_enum('offset_direction')
        proc_layout.addWidget(self.offset_direction_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Offset size: "), row, 0, 1, 1)
        self.offset_size = self.coord_line_edit()
        proc_layout.addWidget(self.offset_size, row, 1, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("Medication status: "), row, 0, 1, 1)
        self.medic_combo = self.combo_from_enum('medication_status')
        proc_layout.addWidget(self.medic_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QWidget(), row, 1, 1, 3)

        self.update_procedure()
コード例 #14
0
class ProcedureWidget(QWidget):
    def __init__(self, procedure_settings):
        super(ProcedureWidget, self).__init__()

        # Settings
        self.procedure_settings = procedure_settings

        # populate with defaults if empty
        if not self.procedure_settings:
            self.update_settings_from_db(-1)

        self.proc_enums = DBWrapper().return_enums('procedure')
        proc_layout = QGridLayout(self)

        row = 0
        proc_layout.addWidget(QLabel("Previous procedures: "), row, 0, 1, 1)
        self.prev_proc = QComboBox()
        self.prev_proc.setEnabled(True)
        self.check_all_procedures(None, False)
        self.prev_proc.currentIndexChanged.connect(
            self.procedure_selection_change)
        proc_layout.addWidget(self.prev_proc, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Target name: "), row, 0, 1, 1)
        self.target_name = QLineEdit("")
        proc_layout.addWidget(self.target_name, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Type: "), row, 0, 1, 1)
        self.type_combo = self.combo_from_enum('type')
        proc_layout.addWidget(self.type_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Recording configuration: "), row, 0, 1,
                              1)
        self.rec_combo = self.combo_from_enum('recording_config')
        proc_layout.addWidget(self.rec_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Electrode configuration: "), row, 0, 1,
                              1)
        self.electrode_combo = self.combo_from_enum('electrode_config')
        proc_layout.addWidget(self.electrode_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Distance to target: "), row, 0, 1, 1)
        self.dist_to_target = self.coord_line_edit()
        proc_layout.addWidget(self.dist_to_target, row, 1, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("Entry (x, y, z): "), row, 0, 1, 1)
        self.entry_x = self.coord_line_edit()
        proc_layout.addWidget(self.entry_x, row, 1, 1, 1)
        self.entry_y = self.coord_line_edit()
        proc_layout.addWidget(self.entry_y, row, 2, 1, 1)
        self.entry_z = self.coord_line_edit()
        proc_layout.addWidget(self.entry_z, row, 3, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("Target (x, y, z): "), row, 0, 1, 1)
        self.target_x = self.coord_line_edit()
        proc_layout.addWidget(self.target_x, row, 1, 1, 1)
        self.target_y = self.coord_line_edit()
        proc_layout.addWidget(self.target_y, row, 2, 1, 1)
        self.target_z = self.coord_line_edit()
        proc_layout.addWidget(self.target_z, row, 3, 1, 1)

        row += 1
        self.comp_dist_to_target = QLabel(
            "Computed distance: 0.000 mm; Difference: 0.000 mm")
        proc_layout.addWidget(self.comp_dist_to_target, row, 0, 1, 2)

        row += 1
        proc_layout.addWidget(QLabel("A (x, y, z): "), row, 0, 1, 1)
        self.a_x = self.coord_line_edit()
        proc_layout.addWidget(self.a_x, row, 1, 1, 1)
        self.a_y = self.coord_line_edit()
        proc_layout.addWidget(self.a_y, row, 2, 1, 1)
        self.a_z = self.coord_line_edit()
        proc_layout.addWidget(self.a_z, row, 3, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("E (x, y, z): "), row, 0, 1, 1)
        self.e_x = self.coord_line_edit()
        proc_layout.addWidget(self.e_x, row, 1, 1, 1)
        self.e_y = self.coord_line_edit()
        proc_layout.addWidget(self.e_y, row, 2, 1, 1)
        self.e_z = self.coord_line_edit()
        proc_layout.addWidget(self.e_z, row, 3, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("Offset direction: "), row, 0, 1, 1)
        self.offset_direction_combo = self.combo_from_enum('offset_direction')
        proc_layout.addWidget(self.offset_direction_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QLabel("Offset size: "), row, 0, 1, 1)
        self.offset_size = self.coord_line_edit()
        proc_layout.addWidget(self.offset_size, row, 1, 1, 1)

        row += 1
        proc_layout.addWidget(QLabel("Medication status: "), row, 0, 1, 1)
        self.medic_combo = self.combo_from_enum('medication_status')
        proc_layout.addWidget(self.medic_combo, row, 1, 1, 3)

        row += 1
        proc_layout.addWidget(QWidget(), row, 1, 1, 3)

        self.update_procedure()

    def check_all_procedures(self, subject_id, block):
        self.prev_proc.blockSignals(block)
        self.all_procedures = DBWrapper().list_all_procedures(subject_id)
        self.prev_proc.clear()
        self.prev_proc.addItem('')
        self.prev_proc.addItems([
            ' '.join([
                x.target_name, x.recording_config,
                x.date.strftime('%Y-%m-%d')
            ]) for x in self.all_procedures
        ])
        self.prev_proc.setCurrentIndex(0)
        self.prev_proc.blockSignals(False)

    def combo_from_enum(self, enum_name):
        combo = QComboBox()
        combo.addItems(self.proc_enums[enum_name] if enum_name in
                       self.proc_enums.keys() else [])
        combo.setCurrentText('none')
        return combo

    def coord_line_edit(self):
        template = QRegExp(r"[-]?\d*\.?\d{0,3}")
        validator = QRegExpValidator(template)

        line = QLineEdit("0.0")
        line.setValidator(validator)
        line.setFixedWidth(60)
        # line.editingFinished.connect(self.update_dist_to_target)
        line.textChanged.connect(self.update_dist_to_target)

        return line

    def update_dist_to_target(self, new_string):
        if new_string not in ["-", ".", "", "-."]:
            ddt = np.sqrt(
                (float(self.target_x.text()) - float(self.entry_x.text()))**2 +
                (float(self.target_y.text()) - float(self.entry_y.text()))**2 +
                (float(self.target_z.text()) - float(self.entry_z.text()))**2)
            diff = float(self.dist_to_target.text()) - ddt
            self.comp_dist_to_target.setText(
                "Computed distance: {:.3f} mm; Difference: {:.3f} mm".format(
                    ddt, diff))

    def change_subject(self, sub_id, block=False):
        self.check_all_procedures(sub_id, block)

    def procedure_selection_change(self):
        if self.prev_proc.currentIndex() > 0:
            self.update_settings_from_db(
                self.all_procedures[self.prev_proc.currentIndex() -
                                    1].procedure_id)
        else:
            self.update_settings_from_db(-1)
        self.update_procedure()

    def update_settings_from_db(self, idx):
        self.procedure_settings.update(DBWrapper().load_procedure_details(
            idx, exclude=['subject', 'procedure_id']))

    def update_procedure(self):
        self.target_name.setText(self.read_dict_value('target_name'))
        self.type_combo.setCurrentText(self.read_dict_value('type'))
        self.rec_combo.setCurrentText(self.read_dict_value('recording_config'))
        self.electrode_combo.setCurrentText(
            self.read_dict_value('electrode_config'))
        self.medic_combo.setCurrentText(
            self.read_dict_value('medication_status'))
        self.offset_size.setText(str(self.read_dict_value('offset_size')))
        self.offset_direction_combo.setCurrentText(
            self.read_dict_value('offset_direction'))
        entry = self.read_dict_value('entry')
        if entry is None:
            entry = [0., 0., 0.]
        self.entry_x.setText(str(entry[0]))
        self.entry_y.setText(str(entry[1]))
        self.entry_z.setText(str(entry[2]))
        target = self.read_dict_value('target')
        if target is None:
            target = [0., 0., 0.]
        self.target_x.setText(str(target[0]))
        self.target_y.setText(str(target[1]))
        self.target_z.setText(str(target[2]))
        ddt = self.read_dict_value('distance_to_target')
        if ddt is None:
            ddt = 0.000
        self.dist_to_target.setText(str(ddt))
        # self.update_dist_to_target()
        a = self.read_dict_value('a')
        if a is None:
            a = [0., 0., 0.]
        self.a_x.setText(str(a[0]))
        self.a_y.setText(str(a[1]))
        self.a_z.setText(str(a[2]))
        e = self.read_dict_value('e')
        if e is None:
            e = [0., 0., 0.]
        self.e_x.setText(str(e[0]))
        self.e_y.setText(str(e[1]))
        self.e_z.setText(str(e[2]))

    def read_dict_value(self, value):
        return self.procedure_settings[
            value] if value in self.procedure_settings.keys() else None

    def to_dict(self):
        self.procedure_settings['type'] = self.type_combo.currentText()
        self.procedure_settings['a'] = np.array([
            float(self.a_x.text()),
            float(self.a_y.text()),
            float(self.a_z.text())
        ],
                                                dtype=np.float)
        self.procedure_settings['distance_to_target'] = float(
            self.dist_to_target.text())
        self.procedure_settings['e'] = np.array([
            float(self.e_x.text()),
            float(self.e_y.text()),
            float(self.e_z.text())
        ],
                                                dtype=np.float)
        self.procedure_settings[
            'electrode_config'] = self.electrode_combo.currentText()
        self.procedure_settings['entry'] = np.array([
            float(self.entry_x.text()),
            float(self.entry_y.text()),
            float(self.entry_z.text())
        ],
                                                    dtype=np.float)
        self.procedure_settings[
            'medication_status'] = self.medic_combo.currentText()
        self.procedure_settings['target_name'] = self.target_name.text()
        self.procedure_settings[
            'recording_config'] = self.rec_combo.currentText()
        self.procedure_settings['target'] = np.array([
            float(self.target_x.text()),
            float(self.target_y.text()),
            float(self.target_z.text())
        ],
                                                     dtype=np.float)
        self.procedure_settings[
            'offset_direction'] = self.offset_direction_combo.currentText()
        self.procedure_settings['offset_size'] = float(self.offset_size.text())