예제 #1
0
class PlotWidget(QtGui.QWidget):
    COLORS = 'rbgcmyk'

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.history_s = 20.0
        self.next_color = 0
        self.paused = False

        self.last_draw_time = 0.0

        self.figure = matplotlib.figure.Figure()
        self.canvas = FigureCanvas(self.figure)

        self.canvas.mpl_connect('key_press_event', self.handle_key_press)
        self.canvas.mpl_connect('key_release_event', self.handle_key_release)

        self.left_axis = self.figure.add_subplot(111)
        self.left_axis.grid()
        self.left_axis.fmt_xdata = lambda x: '%.3f' % x

        self.left_axis.legend_loc = LEFT_LEGEND_LOC

        self.right_axis = None

        def draw():
            # NOTE jpieper: For some reason, on the first repaint
            # event, the height is negative, which throws spurious
            # errors.  Paper over that here.
            l, b, w, h = self.figure.bbox.bounds
            if h < 0:
                return
            FigureCanvas.draw(self.canvas)
            self.canvas.repaint()

        self.canvas.draw = draw

        self.toolbar = backend_qt4agg.NavigationToolbar2QT(self.canvas, self)
        self.pause_action = QtGui.QAction(u'Pause', self)
        self.pause_action.setCheckable(True)
        self.pause_action.toggled.connect(self._handle_pause)
        self.toolbar.addAction(self.pause_action)

        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.toolbar, 0)
        layout.addWidget(self.canvas, 1)

        self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)

    def _handle_pause(self, value):
        self.paused = value

    def add_plot(self, name, signal, axis_number):
        axis = self.left_axis
        if axis_number == 1:
            if self.right_axis is None:
                self.right_axis = self.left_axis.twinx()
                self.right_axis.legend_loc = RIGHT_LEGEND_LOC
            axis = self.right_axis
        item = PlotItem(axis, self, name, signal)
        return item

    def remove_plot(self, item):
        item.remove()

    def data_update(self):
        now = time.time()
        elapsed = now - self.last_draw_time
        if elapsed > 0.1:
            self.last_draw_time = now
            self.canvas.draw()

    def _get_axes_keys(self):
        result = []
        result.append(('1', self.left_axis))
        if self.right_axis:
            result.append(('2', self.right_axis))
        return result

    def handle_key_press(self, event):
        if event.key not in ['1', '2']:
            return
        for key, axis in self._get_axes_keys():
            if key == event.key:
                axis.set_navigate(True)
            else:
                axis.set_navigate(False)

    def handle_key_release(self, event):
        if event.key not in ['1', '2']:
            return
        for key, axis in self._get_axes_keys():
            axis.set_navigate(True)
