コード例 #1
0
ファイル: plot_widget.py プロジェクト: thiemar/px4esc
class RealtimePlotWidget(QWidget):
    COLORS = [
        Qt.red, Qt.blue, Qt.green, Qt.magenta, Qt.cyan, Qt.darkRed,
        Qt.darkBlue, Qt.darkGreen, Qt.darkYellow, Qt.gray
    ]

    def __init__(self, parent=None):
        super(RealtimePlotWidget, self).__init__(parent)
        self._plot_widget = PlotWidget()
        self._plot_widget.setBackground((0, 0, 0))
        self._plot_widget.addLegend()
        self._plot_widget.showButtons()
        self._plot_widget.enableAutoRange()
        self._plot_widget.showGrid(x=True, y=True, alpha=0.2)
        vbox = QVBoxLayout()
        vbox.addWidget(self._plot_widget)
        self.setLayout(vbox)

        self._color_index = 0
        self._curves = {}

    def add_curve(self, curve_id, curve_name, data_x=[], data_y=[]):
        color = QColor(self.COLORS[self._color_index % len(self.COLORS)])
        self._color_index += 1
        pen = mkPen(color, width=1)
        plot = self._plot_widget.plot(name=curve_name, pen=pen)
        data_x = numpy.array(data_x)
        data_y = numpy.array(data_y)
        self._curves[curve_id] = {'x': data_x, 'y': data_y, 'plot': plot}

    def remove_curve(self, curve_id):
        curve_id = str(curve_id)
        if curve_id in self._curves:
            self._plot_widget.removeItem(self._curves[curve_id]['plot'])
            del self._curves[curve_id]

    def set_x_range(self, left, right):
        self._plot_widget.setRange(xRange=(left, right))

    def update_values(self, curve_id, x, y):
        curve = self._curves[curve_id]
        curve['x'] = numpy.append(curve['x'], x)
        curve['y'] = numpy.append(curve['y'], y)

    def redraw(self):
        for curve in self._curves.values():
            if len(curve['x']):
                curve['plot'].setData(curve['x'], curve['y'])

    def lazy_redraw(self, period):
        timestamp = time.time()
        if not hasattr(self, '_prev_lazy_redraw'):
            self._prev_lazy_redraw = 0.0
        if timestamp - self._prev_lazy_redraw > period:
            self._prev_lazy_redraw = timestamp
            self.redraw()
コード例 #2
0
ファイル: plot_widget.py プロジェクト: Aerobota/sapog
class RealtimePlotWidget(QWidget):
    COLORS = [Qt.red, Qt.blue, Qt.green, Qt.magenta, Qt.cyan,
              Qt.darkRed, Qt.darkBlue, Qt.darkGreen, Qt.darkYellow, Qt.gray]

    def __init__(self, parent=None):
        super(RealtimePlotWidget, self).__init__(parent)
        self._plot_widget = PlotWidget()
        self._plot_widget.setBackground((0, 0, 0))
        self._plot_widget.addLegend()
        self._plot_widget.showButtons()
        self._plot_widget.enableAutoRange()
        self._plot_widget.showGrid(x=True, y=True, alpha=0.2)
        vbox = QVBoxLayout()
        vbox.addWidget(self._plot_widget)
        self.setLayout(vbox)

        self._color_index = 0
        self._curves = {}

    def add_curve(self, curve_id, curve_name, data_x=[], data_y=[]):
        color = QColor(self.COLORS[self._color_index % len(self.COLORS)])
        self._color_index += 1
        pen = mkPen(color, width=1)
        plot = self._plot_widget.plot(name=curve_name, pen=pen)
        data_x = numpy.array(data_x)
        data_y = numpy.array(data_y)
        self._curves[curve_id] = {'x': data_x, 'y': data_y, 'plot': plot}

    def remove_curve(self, curve_id):
        curve_id = str(curve_id)
        if curve_id in self._curves:
            self._plot_widget.removeItem(self._curves[curve_id]['plot'])
            del self._curves[curve_id]

    def set_x_range(self, left, right):
        self._plot_widget.setRange(xRange=(left, right))

    def update_values(self, curve_id, x, y):
        curve = self._curves[curve_id]
        curve['x'] = numpy.append(curve['x'], x)
        curve['y'] = numpy.append(curve['y'], y)

    def redraw(self):
        for curve in self._curves.values():
            if len(curve['x']):
                curve['plot'].setData(curve['x'], curve['y'])

    def lazy_redraw(self, period):
        timestamp = time.time()
        if not hasattr(self, '_prev_lazy_redraw'):
            self._prev_lazy_redraw = 0.0
        if timestamp - self._prev_lazy_redraw > period:
            self._prev_lazy_redraw = timestamp
            self.redraw()
コード例 #3
0
class Canvas2Dupgraded(PlotWidget, AnimatedWidget):
    def __init__(self, parent=None, data_dict=None):
        super(Canvas2Dupgraded, self).__init__()
        self.shareData(**data_dict)
        self.plotWidget = PlotWidget(self)
        self._i = self.current_state
        self.title = self.options['column']
        self.graph_data = self.plot_data[self.title].tolist()
        self.internal_iterations = len(self.graph_data)
        self.createPlotCanvas()

    def createPlotCanvas(self):
        self.null_data = np.array([i for i in range(self.internal_iterations)])
        self.plotWidget.setTitle(self.title)
        self.plotWidget.setGeometry(0, 0, self.geom[0] - 60, self.geom[1] - 60)
        self.plotWidget.setXRange(0, self.internal_iterations)
        self.plotWidget.setYRange(np.min(self.graph_data),
                                  np.max(self.graph_data),
                                  padding=0.1)
        self.plotWidget.enableAutoRange('xy', False)
        self.plotData = self.plotWidget.plot(
            self.graph_data[:self._i],
            pen=pg.mkPen(color=self.options['color'][0],
                         width=self.options['marker_size']),
            name="data1",
            clear=True)

    def on_resize_geometry_reset(self, geom):
        """
        when another widget is promoted, this window must resize too
        this means resetting the graph unfortunately
        """
        self.plotWidget.setGeometry(0, 0, geom[0] - 60, geom[1] - 60)

    def set_i(self, value, trigger=False, record=False):
        self._i = value
        self._i %= self.internal_iterations
        self.plotData.setData(self.null_data[:self._i],
                              self.graph_data[:self._i])
        pg.QtGui.QApplication.processEvents()
コード例 #4
0
    def __init__(self, atri_plot: PlotWidget, vent_plot: PlotWidget, data_size: int):
        print("Graphs handler init")

        # noinspection PyArgumentList
        atri_plot.setRange(xRange=[-1, data_size], yRange=[-0.5, 5.5], padding=0)
        atri_plot.setLimits(xMin=-1, xMax=data_size, maxXRange=data_size + 1, yMin=-0.5, yMax=5.5)
        atri_plot.setMouseEnabled(x=True, y=False)
        atri_plot.enableAutoRange(x=False, y=True)
        atri_plot.setAutoVisible(x=False, y=True)
        atri_plot.showGrid(x=True, y=True)
        atri_plot.hideButtons()
        atri_plot.setMenuEnabled(False)
        atri_plot.setLabel('left', "Amplitude", units='V', **{'color': '#FFF', 'font-size': '10pt'})
        atri_plot.setLabel('bottom', "Time", units='s', **{'color': '#FFF', 'font-size': '10pt'})
        atri_plot.getAxis('bottom').setHeight(30)
        # noinspection PyArgumentList
        vent_plot.setRange(xRange=[-1, data_size], yRange=[-0.5, 5.5], padding=0)
        vent_plot.setLimits(xMin=-1, xMax=data_size, maxXRange=data_size + 1, yMin=-0.5, yMax=5.5)
        vent_plot.setMouseEnabled(x=True, y=False)
        vent_plot.enableAutoRange(x=False, y=True)
        vent_plot.setAutoVisible(x=False, y=True)
        vent_plot.showGrid(x=True, y=True)
        vent_plot.hideButtons()
        vent_plot.setMenuEnabled(False)
        vent_plot.setLabel('left', "Amplitude", units='V', **{'color': '#FFF', 'font-size': '10pt'})
        vent_plot.setLabel('bottom', "Time", units='s', **{'color': '#FFF', 'font-size': '10pt'})
        vent_plot.getAxis('bottom').setHeight(30)

        # Initialize graphs to 0
        self._atri_data = np.zeros(data_size)
        self._vent_data = np.zeros(data_size)

        # Create new sense plots for the atrial and ventricular graphs, in blue
        self._atri_plot = atri_plot.plot(pen=(0, 229, 255))
        self._vent_plot = vent_plot.plot(pen=(0, 229, 255))

        self._plot_data()
