示例#1
0
    def test_my_plot_item_add_item(self):
        plot_item = MyPlotItem()
        x = [1]
        y = [1]
        plot = PlotCurveItem(x, y)
        plot_item.addItem(plot)

        self.assertEqual(len(plot_item._myItemList), 1)

        x = y = np.zeros(2)
        plot2 = PathItem(x, y)
        plot_item.addItem(plot2)
        self.assertEqual(len(plot_item._myItemList), 2)
示例#2
0
    def test_clear_event_items(self):
        plot_item = MyPlotItem()
        x = [1]
        y = [1]
        plot = PlotCurveItem(x, y)
        plot_item.addItem(plot)

        plot2 = PlotCurveItem(y, x)
        plot_item.add_event_item(plot2)

        self.assertEqual(len(plot_item._myEventItemList), 1)
        self.assertEqual(len(plot_item.listDataItems()), 2)

        plot_item.clear_event_items()
        self.assertEqual(len(plot_item._myEventItemList), 0)

        self.assertEqual(len(plot_item.listDataItems()), 1)
示例#3
0
    def test_clear(self):
        plot_item = MyPlotItem()
        x = [1]
        y = [1]
        plot = PlotCurveItem(x, y)
        plot_item.addItem(plot)

        plot2 = PlotCurveItem(y, x)
        plot_item.add_event_item(plot2)

        x = y = np.zeros(2)
        plot3 = PathItem(x, y)
        plot_item.add_event_item(plot3)

        plot_item.clear()
        self.assertEqual(len(plot_item.listDataItems()), 0)
        self.assertEqual(len(plot_item._myEventItemList), 0)
        self.assertEqual(len(plot_item._myItemList), 0)
示例#4
0
    def test_clear_event_items(self):
        plot_item = MyPlotItem()
        x = [1]
        y = [1]
        plot = PlotCurveItem(x, y)
        plot_item.addItem(plot)

        plot2 = PlotCurveItem(y, x)
        plot_item.add_event_item(plot2)

        self.assertEqual(len(plot_item._myEventItemList), 1)
        self.assertEqual(len(plot_item.listDataItems()), 2)

        plot_item.clear_event_items()
        self.assertEqual(len(plot_item._myEventItemList), 0)

        self.assertEqual(len(plot_item.listDataItems()), 1)
示例#5
0
    def _create_right_widget(self, parent=None):
        wig = pg.GraphicsLayoutWidget(parent)

        # Main plot
        self.plot_widget = MyPlotItem(title='Current Trace', name='Plot')
        wig.addItem(self.plot_widget)
        self.plot_widget.enableAutoRange('xy', False)
        self.p1 = self.plot_widget.plot(
        )  # create an empty plot curve to be filled later

        # Tool bar for main plot.  Contains zoom button and different checkboxes
        self.plot_tool_bar = PlotToolBar(self)
        if not parent is None:
            parent.addToolBar(self.plot_tool_bar)

        event_finder_plots_layout = LayoutWidget()
        event_finder_plots_layout.addWidget(self.plot_tool_bar,
                                            row=1,
                                            col=0,
                                            colspan=3)
        event_finder_plots_layout.addWidget(wig, row=2, col=0, colspan=3)

        return event_finder_plots_layout
示例#6
0
    def test_my_plot_item_add_item(self):
        plot_item = MyPlotItem()
        x = [1]
        y = [1]
        plot = PlotCurveItem(x, y)
        plot_item.addItem(plot)

        self.assertEqual(len(plot_item._myItemList), 1)

        x = y = np.zeros(2)
        plot2 = PathItem(x, y)
        plot_item.addItem(plot2)
        self.assertEqual(len(plot_item._myItemList), 2)
示例#7
0
    def _create_right_widget(self, parent=None):
        wig = pg.GraphicsLayoutWidget(parent)

        # Main plot
        self.plot_widget = MyPlotItem(title='Current Trace', name='Plot')
        wig.addItem(self.plot_widget)
        self.plot_widget.enableAutoRange('xy', False)
        self.p1 = self.plot_widget.plot()  # create an empty plot curve to be filled later

        # Tool bar for main plot.  Contains zoom button and different checkboxes
        self.plot_tool_bar = PlotToolBar(self)
        if not parent is None:
            parent.addToolBar(self.plot_tool_bar)

        event_finder_plots_layout = LayoutWidget()
        event_finder_plots_layout.addWidget(self.plot_tool_bar, row=1, col=0, colspan=3)
        event_finder_plots_layout.addWidget(wig, row=2, col=0, colspan=3)

        return event_finder_plots_layout