예제 #2
0
class Tplot(QtGui.QMainWindow):
    def __init__(self):
        super(Tplot, self).__init__()

        self.ui = ui_tplot_main_window.Ui_TplotMainWindow()
        self.ui.setupUi(self)

        self.figure = matplotlib.figure.Figure()
        self.canvas = FigureCanvas(self.figure)

        self.canvas.mpl_connect('motion_notify_event', self.handle_mouse)
        self.canvas.mpl_connect('key_press_event', self.handle_key_press)
        self.canvas.mpl_connect('key_release_event', self.handle_key_release)

        # Make QT drawing not be super slow.  See:
        # https://github.com/matplotlib/matplotlib/issues/2559/
        def draw():
            FigureCanvas.draw(self.canvas)
            self.canvas.repaint()

        self.canvas.draw = draw

        self.left_axis = self.figure.add_subplot(111)
        self.left_axis.tplot_name = 'Left'

        self.axes = {
            'Left' : self.left_axis,
            }

        layout = QtGui.QVBoxLayout(self.ui.plotFrame)
        layout.addWidget(self.canvas, 1)

        self.toolbar = backend_qt4agg.NavigationToolbar2QT(self.canvas, self)
        self.addToolBar(self.toolbar)

        self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
        self.canvas.setFocus()

        self.log = None
        self.COLORS = 'rgbcmyk'
        self.next_color = 0

        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.handle_timeout)

        self.time_start = None
        self.time_end = None
        self.time_current = None

        self.ui.recordCombo.currentIndexChanged.connect(
            self.handle_record_combo)
        self.ui.addPlotButton.clicked.connect(self.handle_add_plot_button)
        self.ui.removeButton.clicked.connect(self.handle_remove_button)
        self.ui.treeWidget.itemExpanded.connect(self.handle_item_expanded)
        self.tree_items = []
        self.ui.treeWidget.header().setResizeMode(
            QtGui.QHeaderView.ResizeToContents)
        self.ui.timeSlider.valueChanged.connect(self.handle_time_slider)
        self._updating_slider = BoolGuard()

        self.ui.fastReverseButton.clicked.connect(
            self.handle_fast_reverse_button)
        self.ui.stepBackButton.clicked.connect(
            self.handle_step_back_button)
        self.ui.playReverseButton.clicked.connect(
            self.handle_play_reverse_button)
        self.ui.stopButton.clicked.connect(self.handle_stop_button)
        self.ui.playButton.clicked.connect(self.handle_play_button)
        self.ui.stepForwardButton.clicked.connect(
            self.handle_step_forward_button)
        self.ui.fastForwardButton.clicked.connect(
            self.handle_fast_forward_button)

    def open(self, filename):
        try:
            maybe_log = Log(filename)
        except Exception as e:
            QtGui.QMessageBox.warning(self, 'Could not open log',
                                      'Error: ' + str(e))
            raise
            return

        # OK, we're good, clear out our UI.
        self.ui.treeWidget.clear()
        self.tree_items = []
        self.ui.recordCombo.clear()
        self.ui.xCombo.clear()
        self.ui.yCombo.clear()

        self.log = maybe_log
        for name in self.log.records.keys():
            self.ui.recordCombo.addItem(name)

            item = QtGui.QTreeWidgetItem()
            item.setText(0, name)
            self.ui.treeWidget.addTopLevelItem(item)
            self.tree_items.append(item)

            exemplar = self.log.records[name]
            def add_item(parent, element):
                if 'fields' not in element:
                    return
                for field in element['fields']:
                    name = field['name']

                    item = QtGui.QTreeWidgetItem(parent)
                    item.setText(0, name)

                    if 'nelements' in field:
                        child = field['children'][0]
                        # If this is a type with only one child, just
                        # fall down to it.
                        if 'children' in child and len(child['children']) == 1:
                            child = ['children'][0]
                        for i in range(field['nelements']):
                            subitem = QtGui.QTreeWidgetItem(item)
                            subitem.setText(0, str(i))
                            add_item(subitem, child)
                    elif 'children' in field:
                        for child in field['children']:
                            add_item(item, child)
            add_item(item, exemplar)

    def handle_record_combo(self):
        record = self.ui.recordCombo.currentText()
        self.ui.xCombo.clear()
        self.ui.yCombo.clear()

        exemplar = self.log.records[record]
        default_x = None
        index = [0, None]

        def add_item(index, parent, element):
            if 'fields' not in element:
                return
            for field in element['fields']:
                name = field['name']
                this_name = parent
                if len(this_name):
                    this_name += '.'
                this_name += name

                if 'nelements' in field:
                    child = field['children'][0]
                    if 'children' in child and len(child['children']) == 1:
                        child = ['children'][0]
                    for i in range(field['nelements']):
                        add_item(index, this_name + "." + str(i), child)
                elif 'children' in field:
                    for child in field['children']:
                        add_item(index, this_name, child)
                else:
                    self.ui.xCombo.addItem(this_name)
                    self.ui.yCombo.addItem(this_name)

                    if name == 'timestamp':
                        index[1] = index[0]

                    index[0] += 1

        add_item(index, '', exemplar)
        default_x = index[1]

        if default_x:
            self.ui.xCombo.setCurrentIndex(default_x)

    def handle_add_plot_button(self):
        self.log.get_all()

        record = self.ui.recordCombo.currentText()
        xname = self.ui.xCombo.currentText()
        yname = self.ui.yCombo.currentText()

        data = self.log.all[record]
        xdata = [_get_data(x, xname) for x in data]
        ydata = [_get_data(x, yname) for x in data]

        line = matplotlib.lines.Line2D(xdata, ydata)
        line.tplot_record_name = record
        if 'timestamp' in [x['name'] for x in self.log.records[record]['fields']]:
            line.tplot_has_timestamp = True
        line.tplot_xname = xname
        line.tplot_yname = yname
        label = self.make_label(record, xname, yname)
        line.set_label(label)
        line.set_color(self.COLORS[self.next_color])
        self.next_color = (self.next_color + 1) % len(self.COLORS)

        axis = self.get_current_axis()

        axis.add_line(line)
        axis.relim()
        axis.autoscale_view()
        axis.legend(loc=LEGEND_LOC[axis.tplot_name])

        self.ui.plotsCombo.addItem(label, line)
        self.ui.plotsCombo.setCurrentIndex(self.ui.plotsCombo.count() - 1)

        self.canvas.draw()

    def make_label(self, record, xname, yname):
        if xname == 'timestamp':
            return '%s.%s' % (record, yname)
        return '%s %s vs. %s' % (record, yname, xname)

    def get_current_axis(self):
        requested = self.ui.axisCombo.currentText()
        maybe_result = self.axes.get(requested, None)
        if maybe_result:
            return maybe_result

        result = self.left_axis.twinx()
        self.axes[requested] = result
        result.tplot_name = requested

        return result

    def get_all_axes(self):
        return self.axes.values()

    def handle_remove_button(self):
        index = self.ui.plotsCombo.currentIndex()
        if index < 0:
            return
        line = self.ui.plotsCombo.itemData(index)
        if hasattr(line, 'tplot_marker'):
            line.tplot_marker.remove()
        line.remove()
        self.ui.plotsCombo.removeItem(index)

        self.canvas.draw()

    def handle_item_expanded(self):
        self.update_timeline()

    def update_timeline(self):
        if self.time_start is not None:
            return

        self.log.get_all()

        # Look through all the records for those which have a
        # "timestamp" field.  Find the minimum and maximum of each.
        for record, exemplar in self.log.records.iteritems():
            if record not in self.log.all:
                continue
            timestamp_getter = _make_timestamp_getter(self.log.all[record])
            if timestamp_getter is None:
                continue

            these_times = [timestamp_getter(x) for x in self.log.all[record]]
            if len(these_times) == 0:
                continue
            this_min = min(these_times)
            this_max = max(these_times)

            if self.time_start is None or this_min < self.time_start:
                self.time_start = this_min
            if self.time_end is None or this_max > self.time_end:
                self.time_end = this_max

        self.time_current = self.time_start
        self.update_time(self.time_current, update_slider=False)

    def handle_mouse(self, event):
        if not event.inaxes:
            return
        self.statusBar().showMessage('%f,%f' % (event.xdata, event.ydata))

    def handle_key_press(self, event):
        if event.key not in ['1', '2', '3', '4']:
            return
        index = ord(event.key) - ord('1')
        for key, value in self.axes.iteritems():
            if key == AXES[index]:
                value.set_navigate(True)
            else:
                value.set_navigate(False)

    def handle_key_release(self, event):
        if event.key not in ['1', '2', '3', '4']:
            return
        for key, value in self.axes.iteritems():
            value.set_navigate(True)

    def update_time(self, new_time, update_slider=True):
        new_time = max(self.time_start, min(self.time_end, new_time))
        self.time_current = new_time

        # Update the tree view.
        self.update_tree_view(new_time)

        # Update dots on the plot.
        self.update_plot_dots(new_time)

        # Update the text fields.
        dt = datetime.datetime.utcfromtimestamp(new_time)
        self.ui.clockEdit.setText('%04d-%02d-%02d %02d:%02d:%02.3f' % (
                dt.year, dt.month, dt.day,
                dt.hour, dt.minute, dt.second + dt.microsecond / 1e6))
        self.ui.elapsedEdit.setText('%.3f' % (new_time - self.time_start))

        if update_slider:
            with self._updating_slider:
                elapsed = new_time - self.time_start
                total_time = self.time_end - self.time_start
                self.ui.timeSlider.setValue(
                    int(1000 * elapsed / total_time))

    def handle_time_slider(self):
        if self._updating_slider.active():
            return

        if self.time_end is None or self.time_start is None:
            return

        total_time = self.time_end - self.time_start
        current = self.ui.timeSlider.value() / 1000.0
        self.update_time(self.time_start + current * total_time,
                         update_slider=False)

    def update_tree_view(self, time):
        for item in self.tree_items:
            name = item.text(0)
            if name not in self.log.all:
                continue
            all_data = self.log.all[name]

            timestamp_getter = _make_timestamp_getter(all_data)
            if timestamp_getter is None:
                continue

            this_data_index = _bisect(all_data, time, key=timestamp_getter)
            if this_data_index is None:
                _clear_tree_widget(item)
            else:
                this_data = all_data[this_data_index]
                _set_tree_widget_data(item, this_data)

    def update_plot_dots(self, new_time):
        updated = False
        for axis in self.get_all_axes():
            for line in axis.lines:
                if not hasattr(line, 'tplot_record_name'):
                    continue
                if not hasattr(line, 'tplot_has_timestamp'):
                    continue

                all_data = self.log.all[line.tplot_record_name]
                timestamp_getter = _make_timestamp_getter(all_data)
                this_index = _bisect(all_data, new_time, timestamp_getter)
                if this_index is None:
                    continue

                this_data = all_data[this_index]

                if not hasattr(line, 'tplot_marker'):
                    line.tplot_marker = matplotlib.lines.Line2D([], [])
                    line.tplot_marker.set_marker('o')
                    line.tplot_marker.set_color(line._color)
                    self.left_axis.add_line(line.tplot_marker)

                updated = True
                xdata = [_get_data(this_data, line.tplot_xname)]
                ydata = [_get_data(this_data, line.tplot_yname)]
                line.tplot_marker.set_data(xdata, ydata)

        if updated:
            self.canvas.draw()


    def handle_fast_reverse_button(self):
        self.play_start(-self.ui.fastReverseSpin.value())

    def handle_step_back_button(self):
        self.play_stop()
        self.update_time(self.time_current - self.ui.stepBackSpin.value())

    def handle_play_reverse_button(self):
        self.play_start(-1.0)

    def handle_stop_button(self):
        self.play_stop()

    def handle_play_button(self):
        self.play_start(1.0)

    def handle_step_forward_button(self):
        self.play_stop()
        self.update_time(self.time_current + self.ui.stepForwardSpin.value())

    def handle_fast_forward_button(self):
        self.play_start(self.ui.fastForwardSpin.value())

    def play_stop(self):
        self.speed = None
        self.last_time = None
        self.timer.stop()

    def play_start(self, speed):
        self.speed = speed
        self.last_time = time.time()
        self.timer.start(100)

    def handle_timeout(self):
        assert self.last_time is not None
        this_time = time.time()
        delta_t = this_time - self.last_time
        self.last_time = this_time

        self.update_time(self.time_current + delta_t * self.speed)