コード例 #5
0
ファイル: bus_monitor.py プロジェクト: hsteinhaus/gui_tool
class BusMonitorWidget(QGroupBox):
    DEFAULT_PLOT_X_RANGE = 120
    BUS_LOAD_PLOT_MAX_SAMPLES = 5000

    def __init__(self, parent, node, iface_name):
        super(BusMonitorWidget, self).__init__(parent)
        self.setTitle('CAN bus activity (%s)' % iface_name.split(os.path.sep)[-1])

        self._node = node
        self._hook_handle = self._node.can_driver.add_io_hook(self._frame_hook)

        self._columns = [
            BasicTable.Column('Dir',
                              lambda e: (e[0].upper()),
                              searchable=False),
            BasicTable.Column('Local Time', TimestampRenderer(), searchable=False),
            BasicTable.Column('CAN ID',
                              lambda e: (('%0*X' % (8 if e[1].extended else 3, e[1].id)).rjust(8),
                                         colorize_can_id(e[1]))),
            BasicTable.Column('Data Hex',
                              lambda e: (' '.join(['%02X' % x for x in e[1].data]).ljust(3 * e[1].MAX_DATA_LENGTH),
                                         colorize_transfer_id(e))),
            BasicTable.Column('Data ASCII',
                              lambda e: (''.join([(chr(x) if 32 <= x <= 126 else '.') for x in e[1].data]),
                                         colorize_transfer_id(e))),
            BasicTable.Column('Src',
                              lambda e: render_node_id_with_color(e[1], 'src')),
            BasicTable.Column('Dst',
                              lambda e: render_node_id_with_color(e[1], 'dst')),
            BasicTable.Column('Data Type',
                              lambda e: render_data_type_with_color(e[1]),
                              resize_mode=QHeaderView.Stretch),
        ]

        self._log_widget = RealtimeLogWidget(self, columns=self._columns, font=get_monospace_font(),
                                             post_redraw_hook=self._redraw_hook)
        self._log_widget.on_selection_changed = self._update_measurement_display

        def flip_row_mark(row, col):
            if col == 0:
                item = self._log_widget.table.item(row, col)
                if item.icon().isNull():
                    item.setIcon(get_icon('circle'))
                    flash(self, 'Row %d was marked, click again to unmark', row, duration=3)
                else:
                    item.setIcon(QIcon())

        self._log_widget.table.cellPressed.connect(flip_row_mark)

        self._stat_update_timer = QTimer(self)
        self._stat_update_timer.setSingleShot(False)
        self._stat_update_timer.timeout.connect(self._update_stat)
        self._stat_update_timer.start(500)

        self._traffic_stat = TrafficStatCounter()

        self._stat_frames_tx = QLabel('N/A', self)
        self._stat_frames_rx = QLabel('N/A', self)
        self._stat_traffic = QLabel('N/A', self)

        self._load_plot = PlotWidget(background=(0, 0, 0))
        self._load_plot.setRange(xRange=(0, self.DEFAULT_PLOT_X_RANGE), padding=0)
        self._load_plot.setMaximumHeight(150)
        self._load_plot.setMinimumHeight(100)
        self._load_plot.setMinimumWidth(100)
        self._load_plot.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self._load_plot.showGrid(x=True, y=True, alpha=0.4)
        self._load_plot.setToolTip('Frames per second')
        self._load_plot.getPlotItem().getViewBox().setMouseEnabled(x=True, y=False)
        self._load_plot.enableAutoRange()
        self._bus_load_plot = self._load_plot.plot(name='Frames per second', pen=mkPen(QColor(Qt.lightGray), width=1))
        self._bus_load_samples = [], []
        self._started_at_mono = time.monotonic()

        layout = QVBoxLayout(self)

        layout.addWidget(self._log_widget, 1)

        stat_vars_layout = QGridLayout(self)
        stat_layout_next_row = 0

        def add_stat_row(label, value):
            nonlocal stat_layout_next_row
            stat_vars_layout.addWidget(QLabel(label, self), stat_layout_next_row, 0)
            stat_vars_layout.addWidget(value, stat_layout_next_row, 1)
            value.setMinimumWidth(75)
            stat_layout_next_row += 1

        add_stat_row('Frames transmitted:', self._stat_frames_tx)
        add_stat_row('Frames received:', self._stat_frames_rx)
        add_stat_row('Frames per second:', self._stat_traffic)
        stat_vars_layout.setRowStretch(stat_layout_next_row, 1)

        stat_layout = QHBoxLayout(self)
        stat_layout.addLayout(stat_vars_layout)
        stat_layout.addWidget(self._load_plot, 1)

        layout.addLayout(stat_layout, 0)
        self.setLayout(layout)

    def close(self):
        self._hook_handle.remove()

    def _update_stat(self):
        bus_load, ts_mono = self._traffic_stat.get_frames_per_second()
        self._stat_traffic.setText(str(int(bus_load + 0.5)))

        if len(self._bus_load_samples[0]) >= self.BUS_LOAD_PLOT_MAX_SAMPLES:
            self._bus_load_samples[0].pop(0)
            self._bus_load_samples[1].pop(0)

        self._bus_load_samples[1].append(bus_load)
        self._bus_load_samples[0].append(ts_mono - self._started_at_mono)

        self._bus_load_plot.setData(*self._bus_load_samples)

        (xmin, xmax), _ = self._load_plot.viewRange()
        diff = xmax - xmin
        xmax = self._bus_load_samples[0][-1]
        xmin = self._bus_load_samples[0][-1] - diff
        self._load_plot.setRange(xRange=(xmin, xmax), padding=0)

    def _redraw_hook(self):
        self._stat_frames_tx.setText(str(self._traffic_stat.tx))
        self._stat_frames_rx.setText(str(self._traffic_stat.rx))

    def _frame_hook(self, direction, frame):
        self._traffic_stat.add_frame(direction, frame)
        self._log_widget.add_item_async((direction, frame))

    def _update_measurement_display(self, selected_rows_cols):
        if not selected_rows_cols:
            return

        min_row = min([row for row, _ in selected_rows_cols])
        max_row = max([row for row, _ in selected_rows_cols])

        def get_row_ts(row):
            return TimestampRenderer.parse_timestamp(self._log_widget.table.item(row, 1).text())

        def get_load_str(num_frames, dt):
            if dt >= 1e-6:
                return 'average load %.1f FPS' % (num_frames / dt)
            return 'average load is unknown'

        if min_row == max_row:
            num_frames = min_row
            first_ts = get_row_ts(0)
            current_ts = get_row_ts(min_row)
            dt = current_ts - first_ts
            flash(self, '%d frames from beginning, %.3f sec since first frame, %s',
                  num_frames, dt, get_load_str(num_frames, dt))
        else:
            num_frames = max_row - min_row + 1
            first_ts = get_row_ts(min_row)
            last_ts = get_row_ts(max_row)
            dt = last_ts - first_ts
            flash(self, '%d frames, timedelta %.6f sec, %s',
                  num_frames, dt, get_load_str(num_frames, dt))