示例#8
0
class EventFindingTab(BaseQSplitterDataFile):
    """
    A QtGui.QSplitter that contains event finding options on the left and a plot on the right.
    """

    def __init__(self, parent=None):
        """
        :param PySide.QtGui.QMainWindow parent: Parent main window (optional).
        """
        # Define the instance elements
        self.events = []  # holds the events from the most recent analysis run
        self.plot_widget = None
        self.p1 = None
        self.plot_tool_bar = None
        super(EventFindingTab, self).__init__(parent)

    def get_current_analysis_parameters(self):
        """
        Reads the current analysis parameters from the gui and returns the results in a dictionary. Returns
        dictionary entry 'error' with text of the error if one exists.

        :returns: DictType -- the event analysis parameters. These include:

            - 'error' - Text if there was an error

            OR

            - 'min_event_length'
            - 'max_event_length'
            - 'baseline_strategy'
            - 'baseline_filter_parameter'
            - 'baseline_current'

            etc...
        """

        threshold_type = baseline_type = None
        # Get Min_event length in microseconds
        try:
            min_event_length = float(self.min_event_length_edit.text())
        except ValueError:
            return {'error': 'Could not read float from Min Event Length text box.  Please fix.'}
        # Get Max Event Length in microseconds
        try:
            max_event_length = float(self.max_event_length_edit.text())
        except ValueError:
            return {'error': 'Could not read float from Max Event Length text box.  Please fix.'}
        if min_event_length >= max_event_length:
            return {'error': 'Min Event Length is greater than Max Event Length.  Please fix.'}

        baseline_type_str = str(self.baseline_type_combo.currentText())
        if baseline_type_str == 'Adaptive':
            try:
                baseline_filter_parameter = float(self.baseline_filter_parameter_edit.text())
            except ValueError:
                return {'error': 'Could not read float from Baseline Filter Parameter text box.  Please fix.'}
            try:
                variance_filter_parameter = float(self.variance_filter_parameter_edit.text())
            except ValueError:
                return {'error': 'Could not read float from Variance Filter Parameter text box.  Please fix.'}
            baseline_type = AdaptiveBaselineStrategy(baseline_filter_parameter=baseline_filter_parameter,
                                                     variance_filter_parameter=variance_filter_parameter)
        elif baseline_type_str == 'Fixed':
            try:
                baseline_current = float(self.baseline_current_edit.text())
                baseline_type = FixedBaselineStrategy(baseline=baseline_current)
            except ValueError:
                return {'error': 'Could not read float from Baseline Current text box.  Please fix.'}

        threshold_direction_str = str(self.threshold_direction_combo.currentText())
        detect_positive_events = (threshold_direction_str in ('Positive', 'Both'))
        detect_negative_events = (threshold_direction_str in ('Negative', 'Both'))

        threshold_type_str = str(self.threshold_type_combo.currentText())
        if threshold_type_str == 'Noise Based':
            try:
                start_stddev = float(self.threshold_stdev_start.text())
            except ValueError:
                return {'error': 'Could not read float from Start StdDev text box.  Please fix.'}
            try:
                end_stddev = float(self.threshold_stdev_end.text())
            except ValueError:
                return {'error': 'Could not read float from End StdDev text box.  Please fix.'}
            threshold_type = NoiseBasedThresholdStrategy(start_std_dev=start_stddev, end_std_dev=end_stddev)
        elif threshold_type_str == 'Absolute Change':
            try:
                absolute_change_start = float(self.absolute_change_start_edit.text())
            except ValueError:
                return {'error': 'Could not read float from Absolute Change Start.  Please fix.'}
            try:
                absolute_change_end = float(self.absolute_change_end_edit.text())
            except ValueError:
                return {'error': 'Could not read float from Absolute Change End.  Please fix.'}
            threshold_type = AbsoluteChangeThresholdStrategy(change_start=absolute_change_start,
                                                             change_end=absolute_change_end)
        elif threshold_type_str == 'Percent Change':
            try:
                percent_change_start = float(self.percentage_change_start_edit.text())
            except ValueError:
                return {'error': 'Could not read float from Percent Change Start text box.  Please fix.'}
            try:
                percent_change_end = float(self.percentage_change_end_edit.text())
            except ValueError:
                return {'error': 'Could not read float from Percent Change End text box.  Please fix.'}
            threshold_type = PercentChangeThresholdStrategy(percent_change_start=percent_change_start,
                                                            percent_change_end=percent_change_end)

        parameters = Parameters(min_event_length=min_event_length, max_event_length=max_event_length,
                                detect_positive_events=detect_positive_events,
                                detect_negative_events=detect_negative_events, baseline_strategy=baseline_type,
                                threshold_strategy=threshold_type)
        return parameters

    def plot_data(self, plot_options):
        """
        Plots waveform in datadict.
        :param DictType plot_options: Dictionary of plot options. Must contain:

        * Data at plot_options['data'][0]
        * sample_rate at plot_options['sample_rate']
        * plot_range at plot_options['plot_range']. This can be [start,stop], or
          'all' for 0:n.

        """
        # Read the first file, store data in dictionary
        data = plot_options['data'][0]
        sample_rate = plot_options['sample_rate']
        plot_range = plot_options['plot_range']

        n = len(data)
        # If problem with input, just plot all the data
        if plot_range == 'all' or len(plot_range) != 2 or plot_range[1] <= plot_range[0]:
            plot_range = [0, n]
        else:  # no problems!
            n = plot_range[1] - plot_range[0] + 1

        ts = 1 / sample_rate

        times = linspace(ts * plot_range[0], ts * plot_range[1], n)
        y_data = data[plot_range[0]:(plot_range[1] + 1)]

        self.plot_widget.clear_event_items()
        self.p1.setData(x=times, y=y_data)
        self.plot_widget.autoRange()
        self._dispatch_process_events()

    def _analyze_data_thread_callback(self, results):
        """
        Callback for updates from the AnalyzeDataThread.
        """
        if 'status_text' in results:
            self._dispatch_status_update(results['status_text'])
        if 'Events' in results:
            single_plot = False
            events = results['Events']
            if len(events) < 1:
                return
            elif len(self.events) < 1:
                # if this is our first time plotting events, include the single event plot!
                single_plot = True
            self.events += events
            self.event_display_edit.setMaxLength(int(len(self.events) / 10) + 1)
            self.event_display_edit.setValidator(QtGui.QIntValidator(1, len(self.events), self.event_display_edit))
            self.eventCountText.setText('/' + str(len(self.events)))
            if self.plot_tool_bar.is_plot_during_checked():
                self.plotEventsOnMainPlot(events)
                self.addEventsToConcatEventPlot(events)
            if single_plot:
                self.event_display_edit.setText('1')
            self._dispatch_process_events()
            self.analyzethread.readyForEvents = True
        if 'done' in results:
            if results['done']:
                self.stop_analyze_button.setEnabled(False)

    def _on_analyze(self):
        """
        Searches for events in the file that is currently highlighted in the files list.
        """
        selected_items = self.file_list_widget.selectedItems()
        if len(selected_items) > 0:
            curr_item = selected_items[0]
        else:
            return

        parameters = self.get_current_analysis_parameters()
        if isinstance(parameters, dict) and 'error' in parameters:
            self._dispatch_status_update(parameters['error'])
            return

        debug = self.debug_check_box.isChecked()

        # Clear the current events
        del self.events[:]
        # self.prev_concat_time = 0.

        file_names = [str(curr_item.get_file_name())]

        self._dispatch_status_update("Event Count: 0 Percent Done: 0")

        # Start analyzing data in new analyzethread.
        self.analyzethread = AnalyzeDataThread(file_names, parameters, debug)
        self.analyzethread.dataReady.connect(self._analyze_data_thread_callback)
        self.add_thread(self.analyzethread)
        self.analyzethread.start()

        self.stop_analyze_button.setEnabled(True)

    def _on_analyze_stop(self):
        """
        Called when the user clicks the Stop button.
        """
        self.clean_threads()
        self.stop_analyze_button.setEnabled(False)
        self._dispatch_status_update("Analyze aborted.")

    def _on_file_item_doubleclick(self, item):
        """
        Called when baseline_filter_parameter file list item is double clicked.
        Starts the plotting thread, which opens the file, parses data, then passes to plotData
        """
        # adding by emitting signal in different thread
        self._dispatch_status_update("Plotting...")
        decimates = self.plot_tool_bar.is_decimate_checked()
        thread = PlotThread(self.p1, filename=str(item.get_file_name()), decimate=decimates)
        thread.dataReady.connect(self._on_file_item_doubleclick_callback)
        self.add_thread(thread)
        thread.start()

    def _on_file_item_doubleclick_callback(self, results):
        if 'plot_options' in results:
            self.plot_data(results['plot_options'])
        if 'status_text' in results:
            self._dispatch_status_update(results['status_text'])
        if 'thread' in results:
            self.remove_thread(results['thread'])

    def _on_file_item_selection_changed(self):
        self.analyze_button.setEnabled(True)

    def _create_right_widget(self, parent=None):
        wig = pg.GraphicsLayoutWidget(parent)

        # Main plot
        self.plot_widget = MyPlotItem(title='Current Trace', name='Plot')
        wig.addItem(self.plot_widget)
        self.plot_widget.enableAutoRange('xy', False)
        self.p1 = self.plot_widget.plot()  # create an empty plot curve to be filled later

        # Tool bar for main plot.  Contains zoom button and different checkboxes
        self.plot_tool_bar = PlotToolBar(self)
        if not parent is None:
            parent.addToolBar(self.plot_tool_bar)

        event_finder_plots_layout = LayoutWidget()
        event_finder_plots_layout.addWidget(self.plot_tool_bar, row=1, col=0, colspan=3)
        event_finder_plots_layout.addWidget(wig, row=2, col=0, colspan=3)

        return event_finder_plots_layout

    def open_data_files(self, file_names=None):
        are_files_opened = super(EventFindingTab, self).open_data_files(file_names)
        if are_files_opened:
            self.analyze_button.setEnabled(False)

    def _create_left_widget(self, parent=None):
        """
        Initializes everything in the options pane on the left side of the splitter.
        """
        scroll_area = QtGui.QScrollArea()
        scroll_area.setWidgetResizable(True)

        list_form_layout = super(EventFindingTab, self)._create_left_widget(parent)

        # Other GUI controls
        #
        self.analyze_button = QtGui.QPushButton("&Find Events")
        self.connect(self.analyze_button, QtCore.SIGNAL('clicked()'), self._on_analyze)
        self.analyze_button.setEnabled(False)

        self.stop_analyze_button = QtGui.QPushButton("&Stop")
        self.connect(self.stop_analyze_button, QtCore.SIGNAL('clicked()'), self._on_analyze_stop)
        self.stop_analyze_button.setEnabled(False)

        self.debug_check_box = QtGui.QCheckBox()
        self.debug_check_box.setChecked(False)
        self.debug_check_box.setText('Debug')
        self.debug_check_box.setToolTip("When checked, extra data will be saved to the EventDatabase. This"
                                        " data includes the data, baseline, and thresholds at each datapoint.")

        # Analysis options
        self.min_event_length_edit = QtGui.QLineEdit()
        self.min_event_length_edit.setText('10.0')
        self.min_event_length_edit.setValidator(QtGui.QDoubleValidator(0, 1e12, 5, self.min_event_length_edit))
        self.max_event_length_edit = QtGui.QLineEdit()
        self.max_event_length_edit.setText('1000.0')
        self.max_event_length_edit.setValidator(QtGui.QDoubleValidator(0, 1e12, 5, self.max_event_length_edit))

        fixed_analysis_options = QtGui.QFormLayout()
        fixed_analysis_options.addRow('Min Event Length [us]:', self.min_event_length_edit)
        fixed_analysis_options.addRow('Max Event Length [us]:', self.max_event_length_edit)

        # Baseline options
        baseline_options = QtGui.QStackedLayout()
        self.baseline_type_combo = QtGui.QComboBox()
        self.baseline_type_combo.addItems(('Adaptive', 'Fixed'))
        self.baseline_type_combo.activated.connect(baseline_options.setCurrentIndex)

        adaptive_options_layout = QtGui.QFormLayout()
        self.baseline_filter_parameter_edit = QtGui.QLineEdit()
        self.baseline_filter_parameter_edit.setValidator(QtGui.QDoubleValidator(0, 10, 5, self.baseline_filter_parameter_edit))
        self.baseline_filter_parameter_edit.setText('0.93')
        adaptive_options_layout.addRow('Baseline Filter Parameter:', self.baseline_filter_parameter_edit)
        self.variance_filter_parameter_edit = QtGui.QLineEdit()
        self.variance_filter_parameter_edit.setValidator(QtGui.QDoubleValidator(0, 10, 5, self.variance_filter_parameter_edit))
        self.variance_filter_parameter_edit.setText('0.99')
        adaptive_options_layout.addRow('Variance Filter Parameter:', self.variance_filter_parameter_edit)
        # need to cast to widget to add to QStackedLayout
        adaptive_options_widget = QtGui.QWidget()
        adaptive_options_widget.setLayout(adaptive_options_layout)

        choose_baseline_btn = QtGui.QPushButton('Baseline:')
        choose_baseline_btn.setToolTip('Click to choose the baseline from the plot.')

        fixed_options_layout = QtGui.QFormLayout()
        self.baseline_current_edit = QtGui.QLineEdit()
        self.baseline_current_edit.setValidator(QtGui.QDoubleValidator(-9999, 9999, 9, self.baseline_current_edit))
        self.baseline_current_edit.setText('0.0')
        fixed_options_layout.addRow(choose_baseline_btn, self.baseline_current_edit)
        fixed_options_widget = QtGui.QWidget()
        fixed_options_widget.setLayout(fixed_options_layout)

        baseline_options.addWidget(adaptive_options_widget)
        baseline_options.addWidget(fixed_options_widget)

        baseline_form = QtGui.QFormLayout()
        baseline_form.addRow('Baseline Type:', self.baseline_type_combo)

        # Threshold options
        threshold_options = QtGui.QStackedLayout()
        self.threshold_type_combo = QtGui.QComboBox()
        self.threshold_type_combo.addItem('Noise Based')
        self.threshold_type_combo.addItem('Absolute Change')
        self.threshold_type_combo.addItem('Percent Change')
        self.threshold_type_combo.activated.connect(threshold_options.setCurrentIndex)

        threshold_form = QtGui.QFormLayout()
        self.threshold_direction_combo = QtGui.QComboBox()
        self.threshold_direction_combo.addItems(('Both', 'Positive', 'Negative'))
        threshold_form.addRow('Threshold Direction:', self.threshold_direction_combo)
        threshold_form.addRow('Threshold Type:', self.threshold_type_combo)

        noise_based_options_layout = QtGui.QFormLayout()
        self.threshold_stdev_start = QtGui.QLineEdit()
        self.threshold_stdev_start.setValidator(QtGui.QDoubleValidator(-9999, 9999, 4, self.threshold_stdev_start))
        self.threshold_stdev_start.setText('5.0')
        noise_based_options_layout.addRow('Start StdDev:', self.threshold_stdev_start)
        self.threshold_stdev_end = QtGui.QLineEdit()
        self.threshold_stdev_end.setValidator(QtGui.QDoubleValidator(-9999, 9999, 4, self.threshold_stdev_end))
        self.threshold_stdev_end.setText('1.0')
        noise_based_options_layout.addRow('End StdDev:', self.threshold_stdev_end)

        absolute_drop_options_layout = QtGui.QFormLayout()
        self.absolute_change_start_edit = QtGui.QLineEdit()
        self.absolute_change_start_edit.setValidator(
            QtGui.QDoubleValidator(-9999, 9999, 9, self.absolute_change_start_edit))
        self.absolute_change_start_edit.setText('0.1')
        absolute_drop_options_layout.addRow('Absolute Change Start [uA]:', self.absolute_change_start_edit)
        self.absolute_change_end_edit = QtGui.QLineEdit()
        self.absolute_change_end_edit.setValidator(
            QtGui.QDoubleValidator(-9999, 9999, 9, self.absolute_change_end_edit))
        self.absolute_change_end_edit.setText('0.0')
        absolute_drop_options_layout.addRow('Absolute Change End [uA]:', self.absolute_change_end_edit)

        percentage_change_options_layout = QtGui.QFormLayout()
        self.percentage_change_start_edit = QtGui.QLineEdit()
        self.percentage_change_start_edit.setValidator(
            QtGui.QDoubleValidator(0, 9999, 5, self.percentage_change_start_edit))
        self.percentage_change_start_edit.setText('10.0')
        percentage_change_options_layout.addRow('Percent Change Start:', self.percentage_change_start_edit)
        self.percentage_change_end_edit = QtGui.QLineEdit()
        self.percentage_change_end_edit.setValidator(
            QtGui.QDoubleValidator(0, 9999, 5, self.percentage_change_end_edit))
        self.percentage_change_end_edit.setText('0.0')
        percentage_change_options_layout.addRow('Percent Change End:', self.percentage_change_end_edit)

        noise_based_options = QtGui.QWidget()
        noise_based_options.setLayout(noise_based_options_layout)

        absolute_drop_options = QtGui.QWidget()
        absolute_drop_options.setLayout(absolute_drop_options_layout)

        percentage_change_options_widget = QtGui.QWidget()
        percentage_change_options_widget.setLayout(percentage_change_options_layout)

        threshold_options.addWidget(noise_based_options)
        threshold_options.addWidget(absolute_drop_options)
        threshold_options.addWidget(percentage_change_options_widget)

        hbox = QtGui.QHBoxLayout()

        for w in [self.analyze_button, self.stop_analyze_button]:
            hbox.addWidget(w)
            hbox.setAlignment(w, QtCore.Qt.AlignVCenter)

        # Left vertical layout with settings
        vbox_left = QtGui.QVBoxLayout()
        vbox_left.addLayout(list_form_layout)
        vbox_left.addLayout(fixed_analysis_options)
        vbox_left.addLayout(baseline_form)
        vbox_left.addLayout(baseline_options)
        vbox_left.addLayout(threshold_form)
        vbox_left.addLayout(threshold_options)
        vbox_left.addWidget(self.debug_check_box)
        vbox_left.addLayout(hbox)

        vbox_left_widget = QtGui.QWidget()
        vbox_left_widget.setLayout(vbox_left)

        scroll_area.setWidget(vbox_left_widget)

        return scroll_area
