Matplotlib axes formatting - python

I want to add the current date and time to the x-axes of a real time plot. I have tried almost everything beginning with custom ticks to custom axes, but I cannot seem to add it. How should I do this?
import sys
import pylab
from pylab import *
from PyQt4 import QtGui,QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg \
import FigureCanvasQTAgg as FigureCanvas
import MySQLdb as mdb
class CPUMonitor(FigureCanvas):
def __init__(self):
self.date = []
conn = mdb.connect("serv","user","pass","db")
self.cur = conn.cursor()
self.before = self.prepare_cpu_usage()
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
self.ax.set_title("Pong | CPU Utilization")
self.ax.set_xlabel("Datetime")
self.ax.set_ylabel("CPU %")
self.ax.set_autoscale_on(False)
self.user =[]
self.l_user, = self.ax.plot([],self.user, label='Total %')
self.ax.legend()
self.fig.canvas.draw()
self.cnt = 0
self.timerEvent(None)
self.timer1 = QtCore.QTimer()
self.timer1.timeout.connect(self.get_database_data)
self.timer1.start(5)
self.timer = self.startTimer(5000)
def get_database_data(self):
self.cur.execute("SELECT cpu,date FROM status WHERE date = (SELECT MAX(date) FROM status);")
self.data_db = self.cur.fetchone()
return self.data_db
def prepare_cpu_usage(self):
t = self.get_database_data()
return [t[0],t[1]]
def get_cpu_usage(self):
now = self.prepare_cpu_usage()
self.before = now
print self.before
return self.before
def datetime(self):
self.dates = self.get_cpu_usage()
self.dates = self.dates[1]
self.date.append(self.dates)
return str(self.dates)
def timerEvent(self, evt):
result = self.get_cpu_usage()
self.user.append(result[0])
self.l_user.set_data(range(len(self.user)), self.user)
self.fig.canvas.draw()
CurrentXAxis=pylab.arange(len(self.user)-1000,len(self.user),1)
self.ax.axis([CurrentXAxis.min(),CurrentXAxis.max(),0,100])
self.cnt += 1
app = QtGui.QApplication(sys.argv)
widget = CPUMonitor()
widget.setWindowTitle("Pong: CPU Usage")
widget.show()
sys.exit(app.exec_())

You can check a simple example first to get an idea of how to make date markers work: http://matplotlib.sourceforge.net/examples/pylab_examples/finance_demo.html
First you will need to import the date plotting classes, for example:
from matplotlib.dates import DateFormatter, WeekdayLocator, MONDAY
The documentation is available at http://matplotlib.sourceforge.net/api/dates_api.html
Then in the definition of the figure, set the locator (for tick marks), and formatter (for tick labels). The code below sets tick marks on every monday:
self.ax.xaxis.set_major_locator(WeekdayLocator(MONDAY))
self.ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
self.ax.xaxis_date()
You should use datetime.datetime now for X-axis values and ranges instead of integers. I expect MySQLdb to return datetime.datetime objects, otherwise you will have to convert the timestamps.
The date formatter will complain if you try to plot an empty graph. Don't forget to set reasonable initial limits.
Here is an example of your code, where I stripped the database code (and some more) and replaced it with generated values:
import sys
from pylab import *
from PyQt4 import QtGui
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.dates import DateFormatter, WeekdayLocator, MONDAY
import datetime
import random
class CPUMonitor(FigureCanvas):
def __init__(self):
# Dummy variable to simulate time.
self.delay = 0
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
self.ax.set_title("Pong | CPU Utilization")
self.ax.set_xlabel("Datetime")
self.ax.set_ylabel("CPU %")
self.ax.xaxis.set_major_locator(WeekdayLocator(MONDAY))
self.ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
self.ax.xaxis_date()
# Set resonable initial limits.
td = datetime.timedelta(1)
self.ax.set_xlim(datetime.datetime.now(), datetime.datetime.now() + td)
self.dates = []
self.user =[]
self.l_user, = self.ax.plot([],self.user, label='Total %')
self.ax.legend()
self.timer = self.startTimer(5000)
def get_database_data(self):
self.delay += 1
td = datetime.timedelta(0, self.delay * 5)
return [random.random(), datetime.datetime.now() + td]
def prepare_cpu_usage(self):
t = self.get_database_data()
return [t[0],t[1]]
def get_cpu_usage(self):
return self.prepare_cpu_usage()
def timerEvent(self, evt):
result = self.get_cpu_usage()
self.user.append(result[0])
self.dates.append(result[1])
self.l_user.set_data(self.dates, self.user)
if len(self.dates) >= 2:
self.ax.set_xlim(self.dates[0], self.dates[-1])
self.draw()
app = QtGui.QApplication(sys.argv)
widget = CPUMonitor()
widget.setWindowTitle("Pong: CPU Usage")
widget.show()
sys.exit(app.exec_())