コード例 #6
0
class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)

        self.title = "Motor Control"
        self.setWindowTitle(self.title)

        #Application Size
        self.left = 100
        self.top = 100
        self.width = 1000
        self.height = 700
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.initUI()

    def initUI(self):

        self.setStyleSheet(qdarkstyle.load_stylesheet())

        self.horizontalLayout = QHBoxLayout()
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setSizeConstraint(QLayout.SetDefaultConstraint)
        self.verticalLayout.setSpacing(6)
        self.gridLayout = QGridLayout()

        self.imageLabel = QLabel()
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.imageLabel.sizePolicy().hasHeightForWidth())
        self.imageLabel.setSizePolicy(sizePolicy)
        self.imageLabel.setMinimumSize(QSize(200, 130))
        self.imageLabel.setMaximumSize(QSize(200, 130))
        self.imageLabel.setPixmap(
            QPixmap("./Arduino/logo/CUAtHomeLogo-Horz.png").scaled(
                200, 130, Qt.KeepAspectRatio, Qt.FastTransformation))
        self.verticalLayout.addWidget(self.imageLabel)

        self.startbutton = QPushButton("Start", self)
        self.startbutton.setCheckable(False)
        self.startbutton.clicked.connect(self.startbutton_pushed)
        self.startbutton.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.startbutton, 0, 0, 1, 1)

        self.stopbutton = QPushButton("Stop", self)
        self.stopbutton.setCheckable(False)
        self.stopbutton.clicked.connect(self.stopbutton_pushed)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.stopbutton.sizePolicy().hasHeightForWidth())
        self.stopbutton.setSizePolicy(sizePolicy)
        self.stopbutton.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.stopbutton, 0, 1, 1, 1)

        self.clearbutton = QPushButton("Clear", self)
        self.clearbutton.setCheckable(False)
        self.clearbutton.clicked.connect(self.clearbutton_pushed)
        self.clearbutton.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.clearbutton, 1, 0, 1, 1)

        self.savebutton = QPushButton("Save", self)
        self.savebutton.setCheckable(False)
        self.savebutton.clicked.connect(self.savebutton_pushed)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.savebutton.sizePolicy().hasHeightForWidth())
        self.savebutton.setSizePolicy(sizePolicy)
        self.savebutton.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.savebutton, 1, 1, 1, 1)

        self.settings = QPushButton("Settings", self)
        self.settings.clicked.connect(self.settingsMenu)
        self.settings.setMaximumSize(QSize(300, 20))
        self.gridLayout.addWidget(self.settings, 2, 0, 1, 2)

        self.checkBoxShowAll = QCheckBox("Show All Plots", self)
        self.checkBoxShowAll.setMaximumSize(QSize(100, 20))
        self.checkBoxShowAll.setChecked(True)
        self.checkBoxShowAll.toggled.connect(self.visibilityAll)
        self.gridLayout.addWidget(self.checkBoxShowAll, 3, 0, 1, 1)

        self.checkBoxHideAll = QCheckBox("Hide All Plots", self)
        self.checkBoxHideAll.setChecked(False)
        self.checkBoxHideAll.toggled.connect(self.hideAll)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.checkBoxHideAll.sizePolicy().hasHeightForWidth())
        self.checkBoxHideAll.setSizePolicy(sizePolicy)
        self.checkBoxHideAll.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.checkBoxHideAll, 3, 1, 1, 1)

        self.checkBoxPlot1 = QCheckBox("Plot 1", self)
        self.checkBoxPlot1.toggled.connect(self.visibility1)
        self.checkBoxPlot1.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.checkBoxPlot1, 4, 0, 1, 1)

        self.checkBoxPlot2 = QCheckBox("Plot 2", self)
        self.checkBoxPlot2.toggled.connect(self.visibility2)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.checkBoxPlot2.sizePolicy().hasHeightForWidth())
        self.checkBoxPlot2.setSizePolicy(sizePolicy)
        self.checkBoxPlot2.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.checkBoxPlot2, 4, 1, 1, 1)

        self.checkBoxShowAll.stateChanged.connect(self.checkbox_logic)
        self.checkBoxHideAll.stateChanged.connect(self.checkbox_logic)
        self.checkBoxPlot1.stateChanged.connect(self.checkbox_logic)
        self.checkBoxPlot2.stateChanged.connect(self.checkbox_logic)

        self.PowerScalingLabel = QLabel("Power Scaling (%)", self)
        self.PowerScalingLabel.setMinimumSize(QSize(100, 20))
        self.PowerScalingLabel.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.PowerScalingLabel, 7, 0, 1, 1)
        self.PowerScalingInput = QLineEdit("", self)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.PowerScalingInput.sizePolicy().hasHeightForWidth())
        self.PowerScalingInput.setSizePolicy(sizePolicy)
        self.PowerScalingInput.setMaximumSize(QSize(100, 20))
        #self.PowerScalingInput.setValidator(QRegExpValidator(QRegExp("^[0-9][0-9]?$|^100$"))) #0-1 as a float FIX THIS
        self.gridLayout.addWidget(self.PowerScalingInput, 7, 1, 1, 1)

        self.FrequencyLabel = QLabel("Frequency (Hz)", self)
        self.FrequencyLabel.setMinimumSize(QSize(100, 20))
        self.FrequencyLabel.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.FrequencyLabel, 8, 0, 1, 1)
        self.FrequencyInput = QLineEdit("", self)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.FrequencyInput.sizePolicy().hasHeightForWidth())
        self.FrequencyInput.setSizePolicy(sizePolicy)
        self.FrequencyInput.setMaximumSize(QSize(100, 20))
        self.FrequencyInput.setValidator(QDoubleValidator())
        self.gridLayout.addWidget(self.FrequencyInput, 8, 1, 1, 1)

        PID_validator = QDoubleValidator(
            0.0000, 50.000, 4, notation=QDoubleValidator.StandardNotation)

        self.PCheckBox = QCheckBox("P", self)
        self.PCheckBox.setMaximumSize(QSize(100, 20))
        self.PCheckBox.setChecked(True)
        self.PCheckBox.toggled.connect(self.PCheckBoxLogic)
        self.gridLayout.addWidget(self.PCheckBox, 9, 0, 1, 1)
        self.PInput = QLineEdit("", self)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.PInput.sizePolicy().hasHeightForWidth())
        self.PInput.setSizePolicy(sizePolicy)
        self.PInput.setMaximumSize(QSize(100, 20))
        self.PInput.setValidator(PID_validator)
        self.gridLayout.addWidget(self.PInput, 9, 1, 1, 1)

        self.ICheckBox = QCheckBox("I", self)
        self.ICheckBox.setMaximumSize(QSize(100, 20))
        self.ICheckBox.setChecked(True)
        self.ICheckBox.toggled.connect(self.ICheckBoxLogic)
        self.gridLayout.addWidget(self.ICheckBox, 10, 0, 1, 1)
        self.IInput = QLineEdit("", self)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.IInput.sizePolicy().hasHeightForWidth())
        self.IInput.setSizePolicy(sizePolicy)
        self.IInput.setMaximumSize(QSize(100, 20))
        self.IInput.setValidator(PID_validator)
        self.gridLayout.addWidget(self.IInput, 10, 1, 1, 1)

        self.DCheckBox = QCheckBox("D", self)
        self.DCheckBox.setMaximumSize(QSize(100, 20))
        self.DCheckBox.setChecked(True)
        self.DCheckBox.toggled.connect(self.DCheckBoxLogic)
        self.gridLayout.addWidget(self.DCheckBox, 11, 0, 1, 1)
        self.DInput = QLineEdit("", self)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.DInput.sizePolicy().hasHeightForWidth())
        self.DInput.setSizePolicy(sizePolicy)
        self.DInput.setMaximumSize(QSize(100, 20))
        self.DInput.setValidator(PID_validator)
        self.gridLayout.addWidget(self.DInput, 11, 1, 1, 1)

        self.LabType = QComboBox()
        self.LabType.addItems(["Position", "Speed"])
        #self.LabType.activated.connect(self.getLabType)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.LabType.sizePolicy().hasHeightForWidth())
        self.LabType.setSizePolicy(sizePolicy)
        self.LabType.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.LabType, 5, 1, 1, 1)
        self.LabLabel = QLabel("Lab Type")
        self.LabLabel.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.LabLabel, 5, 0, 1, 1)

        self.inputForms = QComboBox()
        self.inputForms.addItems(["Sine", "Step"])
        self.inputForms.activated.connect(self.getInput)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.inputForms.sizePolicy().hasHeightForWidth())
        self.inputForms.setSizePolicy(sizePolicy)
        self.inputForms.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.inputForms, 6, 1, 1, 1)
        self.inputType = QLabel("Input Type")
        self.inputType.setMaximumSize(QSize(100, 20))
        self.gridLayout.addWidget(self.inputType, 6, 0, 1, 1)

        self.verticalLayout.addLayout(self.gridLayout)
        spacerItem = QSpacerItem(20, 80, QSizePolicy.Minimum,
                                 QSizePolicy.Fixed)
        self.verticalLayout.addItem(spacerItem)

        #What is this?

        self.label = QLabel()
        self.label.setMaximumSize(QSize(200, 130))
        self.label.setText("")
        self.verticalLayout.addWidget(self.label)

        self.horizontalLayout.addLayout(self.verticalLayout)
        self.rightVerticalLayout = QVBoxLayout()

        self.graphWidgetOutput = PlotWidget()
        self.graphWidgetInput = PlotWidget()

        #Adds grid lines
        self.graphWidgetOutput.showGrid(x=True, y=True, alpha=None)
        self.graphWidgetInput.showGrid(x=True, y=True, alpha=None)

        #self.graphWidget.setXRange(0, 100, padding=0) #Doesn't move with the plot. Can drag around
        #self.graphWidget.setLimits(xMin=0, xMax=100)#, yMin=c, yMax=d) #Doesn't move with the plot. Cannot drag around

        #self.graphWidget.setYRange(0, 4, padding=0)
        self.graphWidgetOutput.setYRange(-11, 11, padding=0)
        self.graphWidgetOutput.enableAutoRange()
        self.graphWidgetInput.setYRange(-11, 11, padding=0)
        self.graphWidgetInput.enableAutoRange()

        #Changes background color of graph
        self.graphWidgetOutput.setBackground((0, 0, 0))
        self.graphWidgetInput.setBackground((0, 0, 0))

        #Adds a legend after data starts to plot NOT before
        self.graphWidgetOutput.addLegend()

        #Adds title to graphs
        self.graphWidgetOutput.setTitle("Response", color="w", size="12pt")
        self.graphWidgetInput.setTitle("PWM Actuation Signal",
                                       color="w",
                                       size="12pt")

        self.rightVerticalLayout.addWidget(self.graphWidgetOutput)
        self.rightVerticalLayout.addWidget(self.graphWidgetInput)
        self.horizontalLayout.addLayout(self.rightVerticalLayout)

        self.setLayout(self.horizontalLayout)

        #Plot time update settings
        self.timer = QTimer()
        self.timer.setInterval(
            50
        )  #Changes the plot speed. Defaulted to 50. Can be placed in startbutton_pushed() method
        self.initialState()
        time.sleep(2)
        try:
            self.timer.timeout.connect(self.update)
        except:
            raise Exception("Not Connected")
        #self.show()

    #Checkbox logic
    def checkbox_logic(self, state):

        # checking if state is checked
        if state == Qt.Checked:

            if self.sender() == self.checkBoxShowAll:
                self.checkBoxHideAll.setChecked(False)
                self.checkBoxPlot1.setChecked(False)
                self.checkBoxPlot2.setChecked(False)
                #self.checkBoxShow.stateChanged.disconnect(self.uncheck)

            elif self.sender() == self.checkBoxHideAll:
                #self.checkBoxShow.stateChanged.connect(self.uncheck)
                self.checkBoxShowAll.setChecked(False)
                self.checkBoxPlot1.setChecked(False)
                self.checkBoxPlot2.setChecked(False)

            elif self.sender() == self.checkBoxPlot1:
                self.checkBoxShowAll.setChecked(False)
                self.checkBoxHideAll.setChecked(False)
                self.checkBoxPlot2.setChecked(False)

            elif self.sender() == self.checkBoxPlot2:
                self.checkBoxShowAll.setChecked(False)
                self.checkBoxHideAll.setChecked(False)
                self.checkBoxPlot1.setChecked(False)

    #Resets data arrays and establishes serial communcation. Disables itself after clicking
    def startbutton_pushed(self):
        self.initialState(
        )  #Reinitializes arrays in case you have to retake data
        self.size = self.serial_values[3]  #Value from settings. Windows data
        #self.buffersize = self.serial_values[4] #Value from settings. Restricts buffer data
        '''
        self.ser = serial.Serial(port = self.serial_values[0], 
                                 baudrate = self.serial_values[1],
                                 timeout = self.serial_values[2])
        self.ser.flushInput()
        self.ser.write(b'A')
        time.sleep(2)
        print("Recording Data")
        self.timer.start()
        #self.timer.setInterval(50)
        self.curve()
        self.startbutton.clicked.disconnect(self.startbutton_pushed)
        '''
        self.serialInstance = SerialComm(self.serial_values[0],
                                         self.serial_values[1],
                                         self.serial_values[2])
        self.serialInstance.serialOpen()
        time.sleep(2)
        print("Recording Data")
        self.timer.start()
        self.curve()
        self.startbutton.clicked.disconnect(self.startbutton_pushed)

    #Stops timer and ends serial communication
    def stopbutton_pushed(self):
        self.timer.stop()
        #self.ser.close()
        self.serialInstance.serialClose()
        '''
        print("y1 zeros:", self.y1_zeros)
        print("y2 zeros:", self.y2_zeros)
        print("y1 full:", self.y1)
        print("y2 full:", self.y2)
        '''

    #Resets both plotting windows and reenables Start Button
    def clearbutton_pushed(self):
        self.graphWidgetOutput.clear()
        self.graphWidgetInput.clear()
        self.graphWidgetOutput.enableAutoRange(axis=None,
                                               enable=True,
                                               x=None,
                                               y=None)
        self.startbutton.clicked.connect(self.startbutton_pushed)

    #Dumps data into a csv file to a selected path
    def savebutton_pushed(self):
        self.createCSV()
        path = QFileDialog.getSaveFileName(self, 'Save CSV', os.getenv('HOME'),
                                           'CSV(*.csv)')
        if path[0] != '':
            with open(path[0], 'w', newline='') as csvfile:
                csvwriter = csv.writer(csvfile)
                csvwriter.writerow(self.header)
                csvwriter.writerows(self.data_set)

    #Creates csv data
    def createCSV(self):
        self.header = ['time', 'y1', 'y2']
        self.data_set = zip(self.time, self.y1, self.y2)

    #Initilizes lists/arrays
    def initialState(self):
        self.buffersize = 500  #np array size that is used to plot data
        self.step = 0  #Used for repositioning data in plot window to the left

        #Data buffers. What is being plotted in the 2 windows
        self.time_zeros = np.zeros(self.buffersize + 1, float)
        self.y1_zeros = np.zeros(self.buffersize + 1, float)
        self.y2_zeros = np.zeros(self.buffersize + 1, float)
        self.y3_zeros = np.zeros(self.buffersize + 1, float)

        #Complete data. What will be written to the csv file
        self.time = list()
        self.y1 = list()
        self.y2 = list()
        self.y3 = list()

        self.getLabType()

    '''
    def readValues(self):
        arduinoData = self.ser.readline().decode().replace('\r\n','').split(",")
        return arduinoData
    '''

    #Initializes data# to have specific attributes
    def curve(self):
        pen1 = pg.mkPen(color=(255, 0, 0), width=1)
        pen2 = pg.mkPen(color=(0, 255, 0), width=1)
        pen3 = pg.mkPen(color=(0, 0, 255), width=1)

        self.data1 = self.graphWidgetOutput.plot(pen=pen1,
                                                 name="Data 1")  #Response
        self.data2 = self.graphWidgetOutput.plot(pen=pen2,
                                                 name="Data 2")  #Setpoint
        self.data3 = self.graphWidgetInput.plot(
            pen=pen3, name="Data 3")  #PWM Actuation Signal

    #Connected to timer to update plot. Incoming data is in the form of timestamp,data1,data2...
    def update(self):
        #fulldata = self.readValues()
        #print(fulldata)
        fulldata = self.serialInstance.readValues()

        self.step = self.step + 1

        time_index = int(self.time_zeros[self.buffersize])
        self.time_zeros[time_index] = self.time_zeros[time_index +
                                                      self.size] = float(
                                                          fulldata[0])
        self.time_zeros[self.buffersize] = time_index = (time_index +
                                                         1) % self.size
        self.time.append(fulldata[0])

        i = int(self.y1_zeros[self.buffersize])
        self.y1_zeros[i] = self.y1_zeros[i + self.size] = float(fulldata[1])
        self.y1_zeros[self.buffersize] = i = (i + 1) % self.size
        self.y1.append(fulldata[1])

        j = int(self.y2_zeros[self.buffersize])
        self.y2_zeros[j] = self.y2_zeros[j + self.size] = float(fulldata[2])
        self.y2_zeros[self.buffersize] = j = (j + 1) % self.size
        self.y2.append(fulldata[2])

        k = int(self.y3_zeros[self.buffersize])
        self.y3_zeros[k] = self.y3_zeros[k + self.size] = float(fulldata[3])
        self.y3_zeros[self.buffersize] = k = (k + 1) % self.size
        self.y3.append(fulldata[3])

        self.data1.setData(self.time_zeros[time_index:time_index + self.size],
                           self.y1_zeros[i:i + self.size])
        self.data1.setPos(self.step, 0)
        self.data2.setData(self.time_zeros[time_index:time_index + self.size],
                           self.y2_zeros[j:j + self.size])
        self.data2.setPos(self.step, 0)
        self.data3.setData(self.time_zeros[time_index:time_index + self.size],
                           self.y3_zeros[k:k + self.size])
        self.data3.setPos(self.step, 0)

    #Below 4 change visibility of data# in the curves() method
    def visibilityAll(self):
        showall = self.sender()
        if showall.isChecked() == True:
            self.data1.setVisible(True)
            self.data2.setVisible(True)

    def hideAll(self):
        disappearall = self.sender()
        if disappearall.isChecked() == True:
            self.data1.setVisible(False)
            self.data2.setVisible(False)

    def visibility1(self):
        test1 = self.sender()
        if test1.isChecked() == True:
            self.data1.setVisible(True)
            self.data2.setVisible(False)

    def visibility2(self):
        test2 = self.sender()
        if test2.isChecked() == True:
            self.data2.setVisible(True)
            self.data1.setVisible(False)

    #Class instance of settings menu. Creates a dialog (popup)
    def settingsMenu(self):
        self.settingsPopUp = Dialog1()
        self.settingsPopUp.show()
        #self.settingsPopUp.exec()
        self.serial_values = self.settingsPopUp.getDialogValues()

    def PCheckBoxLogic(self):
        test1 = self.sender()
        if test1.isChecked() == True:
            self.PInput.setEnabled(True)
        elif test1.isChecked() == False:
            self.PInput.setEnabled(False)

    def ICheckBoxLogic(self):
        test1 = self.sender()
        if test1.isChecked() == True:
            self.IInput.setEnabled(True)
        elif test1.isChecked() == False:
            self.IInput.setEnabled(False)

    def DCheckBoxLogic(self):
        test1 = self.sender()
        if test1.isChecked() == True:
            self.DInput.setEnabled(True)
        elif test1.isChecked() == False:
            self.DInput.setEnabled(False)

    def PIDInput(self):
        if self.PInput.text() == "" or self.PCheckBox.checkState() == False:
            self.Pvalue = 0
        else:
            self.Pvalue = self.PInput.text()

        if self.IInput.text() == "" or self.ICheckBox.checkState() == False:
            self.Ivalue = 0
        else:
            self.Ivalue = self.IInput.text()

        if self.DInput.text() == "" or self.DCheckBox.checkState() == False:
            self.Dvalue = 0
        else:
            self.Dvalue = self.DInput.text()
        return ([self.Pvalue, self.Ivalue, self.Dvalue])

    #Function that connects output pyqtgraph widget, and the combobox
    def getInput(self):
        self.inputType = str(self.inputForms.currentText())
        pen_input = pg.mkPen(color=(255, 0, 0), width=1)

        if self.inputType == "Sine":
            print("Sine")
            self.graphWidgetInput.clear()
            self.x_input = np.arange(0, 10, 0.1)
            self.y_input = np.sin(self.x_input)
            self.data_input = self.graphWidgetInput.plot(self.x_input,
                                                         self.y_input,
                                                         pen=pen_input)
            self.data_input.setData(self.x_input, self.y_input)
            self.graphWidgetInput.setYRange(-2, 2, padding=0)

        elif self.inputType == "Step":
            print("Step")
            self.graphWidgetInput.clear()
            self.x_input = np.arange(0, 10, 0.1)
            self.y_input = np.heaviside(self.x_input, 1)
            self.data_input = self.graphWidgetInput.plot(self.x_input,
                                                         self.y_input,
                                                         pen=pen_input)
            self.data_input.setData(self.x_input, self.y_input)
            self.graphWidgetInput.setYRange(-2, 2, padding=0)

    def getLabType(self):
        self.inputType = str(self.LabType.currentText())
        if self.inputType == "Position":
            print("Lab: Position")
            return (
                self.graphWidgetOutput.setLabel(
                    'left',
                    "<span style=\"color:white;font-size:16px\">&theta; (°)</span>"
                ),
                self.graphWidgetInput.setLabel(
                    'left',
                    "<span style=\"color:white;font-size:16px\">Voltage</span>"
                ),
                self.graphWidgetOutput.setLabel(
                    'bottom',
                    "<span style=\"color:white;font-size:16px\">Time (s)</span>"
                ),
                self.graphWidgetInput.setLabel(
                    'bottom',
                    "<span style=\"color:white;font-size:16px\">Time (s)</span>"
                ))
        elif self.inputType == "Speed":
            print("Lab: Speed")
            return (
                self.graphWidgetOutput.setLabel(
                    'left',
                    "<span style=\"color:white;font-size:16px\">&omega; (°/s)</span>"
                ),
                self.graphWidgetInput.setLabel(
                    'left',
                    "<span style=\"color:white;font-size:16px\">Voltage</span>"
                ),
                self.graphWidgetOutput.setLabel(
                    'bottom',
                    "<span style=\"color:white;font-size:16px\">Time (s)</span>"
                ),
                self.graphWidgetInput.setLabel(
                    'bottom',
                    "<span style=\"color:white;font-size:16px\">Time (s)</span>"
                ),
            )