示例#9
0
    def _create_event_viewer_plot_widget(self):
        wig = pg.GraphicsLayoutWidget()
        wig2 = pg.GraphicsLayoutWidget()

        # Main plot
        self.eventview_plotwid = MyPlotItem(title='Current Trace', name='Plot')
        wig.addItem(self.eventview_plotwid)
        self.eventview_plotwid.enableAutoRange('xy', False)

        wig.nextRow()
        # Create Qwt plot for concatenated events
        self.plot_concatevents = wig.addPlot(title='Concatenated Events', name='Concat')

        self.eventviewer_plots = []

        # Now add 9 plots to view events in
        for i in xrange(3):
            wig2.nextRow()
            for j in xrange(3):
                plot = wig2.addPlot(title='Event ' + str(i * 3 + j), name='Single' + str(i * 3 + j))
                self.eventviewer_plots.append(plot)

                # Tool bar for main plot.  Contains zoom button and different checkboxes
                #         self.plotToolBar = PlotToolBar(self)
                #         self.addToolBar(self.plotToolBar)

        event_select_toolbar = QtGui.QToolBar(self)
        if self._parent is not None:
            self._parent.addToolBar(event_select_toolbar)

        button_previous = QtGui.QPushButton(event_select_toolbar)
        button_previous.setText("Previous")
        button_previous.clicked.connect(self.previous_clicked)
        event_select_toolbar.addWidget(button_previous)

        self.event_display_edit = QtGui.QLineEdit()
        self.event_display_edit.setText('0')
        self.event_display_edit.setMaxLength(1)
        self.event_display_edit.setValidator(QtGui.QIntValidator(0, 0, self.event_display_edit))
        self.event_display_edit.textChanged.connect(self._event_display_edit_on_change)
        event_select_toolbar.addWidget(self.event_display_edit)

        self.event_count_text = QtGui.QLabel()
        self.event_count_text.setText('/' + str(0))
        event_select_toolbar.addWidget(self.event_count_text)

        button_previous = QtGui.QPushButton(event_select_toolbar)
        button_previous.setText("Next")
        button_previous.clicked.connect(self.next_clicked)
        event_select_toolbar.addWidget(button_previous)

        frame = QtGui.QSplitter()
        frame.setOrientation(QtCore.Qt.Vertical)
        frame.addWidget(wig)
        frame.addWidget(wig2)

        event_finder_plots_layout = LayoutWidget()
        # event_finder_plots_layout.addWidget(self.plotToolBar, row=1, col=0, colspan=3)
        event_finder_plots_layout.addWidget(frame, row=2, col=0, colspan=3)
        event_finder_plots_layout.addWidget(event_select_toolbar, row=5, col=0, colspan=3)

        return event_finder_plots_layout