Related

How to refresh AxesSubplot in python matplotlib?

In a QMainWindow, I am creating an AxesSubplot. It is created in the function initEventGraph when the window starts up with:
self.canvas = FigureCanvas(plt.Figure(figsize=(8.80,7.30), dpi=80, tight_layout=True))
self.canvas.setParent(parent)
self.canvas.move(10,20)
self.ax = self.canvas.figure.subplots()
In my code, I have indicated FUNCTION CALL # to illustrate the sequence of function calls for creating the plot.
I start off with wanting to plot 1 day of data.
I created some data with varying datetimes that are both less than 1 day old and more than 1 day old.
data = {"Apple" : { "location" : [25,50,10], 'time' : [datetime.datetime.now(), datetime.datetime.now() - datetime.timedelta(days = 5), datetime.datetime.now() - datetime.timedelta(days = 1) ] },
"Orange" : {"location" : [9,12,89] , 'time' : [datetime.datetime.now() - datetime.timedelta(days = 0.1), datetime.datetime.now() - datetime.timedelta(days = 0.15), datetime.datetime.now() - datetime.timedelta(days = 2)]}
}
When I run the GUI and it plots with the plot with num_days=1, it definitely does show the data that is 1 day old or newer.
Now, the problem is that when I activate the function refreshButtonPressed by clicking on the Refresh button, I want to display the past 10 days of data, but the plot does not refresh. This is done by calling the plot function with num_days=10. There should be a difference because the graph was initially created with num_days=1.
I would expect the days older than 1 day old to show up in the plot. But there are no points for these in the plot and the axes do not refresh either.
I've tried many things, such as calling the following within the plot function:
self.ax.cla()
self.ax.clf()
self.ax.show()
self.ax.clear()
self.ax.draw()
plt.show()
However, no matter what I do, I can't get the plot to refresh. I'm really stuck.
Here is my minimal example code. It's just to very simply illustrate the problem:
from __future__ import annotations
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QSizePolicy, QGroupBox, QMessageBox, QTableWidgetItem, QLineEdit, QMainWindow, QHeaderView, QDesktopWidget, QPushButton, QLabel, QTableWidget
import datetime
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
import sys
class TestClass(QMainWindow):
def __init__(self, numDays): ##FUNCTION CALL #1
super(TestClass, self).__init__()
width = 1295
height = 700
self.setFixedSize(width, height)
self.setWindowTitle("Test")
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.numDays = numDays
self.initUI()
def initUI(self): #FUNCTION CALL #2
current_time = datetime.datetime.now()
self.createEventGraph(current_time=current_time, numDays=self.numDays)
self.createRefreshButton()
def createEventGraph(self, current_time, numDays): #FUNCTION CALL #3
self.eventGraphGroupBox = QGroupBox(self)
self.eventGraphGroupBox.setGeometry(QtCore.QRect(555,20,725,610))
self.initEventGraph(parent=self.eventGraphGroupBox, current_time=current_time, numDays=numDays)
def initEventGraph( self, parent, current_time , numDays ): #FUNCTION CALL #4
self.canvas = FigureCanvas(plt.Figure(figsize=(8.80,7.30), dpi=80, tight_layout=True))
self.canvas.setParent(parent)
self.canvas.move(10,20)
self.ax = self.canvas.figure.subplots()
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.plot(current_time=current_time, num_days=numDays)
def refreshButtonPressed(self ) :
current_time = datetime.datetime.now()
self.plot(current_time=current_time, num_days=10)
def createRefreshButton( self ) :
self.refreshButton = QPushButton(self)
self.refreshButton.setText("Refresh")
self.refreshButton.setGeometry(460,660,80,30)
self.refreshButton.clicked.connect(self.refreshButtonPressed)
def plot(self, current_time, num_days): #FUNCTION CALL #5 and for REFRESH
data = {"Apple" : { "location" : [25,50,10], 'time' : [datetime.datetime.now(), datetime.datetime.now() - datetime.timedelta(days = 5), datetime.datetime.now() - datetime.timedelta(days = 1) ] },
"Orange" : {"location" : [9,12,89] , 'time' : [datetime.datetime.now() - datetime.timedelta(days = 0.1), datetime.datetime.now() - datetime.timedelta(days = 0.15), datetime.datetime.now() - datetime.timedelta(days = 2)]}
}
unique_event_names = dict()
for name, entries in data.items():
if name not in unique_event_names:
unique_event_names[name] = True
for index, time_entry in enumerate(entries['time']):
if time_entry >= current_time - datetime.timedelta(days=num_days):
x_axis = entries['time'][index]
y_axis = entries['location'][index]
self.ax.scatter(x_axis,y_axis, label=name)
self.ax.xaxis.set_major_locator(ticker.LinearLocator(10))
self.ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S %d-%m-%y"))
self.ax.tick_params(labelrotation=15)
self.ax.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
mode="expand", ncol=len(unique_event_names))
self.ax.set_ylabel("Location")
self.ax.grid()
def window():
app = QApplication(sys.argv)
theTestClass = TestClass(numDays = 1)
theTestClass.show()
sys.exit(app.exec_())
if __name__ == "__main__":
window()
Edit : I had a typo in my code that was causing every data point to show. Fixed it.
There are a few changes you could make and try.
It is best practice not to import pyplot if you are embedding. Instead use, from matplotlib.figure import Figure
Clear the figure before plotting with self.fig.clear() and create new axes.
Finally, refresh the canvas with self.canvas.draw()
from __future__ import annotations
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QSizePolicy, QGroupBox, QMessageBox, QTableWidgetItem, QLineEdit, QMainWindow, QHeaderView, QDesktopWidget, QPushButton, QLabel, QTableWidget
import datetime
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys
class TestClass(QMainWindow):
def __init__(self, numDays): ##FUNCTION CALL #1
super(TestClass, self).__init__()
width = 1295
height = 700
self.setFixedSize(width, height)
self.setWindowTitle("Test")
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.numDays = numDays
self.initUI()
def initUI(self): #FUNCTION CALL #2
current_time = datetime.datetime.now()
self.createEventGraph(current_time=current_time, numDays=self.numDays)
self.createRefreshButton()
def createEventGraph(self, current_time, numDays): #FUNCTION CALL #3
self.eventGraphGroupBox = QGroupBox(self)
self.eventGraphGroupBox.setGeometry(QtCore.QRect(555,20,725,610))
self.initEventGraph(parent=self.eventGraphGroupBox, current_time=current_time, numDays=numDays)
def initEventGraph( self, parent, current_time , numDays ): #FUNCTION CALL #4
self.fig = Figure(figsize=(8.80,7.30), dpi=80, tight_layout=True)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(parent)
self.canvas.move(10,20)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.plot(current_time=current_time, num_days=numDays)
def refreshButtonPressed(self ) :
current_time = datetime.datetime.now()
self.plot(current_time=current_time, num_days=10)
def createRefreshButton( self ) :
self.refreshButton = QPushButton(self)
self.refreshButton.setText("Refresh")
self.refreshButton.setGeometry(460,660,80,30)
self.refreshButton.clicked.connect(self.refreshButtonPressed)
def plot(self, current_time, num_days): #FUNCTION CALL #5 and for REFRESH
self.fig.clear()
# Create a 1x1 subplot
self.ax = self.fig.add_subplot(1, 1, 1)
data = {"Apple" : { "location" : [25,50,10], 'time' : [datetime.datetime.now(), datetime.datetime.now() - datetime.timedelta(days = 5), datetime.datetime.now() - datetime.timedelta(days = 1) ] },
"Orange" : {"location" : [9,12,89] , 'time' : [datetime.datetime.now() - datetime.timedelta(days = 0.1), datetime.datetime.now() - datetime.timedelta(days = 0.15), datetime.datetime.now() - datetime.timedelta(days = 2)]}
}
unique_event_names = dict()
for name, entries in data.items():
if name not in unique_event_names:
unique_event_names[name] = True
for index, time_entry in enumerate(entries['time']):
if time_entry >= current_time - datetime.timedelta(days=num_days):
x_axis = entries['time'][index]
y_axis = entries['location'][index]
self.ax.scatter(x_axis,y_axis, label=name)
self.ax.xaxis.set_major_locator(ticker.LinearLocator(10))
self.ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S %d-%m-%y"))
self.ax.tick_params(labelrotation=15)
self.ax.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
mode="expand", ncol=len(unique_event_names))
self.ax.set_ylabel("Location")
self.ax.grid()
# refresh canvas
self.canvas.draw()
def window():
app = QApplication(sys.argv)
theTestClass = TestClass(numDays = 1)
theTestClass.show()
sys.exit(app.exec_())
if __name__ == "__main__":
window()