コード例 #7
0
    def _load_analysis(self, x_axis_scale):
        this_question = db.question.find_one({'_id': self.index})
        title = this_question['title']
        content = this_question['content']
        time_tuple = localtime(this_question['created_time'])
        question_created_time = "%d-%d-%d %d:%d:%d" % (
            time_tuple.tm_year, time_tuple.tm_mon, time_tuple.tm_mday,
            time_tuple.tm_hour, time_tuple.tm_min, time_tuple.tm_sec)
        if content != '':
            self.textEdit.setText(title + '\n\n   提问时间:' +
                                  question_created_time + '\n   问题描述:\n' +
                                  content)
        else:
            self.textEdit.setText(title + '\n\n   提问时间:' +
                                  question_created_time)

        trend = db.question_trend.find_one({'_id': self.index})

        time_trend = trend['time']
        str_time = []
        for num_time in time_trend:
            local_time = localtime(num_time)
            str_time.append("%d-%d\n%d:%d\n" %
                            (local_time.tm_mon, local_time.tm_mday,
                             local_time.tm_hour, local_time.tm_min))
        ticks = [(i, j) for i, j in zip(time_trend, str_time)]

        rank_trend = trend['rank']
        convert_rank = [51 - i for i in rank_trend]
        rank_ticks = [(i, j) for i, j in zip(convert_rank, rank_trend)]
        rankAxis = AxisItem(orientation='left')
        rankAxis.setTicks([
            rank_ticks,
        ])
        strAxis = AxisItem(orientation='bottom', maxTickLength=5)
        strAxis.setTicks([
            ticks,
        ])
        rank_plot = PlotWidget(axisItems={
            'bottom': strAxis,
            'left': rankAxis
        },
                               background=None)
        rank_plot.setObjectName("tab")
        rank_plot.plot(x=time_trend,
                       y=convert_rank,
                       pen=(0, 255, 0),
                       symbol='o')
        rank_plot.enableAutoRange('x', x_axis_scale)
        self._add_analysis_tab(rank_plot, '排名')

        heat_trend = trend['heat']
        # strAxis.setStyle(autoExpandTextSpace=True)
        strAxis = AxisItem(orientation='bottom', maxTickLength=5)
        strAxis.setTicks([
            ticks,
        ])
        heat_plot = PlotWidget(axisItems={'bottom': strAxis}, background=None)
        heat_plot.setObjectName("tab")
        heat_plot.plot(x=time_trend, y=heat_trend, pen=(255, 0, 0), symbol='o')
        heat_plot.enableAutoRange('x', x_axis_scale)
        self._add_analysis_tab(heat_plot, '热度')

        answer_count_trend = trend['answer_count']
        strAxis = AxisItem(orientation='bottom', maxTickLength=5)
        strAxis.setTicks([
            ticks,
        ])
        answer_count_plot = PlotWidget(axisItems={'bottom': strAxis},
                                       background=None)
        answer_count_plot.setObjectName("tab")
        answer_count_plot.plot(x=time_trend,
                               y=answer_count_trend,
                               pen=(0, 0, 255),
                               symbol='o')
        answer_count_plot.enableAutoRange('x', x_axis_scale)
        self._add_analysis_tab(answer_count_plot, '回答量')

        follower_count_trend = trend['follower_count']
        strAxis = AxisItem(orientation='bottom', maxTickLength=5)
        strAxis.setTicks([
            ticks,
        ])
        follower_count_plot = PlotWidget(axisItems={'bottom': strAxis},
                                         background=None)
        follower_count_plot.setObjectName("tab")
        follower_count_plot.plot(x=time_trend,
                                 y=follower_count_trend,
                                 pen=(19, 234, 201),
                                 symbolBrush=(19, 234, 201),
                                 symbol='o',
                                 symbolPen='w')
        follower_count_plot.enableAutoRange('x', x_axis_scale)
        self._add_analysis_tab(follower_count_plot, '关注数')

        visitor_count_trend = trend['visitor_count']
        strAxis = AxisItem(orientation='bottom', maxTickLength=5)
        strAxis.setTicks([
            ticks,
        ])
        visitor_count_plot = PlotWidget(axisItems={'bottom': strAxis},
                                        background=None)
        visitor_count_plot.setObjectName("tab")
        visitor_count_plot.plot(x=time_trend,
                                y=visitor_count_trend,
                                pen=(195, 46, 212),
                                symbolBrush=(195, 46, 212),
                                symbol='t',
                                symbolPen='w')
        visitor_count_plot.enableAutoRange('x', x_axis_scale)
        self._add_analysis_tab(visitor_count_plot, '浏览量')