示例#10
0
class EventViewingTab(_ThreadManager, QtGui.QSplitter):
    """
    A QtGui.Splitter that contains a file opener list on the left and plots on the right.
    """

    def __init__(self, parent=None):
        """
        :param PySide.QtGui.QMainWindow parent: Parent main window (optional).
        """
        super(EventViewingTab, self).__init__(parent)

        self._parent = parent

        self.open_directory_changed_callback = None

        options = self._create_event_viewer_options()
        plots = self._create_event_viewer_plot_widget()

        # Put everything in baseline_filter_parameter scroll area
        scroll_plots = QtGui.QScrollArea()
        scroll_plots.setWidgetResizable(True)

        scroll_plots.setWidget(plots)

        self.addWidget(options)
        self.addWidget(scroll_plots)

    def open_event_databases(self, file_names=None):
        """
        Adds the files to the list widget.

        :param ListType<StringType> file_names: (Optional) List of file names to be added to the list widget. If not
                                                included, then a QtGui.QFileDialog will be opened to select files.
        """
        if file_names is None:
            file_names = QtGui.QFileDialog.getOpenFileNames(self, 'Open event database', '.', '*.h5')[0]

        if len(file_names) > 0:
            self.eventview_list_widget.clear()
        else:
            return
        for w in file_names:
            item = FileListItem(w)
            self.eventview_list_widget.addItem(item)

    def _create_event_viewer_options(self):
        scroll_area = QtGui.QScrollArea()
        scroll_area.setWidgetResizable(True)

        # Create baseline_filter_parameter list for files want to analyze
        self.eventview_list_widget = QtGui.QListWidget()
        self.eventview_list_widget.itemDoubleClicked.connect(self._on_eventview_file_item_doubleclick)
        self.eventview_list_widget.setMaximumHeight(100)

        fixed_analysis_options = QtGui.QFormLayout()
        fixed_analysis_options.addRow('Event Databases:', self.eventview_list_widget)

        vbox_left = QtGui.QVBoxLayout()
        vbox_left.addLayout(fixed_analysis_options)

        vbox_left_widget = QtGui.QWidget()
        vbox_left_widget.setLayout(vbox_left)

        scroll_area.setWidget(vbox_left_widget)

        return scroll_area

    def _on_eventview_file_item_doubleclick(self, item):
        """
        """
        self.event_view_item = item

        h5file = eD.open_file(item.get_file_name())

        event_count = h5file.get_event_count()

        if h5file.is_debug():
            self.plot_debug(h5file)

        h5file.close()

        self.event_display_edit.setMaxLength(int(event_count / 10) + 1)
        self.event_display_edit.setValidator(QtGui.QIntValidator(1, event_count, self.event_display_edit))
        self.event_count_text.setText('/' + str(event_count))
        self.event_display_edit.setText('')
        self.event_display_edit.setText('1')

    def plot_debug(self, event_database):
        """
        Plots the data, baseline, and thresholds of the debug group in the event_database, if they exist,
        in the main plot.

        :param event_database: An already open\
                :class:`EventDatabase <pypore.filetypes.event_database.EventDatabase>`.
        """
        if not event_database.is_debug():
            return

        self.eventview_plotwid.clear()

        sample_rate = event_database.get_sample_rate()

        # TODO remove the step_size.
        step_size = 1000

        data = event_database.root.debug.data[0][::step_size]

        data_size = data.size
        times = np.linspace(0, data_size *1.0/sample_rate, data_size)
        item = PathItem(times, data)
        item.setPen(pg.mkPen('w'))
        self.eventview_plotwid.addItem(item)

        baseline = event_database.root.debug.baseline[0][::step_size]
        item = PathItem(times, baseline)
        item.setPen(pg.mkPen('y'))
        self.eventview_plotwid.addItem(item)

        threshold_p = event_database.root.debug.threshold_positive[0][::step_size]
        item = PathItem(times, threshold_p)
        item.setPen(pg.mkPen('g'))
        self.eventview_plotwid.addItem(item)

        threshold_n = event_database.root.debug.threshold_negative[0][::step_size]
        item = PathItem(times, threshold_n)
        item.setPen(pg.mkPen('g'))
        self.eventview_plotwid.addItem(item)

    def plot_single_events(self, event):
        """
        Plots the event on the plot with
        """
        h5file = eD.open_file(self.event_view_item.get_file_name(), mode='r')

        event_count = h5file.get_event_count()

        for i in xrange(3):
            for j in xrange(3):
                pos = 3 * i + j
                if pos + event >= event_count or pos + event < 0:
                    self.eventviewer_plots[pos].clear()
                    self.eventviewer_plots[pos].setTitle('')
                else:
                    self.plot_single_event(h5file, event + pos, self.eventviewer_plots[pos])
                    self.eventviewer_plots[pos].setTitle('Event ' + str(event + pos + 1))

        h5file.close()

    def plot_single_event(self, h5file, position, plot):
        sample_rate = h5file.get_sample_rate()
        row = h5file.get_event_row(position)
        array_row = row['array_row']
        event_length = row['event_length']
        raw_points_per_side = row['raw_points_per_side']

        raw_data = h5file.get_raw_data_at(array_row)

        n = len(raw_data)

        times = np.linspace(0.0, 1.0 * n / sample_rate, n)

        plot.clear()
        plot.plot(times, raw_data)
        # plot the event points in yellow
        plot.plot(times[raw_points_per_side:raw_points_per_side + event_length],
                  raw_data[raw_points_per_side:raw_points_per_side + event_length], pen='y')

        # Plot the cusum levels
        n_levels = row['n_levels']
        baseline = row['baseline']
        # left, start-1, start,
        levels = h5file.get_levels_at(array_row)
        indices = h5file.get_level_lengths_at(array_row)

        level_times = np.zeros(2 * n_levels + 4)
        level_values = np.zeros(2 * n_levels + 4)

        level_times[1] = 1.0 * (raw_points_per_side - 1) / sample_rate
        level_values[0] = level_values[1] = baseline
        i = 0
        length = 0
        for i in xrange(n_levels):
            level_times[2 * i + 2] = times[raw_points_per_side] + 1.0 * length / sample_rate
            level_values[2 * i + 2] = levels[i]
            level_times[2 * i + 3] = times[raw_points_per_side] + 1.0 * (length + indices[i]) / sample_rate
            level_values[2 * i + 3] = levels[i]
            length += indices[i]
        i += 1
        level_times[2 * i + 2] = times[raw_points_per_side + event_length]
        level_times[2 * i + 3] = times[n - 1]
        level_values[2 * i + 2] = level_values[2 * i + 3] = baseline

        plot.plot(level_times, level_values, pen='g')

    def previous_clicked(self):
        self.move_event_display_by(-1 * len(self.eventviewer_plots))

    def next_clicked(self):
        self.move_event_display_by(len(self.eventviewer_plots))

    def move_event_display_by(self, count):
        """
        Changes the event displayed on the event display plot to
        current value + count
        """
        h5_event_count = 0
        try:
            h5file = eD.open_file(self.event_view_item.get_file_name())
            h5_event_count = h5file.get_event_count()
            h5file.close()
        except:
            return
        try:
            event_count = int(self.event_display_edit.text())
            if 0 < event_count + count <= h5_event_count:
                self.event_display_edit.setText(str(event_count + count))
        except ValueError:
            # if we can't parse the event display text but there are events,
            # just set to zero
            if h5_event_count > 0:
                self.event_display_edit.setText('1')

    def _event_display_edit_on_change(self, text):
        if len(text) < 1:
            return
        position = int(self.event_display_edit.text())
        self.plot_single_events(position - 1)
        return

    def _create_event_viewer_plot_widget(self):
        wig = pg.GraphicsLayoutWidget()
        wig2 = pg.GraphicsLayoutWidget()

        # Main plot
        self.eventview_plotwid = MyPlotItem(title='Current Trace', name='Plot')
        wig.addItem(self.eventview_plotwid)
        self.eventview_plotwid.enableAutoRange('xy', False)

        wig.nextRow()
        # Create Qwt plot for concatenated events
        self.plot_concatevents = wig.addPlot(title='Concatenated Events', name='Concat')

        self.eventviewer_plots = []

        # Now add 9 plots to view events in
        for i in xrange(3):
            wig2.nextRow()
            for j in xrange(3):
                plot = wig2.addPlot(title='Event ' + str(i * 3 + j), name='Single' + str(i * 3 + j))
                self.eventviewer_plots.append(plot)

                # Tool bar for main plot.  Contains zoom button and different checkboxes
                #         self.plotToolBar = PlotToolBar(self)
                #         self.addToolBar(self.plotToolBar)

        event_select_toolbar = QtGui.QToolBar(self)
        if self._parent is not None:
            self._parent.addToolBar(event_select_toolbar)

        button_previous = QtGui.QPushButton(event_select_toolbar)
        button_previous.setText("Previous")
        button_previous.clicked.connect(self.previous_clicked)
        event_select_toolbar.addWidget(button_previous)

        self.event_display_edit = QtGui.QLineEdit()
        self.event_display_edit.setText('0')
        self.event_display_edit.setMaxLength(1)
        self.event_display_edit.setValidator(QtGui.QIntValidator(0, 0, self.event_display_edit))
        self.event_display_edit.textChanged.connect(self._event_display_edit_on_change)
        event_select_toolbar.addWidget(self.event_display_edit)

        self.event_count_text = QtGui.QLabel()
        self.event_count_text.setText('/' + str(0))
        event_select_toolbar.addWidget(self.event_count_text)

        button_previous = QtGui.QPushButton(event_select_toolbar)
        button_previous.setText("Next")
        button_previous.clicked.connect(self.next_clicked)
        event_select_toolbar.addWidget(button_previous)

        frame = QtGui.QSplitter()
        frame.setOrientation(QtCore.Qt.Vertical)
        frame.addWidget(wig)
        frame.addWidget(wig2)

        event_finder_plots_layout = LayoutWidget()
        # event_finder_plots_layout.addWidget(self.plotToolBar, row=1, col=0, colspan=3)
        event_finder_plots_layout.addWidget(frame, row=2, col=0, colspan=3)
        event_finder_plots_layout.addWidget(event_select_toolbar, row=5, col=0, colspan=3)

        return event_finder_plots_layout
