#!/usr/bin/env python3
import sys
import time
import numpy as np
from PyQt5 import QtWidgets
from PyQt5.QtCore import pyqtSignal, pyqtSlot
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
from threading import Thread, Event
# Routine to acquire and serve data
# This might be a camera driver, notifying when a new frame is available
# callback is a function to call with new data
# threadkill is an Event to signal program shutdown
def generate_data(callback, threadkill):
while not threadkill.is_set():
# Make a pretty pattern
height, width = 40, 30
data = np.zeros((height, width))
data += np.cos(np.arange(0, 10*np.pi, 10*np.pi/height) - 9*time.monotonic())[:,np.newaxis]
data += np.cos(np.arange(0, 4*np.pi, 4*np.pi/width) + 4*time.monotonic())[np.newaxis,:]
callback(data)
time.sleep(0.03)
class ImageDisplay(pg.GraphicsLayoutWidget):
# Signal to indicate new data acquisition
# Note: signals need to be defined inside a QObject class/subclass
data_acquired = pyqtSignal(np.ndarray)
# Scale and offset parameters for image
mm_per_pixel = (0.12, 0.34)
mm_offset = (34.5, 56.7)
def __init__(self):
super().__init__()
self.data = np.zeros((1, 1))
# Image plot panel
self.plot = self.addPlot(row=0, col=0)
self.image = pg.ImageItem(self.data)
self.plot.addItem(self.image)
self.plot.setLabels(bottom='Position (mm)', left='Position (mm)')
# Draw axes and ticks above image/data
[ self.plot.getAxis(ax).setZValue(10) for ax in self.plot.axes ]
# Colourmap bar
self.cbar = pg.HistogramLUTItem(image=self.image)
self.cbar.gradient.loadPreset('thermal')
self.addItem(self.cbar, row=0, col=1)
# Connect the signal
self.data_acquired.connect(self.update_data)
# Make and start the background thread to acquire data
# Pass it the signal.emit as the callback function
self.threadkill = Event()
self.thread = Thread(target=generate_data, args=(self.data_acquired.emit, self.threadkill))
self.thread.start()
# Kill our data acquisition thread when shutting down
def closeEvent(self, close_event):
self.threadkill.set()
# Slot to receive acquired data and update plot
@pyqtSlot(np.ndarray)
def update_data(self, data):
self.image.setImage(data)
# Scale/translate image to correct size/position on axes
self.image.resetTransform()
self.image.translate(self.mm_offset[0] - self.mm_per_pixel[0]/2, self.mm_offset[1] - self.mm_per_pixel[1]/2)
self.image.scale(self.mm_per_pixel[0], self.mm_per_pixel[1])
def main():
app = QtWidgets.QApplication(sys.argv)
window = ImageDisplay()
window.show()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
sys.exit(app.exec_())
if __name__ == '__main__':
main()