コード例 #8
0
class Plotter(QWidget):
    MAX_DATA_POINTS_PER_CURVE = 200000

    COLORS = [
        Qt.red,
        Qt.green,
        Qt.blue,  # RGB - http://ux.stackexchange.com/questions/79561
        Qt.yellow,
        Qt.cyan,
        Qt.magenta,  # Close to RGB
        Qt.darkRed,
        Qt.darkGreen,
        Qt.darkBlue,  # Darker RGB
        Qt.darkYellow,
        Qt.darkCyan,
        Qt.darkMagenta,  # Close to RGB
        Qt.gray,
        Qt.darkGray
    ]  # Leftovers

    INITIAL_X_RANGE = 60

    def __init__(self, parent=None):
        # Parent
        super(Plotter, self).__init__(parent)
        self.setWindowTitle('UAVCAN Plotter')
        self.setWindowIcon(APP_ICON)

        # Redraw timer
        self._update_timer = QTimer()
        self._update_timer.timeout.connect(self._update)
        self._update_timer.setSingleShot(False)
        self._update_timer.start(30)

        # PyQtGraph
        self._plot_widget = PlotWidget()
        self._plot_widget.setBackground((0, 0, 0))
        self._legend = self._plot_widget.addLegend()
        self._plot_widget.setRange(xRange=(0, self.INITIAL_X_RANGE), padding=0)
        self._plot_widget.showButtons()
        self._plot_widget.enableAutoRange()
        self._plot_widget.showGrid(x=True, y=True, alpha=0.4)

        # Controls
        # https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
        button_add_matcher = QtGui.QPushButton('New matcher', self)
        button_add_matcher.setIcon(QtGui.QIcon.fromTheme('list-add'))
        button_add_matcher.setToolTip('Add new curve matcher')
        button_add_matcher.clicked.connect(lambda: NewCurveMatcherWindow(
            self, lambda: sorted(self._active_messages), self.
            _add_curve_matcher).show())

        button_clear_plots = QtGui.QPushButton('Clear plots', self)
        button_clear_plots.setIcon(QtGui.QIcon.fromTheme('edit-clear'))
        button_clear_plots.setToolTip('Clear the plotting area')
        button_clear_plots.clicked.connect(lambda: self._remove_all_curves())

        def delete_all_matchers():
            self._curve_matchers = []
            for i in reversed(range(self._curve_matcher_container.count())):
                self._curve_matcher_container.itemAt(i).widget().deleteLater()
            self._remove_all_curves()

        button_delete_all_matchers = QtGui.QPushButton('Delete matchers', self)
        button_delete_all_matchers.setIcon(
            QtGui.QIcon.fromTheme('edit-delete'))
        button_delete_all_matchers.setToolTip('Delete all matchers')
        button_delete_all_matchers.clicked.connect(delete_all_matchers)

        self._autoscroll = QtGui.QCheckBox('Autoscroll', self)
        self._autoscroll.setChecked(True)
        self._max_x = self.INITIAL_X_RANGE

        # Layout
        control_panel = QHBoxLayout()
        control_panel.addWidget(button_add_matcher)
        control_panel.addWidget(button_clear_plots)
        control_panel.addWidget(self._autoscroll)
        control_panel.addStretch()
        control_panel.addWidget(button_delete_all_matchers)

        self._curve_matcher_container = QVBoxLayout()

        layout = QVBoxLayout()
        layout.addWidget(self._plot_widget, 1)
        layout.addLayout(control_panel)
        layout.addLayout(self._curve_matcher_container)
        self.setLayout(layout)

        # Logic
        self._color_index = 0
        self._curves = {}
        self._message_queue = multiprocessing.Queue()
        self._active_messages = set()  # set(data type name)
        self._curve_matchers = []

        # Defaults
        self._add_curve_matcher(
            CurveMatcher('uavcan.protocol.debug.KeyValue', 'value',
                         [('key', None)]))

    def _add_curve_matcher(self, matcher):
        self._curve_matchers.append(matcher)
        view = CurveMatcherView(matcher, self)

        def remove():
            self._curve_matchers.remove(matcher)
            self._curve_matcher_container.removeWidget(view)
            view.setParent(None)
            view.deleteLater()

        view.on_remove = remove
        self._curve_matcher_container.addWidget(view)

    def _update(self):
        # Processing messages
        while True:
            try:
                m = self._message_queue.get_nowait()
                self._process_message(m)
            except queue.Empty:
                break
        # Updating curves
        for curve in self._curves.values():
            if len(curve['x']):
                if len(curve['x']) > self.MAX_DATA_POINTS_PER_CURVE:
                    curve['x'] = curve['x'][-self.MAX_DATA_POINTS_PER_CURVE:]
                    curve['y'] = curve['y'][-self.MAX_DATA_POINTS_PER_CURVE:]
                assert len(curve['x']) == len(curve['y'])
                curve['plot'].setData(curve['x'], curve['y'])
                self._max_x = max(self._max_x, curve['x'][-1])
        # Updating view range
        if self._autoscroll.checkState():
            (xmin, xmax), _ = self._plot_widget.viewRange()
            diff = xmax - xmin
            xmax = self._max_x
            xmin = self._max_x - diff
            self._plot_widget.setRange(xRange=(xmin, xmax), padding=0)

    def _process_message(self, m):
        self._active_messages.add(m.data_type_name)
        for matcher in self._curve_matchers:
            if matcher.match(m):
                name, x, y = matcher.extract_curve_name_x_y(m)
                self._draw_curve(name, x, y)

    def _remove_all_curves(self):
        for curve in self._curves.values():
            self._plot_widget.removeItem(curve['plot'])
        self._plot_widget.clear()
        self._curves = {}
        self._color_index = 0
        self._legend.scene().removeItem(self._legend)
        self._legend = self._plot_widget.addLegend()

    def _draw_curve(self, name, x, y):
        if name not in self._curves:
            logging.info('Adding curve %r', name)
            color = self.COLORS[self._color_index % len(self.COLORS)]
            self._color_index += 1
            pen = mkPen(QColor(color), width=1)
            plot = self._plot_widget.plot(name=name, pen=pen)
            self._curves[name] = {
                'x': numpy.array([]),
                'y': numpy.array([]),
                'plot': plot
            }

        curve = self._curves[name]
        curve['x'] = numpy.append(curve['x'],
                                  [x] if isinstance(x, (float, int)) else x)
        curve['y'] = numpy.append(curve['y'],
                                  [y] if isinstance(y, (float, int)) else y)
        assert len(curve['x']) == len(curve['y'])

    def push_received_message(self, msg):
        self._message_queue.put_nowait(msg)
コード例 #9
0
ファイル: bus_monitor.py プロジェクト: hsteinhaus/gui_tool
class BusMonitorWidget(QGroupBox):
    DEFAULT_PLOT_X_RANGE = 120
    BUS_LOAD_PLOT_MAX_SAMPLES = 5000

    def __init__(self, parent, node, iface_name):
        super(BusMonitorWidget, self).__init__(parent)
        self.setTitle('CAN bus activity (%s)' %
                      iface_name.split(os.path.sep)[-1])

        self._node = node
        self._hook_handle = self._node.can_driver.add_io_hook(self._frame_hook)

        self._columns = [
            BasicTable.Column('Dir',
                              lambda e: (e[0].upper()),
                              searchable=False),
            BasicTable.Column('Local Time',
                              TimestampRenderer(),
                              searchable=False),
            BasicTable.Column(
                'CAN ID', lambda e:
                (('%0*X' % (8 if e[1].extended else 3, e[1].id)).rjust(8),
                 colorize_can_id(e[1]))),
            BasicTable.Column(
                'Data Hex', lambda e:
                (' '.join(['%02X' % x for x in e[1].data]).ljust(3 * e[
                    1].MAX_DATA_LENGTH), colorize_transfer_id(e))),
            BasicTable.Column(
                'Data ASCII', lambda e:
                (''.join([(chr(x) if 32 <= x <= 126 else '.')
                          for x in e[1].data]), colorize_transfer_id(e))),
            BasicTable.Column(
                'Src', lambda e: render_node_id_with_color(e[1], 'src')),
            BasicTable.Column(
                'Dst', lambda e: render_node_id_with_color(e[1], 'dst')),
            BasicTable.Column('Data Type',
                              lambda e: render_data_type_with_color(e[1]),
                              resize_mode=QHeaderView.Stretch),
        ]

        self._log_widget = RealtimeLogWidget(
            self,
            columns=self._columns,
            font=get_monospace_font(),
            post_redraw_hook=self._redraw_hook)
        self._log_widget.on_selection_changed = self._update_measurement_display

        def flip_row_mark(row, col):
            if col == 0:
                item = self._log_widget.table.item(row, col)
                if item.icon().isNull():
                    item.setIcon(get_icon('circle'))
                    flash(self,
                          'Row %d was marked, click again to unmark',
                          row,
                          duration=3)
                else:
                    item.setIcon(QIcon())

        self._log_widget.table.cellPressed.connect(flip_row_mark)

        self._stat_update_timer = QTimer(self)
        self._stat_update_timer.setSingleShot(False)
        self._stat_update_timer.timeout.connect(self._update_stat)
        self._stat_update_timer.start(500)

        self._traffic_stat = TrafficStatCounter()

        self._stat_frames_tx = QLabel('N/A', self)
        self._stat_frames_rx = QLabel('N/A', self)
        self._stat_traffic = QLabel('N/A', self)

        self._load_plot = PlotWidget(background=(0, 0, 0))
        self._load_plot.setRange(xRange=(0, self.DEFAULT_PLOT_X_RANGE),
                                 padding=0)
        self._load_plot.setMaximumHeight(150)
        self._load_plot.setMinimumHeight(100)
        self._load_plot.setMinimumWidth(100)
        self._load_plot.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self._load_plot.showGrid(x=True, y=True, alpha=0.4)
        self._load_plot.setToolTip('Frames per second')
        self._load_plot.getPlotItem().getViewBox().setMouseEnabled(x=True,
                                                                   y=False)
        self._load_plot.enableAutoRange()
        self._bus_load_plot = self._load_plot.plot(name='Frames per second',
                                                   pen=mkPen(QColor(
                                                       Qt.lightGray),
                                                             width=1))
        self._bus_load_samples = [], []
        self._started_at_mono = time.monotonic()

        layout = QVBoxLayout(self)

        layout.addWidget(self._log_widget, 1)

        stat_vars_layout = QGridLayout(self)
        stat_layout_next_row = 0

        def add_stat_row(label, value):
            nonlocal stat_layout_next_row
            stat_vars_layout.addWidget(QLabel(label, self),
                                       stat_layout_next_row, 0)
            stat_vars_layout.addWidget(value, stat_layout_next_row, 1)
            value.setMinimumWidth(75)
            stat_layout_next_row += 1

        add_stat_row('Frames transmitted:', self._stat_frames_tx)
        add_stat_row('Frames received:', self._stat_frames_rx)
        add_stat_row('Frames per second:', self._stat_traffic)
        stat_vars_layout.setRowStretch(stat_layout_next_row, 1)

        stat_layout = QHBoxLayout(self)
        stat_layout.addLayout(stat_vars_layout)
        stat_layout.addWidget(self._load_plot, 1)

        layout.addLayout(stat_layout, 0)
        self.setLayout(layout)

    def close(self):
        self._hook_handle.remove()

    def _update_stat(self):
        bus_load, ts_mono = self._traffic_stat.get_frames_per_second()
        self._stat_traffic.setText(str(int(bus_load + 0.5)))

        if len(self._bus_load_samples[0]) >= self.BUS_LOAD_PLOT_MAX_SAMPLES:
            self._bus_load_samples[0].pop(0)
            self._bus_load_samples[1].pop(0)

        self._bus_load_samples[1].append(bus_load)
        self._bus_load_samples[0].append(ts_mono - self._started_at_mono)

        self._bus_load_plot.setData(*self._bus_load_samples)

        (xmin, xmax), _ = self._load_plot.viewRange()
        diff = xmax - xmin
        xmax = self._bus_load_samples[0][-1]
        xmin = self._bus_load_samples[0][-1] - diff
        self._load_plot.setRange(xRange=(xmin, xmax), padding=0)

    def _redraw_hook(self):
        self._stat_frames_tx.setText(str(self._traffic_stat.tx))
        self._stat_frames_rx.setText(str(self._traffic_stat.rx))

    def _frame_hook(self, direction, frame):
        self._traffic_stat.add_frame(direction, frame)
        self._log_widget.add_item_async((direction, frame))

    def _update_measurement_display(self, selected_rows_cols):
        if not selected_rows_cols:
            return

        min_row = min([row for row, _ in selected_rows_cols])
        max_row = max([row for row, _ in selected_rows_cols])

        def get_row_ts(row):
            return TimestampRenderer.parse_timestamp(
                self._log_widget.table.item(row, 1).text())

        def get_load_str(num_frames, dt):
            if dt >= 1e-6:
                return 'average load %.1f FPS' % (num_frames / dt)
            return 'average load is unknown'

        if min_row == max_row:
            num_frames = min_row
            first_ts = get_row_ts(0)
            current_ts = get_row_ts(min_row)
            dt = current_ts - first_ts
            flash(self,
                  '%d frames from beginning, %.3f sec since first frame, %s',
                  num_frames, dt, get_load_str(num_frames, dt))
        else:
            num_frames = max_row - min_row + 1
            first_ts = get_row_ts(min_row)
            last_ts = get_row_ts(max_row)
            dt = last_ts - first_ts
            flash(self, '%d frames, timedelta %.6f sec, %s', num_frames, dt,
                  get_load_str(num_frames, dt))