示例#11
0
    def test_plot_clear(self):
        # tests to make sure that when the clear flag is passed
        # to plots, that clear works as expected
        plot_item = MyPlotItem()
        x = [1]
        y = [1]
        plot = PlotCurveItem(x, y)
        plot_item.addItem(plot)

        plot2 = PlotCurveItem(y, x)
        plot_item.add_event_item(plot2)

        plot5 = PlotCurveItem(y, x)
        plot_item.addItem(plot5)

        x = y = np.zeros(2)
        plot3 = PathItem(x, y)
        plot_item.add_event_item(plot3)

        plot4 = plot_item.plot(clear=True)

        # Test that all of the plots have been cleared,
        # and that plot4 was added successfully
        self.assertEqual(len(plot_item.listDataItems()), 1)
        self.assertEqual(len(plot_item._myEventItemList), 0)
        self.assertEqual(len(plot_item._myItemList), 1)
        self.assertEqual(plot_item.listDataItems()[0], plot4)
        self.assertEqual(plot_item._myItemList[0], plot4)
示例#12
0
class EventFindingTab(BaseQSplitterDataFile):
    """
    A QtGui.QSplitter that contains event finding options on the left and a plot on the right.
    """
    def __init__(self, parent=None):
        """
        :param PySide.QtGui.QMainWindow parent: Parent main window (optional).
        """
        # Define the instance elements
        self.events = []  # holds the events from the most recent analysis run
        self.plot_widget = None
        self.p1 = None
        self.plot_tool_bar = None
        super(EventFindingTab, self).__init__(parent)

    def get_current_analysis_parameters(self):
        """
        Reads the current analysis parameters from the gui and returns the results in a dictionary. Returns
        dictionary entry 'error' with text of the error if one exists.

        :returns: DictType -- the event analysis parameters. These include:

            - 'error' - Text if there was an error

            OR

            - 'min_event_length'
            - 'max_event_length'
            - 'baseline_strategy'
            - 'baseline_filter_parameter'
            - 'baseline_current'

            etc...
        """

        threshold_type = baseline_type = None
        # Get Min_event length in microseconds
        try:
            min_event_length = float(self.min_event_length_edit.text())
        except ValueError:
            return {
                'error':
                'Could not read float from Min Event Length text box.  Please fix.'
            }
        # Get Max Event Length in microseconds
        try:
            max_event_length = float(self.max_event_length_edit.text())
        except ValueError:
            return {
                'error':
                'Could not read float from Max Event Length text box.  Please fix.'
            }
        if min_event_length >= max_event_length:
            return {
                'error':
                'Min Event Length is greater than Max Event Length.  Please fix.'
            }

        baseline_type_str = str(self.baseline_type_combo.currentText())
        if baseline_type_str == 'Adaptive':
            try:
                baseline_filter_parameter = float(
                    self.baseline_filter_parameter_edit.text())
            except ValueError:
                return {
                    'error':
                    'Could not read float from Baseline Filter Parameter text box.  Please fix.'
                }
            try:
                variance_filter_parameter = float(
                    self.variance_filter_parameter_edit.text())
            except ValueError:
                return {
                    'error':
                    'Could not read float from Variance Filter Parameter text box.  Please fix.'
                }
            baseline_type = AdaptiveBaselineStrategy(
                baseline_filter_parameter=baseline_filter_parameter,
                variance_filter_parameter=variance_filter_parameter)
        elif baseline_type_str == 'Fixed':
            try:
                baseline_current = float(self.baseline_current_edit.text())
                baseline_type = FixedBaselineStrategy(
                    baseline=baseline_current)
            except ValueError:
                return {
                    'error':
                    'Could not read float from Baseline Current text box.  Please fix.'
                }

        threshold_direction_str = str(
            self.threshold_direction_combo.currentText())
        detect_positive_events = (threshold_direction_str
                                  in ('Positive', 'Both'))
        detect_negative_events = (threshold_direction_str
                                  in ('Negative', 'Both'))

        threshold_type_str = str(self.threshold_type_combo.currentText())
        if threshold_type_str == 'Noise Based':
            try:
                start_stddev = float(self.threshold_stdev_start.text())
            except ValueError:
                return {
                    'error':
                    'Could not read float from Start StdDev text box.  Please fix.'
                }
            try:
                end_stddev = float(self.threshold_stdev_end.text())
            except ValueError:
                return {
                    'error':
                    'Could not read float from End StdDev text box.  Please fix.'
                }
            threshold_type = NoiseBasedThresholdStrategy(
                start_std_dev=start_stddev, end_std_dev=end_stddev)
        elif threshold_type_str == 'Absolute Change':
            try:
                absolute_change_start = float(
                    self.absolute_change_start_edit.text())
            except ValueError:
                return {
                    'error':
                    'Could not read float from Absolute Change Start.  Please fix.'
                }
            try:
                absolute_change_end = float(
                    self.absolute_change_end_edit.text())
            except ValueError:
                return {
                    'error':
                    'Could not read float from Absolute Change End.  Please fix.'
                }
            threshold_type = AbsoluteChangeThresholdStrategy(
                change_start=absolute_change_start,
                change_end=absolute_change_end)
        elif threshold_type_str == 'Percent Change':
            try:
                percent_change_start = float(
                    self.percentage_change_start_edit.text())
            except ValueError:
                return {
                    'error':
                    'Could not read float from Percent Change Start text box.  Please fix.'
                }
            try:
                percent_change_end = float(
                    self.percentage_change_end_edit.text())
            except ValueError:
                return {
                    'error':
                    'Could not read float from Percent Change End text box.  Please fix.'
                }
            threshold_type = PercentChangeThresholdStrategy(
                percent_change_start=percent_change_start,
                percent_change_end=percent_change_end)

        parameters = Parameters(min_event_length=min_event_length,
                                max_event_length=max_event_length,
                                detect_positive_events=detect_positive_events,
                                detect_negative_events=detect_negative_events,
                                baseline_strategy=baseline_type,
                                threshold_strategy=threshold_type)
        return parameters

    def plot_data(self, plot_options):
        """
        Plots waveform in datadict.
        :param DictType plot_options: Dictionary of plot options. Must contain:

        * Data at plot_options['data'][0]
        * sample_rate at plot_options['sample_rate']
        * plot_range at plot_options['plot_range']. This can be [start,stop], or
          'all' for 0:n.

        """
        # Read the first file, store data in dictionary
        data = plot_options['data'][0]
        sample_rate = plot_options['sample_rate']
        plot_range = plot_options['plot_range']

        n = len(data)
        # If problem with input, just plot all the data
        if plot_range == 'all' or len(
                plot_range) != 2 or plot_range[1] <= plot_range[0]:
            plot_range = [0, n]
        else:  # no problems!
            n = plot_range[1] - plot_range[0] + 1

        ts = 1 / sample_rate

        times = linspace(ts * plot_range[0], ts * plot_range[1], n)
        y_data = data[plot_range[0]:(plot_range[1] + 1)]

        self.plot_widget.clear_event_items()
        self.p1.setData(x=times, y=y_data)
        self.plot_widget.autoRange()
        self._dispatch_process_events()

    def _analyze_data_thread_callback(self, results):
        """
        Callback for updates from the AnalyzeDataThread.
        """
        if 'status_text' in results:
            self._dispatch_status_update(results['status_text'])
        if 'Events' in results:
            single_plot = False
            events = results['Events']
            if len(events) < 1:
                return
            elif len(self.events) < 1:
                # if this is our first time plotting events, include the single event plot!
                single_plot = True
            self.events += events
            self.event_display_edit.setMaxLength(
                int(len(self.events) / 10) + 1)
            self.event_display_edit.setValidator(
                QtGui.QIntValidator(1, len(self.events),
                                    self.event_display_edit))
            self.eventCountText.setText('/' + str(len(self.events)))
            if self.plot_tool_bar.is_plot_during_checked():
                self.plotEventsOnMainPlot(events)
                self.addEventsToConcatEventPlot(events)
            if single_plot:
                self.event_display_edit.setText('1')
            self._dispatch_process_events()
            self.analyzethread.readyForEvents = True
        if 'done' in results:
            if results['done']:
                self.stop_analyze_button.setEnabled(False)

    def _on_analyze(self):
        """
        Searches for events in the file that is currently highlighted in the files list.
        """
        selected_items = self.file_list_widget.selectedItems()
        if len(selected_items) > 0:
            curr_item = selected_items[0]
        else:
            return

        parameters = self.get_current_analysis_parameters()
        if isinstance(parameters, dict) and 'error' in parameters:
            self._dispatch_status_update(parameters['error'])
            return

        debug = self.debug_check_box.isChecked()

        # Clear the current events
        del self.events[:]
        # self.prev_concat_time = 0.

        file_names = [str(curr_item.get_file_name())]

        self._dispatch_status_update("Event Count: 0 Percent Done: 0")

        # Start analyzing data in new analyzethread.
        self.analyzethread = AnalyzeDataThread(file_names, parameters, debug)
        self.analyzethread.dataReady.connect(
            self._analyze_data_thread_callback)
        self.add_thread(self.analyzethread)
        self.analyzethread.start()

        self.stop_analyze_button.setEnabled(True)

    def _on_analyze_stop(self):
        """
        Called when the user clicks the Stop button.
        """
        self.clean_threads()
        self.stop_analyze_button.setEnabled(False)
        self._dispatch_status_update("Analyze aborted.")

    def _on_file_item_doubleclick(self, item):
        """
        Called when baseline_filter_parameter file list item is double clicked.
        Starts the plotting thread, which opens the file, parses data, then passes to plotData
        """
        # adding by emitting signal in different thread
        self._dispatch_status_update("Plotting...")
        decimates = self.plot_tool_bar.is_decimate_checked()
        thread = PlotThread(self.p1,
                            filename=str(item.get_file_name()),
                            decimate=decimates)
        thread.dataReady.connect(self._on_file_item_doubleclick_callback)
        self.add_thread(thread)
        thread.start()

    def _on_file_item_doubleclick_callback(self, results):
        if 'plot_options' in results:
            self.plot_data(results['plot_options'])
        if 'status_text' in results:
            self._dispatch_status_update(results['status_text'])
        if 'thread' in results:
            self.remove_thread(results['thread'])

    def _on_file_item_selection_changed(self):
        self.analyze_button.setEnabled(True)

    def _create_right_widget(self, parent=None):
        wig = pg.GraphicsLayoutWidget(parent)

        # Main plot
        self.plot_widget = MyPlotItem(title='Current Trace', name='Plot')
        wig.addItem(self.plot_widget)
        self.plot_widget.enableAutoRange('xy', False)
        self.p1 = self.plot_widget.plot(
        )  # create an empty plot curve to be filled later

        # Tool bar for main plot.  Contains zoom button and different checkboxes
        self.plot_tool_bar = PlotToolBar(self)
        if not parent is None:
            parent.addToolBar(self.plot_tool_bar)

        event_finder_plots_layout = LayoutWidget()
        event_finder_plots_layout.addWidget(self.plot_tool_bar,
                                            row=1,
                                            col=0,
                                            colspan=3)
        event_finder_plots_layout.addWidget(wig, row=2, col=0, colspan=3)

        return event_finder_plots_layout

    def open_data_files(self, file_names=None):
        are_files_opened = super(EventFindingTab,
                                 self).open_data_files(file_names)
        if are_files_opened:
            self.analyze_button.setEnabled(False)

    def _create_left_widget(self, parent=None):
        """
        Initializes everything in the options pane on the left side of the splitter.
        """
        scroll_area = QtGui.QScrollArea()
        scroll_area.setWidgetResizable(True)

        list_form_layout = super(EventFindingTab,
                                 self)._create_left_widget(parent)

        # Other GUI controls
        #
        self.analyze_button = QtGui.QPushButton("&Find Events")
        self.connect(self.analyze_button, QtCore.SIGNAL('clicked()'),
                     self._on_analyze)
        self.analyze_button.setEnabled(False)

        self.stop_analyze_button = QtGui.QPushButton("&Stop")
        self.connect(self.stop_analyze_button, QtCore.SIGNAL('clicked()'),
                     self._on_analyze_stop)
        self.stop_analyze_button.setEnabled(False)

        self.debug_check_box = QtGui.QCheckBox()
        self.debug_check_box.setChecked(False)
        self.debug_check_box.setText('Debug')
        self.debug_check_box.setToolTip(
            "When checked, extra data will be saved to the EventDatabase. This"
            " data includes the data, baseline, and thresholds at each datapoint."
        )

        # Analysis options
        self.min_event_length_edit = QtGui.QLineEdit()
        self.min_event_length_edit.setText('10.0')
        self.min_event_length_edit.setValidator(
            QtGui.QDoubleValidator(0, 1e12, 5, self.min_event_length_edit))
        self.max_event_length_edit = QtGui.QLineEdit()
        self.max_event_length_edit.setText('1000.0')
        self.max_event_length_edit.setValidator(
            QtGui.QDoubleValidator(0, 1e12, 5, self.max_event_length_edit))

        fixed_analysis_options = QtGui.QFormLayout()
        fixed_analysis_options.addRow('Min Event Length [us]:',
                                      self.min_event_length_edit)
        fixed_analysis_options.addRow('Max Event Length [us]:',
                                      self.max_event_length_edit)

        # Baseline options
        baseline_options = QtGui.QStackedLayout()
        self.baseline_type_combo = QtGui.QComboBox()
        self.baseline_type_combo.addItems(('Adaptive', 'Fixed'))
        self.baseline_type_combo.activated.connect(
            baseline_options.setCurrentIndex)

        adaptive_options_layout = QtGui.QFormLayout()
        self.baseline_filter_parameter_edit = QtGui.QLineEdit()
        self.baseline_filter_parameter_edit.setValidator(
            QtGui.QDoubleValidator(0, 10, 5,
                                   self.baseline_filter_parameter_edit))
        self.baseline_filter_parameter_edit.setText('0.93')
        adaptive_options_layout.addRow('Baseline Filter Parameter:',
                                       self.baseline_filter_parameter_edit)
        self.variance_filter_parameter_edit = QtGui.QLineEdit()
        self.variance_filter_parameter_edit.setValidator(
            QtGui.QDoubleValidator(0, 10, 5,
                                   self.variance_filter_parameter_edit))
        self.variance_filter_parameter_edit.setText('0.99')
        adaptive_options_layout.addRow('Variance Filter Parameter:',
                                       self.variance_filter_parameter_edit)
        # need to cast to widget to add to QStackedLayout
        adaptive_options_widget = QtGui.QWidget()
        adaptive_options_widget.setLayout(adaptive_options_layout)

        choose_baseline_btn = QtGui.QPushButton('Baseline:')
        choose_baseline_btn.setToolTip(
            'Click to choose the baseline from the plot.')

        fixed_options_layout = QtGui.QFormLayout()
        self.baseline_current_edit = QtGui.QLineEdit()
        self.baseline_current_edit.setValidator(
            QtGui.QDoubleValidator(-9999, 9999, 9, self.baseline_current_edit))
        self.baseline_current_edit.setText('0.0')
        fixed_options_layout.addRow(choose_baseline_btn,
                                    self.baseline_current_edit)
        fixed_options_widget = QtGui.QWidget()
        fixed_options_widget.setLayout(fixed_options_layout)

        baseline_options.addWidget(adaptive_options_widget)
        baseline_options.addWidget(fixed_options_widget)

        baseline_form = QtGui.QFormLayout()
        baseline_form.addRow('Baseline Type:', self.baseline_type_combo)

        # Threshold options
        threshold_options = QtGui.QStackedLayout()
        self.threshold_type_combo = QtGui.QComboBox()
        self.threshold_type_combo.addItem('Noise Based')
        self.threshold_type_combo.addItem('Absolute Change')
        self.threshold_type_combo.addItem('Percent Change')
        self.threshold_type_combo.activated.connect(
            threshold_options.setCurrentIndex)

        threshold_form = QtGui.QFormLayout()
        self.threshold_direction_combo = QtGui.QComboBox()
        self.threshold_direction_combo.addItems(
            ('Both', 'Positive', 'Negative'))
        threshold_form.addRow('Threshold Direction:',
                              self.threshold_direction_combo)
        threshold_form.addRow('Threshold Type:', self.threshold_type_combo)

        noise_based_options_layout = QtGui.QFormLayout()
        self.threshold_stdev_start = QtGui.QLineEdit()
        self.threshold_stdev_start.setValidator(
            QtGui.QDoubleValidator(-9999, 9999, 4, self.threshold_stdev_start))
        self.threshold_stdev_start.setText('5.0')
        noise_based_options_layout.addRow('Start StdDev:',
                                          self.threshold_stdev_start)
        self.threshold_stdev_end = QtGui.QLineEdit()
        self.threshold_stdev_end.setValidator(
            QtGui.QDoubleValidator(-9999, 9999, 4, self.threshold_stdev_end))
        self.threshold_stdev_end.setText('1.0')
        noise_based_options_layout.addRow('End StdDev:',
                                          self.threshold_stdev_end)

        absolute_drop_options_layout = QtGui.QFormLayout()
        self.absolute_change_start_edit = QtGui.QLineEdit()
        self.absolute_change_start_edit.setValidator(
            QtGui.QDoubleValidator(-9999, 9999, 9,
                                   self.absolute_change_start_edit))
        self.absolute_change_start_edit.setText('0.1')
        absolute_drop_options_layout.addRow('Absolute Change Start [uA]:',
                                            self.absolute_change_start_edit)
        self.absolute_change_end_edit = QtGui.QLineEdit()
        self.absolute_change_end_edit.setValidator(
            QtGui.QDoubleValidator(-9999, 9999, 9,
                                   self.absolute_change_end_edit))
        self.absolute_change_end_edit.setText('0.0')
        absolute_drop_options_layout.addRow('Absolute Change End [uA]:',
                                            self.absolute_change_end_edit)

        percentage_change_options_layout = QtGui.QFormLayout()
        self.percentage_change_start_edit = QtGui.QLineEdit()
        self.percentage_change_start_edit.setValidator(
            QtGui.QDoubleValidator(0, 9999, 5,
                                   self.percentage_change_start_edit))
        self.percentage_change_start_edit.setText('10.0')
        percentage_change_options_layout.addRow(
            'Percent Change Start:', self.percentage_change_start_edit)
        self.percentage_change_end_edit = QtGui.QLineEdit()
        self.percentage_change_end_edit.setValidator(
            QtGui.QDoubleValidator(0, 9999, 5,
                                   self.percentage_change_end_edit))
        self.percentage_change_end_edit.setText('0.0')
        percentage_change_options_layout.addRow(
            'Percent Change End:', self.percentage_change_end_edit)

        noise_based_options = QtGui.QWidget()
        noise_based_options.setLayout(noise_based_options_layout)

        absolute_drop_options = QtGui.QWidget()
        absolute_drop_options.setLayout(absolute_drop_options_layout)

        percentage_change_options_widget = QtGui.QWidget()
        percentage_change_options_widget.setLayout(
            percentage_change_options_layout)

        threshold_options.addWidget(noise_based_options)
        threshold_options.addWidget(absolute_drop_options)
        threshold_options.addWidget(percentage_change_options_widget)

        hbox = QtGui.QHBoxLayout()

        for w in [self.analyze_button, self.stop_analyze_button]:
            hbox.addWidget(w)
            hbox.setAlignment(w, QtCore.Qt.AlignVCenter)

        # Left vertical layout with settings
        vbox_left = QtGui.QVBoxLayout()
        vbox_left.addLayout(list_form_layout)
        vbox_left.addLayout(fixed_analysis_options)
        vbox_left.addLayout(baseline_form)
        vbox_left.addLayout(baseline_options)
        vbox_left.addLayout(threshold_form)
        vbox_left.addLayout(threshold_options)
        vbox_left.addWidget(self.debug_check_box)
        vbox_left.addLayout(hbox)

        vbox_left_widget = QtGui.QWidget()
        vbox_left_widget.setLayout(vbox_left)

        scroll_area.setWidget(vbox_left_widget)

        return scroll_area