How to embed Matplotlib plot in PyQT widget?

I want to embed Matplotlib plot in my PyQt app using QWidget. This is the code of the widget script.
from PyQt5.QtWidgets import*
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
from entropia import entropy
import matplotlib.pyplot as plt
import numpy as np
import random
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self,parent)
self.canvas = FigureCanvas(Figure())
self.vertical_layout = QVBoxLayout()
self.vertical_layout.addWidget(self.canvas)
self.setLayout(self.vertical_layout)
def draw(self):
QWidget.update(self)
self.canvas.axes = self.canvas.figure.add_subplot(111)
fs = 500
f = random.randint(1, 100)
ts = 1/fs
length_of_signal = 100
t = np.linspace(0,1,length_of_signal)
cosinus_signal = np.cos(2*np.pi*f*t)
sinus_signal = np.sin(2*np.pi*f*t)
self.canvas.axes.clear()
self.canvas.axes.plot(t, cosinus_signal)
self.canvas.axes.plot(t, sinus_signal)
self.canvas.axes.legend(('cosinus', 'sinus'),loc='upper right')
self.canvas.axes.set_title('Cosinus - Sinus Signal')
self.canvas.draw()
I want the plot to be displayed after the pushbutton in another script is clicked. Unfortunately, this is not working. Button is connected to the function, though. If I do something like print(fs) in the "draw" method I see the variable in the python terminal when the button gets clicked.
This is how it looks when the button gets clicked:
When I move the whole thing to the init method the plot is displayed.
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self,parent)
self.canvas = FigureCanvas(Figure())
self.vertical_layout = QVBoxLayout()
self.vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(self.vertical_layout)
fs = 500
f = random.randint(1, 100)
ts = 1/fs
length_of_signal = 100
t = np.linspace(0,1,length_of_signal)
cosinus_signal = np.cos(2*np.pi*f*t)
sinus_signal = np.sin(2*np.pi*f*t)
self.canvas.axes.clear()
self.canvas.axes.plot(t, cosinus_signal)
self.canvas.axes.plot(t, sinus_signal)
self.canvas.axes.legend(('cosinus', 'sinus'),loc='upper right')
self.canvas.axes.set_title('Cosinus - Sinus Signal')
self.canvas.draw()
So, what can I do to display the plot only after calling it from another method?