コード例 #10
0
ファイル: iScan.py プロジェクト: stevens4/sitzlabexpcontrol
class SmartScanGUI(QtGui.QWidget):
    # oh god i'm so sorry. don't listen to him; he's never sorry.
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.setLayout(QtGui.QHBoxLayout())
        
        # a dict to store data
        self.dataMatrix = {}
        self.refData = {}
        
        ########################################## create a plot and associated widget ###########################################################
        self.plotWidget = PlotWidget()
        self.layout().addWidget(self.plotWidget,1)
        
        def xYPlot(plotWidget,x,y,yerr=None,xerr=None,color='w',name='Current'):
            thisPlot = plotWidget.plot(x,y,pen=mkPen(color,width=2))
            plotWidget.addItem(
                ErrorBarItem(
                    x=np.asarray(x),
                    y=np.asarray(y),
                    top=np.asarray(yerr) if yerr is not None else None,
                    bottom=np.asarray(yerr) if yerr is not None else None,
                    left=np.asarray(xerr) if xerr is not None else None,
                    right=np.asarray(xerr) if xerr is not None else None,
                    beam=.05,
                    pen=mkPen(color)
                )
            )
            
        # method for updating plot with new data (plot has to be cleared first)
        def updatePlot():
            self.plotWidget.clear()
            for name, rData in self.refData.iteritems():
                xYPlot(
                    self.plotWidget,
                    rData['data'][0],
                    rData['data'][1],
                    yerr=rData['data'][2],
                    color=rData['color'],
                    name=name
                )
            if len(self.dataMatrix.keys()) >= 1:
                for xVal, yValues in self.dataMatrix.items():
                    if xVal in self.xVals:
                        oldMean = self.yVals[self.xVals.index(xVal)]
                        thisMean = np.mean(yValues)
                        newMean = (oldMean+thisMean)/2.
                        self.yVals[self.xVals.index(xVal)] = newMean
                        newErr = np.std(yValues)/np.sqrt(len(yValues))
                        self.errVals[self.xVals.index(xVal)] = newErr
                    else:
                        self.xVals.append(xVal)
                        mean = np.mean(yValues)
                        self.yVals.append(mean)
                        err = np.std(yValues)/np.sqrt(len(yValues))
                        self.errVals.append(err)
                xYPlot(self.plotWidget,self.xVals,self.yVals,yerr=self.errVals) 

        
        ############################################## configure a control panel layout ########################################################
        cpLayout = QtGui.QVBoxLayout()
        self.layout().addLayout(cpLayout)

        # configure the output widget
        outputPane = QtGui.QTabWidget()
        cpLayout.addWidget(LabelWidget('output',outputPane))


            
        @inlineCallbacks
        def onInit():
            ############################################################# VOLTMETER OUTPUT ###########################################################
            # add volt meter to scan output
            if DEBUG:
                vmURL = TEST_VOLTMETER_SERVER
            else:
                vmURL = VOLTMETER_SERVER
            vmProtocol = yield getProtocol(vmURL)
            vmClient = VoltMeterClient(vmProtocol)    
            vmWidget = VoltMeterOutputWidget(vmClient)
            outputPane.addTab(vmWidget,'voltmeter')


            ############################################################# BEGIN INPUTS ###########################################################

            # configure the input widget
            inputPane = QtGui.QTabWidget()
            inputPane.setTabPosition(inputPane.West)
            cpLayout.addWidget(LabelWidget('input',inputPane),1)    

            
            # algorithm for scan inputs is:
            # 0. check to see if input is disabled
            # 1. create client for server from protocol object
            # 2. create combo widget to hold interval and list widgets
            # 3. create interval widget using client object, add to combo
            # 4. same for list widget
            # 5. add combo widget to base combo widget (resulting in 2-D tab widget)

            
            ############################################################# MANUAL INPUT ###########################################################
            class ManualInputWidget(QtGui.QWidget):
                def __init__(self,parentWidget):
                    QtGui.QWidget.__init__(self)
                    self.parentWidget = parentWidget
                    self.done = False
                
                def initScan(self):
                    return
                
                def checkIfDone(self):
                    return
                
                def next(self):
                    result, valid = QtGui.QInputDialog.getDouble(
                        self.parentWidget, 
                        'next x value', 
                        'enter next x value',
                        decimals=6
                    )
                    if valid:
                        return result
                    else:
                        return None
                
                def cancel(self):
                    return

            inputPane.addTab(
                ManualInputWidget(self),
                'manual'
            )
            
            
            
            ############################################################# MANUAL SCAN INPUT ###########################################################
            
            class ManualScanInputWidget(InputWidget):
                def __init__(self,parent):
                    spinBoxProps = {
                        'rangeMin':-100000,
                        'rangeMax':100000,
                        'stepMin':.000001,
                        'stepMax':100000,
                        'startInit':0,
                        'stopInit':1,
                        'stepSizeInit':.1
                    }
                    
                    def setPosition(position):
                        msgBox = QtGui.QMessageBox()
                        msgBox.setText("next position:\t"+str(position))
                        msgBox.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
                        msgBox.setDefaultButton(QtGui.QMessageBox.Ok)
                        ret = msgBox.exec_()
                        if ret == QtGui.QMessageBox.Ok:
                            return position
                        elif ret == QtGui.QMessageBox.Cancel:
                            return None
                    
                    InputWidget.__init__(self,lambda(x):None,setPosition,spinBoxProperties = spinBoxProps)
                    
            inputPane.addTab(
                ManualScanInputWidget(self),
                'manual scan'
            )

            
            ############################################################# STEPPER MOTOR INPUTS ###########################################################
            
            # load in the stepper motor names
            from config.steppermotor import KDP, BBO, PDL, LID, POL 
            
            # get stepper motor protocol (how to communicate to stepper motor server)
            smProtocol = yield getProtocol(
                TEST_STEPPER_MOTOR_SERVER if DEBUG else STEPPER_MOTOR_SERVER 
            )
            
            # define a chunked (cancellable) client for each stepper motor
            stepperMotorsClients = {}
            for stepperMotorName in (KDP,BBO,PDL,LID,POL):
                stepperMotorsClients[stepperMotorName] = ChunkedStepperMotorClient(smProtocol,stepperMotorName)

            # define an input widget for each stepper motor, each add to input pane
            for smID,smClient in stepperMotorsClients.items():
                spinBoxProps = {
                    'rangeMin':-100000,
                    'rangeMax':100000,
                    'stepMin':1,
                    'stepMax':100000,
                    'startInit':0,
                    'stopInit':100,
                    'stepSizeInit':1
                }
                
                thisInputWidget = InputWidget(
                    smClient.getPosition,
                    smClient.setPosition,
                    cancelCommand = smClient.cancel,
                    spinBoxProperties = spinBoxProps
                )
            
                inputPane.addTab(
                    thisInputWidget,
                    smID
                )
            
            
            
            ############################################################# WAVELENGTH SERVER INPUT ###########################################################

            '''
            # add wavelength client to scan input
            wlProtocol = yield getProtocol(
                TEST_WAVELENGTH_SERVER if DEBUG else WAVELENGTH_SERVER
            )
            wlClient = WavelengthClient(wlProtocol)
            
            spinBoxProps = {
                'rangeMin':24100,
                'rangeMax':25000,
                'stepMin':.01,
                'stepMax':900,
                'startInit':24200,
                'stopInit':24220,
                'stepSizeInit':1
            }
           
            wlInputWidget = InputWidget(
                    wlClient.getWavelength,
                    wlClient.setWavelength,
                    cancelCommand = wlClient.cancelWavelengthSet,
                    spinBoxProperties = spinBoxProps
            )

            inputPane.addTab(
                wlInputWidget,
                'wl'
            )
            '''
            
            
            
            ############################################################# POLARIZER SERVER INPUT ###########################################################
            '''
            # get protocol
            polProtocol = yield getProtocol(
                TEST_POLARIZER_SERVER if DEBUG else POLARIZER_SERVER
            )
            
            # define the client
            polClient = PolarizerClient(polProtocol)
            
            # set limits on spinboxes
            spinBoxProps = {
                'rangeMin':-720,
                'rangeMax':720,
                'stepMin':.01,
                'stepMax':720,
                'startInit':0,
                'stopInit':90,
                'stepSizeInit':5
            }
            
            polInputWidget = InputWidget(
                    polClient.getAngle,
                    polClient.setAngle,
                    cancelCommand = polClient.cancelAngleSet,
                    spinBoxProperties = spinBoxProps
                )

            inputPane.addTab(
                polInputWidget,
                'pol'
            )
            '''
            
            
            
            ############################################################# DDG INPUTS ###########################################################
            
            # load in the delay generator names
            from config.delaygenerator import MAV_PUMP_LAMP, MAV_PUMP_QSW, MAV_PROBE_LAMP, MAV_PROBE_QSW, MAV_NOZZLE
            
            # load in the delay generator limits
            from config.delaygenerator import MIN_DELAY, MAX_DELAY, DELAY_RES
            
            # get the delay generator protocol
            dgProtocol = yield getProtocol(
                TEST_DELAY_GENERATOR_SERVER if DEBUG else DELAY_GENERATOR_SERVER
            )
            dgClient = DelayGeneratorClient(dgProtocol)
            
            # define an input widget for each delay generator, each add to input pane 
            for dgName in (MAV_PUMP_LAMP, MAV_PUMP_QSW, MAV_PROBE_LAMP, MAV_PROBE_QSW, MAV_NOZZLE):
                
                spinBoxProps = {
                    'rangeMin':MIN_DELAY,
                    'rangeMax':MAX_DELAY,
                    'stepMin':DELAY_RES,
                    'stepMax':MAX_DELAY-MIN_DELAY,
                    'startInit':1,
                    'stopInit':1001,
                    'stepSizeInit':10
                }

                # because there is one DG client for all DGs (unlike SM client), we use partial to map the client\
                # commands to individual DGs so that we preserve as much of the same structure as possible.
                thisInputWidget = InputWidget(
                    partial(dgClient.getDelay,dgName),
                    partial(dgClient.setPartnerDelay,dgName),
                    spinBoxProperties = spinBoxProps
                )
            
                inputPane.addTab(
                    thisInputWidget,
                    dgName
                )
                
            ############################################################# END INPUTS ###########################################################


            ############################################################# SCANNING ###########################################################

            #define fundamental scan logic:
            #   1: ask independent variable to change, wait
            #   2: measure dependent
            #   3: perform onStep task with x-y pair (e.g. update plot)
            
            class Scan:
                def __init__(self,inputWidget,outputWidget,onStepFunct,repeats=1,plotWidget=None):
                    self.input = inputWidget
                    self.output = outputWidget
                    self.onStep = onStepFunct
                    self.output.plot = plotWidget
                    self.repeatTotal = repeats
                    self.activeRepeat = 1
                    self.startScan()
                    
                def startScan(self):
                    if len(self.input.scanValues) == 0: self.input.initScan()
                    self.output.initScan()
                    self.paused = False
                    self.done = False
                    self.loop()

                @inlineCallbacks
                def loop(self):
                    # pause if paused
                    if self.paused:
                        resumeCancelDialog = QtGui.QMessageBox()
                        resumeCancelDialog.setText("the scan has been paused.")
                        resumeCancelDialog.setInformativeText("do you want to cancel the scan?")
                        resumeCancelDialog.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
                        resumeCancelDialog.setDefaultButton(QtGui.QMessageBox.No)
                    
                        option = resumeCancelDialog.exec_()
                        
                        if option == QtGui.QMessageBox.Yes:
                            #quit the scan
                            self.cancel()
                            return
                            
                        if option == QtGui.QMessageBox.No:
                            #resume the scan
                            self.resume()
                    
                        while self.paused:
                            QtGui.QApplication.processEvents()
                            sleep(.1)
                        
                    # check if done by asking input
                    self.input.checkIfDone()
                    inputDone = self.input.done
                    
                    # if done, finish to clean up
                    if inputDone:
                        self.finish()
                    
                    # if not done, continue onto next point & measure
                    if not inputDone:
                        inputData = yield self.input.next()
                        if inputData == None:
                            yield self.cancel()
                            return
                        else:
                            self.output.setPlotterXVal(inputData)
                            outputDataDefer = self.output.startAcquisition()
                            outputData = yield outputDataDefer
                            yield self.onStep(inputData,outputData)
                            self.loop()
                    
                def pause(self):
                    self.paused = True
                
                def resume(self):
                    self.paused = False
                    
                def cancel(self):
                    self.done = True
                    self.input.cancel()
                    self.output.cancel()
                    scanToggleClicked()
                    
                @inlineCallbacks    
                def finish(self):
                    if self.activeRepeat == self.repeatTotal:
                        self.done = True
                        yield self.input.cancel()
                        self.output.cancel()
                        scanToggleClicked()
                    else:
                        self.activeRepeat += 1
                        if self.activeRepeat % 2 == 0:
                            self.input.initFlipScan()
                        else:
                            self.input.initScan()
                        self.startScan()

            # define what to do after values are acquired at a position
            def onStepped(input,output):
                # unpack scan step data
                position, output = input, output
                if position == None or output == None: 
                    return
                else:
                    # update data array
                    if position not in self.dataMatrix.keys():
                        self.dataMatrix[position] = []
                    for value in output:
                        self.dataMatrix[position].append(value)

                    # update plot
                    updatePlot()
               
            # define scanning start/pause/cancel logic
            def scanToggleClicked():
                if self.scanning:
                    #currently scanning so check if the scan is done, if not this was a pause
                    if not self.thisScan.done:
                        #pause the scan, pop resume/cancel dialog
                        self.thisScan.pause()

                    else:
                        self.scanning = False
                        scanToggleButton.setText("start")
                        return
                    
                if not self.scanning:
                    #not currently scanning, so start the scan
                    self.scanning = True
                    
                    #gather the agents (classes with specific methods) that progress scan and measure values
                    inputAgent = inputPane.currentWidget()
                    outputAgent = outputPane.currentWidget()
                    updateAgent = onStepped
                    
                    #dump whatever data is in the matrix, prepare the plot
                    for xVal in self.dataMatrix.keys():
                        del self.dataMatrix[xVal]
                    
                    # clear the lists that the plot uses to plot
                    self.xVals = []
                    self.yVals = []
                    self.errVals = []

                    updatePlot()
                    self.plotWidget.enableAutoRange()
                    
                    #define the scan, which automatically starts it
                    numToRepeat = self.repeatSpinBox.value()
                    self.thisScan = Scan(inputAgent, outputAgent, updateAgent, numToRepeat, self.plotWidget)
                    
                    #rename our button so users know about the other half of this function
                    scanToggleButton.setText("pause/cancel")
                
                return
            
            self.scanning = False
            
            # set up the GUI to have the scan start/pause/cancel button and repeat spinbox    
            scanPane = QtGui.QHBoxLayout()
            scanToggleButton = QtGui.QPushButton("start")
            scanToggleButton.clicked.connect(scanToggleClicked)
            scanPane.addWidget(scanToggleButton)
            
            self.repeatSpinBox = QtGui.QSpinBox()
            self.repeatSpinBox.setRange(1,10000)
            self.repeatSpinBox.setValue(1)
            scanPane.addWidget(self.repeatSpinBox)
            
            cpLayout.addWidget(LabelWidget('scan',scanPane))


            ############################################################# LOAD FUNCTIONS ###########################################################

            refLayout = QtGui.QHBoxLayout()
            
            def onLoadClicked():
                dir, filePrefix = filenameGen()
                dir = join(POOHDATAPATH,dir)
                
                refFileName = QtGui.QFileDialog.getOpenFileName(self,'select file', dir,"CSV Files (*.csv)")
                
                rData = np.loadtxt(open(refFileName[0],"rb"),delimiter=",")
                name = refFileName[0].rpartition('/')[2]
                
                color = QtGui.QColorDialog.getColor()
                
                if 'matrix' in refFileName[0]:
                    xVals = rData[:,0]
                    yVals = []
                    errVals = []
                    for rowNum in range(len(xVals)):
                        thisYList = rData[rowNum,1:]
                        yVals.append(np.mean(thisYList))
                        errVals.append(np.std(thisYList)/np.sqrt(len(thisYList)))
                    self.refData[name] = {
                        'color': color,
                        'data': [xVals, yVals, errVals]
                    }
                else:
                    self.refData[name] = {
                        'color': color,
                        'data': [rData[:,0], rData[:,1], rData[:,2]]
                    }
                
                updatePlot()
            
            loadButton = QtGui.QPushButton('load')
            loadButton.clicked.connect(onLoadClicked)
            refLayout.addWidget(SqueezeRow(loadButton))

            def onClearClicked():
                for refs in self.refData.keys():
                    del self.refData[refs]
                    
                updatePlot()

            clearButton = QtGui.QPushButton('clear all')
            clearButton.clicked.connect(onClearClicked)
            refLayout.addWidget(SqueezeRow(clearButton))

            cpLayout.addWidget(LabelWidget('reference',refLayout))    

            
            ############################################################# SAVE FUNCTIONS ###########################################################
            
            saveLayout = QtGui.QHBoxLayout()
            
            def onSaveRawClicked():
                dataType = np.dtype(np.float32)
                orderedDataDict = OrderedDict(sorted(self.dataMatrix.items()))
                data = np.reshape(np.asarray(orderedDataDict.keys(),dtype=dataType),(len(orderedDataDict.keys()),1)) #just x values as a column
                yVals = np.asarray(orderedDataDict.values(),dtype=dataType)
                data = np.hstack((data,yVals))
                saveFile(data,'matrix')
            saveRawButton = QtGui.QPushButton('save (raw)')
            saveRawButton.clicked.connect(onSaveRawClicked)
            saveLayout.addWidget(SqueezeRow(saveRawButton))
            
            def onSaveStatsClicked():
                xData = self.dataMatrix.keys()
                yData = []
                errData = []
                for rawValues in self.dataMatrix.values():
                    yData.append(np.mean(rawValues))
                    errData.append(np.std(rawValues)/np.sqrt(len(rawValues)))
                data = np.asarray([xData, yData, errData], dtype=np.dtype(np.float32))
                saveFile(np.transpose(data),'stats')
            saveStatsButton = QtGui.QPushButton('save (stats)')
            saveStatsButton.clicked.connect(onSaveStatsClicked)
            saveLayout.addWidget(SqueezeRow(saveStatsButton))
                
            def saveFile(dataToSave,prefix):
                dir, filePrefix = filenameGen()
                dir = join(POOHDATAPATH,dir)
                checkPath(dir)
                subDir = QtGui.QFileDialog.getExistingDirectory(self,'select folder', dir)
                desc, valid = QtGui.QInputDialog.getText(self, 'enter file description','description' )
                
                if not valid:
                    desc = None
                else:
                    saveCSV(dataToSave, subDir=subDir, description=prefix+'_'+desc)
            
            cpLayout.addWidget(LabelWidget('save',saveLayout))    

        onInit()

    def closeEvent(self, event):
        if reactor.running: reactor.stop()
        event.accept()