示例#13
0
    def _create_event_viewer_plot_widget(self):
        wig = pg.GraphicsLayoutWidget()
        wig2 = pg.GraphicsLayoutWidget()

        # Main plot
        self.eventview_plotwid = MyPlotItem(title='Current Trace', name='Plot')
        wig.addItem(self.eventview_plotwid)
        self.eventview_plotwid.enableAutoRange('xy', False)

        wig.nextRow()
        # Create Qwt plot for concatenated events
        self.plot_concatevents = wig.addPlot(title='Concatenated Events',
                                             name='Concat')

        self.eventviewer_plots = []

        # Now add 9 plots to view events in
        for i in xrange(3):
            wig2.nextRow()
            for j in xrange(3):
                plot = wig2.addPlot(title='Event ' + str(i * 3 + j),
                                    name='Single' + str(i * 3 + j))
                self.eventviewer_plots.append(plot)

                # Tool bar for main plot.  Contains zoom button and different checkboxes
                #         self.plotToolBar = PlotToolBar(self)
                #         self.addToolBar(self.plotToolBar)

        event_select_toolbar = QtGui.QToolBar(self)
        if self._parent is not None:
            self._parent.addToolBar(event_select_toolbar)

        button_previous = QtGui.QPushButton(event_select_toolbar)
        button_previous.setText("Previous")
        button_previous.clicked.connect(self.previous_clicked)
        event_select_toolbar.addWidget(button_previous)

        self.event_display_edit = QtGui.QLineEdit()
        self.event_display_edit.setText('0')
        self.event_display_edit.setMaxLength(1)
        self.event_display_edit.setValidator(
            QtGui.QIntValidator(0, 0, self.event_display_edit))
        self.event_display_edit.textChanged.connect(
            self._event_display_edit_on_change)
        event_select_toolbar.addWidget(self.event_display_edit)

        self.event_count_text = QtGui.QLabel()
        self.event_count_text.setText('/' + str(0))
        event_select_toolbar.addWidget(self.event_count_text)

        button_previous = QtGui.QPushButton(event_select_toolbar)
        button_previous.setText("Next")
        button_previous.clicked.connect(self.next_clicked)
        event_select_toolbar.addWidget(button_previous)

        frame = QtGui.QSplitter()
        frame.setOrientation(QtCore.Qt.Vertical)
        frame.addWidget(wig)
        frame.addWidget(wig2)

        event_finder_plots_layout = LayoutWidget()
        # event_finder_plots_layout.addWidget(self.plotToolBar, row=1, col=0, colspan=3)
        event_finder_plots_layout.addWidget(frame, row=2, col=0, colspan=3)
        event_finder_plots_layout.addWidget(event_select_toolbar,
                                            row=5,
                                            col=0,
                                            colspan=3)

        return event_finder_plots_layout