How to use the Span Selector on a embedded figure of matplotlib widget?

I am working on GUI where I have a system with graphs.
I want to use the spanselector in the graph i do visualize.
I have searched and i can't understand how to use the span selector while calling the matplotlib widget.
This is an example i'm following to plot. it has 3 parts(main,mplwidget,ui file)
the main code file
# ------------------------------------------------------
# ---------------------- main.py -----------------------
# ------------------------------------------------------
from PyQt5.QtWidgets import*
from PyQt5.uic import loadUi
from matplotlib.backends.backend_qt5agg import (NavigationToolbar2QT as NavigationToolbar)
import numpy as np
import random
#from matplotlib.widgets import SpanSelector
class MatplotlibWidget(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
loadUi("qt_designer.ui",self)
self.setWindowTitle("PyQt5 & Matplotlib Example GUI")
self.pushButton_generate_random_signal.clicked.connect(self.update_graph)
self.addToolBar(NavigationToolbar(self.MplWidget.canvas, self))
def update_graph(self):
fs = 500
f = random.randint(1, 100)
ts = 1/fs
length_of_signal = 100
t = np.linspace(0,1,length_of_signal)
cosinus_signal = np.cos(2*np.pi*f*t)
sinus_signal = np.sin(2*np.pi*f*t)
self.MplWidget.canvas.axes.clear()
self.MplWidget.canvas.axes.plot(t, cosinus_signal)
self.MplWidget.canvas.axes.plot(t, sinus_signal)
self.MplWidget.canvas.axes.legend(('cosinus', 'sinus'),loc='upper right')
self.MplWidget.canvas.axes.set_title('Cosinus - Sinus Signal')
self.MplWidget.canvas.draw()
#span = self.MplWidget.canvas.axes.SpanSelector(ax1, onselect, 'horizontal', useblit=True,rectprops=dict(alpha=0.5, facecolor='red'))
def onselect(min_value, max_value):
print(min_value, max_value)
return min_value, max_value
app = QApplication([])
window = MatplotlibWidget()
window.show()
app.exec_()
the mplwidget file
# ------------------------------------------------------
# -------------------- mplwidget.py --------------------
# ------------------------------------------------------
from PyQt5.QtWidgets import*
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
the ui file is attached to this link with all the codes:
here
in the other way this is the example code to use the span selector:
import matplotlib.pyplot as plt
import matplotlib.widgets as mwidgets
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [10, 50, 100])
def onselect(vmin, vmax):
print(vmin, vmax)
rectprops = dict(facecolor='blue', alpha=0.5)
span = mwidgets.SpanSelector(ax, onselect, 'horizontal',span_stays=True,button=1,,rectprops=rectprops)
fig.show()
//////////////////////////////////////////////////////////////////////////////
i tried a lot of ways to assess the span selector but im a little bit confused in the way it works and how i should connect the the structure of code?
if i run whithin The comented line:
span = self.MplWidget.canvas.axes.SpanSelector(ax1, onselect, 'horizontal', useblit=True,rectprops=dict(alpha=0.5, facecolor='red'))
its shown the following error:
AttributeError:'AxesSubplot' object has no attribute 'SpanSelector'
finally, this is the desire result
You have to pass the self.MplWidget.canvas.axes as ax:
# ...
self.MplWidget.canvas.draw()
self.span = SpanSelector(
self.MplWidget.canvas.axes,
self.onselect,
"horizontal",
useblit=True,
rectprops=dict(alpha=0.5, facecolor="red"),
)
def onselect(self, min_value, max_value):
print(min_value, max_value)
Note: since select is a method of the class, it must have self as the first parameter, and it must be invoked with self.select.

