0

Consider the code from How do I plot in real-time in a while loop using matplotlib? for a "scope roll", which I've also pasted below.

By default, running this, produces a plot like this:

usual plot

Since new data shows up on the right edge, and the plot gets scrolled from right to left, I would like the x tick label on the right edge to be 0, and the rest of the tick labels should grow from right to left.

If the line self.ax1.set_xlim(self.ax1.get_xlim()[::-1]) # reverses axis, but also direction is uncommented, then the tick labels are reversed and are as I want them - but so is the direction of the scroll/scope roll:

reversed plot

So, how can I preserve the original behavior (new data shows on right edge; plot scrolls from right to left) - but reverse the plot tick labels, so the x tick labels start with 0 at right edge, and grow from right to left?


The code:

###################################################################
#                                 #
#          PLOT A LIVE GRAPH (PyQt5)          #
#          -----------------------------          #
#      EMBED A MATPLOTLIB ANIMATION INSIDE YOUR       #
#      OWN GUI!                       #
#                                 #
###################################################################

import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading

class CustomMainWindow(QMainWindow):
  def __init__(self):
    super(CustomMainWindow, self).__init__()
    # Define the geometry of the main window
    self.setGeometry(300, 300, 800, 400)
    self.setWindowTitle("my first window")
    # Create FRAME_A
    self.FRAME_A = QFrame(self)
    self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QColor(210,210,235,255).name())
    self.LAYOUT_A = QGridLayout()
    self.FRAME_A.setLayout(self.LAYOUT_A)
    self.setCentralWidget(self.FRAME_A)
    # Place the zoom button
    self.zoomBtn = QPushButton(text = 'zoom')
    self.zoomBtn.setFixedSize(100, 50)
    self.zoomBtn.clicked.connect(self.zoomBtnAction)
    self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
    # Place the matplotlib figure
    self.myFig = CustomFigCanvas()
    self.LAYOUT_A.addWidget(self.myFig, *(0,1))
    # Add the callbackfunc to ..
    myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
    myDataLoop.start()
    self.show()
    return

  def zoomBtnAction(self):
    print("zoom in")
    self.myFig.zoomIn(5)
    return

  def addData_callbackFunc(self, value):
    # print("Add data: " + str(value))
    self.myFig.addData(value)
    return

''' End Class '''


class CustomFigCanvas(FigureCanvas, TimedAnimation):
  def __init__(self):
    self.addedData = []
    print(matplotlib.__version__)
    # The data
    self.xlim = 200
    self.n = np.linspace(0, self.xlim - 1, self.xlim)
    a = []
    b = []
    a.append(2.0)
    a.append(4.0)
    a.append(2.0)
    b.append(4.0)
    b.append(3.0)
    b.append(4.0)
    self.y = (self.n * 0.0) + 50
    # The window
    self.fig = Figure(figsize=(5,5), dpi=100)
    self.ax1 = self.fig.add_subplot(111)
    # self.ax1 settings
    self.ax1.set_xlabel('time')
    self.ax1.set_ylabel('raw data')
    self.line1 = Line2D([], [], color='blue')
    self.line1_tail = Line2D([], [], color='red', linewidth=2)
    self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
    self.ax1.add_line(self.line1)
    self.ax1.add_line(self.line1_tail)
    self.ax1.add_line(self.line1_head)
    self.ax1.set_xlim(0, self.xlim - 1)
    ##self.ax1.set_xlim(self.ax1.get_xlim()[::-1]) # reverses axis, but also direction
    self.ax1.set_ylim(0, 100)
    FigureCanvas.__init__(self, self.fig)
    TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
    return

  def new_frame_seq(self):
    return iter(range(self.n.size))

  def _init_draw(self):
    lines = [self.line1, self.line1_tail, self.line1_head]
    for l in lines:
      l.set_data([], [])
    return

  def addData(self, value):
    self.addedData.append(value)
    return

  def zoomIn(self, value):
    bottom = self.ax1.get_ylim()[0]
    top = self.ax1.get_ylim()[1]
    bottom += value
    top -= value
    self.ax1.set_ylim(bottom,top)
    self.draw()
    return

  def _step(self, *args):
    # Extends the _step() method for the TimedAnimation class.
    try:
      TimedAnimation._step(self, *args)
    except Exception as e:
      self.abc += 1
      print(str(self.abc))
      TimedAnimation._stop(self)
      pass
    return

  def _draw_frame(self, framedata):
    margin = 2
    while(len(self.addedData) > 0):
      self.y = np.roll(self.y, -1)
      self.y[-1] = self.addedData[0]
      del(self.addedData[0])

    self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
    self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
    self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
    self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
    return

''' End Class '''


# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QObject):
  data_signal = pyqtSignal(float)

''' End Class '''



def dataSendLoop(addData_callbackFunc):
  # Setup the signal-slot mechanism.
  mySrc = Communicate()
  mySrc.data_signal.connect(addData_callbackFunc)

  # Simulate some data
  n = np.linspace(0, 499, 500)
  y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
  i = 0

  while(True):
    if(i > 499):
      i = 0
    time.sleep(0.1)
    mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
    i += 1
  ###
###

if __name__== '__main__':
  app = QApplication(sys.argv)
  QApplication.setStyle(QStyleFactory.create('Plastique'))
  myGUI = CustomMainWindow()
  sys.exit(app.exec_())
sdbbs
  • 4,270
  • 5
  • 32
  • 87

1 Answers1

2

As your x axis is fixed from 0 to 199 you can manually set the ticks so that the last tick is exactly at the end of the axis and then assign tick labels to these ticks as desired:

xticks = list(range(24,200,25))
self.ax1.set_xticks(xticks)
self.ax1.set_xticklabels([199-x for x in xticks])

(replace your commented line with these three lines)

enter image description here

Stef
  • 28,728
  • 2
  • 24
  • 52