示例#14
0
class EventViewingTab(_ThreadManager, QtGui.QSplitter):
    """
    A QtGui.Splitter that contains a file opener list on the left and plots on the right.
    """
    def __init__(self, parent=None):
        """
        :param PySide.QtGui.QMainWindow parent: Parent main window (optional).
        """
        super(EventViewingTab, self).__init__(parent)

        self._parent = parent

        self.open_directory_changed_callback = None

        options = self._create_event_viewer_options()
        plots = self._create_event_viewer_plot_widget()

        # Put everything in baseline_filter_parameter scroll area
        scroll_plots = QtGui.QScrollArea()
        scroll_plots.setWidgetResizable(True)

        scroll_plots.setWidget(plots)

        self.addWidget(options)
        self.addWidget(scroll_plots)

    def open_event_databases(self, file_names=None):
        """
        Adds the files to the list widget.

        :param ListType<StringType> file_names: (Optional) List of file names to be added to the list widget. If not
                                                included, then a QtGui.QFileDialog will be opened to select files.
        """
        if file_names is None:
            file_names = QtGui.QFileDialog.getOpenFileNames(
                self, 'Open event database', '.', '*.h5')[0]

        if len(file_names) > 0:
            self.eventview_list_widget.clear()
        else:
            return
        for w in file_names:
            item = FileListItem(w)
            self.eventview_list_widget.addItem(item)

    def _create_event_viewer_options(self):
        scroll_area = QtGui.QScrollArea()
        scroll_area.setWidgetResizable(True)

        # Create baseline_filter_parameter list for files want to analyze
        self.eventview_list_widget = QtGui.QListWidget()
        self.eventview_list_widget.itemDoubleClicked.connect(
            self._on_eventview_file_item_doubleclick)
        self.eventview_list_widget.setMaximumHeight(100)

        fixed_analysis_options = QtGui.QFormLayout()
        fixed_analysis_options.addRow('Event Databases:',
                                      self.eventview_list_widget)

        vbox_left = QtGui.QVBoxLayout()
        vbox_left.addLayout(fixed_analysis_options)

        vbox_left_widget = QtGui.QWidget()
        vbox_left_widget.setLayout(vbox_left)

        scroll_area.setWidget(vbox_left_widget)

        return scroll_area

    def _on_eventview_file_item_doubleclick(self, item):
        """
        """
        self.event_view_item = item

        h5file = eD.open_file(item.get_file_name())

        event_count = h5file.get_event_count()

        if h5file.is_debug():
            self.plot_debug(h5file)

        h5file.close()

        self.event_display_edit.setMaxLength(int(event_count / 10) + 1)
        self.event_display_edit.setValidator(
            QtGui.QIntValidator(1, event_count, self.event_display_edit))
        self.event_count_text.setText('/' + str(event_count))
        self.event_display_edit.setText('')
        self.event_display_edit.setText('1')

    def plot_debug(self, event_database):
        """
        Plots the data, baseline, and thresholds of the debug group in the event_database, if they exist,
        in the main plot.

        :param event_database: An already open\
                :class:`EventDatabase <pypore.filetypes.event_database.EventDatabase>`.
        """
        if not event_database.is_debug():
            return

        self.eventview_plotwid.clear()

        sample_rate = event_database.get_sample_rate()

        # TODO remove the step_size.
        step_size = 1000

        data = event_database.root.debug.data[0][::step_size]

        data_size = data.size
        times = np.linspace(0, data_size * 1.0 / sample_rate, data_size)
        item = PathItem(times, data)
        item.setPen(pg.mkPen('w'))
        self.eventview_plotwid.addItem(item)

        baseline = event_database.root.debug.baseline[0][::step_size]
        item = PathItem(times, baseline)
        item.setPen(pg.mkPen('y'))
        self.eventview_plotwid.addItem(item)

        threshold_p = event_database.root.debug.threshold_positive[
            0][::step_size]
        item = PathItem(times, threshold_p)
        item.setPen(pg.mkPen('g'))
        self.eventview_plotwid.addItem(item)

        threshold_n = event_database.root.debug.threshold_negative[
            0][::step_size]
        item = PathItem(times, threshold_n)
        item.setPen(pg.mkPen('g'))
        self.eventview_plotwid.addItem(item)

    def plot_single_events(self, event):
        """
        Plots the event on the plot with
        """
        h5file = eD.open_file(self.event_view_item.get_file_name(), mode='r')

        event_count = h5file.get_event_count()

        for i in xrange(3):
            for j in xrange(3):
                pos = 3 * i + j
                if pos + event >= event_count or pos + event < 0:
                    self.eventviewer_plots[pos].clear()
                    self.eventviewer_plots[pos].setTitle('')
                else:
                    self.plot_single_event(h5file, event + pos,
                                           self.eventviewer_plots[pos])
                    self.eventviewer_plots[pos].setTitle('Event ' +
                                                         str(event + pos + 1))

        h5file.close()

    def plot_single_event(self, h5file, position, plot):
        sample_rate = h5file.get_sample_rate()
        row = h5file.get_event_row(position)
        event_length = row['event_length']
        raw_points_per_side = row['raw_points_per_side']

        raw_data = h5file.get_raw_data_at(position)

        n = len(raw_data)

        times = np.linspace(0.0, 1.0 * n / sample_rate, n)

        plot.clear()
        plot.plot(times, raw_data)
        # plot the event points in yellow
        plot.plot(
            times[raw_points_per_side:raw_points_per_side + event_length],
            raw_data[raw_points_per_side:raw_points_per_side + event_length],
            pen='y')

        # Plot the cusum levels
        n_levels = row['n_levels']
        baseline = row['baseline']
        # left, start-1, start,
        levels = h5file.get_levels_at(position)
        indices = h5file.get_level_lengths_at(position)

        level_times = np.zeros(2 * n_levels + 4)
        level_values = np.zeros(2 * n_levels + 4)

        level_times[1] = 1.0 * (raw_points_per_side - 1) / sample_rate
        level_values[0] = level_values[1] = baseline
        i = 0
        length = 0
        for i in xrange(n_levels):
            level_times[
                2 * i +
                2] = times[raw_points_per_side] + 1.0 * length / sample_rate
            level_values[2 * i + 2] = levels[i]
            level_times[2 * i + 3] = times[raw_points_per_side] + 1.0 * (
                length + indices[i]) / sample_rate
            level_values[2 * i + 3] = levels[i]
            length += indices[i]
        i += 1
        level_times[2 * i + 2] = times[raw_points_per_side + event_length]
        level_times[2 * i + 3] = times[n - 1]
        level_values[2 * i + 2] = level_values[2 * i + 3] = baseline

        plot.plot(level_times, level_values, pen='g')

    def previous_clicked(self):
        self.move_event_display_by(-1 * len(self.eventviewer_plots))

    def next_clicked(self):
        self.move_event_display_by(len(self.eventviewer_plots))

    def move_event_display_by(self, count):
        """
        Changes the event displayed on the event display plot to
        current value + count
        """
        h5_event_count = 0
        try:
            h5file = eD.open_file(self.event_view_item.get_file_name())
            h5_event_count = h5file.get_event_count()
            h5file.close()
        except:
            return
        try:
            event_count = int(self.event_display_edit.text())
            if 0 < event_count + count <= h5_event_count:
                self.event_display_edit.setText(str(event_count + count))
        except ValueError:
            # if we can't parse the event display text but there are events,
            # just set to zero
            if h5_event_count > 0:
                self.event_display_edit.setText('1')

    def _event_display_edit_on_change(self, text):
        if len(text) < 1:
            return
        position = int(self.event_display_edit.text())
        self.plot_single_events(position - 1)
        return

    def _create_event_viewer_plot_widget(self):
        wig = pg.GraphicsLayoutWidget()
        wig2 = pg.GraphicsLayoutWidget()

        # Main plot
        self.eventview_plotwid = MyPlotItem(title='Current Trace', name='Plot')
        wig.addItem(self.eventview_plotwid)
        self.eventview_plotwid.enableAutoRange('xy', False)

        wig.nextRow()
        # Create Qwt plot for concatenated events
        self.plot_concatevents = wig.addPlot(title='Concatenated Events',
                                             name='Concat')

        self.eventviewer_plots = []

        # Now add 9 plots to view events in
        for i in xrange(3):
            wig2.nextRow()
            for j in xrange(3):
                plot = wig2.addPlot(title='Event ' + str(i * 3 + j),
                                    name='Single' + str(i * 3 + j))
                self.eventviewer_plots.append(plot)

                # Tool bar for main plot.  Contains zoom button and different checkboxes
                #         self.plotToolBar = PlotToolBar(self)
                #         self.addToolBar(self.plotToolBar)

        event_select_toolbar = QtGui.QToolBar(self)
        if self._parent is not None:
            self._parent.addToolBar(event_select_toolbar)

        button_previous = QtGui.QPushButton(event_select_toolbar)
        button_previous.setText("Previous")
        button_previous.clicked.connect(self.previous_clicked)
        event_select_toolbar.addWidget(button_previous)

        self.event_display_edit = QtGui.QLineEdit()
        self.event_display_edit.setText('0')
        self.event_display_edit.setMaxLength(1)
        self.event_display_edit.setValidator(
            QtGui.QIntValidator(0, 0, self.event_display_edit))
        self.event_display_edit.textChanged.connect(
            self._event_display_edit_on_change)
        event_select_toolbar.addWidget(self.event_display_edit)

        self.event_count_text = QtGui.QLabel()
        self.event_count_text.setText('/' + str(0))
        event_select_toolbar.addWidget(self.event_count_text)

        button_previous = QtGui.QPushButton(event_select_toolbar)
        button_previous.setText("Next")
        button_previous.clicked.connect(self.next_clicked)
        event_select_toolbar.addWidget(button_previous)

        frame = QtGui.QSplitter()
        frame.setOrientation(QtCore.Qt.Vertical)
        frame.addWidget(wig)
        frame.addWidget(wig2)

        event_finder_plots_layout = LayoutWidget()
        # event_finder_plots_layout.addWidget(self.plotToolBar, row=1, col=0, colspan=3)
        event_finder_plots_layout.addWidget(frame, row=2, col=0, colspan=3)
        event_finder_plots_layout.addWidget(event_select_toolbar,
                                            row=5,
                                            col=0,
                                            colspan=3)

        return event_finder_plots_layout