예제 #3
0
class CourseMapDialog(QtGui.QDialog):
    """Implements a dialog box to display course maps."""

    time_slider_changed = QtCore.Signal(float)

    def __init__(self, parent):
        QtGui.QDialog.__init__(self, parent)
        self._ui = ui_course_map_dialog.Ui_Dialog()
        self._ui.setupUi(self)

        params = matplotlib.figure.SubplotParams(0, 0, 1, 1, 0, 0)
        self._figure = matplotlib.figure.Figure(subplotpars=params)
        self._canvas = FigureCanvas(self._figure)
        self._canvas.mpl_connect('motion_notify_event', self._handle_mouse)
        self._canvas.mpl_connect('scroll_event', self._handle_scroll)
        self._canvas.mpl_connect('button_release_event',
                                 self._handle_mouse_release)
        self._mouse_start = None

        # Make QT drawing not be super slow.  See:
        # https://github.com/matplotlib/matplotlib/issues/2559/
        def draw():
            FigureCanvas.draw(self._canvas)
            self._canvas.repaint()

        self._canvas.draw = draw
        self._plot = self._figure.add_subplot(111)

        self._gdal_source = course_gdal.GdalSource()
        self._gdal = None

        layout = QtGui.QVBoxLayout(self._ui.mapFrame)
        layout.addWidget(self._canvas, 1)

        self._log_data = dict()

        # TODO sammy make COLORS a project wide config
        self._COLORS = 'rgbcmyk'
        self._next_color = 0
        self._bounds = ((0, 0), (0, 0))
        self._time_current = 0
        self._total_time = 0
        self._ui.timeSlider.valueChanged.connect(self._handle_time_slider)

    def add_log(self, log_name, log):
        log_data = _LogMapData(log_name, log, self._COLORS[self._next_color])
        self._log_data[log_name] = log_data
        self._next_color = (self._next_color + 1) % len(self._COLORS)

        if self._gdal is None:
            self._gdal = self._gdal_source.get_gdal(
                log_data.utm_data[0][1], log_data.utm_data[0][2])
            if self._gdal is not None:
                self._plot.imshow(self._gdal.image, extent=self._gdal.extent)

        self._plot.add_line(log_data.line)
        self._plot.add_line(log_data.marker)
        self._plot.legend(loc=2)
        self._update_scale()

    def update_sync(self):
        for log_data in self._log_data.itervalues():
            log_data.update_line_data()
            log_data.update_marker(self._time_current)

        self._update_scale()

    def _update_scale(self):
        self._plot.relim()
        minx = 1e10
        miny = 1e10
        maxx = -1e10
        maxy = -1e10
        max_time = 0

        for log_data in self._log_data.itervalues():
            bounds = log_data.bounds
            minx = min(minx, bounds[0][0])
            miny = min(miny, bounds[0][1])
            maxx = max(maxx, bounds[1][0])
            maxy = max(maxy, bounds[1][1])
            max_time = max(max_time, log_data.utm_data[-1][0])
        self._total_time = max_time

        x_size = maxx - minx
        y_size = maxy - miny
        xy_delta = x_size - y_size
        if xy_delta > 0:
            miny -= xy_delta / 2
            maxy += xy_delta / 2
        else:
            minx += xy_delta / 2
            maxx -= xy_delta / 2

        self._bounds = ((minx, miny), (maxx, maxy))
        self._plot.set_xlim(left=minx, right=maxx)
        self._plot.set_ylim(bottom=miny, top=maxy)
        self._canvas.draw()

    def _handle_mouse(self, event):
        if not event.inaxes or event.button != 1:
            return

        if self._mouse_start is None:
            self._mouse_start = event
            return

        # Handle a pan event.  What we want is for the point (data)
        # where the mouse was originally clicked to stay under the
        # pointer.
        (width, height) = self._canvas.get_width_height()
        px_x = (self._bounds[1][0] - self._bounds[0][0]) / width
        px_y = (self._bounds[1][1] - self._bounds[0][1]) / height
        x_change = (self._mouse_start.x - event.x) * px_x
        y_change = (self._mouse_start.y - event.y) * px_y
        self._plot.set_xlim(left=self._bounds[0][0] + x_change,
                            right=self._bounds[1][0] + x_change)
        self._plot.set_ylim(bottom=self._bounds[0][1] + y_change,
                            top=self._bounds[1][1] + y_change)
        self._canvas.draw()

    def _update_bounds(self):
        xlim = self._plot.get_xlim()
        ylim = self._plot.get_ylim()
        self._bounds = ((xlim[0], ylim[0]), (xlim[1], ylim[1]))

    def _handle_mouse_release(self, event):
        if event.button != 1:
            return
        self._mouse_start = None
        self._update_bounds()

    def _handle_scroll(self, event):
        # Determine the relative offset of the clicked position to the
        # center of the frame so that we can keep the data under the
        # cursor.
        (width, height) = self._canvas.get_width_height()
        x_off = float(width - event.x) / width
        y_off = float(height - event.y) / height
        x_scale = (self._bounds[1][0] - self._bounds[0][0]) * (event.step / 10.)
        y_scale = (self._bounds[1][1] - self._bounds[0][1]) * (event.step / 10.)

        # Check if we've tried to zoom to far and would invert our axes
        new_xlim = (self._bounds[0][0] + (x_scale * (1. - x_off)),
                    self._bounds[1][0] - (x_scale * x_off))
        new_ylim = (self._bounds[0][1] + (y_scale * (1. - y_off)),
                    self._bounds[1][1] - (y_scale * y_off))
        if (new_xlim[1] <= new_xlim[0]) or (new_ylim[1] <= new_ylim[0]):
            return

        self._plot.set_xlim(left=new_xlim[0], right=new_xlim[1])
        self._plot.set_ylim(bottom=new_ylim[0], top=new_ylim[1])
        self._update_bounds()
        self._canvas.draw()

    def update_time(self, new_time, update_slider=True):
        # Bound the time to our useful range.
        new_time = max(0, min(new_time, self._total_time))
        self._time_current = new_time
        for log_data in self._log_data.itervalues():
            log_data.update_marker(new_time)
        self._canvas.draw()
        self._ui.elapsedTime.setText(str(new_time))

        # if update_slider:
        #     self._ui.timeSlider.setValue(1000 * (new_time / self._total_time))

    def _handle_time_slider(self):
        if self._total_time == 0:
            return
        current = self._ui.timeSlider.value() / 1000.
        self.update_time(current * self._total_time, update_slider=False)
        self.time_slider_changed.emit(self._time_current)