コード例 #11
0
ファイル: uavcan_plotter.py プロジェクト: hsteinhaus/gui_tool
class Plotter(QWidget):
    MAX_DATA_POINTS_PER_CURVE = 200000

    COLORS = [Qt.red, Qt.green, Qt.blue,                        # RGB - http://ux.stackexchange.com/questions/79561
              Qt.yellow, Qt.cyan, Qt.magenta,                   # Close to RGB
              Qt.darkRed, Qt.darkGreen, Qt.darkBlue,            # Darker RGB
              Qt.darkYellow, Qt.darkCyan, Qt.darkMagenta,       # Close to RGB
              Qt.gray, Qt.darkGray]                             # Leftovers

    INITIAL_X_RANGE = 60

    def __init__(self, parent=None):
        # Parent
        super(Plotter, self).__init__(parent)
        self.setWindowTitle('UAVCAN Plotter')
        self.setWindowIcon(APP_ICON)

        # Redraw timer
        self._update_timer = QTimer()
        self._update_timer.timeout.connect(self._update)
        self._update_timer.setSingleShot(False)
        self._update_timer.start(30)

        # PyQtGraph
        self._plot_widget = PlotWidget()
        self._plot_widget.setBackground((0, 0, 0))
        self._legend = self._plot_widget.addLegend()
        self._plot_widget.setRange(xRange=(0, self.INITIAL_X_RANGE), padding=0)
        self._plot_widget.showButtons()
        self._plot_widget.enableAutoRange()
        self._plot_widget.showGrid(x=True, y=True, alpha=0.4)

        # Controls
        # https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
        button_add_matcher = QtGui.QPushButton('New matcher', self)
        button_add_matcher.setIcon(QtGui.QIcon.fromTheme('list-add'))
        button_add_matcher.setToolTip('Add new curve matcher')
        button_add_matcher.clicked.connect(
            lambda: NewCurveMatcherWindow(self, lambda: sorted(self._active_messages), self._add_curve_matcher).show())

        button_clear_plots = QtGui.QPushButton('Clear plots', self)
        button_clear_plots.setIcon(QtGui.QIcon.fromTheme('edit-clear'))
        button_clear_plots.setToolTip('Clear the plotting area')
        button_clear_plots.clicked.connect(lambda: self._remove_all_curves())

        def delete_all_matchers():
            self._curve_matchers = []
            for i in reversed(range(self._curve_matcher_container.count())):
                self._curve_matcher_container.itemAt(i).widget().deleteLater()
            self._remove_all_curves()

        button_delete_all_matchers = QtGui.QPushButton('Delete matchers', self)
        button_delete_all_matchers.setIcon(QtGui.QIcon.fromTheme('edit-delete'))
        button_delete_all_matchers.setToolTip('Delete all matchers')
        button_delete_all_matchers.clicked.connect(delete_all_matchers)

        self._autoscroll = QtGui.QCheckBox('Autoscroll', self)
        self._autoscroll.setChecked(True)
        self._max_x = self.INITIAL_X_RANGE

        # Layout
        control_panel = QHBoxLayout()
        control_panel.addWidget(button_add_matcher)
        control_panel.addWidget(button_clear_plots)
        control_panel.addWidget(self._autoscroll)
        control_panel.addStretch()
        control_panel.addWidget(button_delete_all_matchers)

        self._curve_matcher_container = QVBoxLayout()

        layout = QVBoxLayout()
        layout.addWidget(self._plot_widget, 1)
        layout.addLayout(control_panel)
        layout.addLayout(self._curve_matcher_container)
        self.setLayout(layout)

        # Logic
        self._color_index = 0
        self._curves = {}
        self._message_queue = multiprocessing.Queue()
        self._active_messages = set() # set(data type name)
        self._curve_matchers = []

        # Defaults
        self._add_curve_matcher(CurveMatcher('uavcan.protocol.debug.KeyValue', 'value', [('key', None)]))

    def _add_curve_matcher(self, matcher):
        self._curve_matchers.append(matcher)
        view = CurveMatcherView(matcher, self)

        def remove():
            self._curve_matchers.remove(matcher)
            self._curve_matcher_container.removeWidget(view)
            view.setParent(None)
            view.deleteLater()

        view.on_remove = remove
        self._curve_matcher_container.addWidget(view)

    def _update(self):
        # Processing messages
        while True:
            try:
                m = self._message_queue.get_nowait()
                self._process_message(m)
            except queue.Empty:
                break
        # Updating curves
        for curve in self._curves.values():
            if len(curve['x']):
                if len(curve['x']) > self.MAX_DATA_POINTS_PER_CURVE:
                    curve['x'] = curve['x'][-self.MAX_DATA_POINTS_PER_CURVE:]
                    curve['y'] = curve['y'][-self.MAX_DATA_POINTS_PER_CURVE:]
                assert len(curve['x']) == len(curve['y'])
                curve['plot'].setData(curve['x'], curve['y'])
                self._max_x = max(self._max_x, curve['x'][-1])
        # Updating view range
        if self._autoscroll.checkState():
            (xmin, xmax), _ = self._plot_widget.viewRange()
            diff = xmax - xmin
            xmax = self._max_x
            xmin = self._max_x - diff
            self._plot_widget.setRange(xRange=(xmin, xmax), padding=0)


    def _process_message(self, m):
        self._active_messages.add(m.data_type_name)
        for matcher in self._curve_matchers:
            if matcher.match(m):
                name, x, y = matcher.extract_curve_name_x_y(m)
                self._draw_curve(name, x, y)

    def _remove_all_curves(self):
        for curve in self._curves.values():
            self._plot_widget.removeItem(curve['plot'])
        self._plot_widget.clear()
        self._curves = {}
        self._color_index = 0
        self._legend.scene().removeItem(self._legend)
        self._legend = self._plot_widget.addLegend()

    def _draw_curve(self, name, x, y):
        if name not in self._curves:
            logging.info('Adding curve %r', name)
            color = self.COLORS[self._color_index % len(self.COLORS)]
            self._color_index += 1
            pen = mkPen(QColor(color), width=1)
            plot = self._plot_widget.plot(name=name, pen=pen)
            self._curves[name] = {'x': numpy.array([]), 'y': numpy.array([]), 'plot': plot}

        curve = self._curves[name]
        curve['x'] = numpy.append(curve['x'], [x] if isinstance(x, (float, int)) else x)
        curve['y'] = numpy.append(curve['y'], [y] if isinstance(y, (float, int)) else y)
        assert len(curve['x']) == len(curve['y'])

    def push_received_message(self, msg):
        self._message_queue.put_nowait(msg)