Dynamically update multiple axis in matplotlib

I want to display sensor data on a PyQT GUI with a matplotlib animation.
I already have a working Plot which gets updates every time I receive new sensor value from an external source with this code:
def __init__(self):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
self.axes.grid()
self.xdata = []
self.ydata = []
self.entry_limit = 50
self.line, = self.axes.plot([0], [0], 'r')
def update_figure_with_new_value(self, xval: float, yval: float):
self.xdata.append(xval)
self.ydata.append(yval)
if len(self.xdata) > self.entry_limit:
self.xdata.pop(0)
self.ydata.pop(0)
self.line.set_data(self.xdata, self.ydata)
self.axes.relim()
self.axes.autoscale_view()
self.fig.canvas.draw()
self.fig.canvas.flush_events()
I want now to extend the plot to show another data series with the same x-axis. I tried to achieve this with the following additions to the init-code above:
self.axes2 = self.axes.twinx()
self.y2data = []
self.line2, = self.axes2.plot([0], [0], 'b')
and in the update_figure_with_new_value() function (for test purpose I just tried to add 1 to yval, I will extend the params of the function later):
self.y2data.append(yval+1)
if len(self.y2data) > self.entry_limit:
self.y2data.pop(0)
self.line2.set_data(self.xdata, self.ydata)
self.axes2.relim()
self.axes2.autoscale_view()
But instead of getting two lines in the plot which should have the exact same movement but just shifted by one I get vertical lines for the second plot axis (blue). The first axis (red) remains unchanged and is ok.
How can I use matplotlib to update multiple axis so that they display the right values?
I'm using python 3.4.0 with matplotlib 2.0.0.
Since there is no minimal example available, it's hard to tell the reason for this undesired behaviour. In principle ax.relim() and ax.autoscale_view() should do what you need.
So here is a complete example which works fine and updates both scales when being run with python 2.7, matplotlib 2.0 and PyQt4:
import numpy as np
import matplotlib.pyplot as plt
from PyQt4 import QtGui, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.widget = QtGui.QWidget()
self.setCentralWidget(self.widget)
self.widget.setLayout(QtGui.QVBoxLayout())
self.widget.layout().setContentsMargins(0,0,0,0)
self.widget.layout().setSpacing(0)
self.fig = Figure(figsize=(5,4), dpi=100)
self.axes = self.fig.add_subplot(111)
self.axes.grid()
self.xdata = [0]
self.ydata = [0]
self.entry_limit = 50
self.line, = self.axes.plot([], [], 'r', lw=3)
self.axes2 = self.axes.twinx()
self.y2data = [0]
self.line2, = self.axes2.plot([], [], 'b')
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
self.nav = NavigationToolbar(self.canvas, self.widget)
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.canvas)
self.show()
self.ctimer = QtCore.QTimer()
self.ctimer.timeout.connect(self.update)
self.ctimer.start(150)
def update(self):
y = np.random.rand(1)
self.update_figure_with_new_value(self.xdata[-1]+1,y)
def update_figure_with_new_value(self, xval,yval):
self.xdata.append(xval)
self.ydata.append(yval)
if len(self.xdata) > self.entry_limit:
self.xdata.pop(0)
self.ydata.pop(0)
self.y2data.pop(0)
self.line.set_data(self.xdata, self.ydata)
self.axes.relim()
self.axes.autoscale_view()
self.y2data.append(yval+np.random.rand(1)*0.17)
self.line2.set_data(self.xdata, self.y2data)
self.axes2.relim()
self.axes2.autoscale_view()
self.fig.canvas.draw()
self.fig.canvas.flush_events()
if __name__ == "__main__":
qapp = QtGui.QApplication([])
a = Window()
exit(qapp.exec_())
You may want to test this and report back if it is working or not.