예제 #4
0
class LinePlot(OWWidget):
    name = "Line Plot"
    description = "Make a line plot of data"
    icon = "icons/lineplot.svg"
    want_main_area = False
    DAAP = "daap"
    COLORPREFIX = "C"
    DEFAULTCOLOR = COLORPREFIX + "0"
    FIELDNAMEDATE = "date"
    FIELDNAMENONE = "NONE"
    FIELDNAMEMSGID = "msg id"
    FIELDNAMEFILE = "file"
    SECONDSPERDAY = 86400
    WORDS = "words"
    MESSAGES = "messages"
    storedTable = None
    coloredColumn = -1
    xColumn = 0
    yColumn = 0
    connect = MESSAGES
    form = None
    ax = None

    class Inputs:
        table = Input("Data", Table)

    def __init__(self):
        super().__init__()
        self.form = QFormLayout()
        self.figure = Figure()
        self.canvas = FigureCanvas(self.figure)
        self.form.addWidget(self.canvas)
        self.form.setFieldGrowthPolicy(self.form.AllNonFixedFieldsGrow)
        self.form.setVerticalSpacing(0)
        self.form.setLabelAlignment(Qt.AlignLeft)
        gui.widgetBox(self.controlArea, True, orientation=self.form)

    @Inputs.table
    def storeTable(self, table):
        if table != None and hasattr(table, "domain"):
            self.storedTable = table
            # 20191210 warning: reloading table may require replacing form
            # because set of features changed
            # self.clearCanvas()
            self.drawGraph()
            self.drawWindow()

    def redraw(self):
        if self.storedTable != None: self.drawGraph()

    def drawGraph(self):
        self.ax = self.figure.add_subplot(111)
        if self.connect == self.WORDS: self.plotWords()
        else: self.plotMessages()

    def drawWindow(self):
        form = self.form
        if self.storedTable == None: columnNames = []
        else: columnNames = [x.name for x in self.storedTable.domain.variables]
        if self.form.rowCount() <= 1:
            form.addRow(
                "x-axis:",
                gui.comboBox(None,
                             self,
                             "xColumn",
                             items=columnNames,
                             callback=self.redraw))
            form.addRow(
                "y-axis:",
                gui.comboBox(None,
                             self,
                             "yColumn",
                             items=columnNames,
                             callback=self.redraw))
            form.addRow(
                "split by:",
                gui.comboBox(None,
                             self,
                             "coloredColumn",
                             items=columnNames + [self.FIELDNAMENONE],
                             callback=self.redraw))
            form.addRow(
                "connect:",
                gui.comboBox(None,
                             self,
                             "connect",
                             items=[self.MESSAGES, self.WORDS],
                             callback=self.redraw))
            form.addRow(gui.button(None, self, 'draw', self.redraw))

    def clearCanvas(self):
        while self.form.rowCount() > 1:
            print("deleting line plot row...")
            self.form.removeRow(1)
        self.canvas.draw()
        self.canvas.repaint()

    def getFieldValue(self, table, fieldName, rowId):
        if rowId < len(table):
            for i in range(0, len(table.domain)):
                if table.domain[i].name == fieldName:
                    return (table[rowId].list[i])
            for i in range(0, len(table.domain.metas)):
                if table.domain.metas[i].name == fieldName:
                    return (table[rowId].metas[i])
        sys.exit("getFieldValue: field name not found: " + fieldName)

    def getColumnValues(self, table, columnName):
        return (list(
            set([
                self.getFieldValue(table, columnName, i)
                for i in range(0, len(table))
            ])))

    def makeColorNames(self, columnName):
        columnValueList = sorted(self.getColumnValues(self.storedTable,
                                                      columnName),
                                 reverse=True)
        colorNames = {}
        for i in range(0, len(columnValueList)):
            colorNames[columnValueList[i]] = self.COLORPREFIX + str(i % 10)
        return (colorNames)

    def simplifyLegend(self, ax):
        handles, labels = ax.get_legend_handles_labels()
        nbrOfUniqueLabels = len(set(labels))
        handlesUnique = []
        labelsUnique = []
        seen = {}
        for i in range(0, len(labels)):
            if not labels[i] in seen:
                seen[labels[i]] = True
                labelsUnique.append(labels[i])
                handlesUnique.append(handles[i])
                if len(labelsUnique) >= nbrOfUniqueLabels: break
        return (handlesUnique, labelsUnique)

    def mixSorted(self, thisList):
        strList = []
        numList = []
        for i in range(0, len(thisList)):
            if re.match("^\d+$", thisList[i]): numList.append(int(thisList[i]))
            else: strList.append(thisList[i])
        return (sorted(strList) + [str(x) for x in sorted(numList)])

    def normalizeXvalues(self, xValues):
        for i in range(1, len(xValues)):
            xValues[i] -= xValues[0]
        xValues[0] = 0
        return (xValues)

    def convertSecondsToDays(self, xValues):
        return ([x / self.SECONDSPERDAY for x in xValues])

    def plotWords(self):
        if self.storedTable == None or not hasattr(self.storedTable, "domain"):
            return
        columnNames = [x.name for x in self.storedTable.domain.variables]
        ax = self.ax
        ax.clear()
        lastMsgId = ""
        lastDataValue = None
        dataX = []
        dataY = []
        color = self.DEFAULTCOLOR
        if self.coloredColumn >= 0 and self.coloredColumn < len(columnNames):
            colorNames = self.makeColorNames(columnNames[self.coloredColumn])
        for i in range(0, len(self.storedTable)):
            currentMsgId = self.getFieldValue(self.storedTable,
                                              self.FIELDNAMEMSGID, i)
            newX = self.getFieldValue(self.storedTable,
                                      columnNames[self.xColumn], i)
            newY = self.getFieldValue(self.storedTable,
                                      columnNames[self.yColumn], i)
            if i > 0 and currentMsgId != lastMsgId and self.connect != self.WORDS:
                ax.plot([dataX[-1], newX], [dataY[-1], newY],
                        color=self.DEFAULTCOLOR,
                        label=lastDataValue)
            if currentMsgId != lastMsgId and len(dataX) > 0:
                if self.coloredColumn >= 0 and self.coloredColumn < len(
                        columnNames):
                    color = colorNames[lastDataValue]
                ax.plot(dataX, dataY, color=color, label=lastDataValue)
                dataX = []
                dataY = []
            dataX.append(newX)
            dataY.append(newY)
            lastMsgId = currentMsgId
            lastDataValue = self.getFieldValue(self.storedTable,
                                               columnNames[self.coloredColumn],
                                               i)
        if len(dataX) > 0:
            ax.plot(dataX, dataY, color=color, label=lastDataValue)
            if self.coloredColumn >= 0 and self.coloredColumn < len(
                    columnNames):
                color = colorNames[lastDataValue]
            if len(dataX) > 1:
                ax.plot(dataX, dataY, color=color, label=lastDataValue)
            else:
                ax.scatter(dataX, dataY, color=color, label=lastDataValue)
        title = "x-axis: \"" + columnNames[
            self.xColumn] + "\"" + "; y-axis: \"" + columnNames[
                self.yColumn] + "\""
        if self.coloredColumn >= 0 and self.coloredColumn < len(columnNames):
            title += "; color: \"" + columnNames[self.coloredColumn] + "\""
            handlesUnique, labelsUnique = self.simplifyLegend(ax)
            ax.legend(handlesUnique, labelsUnique)
        ax.title.set_text(title)
        self.canvas.draw()
        self.canvas.repaint()

    def plotMessages(self):
        if self.storedTable == None or not hasattr(self.storedTable, "domain"):
            return
        columnNames = [x.name for x in self.storedTable.domain.variables]
        ax = self.ax
        ax.clear()
        self.fileName = str(
            self.getFieldValue(self.storedTable, self.FIELDNAMEFILE, 0))
        xValues = self.normalizeXvalues([
            self.getFieldValue(self.storedTable, columnNames[self.xColumn], i)
            for i in range(0, len(self.storedTable))
        ])
        if columnNames[self.xColumn] == self.FIELDNAMEDATE:
            xValues = self.convertSecondsToDays(xValues)
        if self.coloredColumn < 0 or self.coloredColumn >= len(columnNames):
            dataX = []
            dataY = []
            for i in range(0, len(self.storedTable)):
                newX = xValues[i]
                newY = self.getFieldValue(self.storedTable,
                                          columnNames[self.yColumn], i)
                dataX.append(newX)
                dataY.append(newY)
            if len(dataX) > 1: ax.plot(dataX, dataY, color=self.DEFAULTCOLOR)
            elif len(dataX) > 0:
                ax.scatter(dataX, dataY, color=self.DEFAULTCOLOR)
        else:
            colorNames = self.makeColorNames(columnNames[self.coloredColumn])
            columnValues = self.getColumnValues(
                self.storedTable, columnNames[self.coloredColumn])
            for columnValue in columnValues:
                dataX = []
                dataY = []
                for i in range(0, len(self.storedTable)):
                    dataValue = self.getFieldValue(
                        self.storedTable, columnNames[self.coloredColumn], i)
                    if dataValue == columnValue:
                        newX = xValues[i]
                        newY = self.getFieldValue(self.storedTable,
                                                  columnNames[self.yColumn], i)
                        dataX.append(newX)
                        dataY.append(newY)
                if len(dataX) > 1:
                    ax.plot(dataX,
                            dataY,
                            color=colorNames[columnValue],
                            label=columnValue)
                elif len(dataX) > 0:
                    ax.scatter(dataX,
                               dataY,
                               color=colorNames[columnValue],
                               label=columnValue)
        title = "x-axis: \"" + columnNames[
            self.xColumn] + "\"" + "; y-axis: \"" + columnNames[
                self.yColumn] + "\""
        if self.coloredColumn >= 0 and self.coloredColumn < len(columnNames):
            title += "; color: \"" + columnNames[self.coloredColumn] + "\""
            handlesUnique, labelsUnique = self.simplifyLegend(ax)
            ax.legend(handlesUnique, labelsUnique)
        ax.title.set_text(title)
        self.canvas.draw()
        self.canvas.repaint()