コード例 #12
0
class RealtimePlotWidget(QWidget):
    AUTO_RANGE_FRACTION = 0.99

    COLORS = [
        Qt.red, Qt.blue, Qt.green, Qt.magenta, Qt.cyan, Qt.darkRed,
        Qt.darkBlue, Qt.darkGreen, Qt.darkYellow, Qt.gray
    ]

    def __init__(self, display_measurements, parent):
        super(RealtimePlotWidget, self).__init__(parent)
        self.setAttribute(
            Qt.WA_DeleteOnClose)  # This is required to stop background timers!
        self._plot_widget = PlotWidget()
        self._plot_widget.setBackground((0, 0, 0))
        self._legend = self._plot_widget.addLegend()
        self._plot_widget.showButtons()
        self._plot_widget.showGrid(x=True, y=True, alpha=0.3)
        vbox = QVBoxLayout(self)
        vbox.addWidget(self._plot_widget)
        self.setLayout(vbox)

        self._last_update_ts = 0
        self._reset_required = False

        self._update_timer = QTimer(self)
        self._update_timer.setSingleShot(False)
        self._update_timer.timeout.connect(self._update)
        self._update_timer.start(200)

        self._color_index = 0
        self._curves = {}

        # Crosshair
        def _render_measurements(cur, ref):
            text = 'time %.6f sec,  y %.6f' % cur
            if ref is None:
                return text
            dt = cur[0] - ref[0]
            dy = cur[1] - ref[1]
            if abs(dt) > 1e-12:
                freq = '%.6f' % abs(1 / dt)
            else:
                freq = 'inf'
            display_measurements(text + ';' + ' ' * 4 +
                                 'dt %.6f sec,  freq %s Hz,  dy %.6f' %
                                 (dt, freq, dy))

        display_measurements(
            'Hover to sample Time/Y, click to set new reference')
        add_crosshair(self._plot_widget, _render_measurements)

        # Final reset
        self.reset()

    def _trigger_auto_reset_if_needed(self):
        ts = time.monotonic()
        dt = ts - self._last_update_ts
        self._last_update_ts = ts
        if dt > 2:
            self._reset_required = True

    def add_curve(self, curve_id, curve_name, data_x=[], data_y=[]):
        color = QColor(self.COLORS[self._color_index % len(self.COLORS)])
        self._color_index += 1
        pen = mkPen(color, width=1)
        plot = self._plot_widget.plot(name=curve_name, pen=pen)
        data_x = numpy.array(data_x)
        data_y = numpy.array(data_y)
        self._curves[curve_id] = {'data': (data_x, data_y), 'plot': plot}
        self._trigger_auto_reset_if_needed()

    def update_values(self, curve_id, x, y):
        curve = self._curves[curve_id]
        old_x, old_y = curve['data']
        curve['data'] = numpy.append(old_x, x), numpy.append(old_y, y)
        self._trigger_auto_reset_if_needed()

    def reset(self):
        for curve in self._curves.keys():
            self._plot_widget.removeItem(self._curves[curve]['plot'])

        self._curves = {}
        self._color_index = 0

        self._plot_widget.enableAutoRange(enable=self.AUTO_RANGE_FRACTION,
                                          x=self.AUTO_RANGE_FRACTION,
                                          y=self.AUTO_RANGE_FRACTION)

        self._legend.scene().removeItem(self._legend)
        self._legend = self._plot_widget.addLegend()

    def _update(self):
        if self._reset_required:
            self.reset()
            self._reset_required = False

        for curve in self._curves.values():
            if len(curve['data'][0]):
                curve['plot'].setData(*curve['data'])
コード例 #13
0
class Canvas2Dupgraded(AbstractCanvas):
    def __init__(self, data_dict=None, parent=None):
        super().__init__(self)
        self.shareData(**data_dict)

        self._i = self.current_state
        self.triggered = True
        self.title = self.options['column']
        self.synchronizedPlot = self.options['synchronizedPlot']
        self.one_onePlot = self.options['one_one']
        # Switch to using white background and black foreground
        pg.setConfigOption('background', 'w')
        pg.setConfigOption('foreground', 'k')
        self.plotWidget = PlotWidget(self)
        self.construct_triggered_plot()



        self.graph_data = self.plot_data[self.title].tolist()
        self.internal_iterations = len(self.graph_data)
        self.createPlotCanvas()

    def construct_triggered_plot(self):
        if self.trigger is not None and self.one_onePlot:
            self.triggered = False
            # shorter list
            self.plot_data = self.plot_data.iloc[self.trigger]
            """
            this displays only datapoints that match exactly the 
            view on the 3D animation i.e. one-one plot
            """
            self.options['line_style'] = 'None'

    def createPlotCanvas(self):
        self.null_data = self.plot_data[self.options['xcolumn']].tolist()
        self.plotWidget.setTitle(self.title)
        self.plotWidget.setLabel('bottom', self.options['xcolumn'])
        self.plotWidget.setGeometry(0, 0, self.geom[0]-60, self.geom[1]-60)
        self.plotWidget.setXRange(0, self.internal_iterations)
        self.plotWidget.setYRange(np.min(self.graph_data), np.max(self.graph_data),
                                  padding=0.1)
        self.plotWidget.enableAutoRange('xy', True)
        if self.synchronizedPlot == False and not self.one_onePlot:
            self.plotData = self.plotWidget.plot(self.null_data, self.graph_data,
                                    pen=pg.mkPen(color=self.options['color'][0],
                                    width=self.options['marker_size']),
                                    name="data1", clear=True)
        else:
            self.plotData = self.plotWidget.plot(self.graph_data[:self._i],
                                    pen=pg.mkPen(color=self.options['color'][0],
                                    width=self.options['marker_size']),
                                    name="data1", clear=True)

    def on_resize_geometry_reset(self, geom):
        """
        when another widget is promoted, this window must resize too
        this means resetting the graph unfortunately
        """
        self.geom = geom
        self.plotWidget.setGeometry(0, 0, self.geom[0]-60, self.geom[1]-60)

    def set_i(self, value, trigger=False, record=False, reset=False):
        if self.synchronizedPlot == False and not self.one_onePlot:
            """
            instant plot cannot obstruct one-one plot
            """
            return
        if trigger and self.triggered:
            self._i = self.trigger[value]
        else:
            self._i = value
        self._i %= self.internal_iterations
        self.plotData.setData(self.null_data[:self._i], self.graph_data[:self._i])
        pg.QtGui.QApplication.processEvents()
        self.plotWidget.setGeometry(0, 0, self.geom[0]-60, self.geom[1]-60)
コード例 #14
0
ファイル: serial_plot.py プロジェクト: zenglongGH/px4esc
class RealtimePlotWidget(QWidget):
    AUTO_RANGE_FRACTION = 0.99

    COLORS = [Qt.red, Qt.blue, Qt.green, Qt.magenta, Qt.cyan,
              Qt.darkRed, Qt.darkBlue, Qt.darkGreen, Qt.darkYellow, Qt.gray]

    def __init__(self, display_measurements, parent):
        super(RealtimePlotWidget, self).__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose)              # This is required to stop background timers!
        self._plot_widget = PlotWidget()
        self._plot_widget.setBackground((0, 0, 0))
        self._legend = self._plot_widget.addLegend()
        self._plot_widget.showButtons()
        self._plot_widget.showGrid(x=True, y=True, alpha=0.3)
        vbox = QVBoxLayout(self)
        vbox.addWidget(self._plot_widget)
        self.setLayout(vbox)

        self._last_update_ts = 0
        self._reset_required = False

        self._update_timer = QTimer(self)
        self._update_timer.setSingleShot(False)
        self._update_timer.timeout.connect(self._update)
        self._update_timer.start(200)

        self._color_index = 0
        self._curves = {}

        # Crosshair
        def _render_measurements(cur, ref):
            text = 'time %.6f sec,  y %.6f' % cur
            if ref is None:
                return text
            dt = cur[0] - ref[0]
            dy = cur[1] - ref[1]
            if abs(dt) > 1e-12:
                freq = '%.6f' % abs(1 / dt)
            else:
                freq = 'inf'
            display_measurements(text + ';' + ' ' * 4 + 'dt %.6f sec,  freq %s Hz,  dy %.6f' % (dt, freq, dy))

        display_measurements('Hover to sample Time/Y, click to set new reference')
        add_crosshair(self._plot_widget, _render_measurements)

        # Final reset
        self.reset()

    def _trigger_auto_reset_if_needed(self):
        ts = time.monotonic()
        dt = ts - self._last_update_ts
        self._last_update_ts = ts
        if dt > 2:
            self._reset_required = True

    def add_curve(self, curve_id, curve_name, data_x=[], data_y=[]):
        color = QColor(self.COLORS[self._color_index % len(self.COLORS)])
        self._color_index += 1
        pen = mkPen(color, width=1)
        plot = self._plot_widget.plot(name=curve_name, pen=pen)
        data_x = numpy.array(data_x)
        data_y = numpy.array(data_y)
        self._curves[curve_id] = {'data': (data_x, data_y), 'plot': plot}
        self._trigger_auto_reset_if_needed()

    def update_values(self, curve_id, x, y):
        curve = self._curves[curve_id]
        old_x, old_y = curve['data']
        curve['data'] = numpy.append(old_x, x), numpy.append(old_y, y)
        self._trigger_auto_reset_if_needed()

    def reset(self):
        for curve in self._curves.keys():
            self._plot_widget.removeItem(self._curves[curve]['plot'])

        self._curves = {}
        self._color_index = 0

        self._plot_widget.enableAutoRange(enable=self.AUTO_RANGE_FRACTION,
                                          x=self.AUTO_RANGE_FRACTION,
                                          y=self.AUTO_RANGE_FRACTION)

        self._legend.scene().removeItem(self._legend)
        self._legend = self._plot_widget.addLegend()

    def _update(self):
        if self._reset_required:
            self.reset()
            self._reset_required = False

        for curve in self._curves.values():
            if len(curve['data'][0]):
                curve['plot'].setData(*curve['data'])