Add real time channel display to PyQt

-----EDIT # 1 -----
In the code box is my attempt at making this work, but I am unable to get my labels and text boxes to show in window... perhaps my Qgridlayout is wrong??? any help or direction would be great! thanks!
----END EDIT #1 ----
What I would like is to display the channels read from channel 1 through 8 below the matplotlib graph. The graph was easy (presuming what I did works) to embed and it refreshes every 5 seconds displaying the last 5 minutes of data. So, what I would like to have two rows to display all eight channels... something like below:
Channel 1: (RAW VALUE) Channel2: (RAW VALUE) .....
Channel 5: (RAW VALUE) Channel6: (RAW VALUE) .....
I am unsure how to have PyQt 'refresh' or 'fetch' the new values every 5 seconds.
Below is my code for what it does now,
#matplotlib and read/write aquisition
import Queue
import datetime as DT
import collections
import matplotlib.pyplot as plt
import numpy as np
import multiprocessing as mp
import time
import datetime
import os
import matplotlib.dates as mdates
import matplotlib.animation as animation
#ADC
from ABE_DeltaSigmaPi import DeltaSigma
from ABE_helpers import ABEHelpers
#PyQt
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
#ADC INFO
import sys
i2c_helper = ABEHelpers()
bus = i2c_helper.get_smbus()
adc = DeltaSigma(bus, 0x68, 0x69, 18)
#Rename file to date
base_dir = '/home/pi/Desktop/DATA'
ts = time.time()
filename_time = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d')
filename_base = os.path.join(base_dir, filename_time)
filename = '%s.txt' % filename_base
# you will want to change read_delay to 5000
read_delay = int(5000) # in milliseconds
write_delay = read_delay/1000.0 # in seconds
window_size = 60
nlines = 8
datenums = collections.deque(maxlen=window_size)
ys = [collections.deque(maxlen=window_size) for i in range(nlines)]
#PyQt window to display readings
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.figure = plt.show()
self.canvas = FigureCanvas(self.figure)
#Labels
self.channel1 = QtGui.QLabel('Channel 1:')
self.channel2 = QtGui.QLabel('Channel 2:')
self.channel3 = QtGui.QLabel('Channel 3:')
self.channel4 = QtGui.QLabel('Channel 4:')
self.channel5 = QtGui.QLabel('Channel 5:')
self.channel6 = QtGui.QLabel('Channel 6:')
self.channel7 = QtGui.QLabel('Channel 7:')
self.channel8 = QtGui.QLabel('Channel 8:')
#textboxes
self.textbox1 = QtGui.QLineEdit()
self.textbox2 = QtGui.QlineEdit()
self.textbox3 = QtGui.QlineEdit()
self.textbox4 = QtGui.QlineEdit()
self.textbox5 = QtGui.QlineEdit()
self.textbox6 = QtGui.QlineEdit()
self.textbox7 = QtGui.QlineEdit()
self.textbox8 = QtGui.QlineEdit()
#timer to refresh textboxes
def refreshtext(self):
self.textbox1.setText(enumerate(row[1]))
self.textbox2.setText(enumerate(row[2]))
self.textbox3.setText(enumerate(row[3]))
self.textbox4.setText(enumerate(row[4]))
self.textbox5.setText(enumerate(row[5]))
self.textbox6.setText(enumerate(row[6]))
self.textbox7.setText(enumerate(row[7]))
self.textbox8.setText(enumerate(row[8]))
#Layout
layout = QtGui.QGridLayout()
layout.setAlignment(QtCore.Qt.AlignCenter)
layout.addWidget(self.canvas,0,0,1,4)
layout.addWidget(self.channel1,1,0,1,1)
layout.addWidget(self.channel2,1,1,1,1)
layout.addWidget(self.channel3,1,2,1,1)
layout.addWidget(self.channel4,1,3,1,1)
layout.addWidget(self.textbox1,2,0,1,1)
layout.addWidget(self.textbox2,2,1,1,1)
layout.addWidget(self.textbox3,2,2,1,1)
layout.addWidget(self.textbox4,2,3,1,1)
layout.addWidget(self.channel5,3,0,1,1)
layout.addWidget(self.channel6,3,1,1,1)
layout.addWidget(self.channel7,3,2,1,1)
layout.addWidget(self.channel8,3,3,1,1)
layout.addWidget(self.textbox5,4,0,1,1)
layout.addWidget(self.textbox6,4,1,1,1)
layout.addWidget(self.textbox7,4,2,1,1)
layout.addWidget(self.textbox8,4,3,1,1)
self.setLayout(layout)
def animate(i, queue):
try:
row = queue.get_nowait()
except Queue.Empty:
return
datenums.append(mdates.date2num(row[0]))
for i, y in enumerate(row[1:]):
ys[i].append(y)
for i, y in enumerate(ys):
lines[i].set_data(datenums, y)
ymin = min(min(y) for y in ys)
ymax = max(max(y) for y in ys)
xmin = min(datenums)
xmax = max(datenums)
if xmin < xmax:
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
fig.canvas.draw()
def write_data(filename, queue):
while True:
delay1 = DT.datetime.now()
row = []
for i in range(nlines):
# read from adc channels and print to screen
channel = adc.read_voltage(i)
row.append(channel)
queue.put([delay1]+row)
#print voltage variables to local file
with open(filename, 'a') as DAQrecording:
time1 = delay1.strftime('%Y-%m-%d')
time2 = delay1.strftime('%H:%M:%S')
row = [time1, time2] + row
row = map(str, row)
DAQrecording.write('{}\n'.format(', '.join(row)))
#Delay until next 5 second interval
delay2 = DT.datetime.now()
difference = (delay2 - delay1).total_seconds()
time.sleep(write_delay - difference)
def main():
global fig, ax, lines
queue = mp.Queue()
proc = mp.Process(target=write_data, args=(filename, queue))
# terminate proc when main process ends
proc.daemon = True
# spawn the writer in a separate process
proc.start()
fig, ax = plt.subplots()
xfmt = mdates.DateFormatter('%H:%M:%S')
ax.xaxis.set_major_formatter(xfmt)
# make matplotlib treat x-axis as times
ax.xaxis_date()
fig.autofmt_xdate(rotation=25)
lines = []
for i in range(nlines):
line, = ax.plot([], [])
lines.append(line)
ani = animation.FuncAnimation(fig, animate, interval=read_delay, fargs=(queue,))
app = QtGui.QApplication(sys.argv)
win = Window()
win.setWindowTitle('Real Time Data Aquisition')
win.show()
timer = QtCore.QTimer()
timer.timeout.connect(self.refreshtext)
timer.start(5000)
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories

Resources