예제 #5
0
파일: tview.py 프로젝트: mjbots/mjmech
class PlotWidget(QtGui.QWidget):
    COLORS = "rbgcmyk"

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.history_s = 20.0
        self.next_color = 0
        self.paused = False

        self.figure = matplotlib.figure.Figure()
        self.canvas = FigureCanvas(self.figure)

        self.canvas.mpl_connect("key_press_event", self.handle_key_press)
        self.canvas.mpl_connect("key_release_event", self.handle_key_release)

        self.left_axis = self.figure.add_subplot(111)
        self.left_axis.grid()
        self.left_axis.fmt_xdata = lambda x: "%.3f" % x

        self.left_axis.legend_loc = LEFT_LEGEND_LOC

        self.right_axis = None

        def draw():
            # NOTE jpieper: For some reason, on the first repaint
            # event, the height is negative, which throws spurious
            # errors.  Paper over that here.
            l, b, w, h = self.figure.bbox.bounds
            if h < 0:
                return
            FigureCanvas.draw(self.canvas)
            self.canvas.repaint()

        self.canvas.draw = draw

        self.toolbar = backend_qt4agg.NavigationToolbar2QT(self.canvas, self)
        self.pause_action = QtGui.QAction(u"Pause", self)
        self.pause_action.setCheckable(True)
        self.pause_action.toggled.connect(self._handle_pause)
        self.toolbar.addAction(self.pause_action)

        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.toolbar, 0)
        layout.addWidget(self.canvas, 1)

        self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)

    def _handle_pause(self, value):
        self.paused = value

    def add_plot(self, name, signal, axis_number):
        axis = self.left_axis
        if axis_number == 1:
            if self.right_axis is None:
                self.right_axis = self.left_axis.twinx()
                self.right_axis.legend_loc = RIGHT_LEGEND_LOC
            axis = self.right_axis
        item = PlotItem(axis, self, name, signal)
        return item

    def remove_plot(self, item):
        item.remove()

    def _get_axes_keys(self):
        result = []
        result.append(("1", self.left_axis))
        if self.right_axis:
            result.append(("2", self.right_axis))
        return result

    def handle_key_press(self, event):
        if event.key not in ["1", "2"]:
            return
        for key, axis in self._get_axes_keys():
            if key == event.key:
                axis.set_navigate(True)
            else:
                axis.set_navigate(False)

    def handle_key_release(self, event):
        if event.key not in ["1", "2"]:
            return
        for key, axis in self._get_axes_keys():
            axis.set_navigate(True)