示例#15
0
    def test_plot_clear(self):
        # tests to make sure that when the clear flag is passed
        # to plots, that clear works as expected
        plot_item = MyPlotItem()
        x = [1]
        y = [1]
        plot = PlotCurveItem(x, y)
        plot_item.addItem(plot)

        plot2 = PlotCurveItem(y, x)
        plot_item.add_event_item(plot2)

        plot5 = PlotCurveItem(y, x)
        plot_item.addItem(plot5)

        x = y = np.zeros(2)
        plot3 = PathItem(x, y)
        plot_item.add_event_item(plot3)

        plot4 = plot_item.plot(clear=True)

        # Test that all of the plots have been cleared,
        # and that plot4 was added successfully
        self.assertEqual(len(plot_item.listDataItems()), 1)
        self.assertEqual(len(plot_item._myEventItemList), 0)
        self.assertEqual(len(plot_item._myItemList), 1)
        self.assertEqual(plot_item.listDataItems()[0], plot4)
        self.assertEqual(plot_item._myItemList[0], plot4)
示例#16
0
    def test_clear(self):
        plot_item = MyPlotItem()
        x = [1]
        y = [1]
        plot = PlotCurveItem(x, y)
        plot_item.addItem(plot)

        plot2 = PlotCurveItem(y, x)
        plot_item.add_event_item(plot2)

        x = y = np.zeros(2)
        plot3 = PathItem(x, y)
        plot_item.add_event_item(plot3)

        plot_item.clear()
        self.assertEqual(len(plot_item.listDataItems()), 0)
        self.assertEqual(len(plot_item._myEventItemList), 0)
        self.assertEqual(len(plot_item._myItemList), 0)