Two lines are created on the live chart and a legend is added. At the next update, the lines on the chart are deleted using self.pw.clear().
But the legend is not deleted, and with each update, a new instance of the legend is added, there are a lot of them and the FPS of the schedule update quickly drops.
Here http://www.pyqtgraph.org/documentation/graphicsItems/plotitem.html says Clear(): “Remove all items from the ViewBox.”
Attempting to Clear / removeItem - have not yet helped (either the syntax is incorrect, or the procedure call is incorrect).
How to delete a legend when updating a chart or stop multiple legends creation?
import random
from PyQt5 import QtGui
import pyqtgraph as pg
import sys
class Mainwindow(QtGui.QMainWindow):
def __init__(self, parent):
super(Mainwindow, self).__init__()
self.centralWidget = QtGui.QWidget()
self.setCentralWidget(self.centralWidget)
self.resize(1000, 500)
self.vbox = QtGui.QVBoxLayout()
self.pw = pg.PlotWidget()
self.vbox.addWidget(self.pw)
self.centralWidget.setLayout(self.vbox)
# Update chart
self.timer = pg.QtCore.QTimer()
self.timer.setSingleShot(False)
self.timer.timeout.connect(self.update)
self.timer.start(10)
def update(self):
x = []
y = []
z = []
for i in range(10000):
x.append(i)
y.append(random.uniform(0, 1))
z.append(1 + random.uniform(0, 1))
self.pw.clear()
line_red = pg.PlotCurveItem(x, y, clear=True, pen='r', name='Red')
line_yellow = pg.PlotCurveItem(x, z, clear=True, pen='y', name='Yellow')
self.pw.addItem(line_red)
self.pw.addItem(line_yellow)
self.pw.addLegend()
app = QtGui.QApplication(sys.argv)
ex = Mainwindow(app)
ex.show()
sys.exit(app.exec_())
You have an XY problem, instead of asking How to update the plot? questions How to eliminate the duplicate legend ?. So I will point out a solution to the underlying problem.
Considering the above, the logic is to only create the items once and update the information using the setData() method.
import random
import sys
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
class Mainwindow(QtGui.QMainWindow):
def __init__(self, parent):
super(Mainwindow, self).__init__()
self.centralWidget = QtGui.QWidget()
self.setCentralWidget(self.centralWidget)
self.resize(1000, 500)
vbox = QtGui.QVBoxLayout(self.centralWidget)
self.pw = pg.PlotWidget()
self.pw.addLegend()
vbox.addWidget(self.pw)
# Update chart
self.timer = pg.QtCore.QTimer()
self.timer.setSingleShot(False)
self.timer.timeout.connect(self.update)
self.timer.start(10)
self.line_red = pg.PlotCurveItem(clear=True, pen="r", name="Red")
self.line_yellow = pg.PlotCurveItem(clear=True, pen="y", name="Yellow")
self.pw.addItem(self.line_red)
self.pw.addItem(self.line_yellow)
def update(self):
x = []
y = []
z = []
for i in range(10000):
x.append(i)
y.append(random.uniform(0, 1))
z.append(1 + random.uniform(0, 1))
self.line_red.setData(x, y)
self.line_yellow.setData(x, z)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ex = Mainwindow(app)
ex.show()
sys.exit(app.exec_())
Related
My ui file contains a widget container with a vertical layout named "VL" and a line edit named "Radiance". I created a single bar graph that I want to change as I input values into the line edit. At the moment, it does just that, except it creates a new plot every time. If I use my "remove" function it doesn't make a whole separate plot, but it ruins the layout of the one. I think the problem lies with my "remove" function and where to put it, please help.
I imported QtWidgets, uic, matplot.figure, and necessary backends:
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('PyQt_App1.ui', self)
self.setWindowTitle("Window Title")
self.Radiance.textChanged.connect(self.animate)
def animate(self):
self.remove()
r = self.Radiance.text()
if r:
rad = float(r)
positions = [0.25]
fig1 = Figure()
ax1f1 = fig1.add_subplot(111)
ax1f1.set_ylim([0, 100])
ax1f1.set_xlim([0, 0.5])
ax1f1.bar(positions, rad, width=0.2, color="g")
self.addmpl(fig1)
else:
r = 0
rad = float(r)
positions = [0.25]
fig1 = Figure()
ax1f1 = fig1.add_subplot(111)
ax1f1.set_ylim([0, 100])
ax1f1.set_xlim([0, 0.5])
ax1f1.bar(positions, rad, width=0.2, color="g")
self.addmpl(fig1)
def addmpl(self, fig):
self.canvas = FigureCanvas(fig)
self.VL.addWidget(self.canvas)
# self.canvas.setParent(self.Frame)
self.canvas.draw()
def remove(self):
self.VL.removeWidget(self.canvas)
self.canvas.close()
if __name__ == '__main__':
import sys
from PyQt5 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
main = MyWindow()
main.show()
sys.exit(app.exec_())
Instead of creating a new figure every time I would just keep references to the current bar plot and the current axes, and use those to update the figure, e.g.
from PyQt5 import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.pyplot import Figure
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
central = QtWidgets.QWidget(self)
self.VL = QtWidgets.QVBoxLayout(central)
self.Radiance = QtWidgets.QLineEdit(self)
self.VL.addWidget(self.Radiance)
self.canvas = FigureCanvas(Figure())
self.VL.addWidget(self.canvas)
self.ax1f1 = self.canvas.figure.subplots()
self.ax1f1.set_ylim([0, 100])
self.ax1f1.set_xlim([0, 0.5])
self.bar = None
self.setWindowTitle("Window Title")
self.setCentralWidget(central)
self.Radiance.textChanged.connect(self.animate)
def animate(self):
r = self.Radiance.text()
try:
rad = float(r)
except ValueError:
rad = 0
positions = [0.25]
if self.bar:
self.bar.remove()
self.bar = self.ax1f1.bar(positions, rad, width=0.2, color="g")
self.canvas.draw()
if __name__ == '__main__':
import sys
from PyQt5 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
main = MyWindow()
main.show()
sys.exit(app.exec_())
Is it possible link the auto-scale of several plots?
I want to scale all the plots with which ever is the biggest range on all curves of all plots.
Is there way to make it with a pyqtgraph function, or should I find the max, min and set the scale with a custom function?
I am using pyqtgraph on PyQt5
Pyqtgraph should automatically scale the y-axis on multiple plots with which ever has the largest range. Here's an example widget with auto-scaling plots in PyQt4 but the concept should be the same for PyQt5.
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import sys
import random
class AutoScaleMultiplePlotWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(AutoScaleMultiplePlotWidget, self).__init__(parent)
self.NUMBER_OF_PLOTS = 4
self.LEFT_X = 0
self.RIGHT_X = 5
self.SPACING = 1
self.x_axis = np.arange(self.LEFT_X, self.RIGHT_X + 1, self.SPACING)
self.buffer_size = int((abs(self.LEFT_X) + abs(self.RIGHT_X) + 1)/self.SPACING)
self.auto_scale_plot_widget = pg.PlotWidget()
self.auto_scale_plot_widget.setLabel('left', 'left axis')
# Create plots
self.left_plot1 = self.auto_scale_plot_widget.plot()
self.left_plot2 = self.auto_scale_plot_widget.plot()
self.left_plot3 = self.auto_scale_plot_widget.plot()
self.left_plot4 = self.auto_scale_plot_widget.plot()
self.left_plot1.setPen((173,255,129), width=1)
self.left_plot2.setPen((172,187,255), width=1)
self.left_plot3.setPen((255,190,116), width=1)
self.left_plot4.setPen((204,120,255), width=1)
self.initialize_plot_buffers()
self.initialize_data_buffers()
self.layout = QtGui.QGridLayout()
self.layout.addWidget(self.auto_scale_plot_widget)
self.start()
def initialize_data_buffers(self):
"""Create blank data buffers for each curve"""
self.data_buffers = []
for trace in range(self.NUMBER_OF_PLOTS):
self.data_buffers.append([0])
def initialize_plot_buffers(self):
"""Add plots into buffer for each curve"""
self.plots = []
self.plots.append(self.left_plot1)
self.plots.append(self.left_plot2)
self.plots.append(self.left_plot3)
self.plots.append(self.left_plot4)
def update_plot(self):
"""Generates new random value and plots curve onto plot"""
for trace in range(self.NUMBER_OF_PLOTS):
if len(self.data_buffers[trace]) >= self.buffer_size:
self.data_buffers[trace].pop(0)
data_point = self.data_buffers[trace][-1] + random.randint(10,50)
self.data_buffers[trace].append(float(data_point))
self.plots[trace].setData(self.x_axis[len(self.x_axis) - len(self.data_buffers[trace]):], self.data_buffers[trace])
def get_auto_scale_plot_layout(self):
return self.layout
def start(self):
self.multiple_axis_plot_timer = QtCore.QTimer()
self.multiple_axis_plot_timer.timeout.connect(self.update_plot)
self.multiple_axis_plot_timer.start(500)
if __name__ == '__main__':
# Create main application window
app = QtGui.QApplication([])
app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
mw = QtGui.QMainWindow()
mw.setWindowTitle('Auto Scale Multiple Plot Example')
# Create plot
auto_scale_plot = AutoScaleMultiplePlotWidget()
# Create and set widget layout
# Main widget container
cw = QtGui.QWidget()
ml = QtGui.QGridLayout()
cw.setLayout(ml)
mw.setCentralWidget(cw)
# Add plot to main layout
ml.addLayout(auto_scale_plot.get_auto_scale_plot_layout(),0,0)
mw.show()
if(sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I tried to create a 2D plot using QGraphicsItem, I was successful in doing that but when I drag the QGraphicsItem there is a delay and the view is distorted.
Searching for a solution, I came across this QGraphicsItem paint delay. I applied the mouseMoveEvent to my QGraphicsView but it did not resolve the problem.
Could someone tell me what is causing the problem and how can I fix it?
Here is my code:
from PyQt4 import QtGui, QtCore
import sys
import numpy as np
class MyGraphicsItem(QtGui.QGraphicsItem):
def __init__(self,dataX,dataY):
super(MyGraphicsItem,self).__init__()
self.Xval = dataX
self.Yval = dataY
self.Xvalmin = np.min(self.Xval)
self.Xvalmax = np.max(self.Xval)
self.Yvalmin = np.min(self.Yval)
self.Yvalmax = np.max(self.Yval)
self.rect = QtCore.QRectF(0,0,100,2)
self.points = []
self._picture = None
def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None):
if self._picture is None:
self._picture = QtGui.QPicture()
QPainter.begin(self._picture)
startPoint = QtCore.QPointF(0, 0)
cubicPath = QtGui.QPainterPath()
cubicPath.moveTo(startPoint)
for i in range(len(self.points) - 2):
points_ = self.points[i:i+3]
cubicPath.cubicTo(*points_)
QPainter.setPen(QtGui.QPen(QtCore.Qt.red))
QPainter.drawLine(0,10,100,10)
QPainter.drawLine(0,-10,0,10)
QPainter.setPen(QtGui.QPen(QtCore.Qt.black))
QPainter.drawPath(cubicPath)
QPainter.end()
else:
self._picture.play(QPainter)
def boundingRect(self):
return self.rect
class mygraphicsview(QtGui.QGraphicsView):
def __init__(self):
super(mygraphicsview,self).__init__()
def mouseMoveEvent(self, event):
QtGui.QGraphicsView.mouseMoveEvent(self,event)
if self.scene().selectedItems():
self.update()
class Mainwindow(QtGui.QMainWindow):
def __init__(self):
super(Mainwindow,self).__init__()
self.main_widget = QtGui.QWidget()
self.vl = QtGui.QVBoxLayout()
self.scene = QtGui.QGraphicsScene()
self.view = mygraphicsview()
self.Xval = np.linspace(0,100,1000)
self.Yval = np.sin(self.Xval)
self.painter = QtGui.QPainter()
self.style = QtGui.QStyleOptionGraphicsItem()
self.item = MyGraphicsItem(self.Xval, self.Yval)
self.item.paint(self.painter, self.style,self.main_widget)
self.item.setFlag(QtGui.QGraphicsItem.ItemIsMovable,True)
self.trans = QtGui.QTransform()
self.trans.scale(5,5)
self.item.setTransform(self.trans)
self.scene = QtGui.QGraphicsScene()
self.scene.addItem(self.item)
self.view.setScene(self.scene)
self.vl.addWidget(self.view)
self.main_widget.setLayout(self.vl)
self.setCentralWidget(self.main_widget)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Mainwindow()
window.show()
sys.exit(app.exec_())
I fixed the issue of dragging delay.
The reason for the occurrence of such a delay is due to the boundinRect() function. The boudingRect was too tight around the item designed.
Adding some marigin to the boundingRect(), solved the problem.
self.rect = QtCore.QRectF(-10,-10,120,25)
form_class = uic.loadUiType("GUI.ui")[0] # Load the UI
class MainWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, target, Afb, np, conversion, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
if self.RadioButton.isChecked():
Ids, Ugs = unipolar_steuerkennlinie(self.target, self.Afb, self.np, self.conversion)
def plot_matplotlibwidget(self, *args):
for x in range(0, 40):
self.matplotlibwidget.axes.plot([args[0][x]], [[args[1][x]]])
self.matplotlibwidget.axes.figure.canvas.draw_idle()
Hi, I have this function and I don't really know how to plot correctly. Now it shows nothing but the does change the scale.
Since it is hard to completely replicate your code and error, I've created you a simple minimal GUI that has a MPL widget and draws a few random lines.
from PyQt4 import QtGui, QtCore
import sys
import functools
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
from matplotlib.figure import Figure
class test(QtGui.QWidget):
def __init__(self,parent=None):
self.widget=QtGui.QWidget.__init__(self, parent)
# Button to plot
self.btnPlot = QtGui.QPushButton('Plot')
self.btnPlot.connect(self.btnPlot, QtCore.SIGNAL('clicked()'), self.btnPlotPressed)
# Line edit for number of lines
self.qleN = QtGui.QLineEdit(str(0))
# Create canvas
self.setupMPLWidget()
# Layout
self.hbox = QtGui.QHBoxLayout()
self.hbox.addWidget(self.btnPlot)
self.hbox.addWidget(self.qleN)
self.hbox.addWidget(self.canvas)
self.setLayout(self.hbox)
self.show()
def btnPlotPressed(self):
"""Plots a few lines."""
# Get number of buttons to add
n=int(self.qleN.text())
# Generate some data
xs,ys=self.randData(n)
# Plot
self.plot_matplotlibwidget(xs,ys)
def randData(self,n):
"""Creates n random data sets."""
ys=[]
xs=[]
for i in range(n):
xs.append(np.arange(n-i))
ys.append(np.random.random(size=xs[-1].shape))
return xs,ys
def plot_matplotlibwidget(self,*args):
"""Plots list of datasets."""
for x in range(0, len(args[0])):
self.ax.plot(args[0][x], args[1][x])
self.ax.figure.canvas.draw_idle()
def setupMPLWidget(self):
"""Sets up a MPL figure to draw on."""
# Create parenting widget
self.plotFrame = QtGui.QWidget()
self.plotFrame.setMaximumWidth(1)
# Create Figure
self.fig = Figure( dpi=100)
#self.fig.set_size_inches(100,100,forward=True)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.plotFrame)
self.ax = self.fig.add_subplot(111)
def main():
#Creating application
app = QtGui.QApplication(sys.argv)
main_win = test()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
It has a little QLE where you can specify how many lines you want to draw. I hope this helps.
Following up on this Question and the solution provided by tcaswell I tried to adopt the code for imshow() to generate a non-freezing window with a slider for image processing, such as gaussian blur filter. (I plotted two images on top of each other, because I want to display a partly transparent mask at a later stage.)
I hope some of you might find this useful, although I could still use some help.
EDIT: You can find the current state in section THIRD CODE below. I am keeping the old versions for other users who would like to dig into the details.
I derived two different working codes, each having some (minor) issues and I would really appreciate some advice.
First code:
As long as the QSlider is dragged around the thread is running. However, you can not simply click the slider bar. Any suggestion?
The image axes are not properly plotted, i.e. they disappear again. Why?
The plot updating is not what I would call fast, although it is faster than calling imshow() everytime. How can I speed this up even more?
The window is still frozen for the very short time during which the plot is updated. (The window dragging while the loop is running is stuttering.) Can this be improved?
To not run into QThread: Destroyed while thread is still running I have put a time.sleep(1) in closeEvent(). I know this is really bad, but how can I avoid it without a new flag?
import time, sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from scipy import misc
from scipy import ndimage
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class ApplicationWindow(QtGui.QMainWindow):
get_data = QtCore.pyqtSignal()
close_request = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.thread = QtCore.QThread(parent=self)
self.worker = Worker(parent=None)
self.worker.moveToThread(self.thread)
self.create_main_frame()
self.close_request.connect(self.thread.quit)
self.startButton.clicked.connect(self.start_calculation)
self.stopButton.clicked.connect(self.stop_calculation)
self.worker.started.connect(self.thread.start)
self.worker.new_pixel_array.connect(self.update_figure)
self.slider.sliderPressed.connect(self.start_calculation)
self.slider.valueChanged.connect(self.slider_value_changed)
self.slider.sliderReleased.connect(self.stop_calculation)
self.get_data.connect(self.worker.get_data)
self.thread.start()
def create_main_frame(self):
self.main_frame = QtGui.QWidget()
self.dpi = 100
self.width = 5
self.height = 5
self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
self.axes = self.fig.add_subplot(111)
self.axes.axis((0,512,0,512))
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.updateGeometry()
self.canvas.draw()
self.background = None
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
self.im1 = self.axes.imshow(misc.ascent(), cmap='bone', interpolation='lanczos', extent=[0,512,0,512], aspect=(1), animated=True)
self.im2 = self.axes.imshow(misc.lena(), cmap='afmhot', interpolation='lanczos', extent=[0,265,0,256], aspect=(1), animated=True)
self.startButton = QtGui.QPushButton(self.tr("Keep Calculating"))
self.stopButton = QtGui.QPushButton(self.tr("Stop Calculation"))
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.slider.setRange(0, 100)
self.slider.setValue(50)
self.slider.setTracking(True)
self.slider.setTickPosition(QtGui.QSlider.TicksBothSides)
layout = QtGui.QGridLayout()
layout.addWidget(self.canvas, 0, 0)
layout.addWidget(self.slider, 1, 0)
layout.addWidget(self.startButton, 2, 0)
layout.addWidget(self.stopButton, 3, 0)
self.main_frame.setLayout(layout)
self.setCentralWidget(self.main_frame)
self.setWindowTitle(self.tr("Gaussian Filter - Slider not clickable"))
def slider_value_changed(self):
#self.worker.blockSignals(False)
self.worker.slider = self.slider.value()
def start_calculation(self):
self.worker.exiting = False
self.worker.slider = self.slider.value()
self.startButton.setEnabled(False)
self.stopButton.setEnabled(True)
self.get_data.emit()
def stop_calculation(self):
self.worker.exiting = True
self.startButton.setEnabled(True)
self.stopButton.setEnabled(False)
self.cleanup_UI()
def update_figure(self, im1_data,im2_data):
self.canvas.restore_region(self.background)
self.im1.set_array(im1_data)
self.im2.set_array(im2_data)
self.axes.draw_artist(self.im1)
self.axes.draw_artist(self.im2)
self.canvas.blit(self.axes.bbox)
def cleanup_UI(self):
self.background = None
self.canvas.draw()
def closeEvent(self, event):
self.stop_calculation()
self.close_request.emit()
time.sleep(1)
## ugly workaround to prevent window from closing before thread is closed. (calculation takes time) How can this be avoided without additional flag?
event.accept()
class Worker(QtCore.QObject):
new_pixel_array = QtCore.pyqtSignal(np.ndarray,np.ndarray)
started = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtCore.QObject.__init__(self, parent)
self.exiting = True
self.slider = 0
#QtCore.pyqtSlot()
def get_data(self):
while self.exiting == False:
self.started.emit()
im1_data = self.gauss(misc.ascent(),self.slider)
im2_data = self.gauss(misc.lena(),self.slider)
self.new_pixel_array.emit(im1_data, im2_data)
print 'Slider Value: ', self.slider
def gauss(self,im,radius):
gaussed = ndimage.gaussian_filter(im, radius)
return gaussed
def main():
app = QtGui.QApplication(sys.argv)
form = ApplicationWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()
Second code:
You can now also click the slider bar.
Background (axes) reconstruction is still not working. Of course calling self.canvas.draw() in cleanup_UI() fixes this somehow.
When the slider bar is clicked, the calculation is performed once, but if the slider is dragged around and released, the calculation is performed twice at the same value. Why? I tried to catch this with blockSignals but then sometimes (when the slider is dragged around really fast and released) the second image in the plot is not updated properly. You recognize it by two different amounts of blur.
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from scipy import misc
from scipy import ndimage
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class ApplicationWindow(QtGui.QMainWindow):
get_data = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.thread = QtCore.QThread(parent=self)
self.worker = Worker(parent=None)
self.worker.moveToThread(self.thread)
self.create_main_frame()
self.startButton.clicked.connect(self.start_calculation)
self.worker.new_pixel_array.connect(self.update_figure)
self.worker.done.connect(self.stop_calculation)
self.slider.sliderPressed.connect(self.start_calculation)
self.slider.valueChanged.connect(self.slider_value_changed)
self.slider.actionTriggered.connect(self.start_calculation)
self.get_data.connect(self.worker.get_data)
self.thread.start()
def create_main_frame(self):
self.main_frame = QtGui.QWidget()
self.dpi = 100
self.width = 5
self.height = 5
self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
self.axes = self.fig.add_subplot(111)
self.axes.axis((0,512,0,512))
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.updateGeometry()
self.canvas.draw()
self.background = None
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
self.im1 = self.axes.imshow(misc.ascent(), cmap='bone', interpolation='lanczos', extent=[0,512,0,512], aspect=(1), animated=True)
self.im2 = self.axes.imshow(misc.lena(), cmap='afmhot', interpolation='lanczos', extent=[0,265,0,256], aspect=(1), animated=True)
self.startButton = QtGui.QPushButton(self.tr("Do a Calculation"))
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.slider.setRange(0, 100)
self.slider.setValue(50)
self.slider.setTracking(True)
self.slider.setTickPosition(QtGui.QSlider.TicksBothSides)
layout = QtGui.QGridLayout()
layout.addWidget(self.canvas, 0, 0)
layout.addWidget(self.slider, 1, 0)
layout.addWidget(self.startButton, 2, 0)
self.main_frame.setLayout(layout)
self.setCentralWidget(self.main_frame)
self.setWindowTitle(self.tr("Gaussian Filter"))
def slider_value_changed(self):
#self.worker.blockSignals(False)
self.worker.slider = self.slider.value()
def start_calculation(self):
self.slider_value_changed()
self.worker.exiting = False
self.startButton.setEnabled(False)
self.get_data.emit()
def stop_calculation(self):
self.worker.exiting = True
self.startButton.setEnabled(True)
self.cleanup_UI()
def update_figure(self, im1_data,im2_data):
self.im1.set_array(im1_data)
self.im2.set_array(im2_data)
self.axes.draw_artist(self.im1)
self.axes.draw_artist(self.im2)
self.canvas.blit(self.axes.bbox)
def cleanup_UI(self):
self.canvas.restore_region(self.background)
#self.canvas.draw()
#self.worker.blockSignals(True)
class Worker(QtCore.QObject):
new_pixel_array = QtCore.pyqtSignal(np.ndarray,np.ndarray)
done = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtCore.QObject.__init__(self, parent)
self.exiting = True
self.slider = 0
#QtCore.pyqtSlot()
def get_data(self):
if self.exiting == False:
im1_data = self.gauss(misc.ascent(),self.slider)
im2_data = self.gauss(misc.lena(),self.slider)
self.new_pixel_array.emit(im1_data,im2_data)
print 'Calculation performed, Slider Value: ', self.slider
self.done.emit()
else: None
def gauss(self,im,radius):
gaussed = ndimage.gaussian_filter(im, radius)
return gaussed
def main():
app = QtGui.QApplication(sys.argv)
form = ApplicationWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()
EDIT: Third Code (Major issues resolved and update rate limited)
The slider is now only starting a new thread when the calculation of the previous one has finished. That was acheived by disconnect.
The Plotting is still slow, (the blur function too).
restore_region still seems to have no effect at all.
I have now put the calculation of both images into threads and return the result via a Queue(). If you see some possibility for improvements, plese let me know.
I once tried to switch to the multiprocessing module and put the calculation inside a Pool(), but it throws me an Can't pickle... error. As I am totally new to multiprocessing, I would very much like to learn more about it.
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from scipy import misc
from scipy import ndimage
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from threading import Thread
from Queue import Queue
class ApplicationWindow(QtGui.QMainWindow):
get_data = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.thread = QtCore.QThread(parent=self)
self.worker = Worker(parent=None)
self.worker.moveToThread(self.thread)
self.create_main_frame()
self.startButton.clicked.connect(self.start_calculation)
self.stopButton.clicked.connect(self.stop_calculation)
self.worker.started.connect(self.thread.start)
self.worker.new_pixel_array.connect(self.update_figure)
self.slider.actionTriggered.connect(self.start_calculation)
self.slider.valueChanged.connect(self.slider_value_changed)
self.worker.done.connect(self.stop_calculation)
self.get_data.connect(self.worker.get_data)
self.thread.start()
def create_main_frame(self):
self.main_frame = QtGui.QWidget()
self.dpi = 100
self.width = 5
self.height = 5
self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
self.axes = self.fig.add_subplot(111)
self.axes.axis((0,512,0,512))
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.updateGeometry()
self.background = None
self.canvas.draw()
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
self.im1 = self.axes.imshow(misc.ascent(), cmap='bone', interpolation='lanczos', extent=[0,512,0,512], aspect=(1), animated=True)
self.im2 = self.axes.imshow(misc.lena(), cmap='afmhot', interpolation='lanczos', extent=[0,265,0,256], aspect=(1), animated=True)
self.startButton = QtGui.QPushButton(self.tr("Start Calculation"))
self.stopButton = QtGui.QPushButton(self.tr("Stop Calculation"))
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.slider.setRange(0, 100)
self.slider.setValue(50)
self.slider.setTracking(True)
self.slider.setTickPosition(QtGui.QSlider.TicksBothSides)
layout = QtGui.QGridLayout()
layout.addWidget(self.canvas, 0, 0)
layout.addWidget(self.slider, 1, 0)
layout.addWidget(self.startButton, 2, 0)
layout.addWidget(self.stopButton, 3, 0)
self.main_frame.setLayout(layout)
self.setCentralWidget(self.main_frame)
self.setWindowTitle(self.tr("Gaussian Filter"))
def slider_value_changed(self):
self.worker.slider = self.slider.value()
def start_calculation(self):
if self.worker.exiting:
self.slider.actionTriggered.disconnect(self.start_calculation)
self.worker.slider = self.slider.value()
self.startButton.setEnabled(False)
self.stopButton.setEnabled(True)
self.get_data.emit()
self.worker.exiting = False
def stop_calculation(self):
if not self.worker.exiting:
self.slider.actionTriggered.connect(self.start_calculation)
self.worker.exiting = True
self.startButton.setEnabled(True)
self.stopButton.setEnabled(False)
self.cleanup_UI()
def update_figure(self, im1_data,im2_data):
#self.canvas.restore_region(self.background)
self.im1.set_array(im1_data)
self.im2.set_array(im2_data)
self.axes.draw_artist(self.im1)
self.axes.draw_artist(self.im2)
self.canvas.blit(self.axes.bbox)
def cleanup_UI(self):
self.background = None
self.canvas.draw()
class Worker(QtCore.QObject):
new_pixel_array = QtCore.pyqtSignal(np.ndarray,np.ndarray)
started = QtCore.pyqtSignal()
done = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtCore.QObject.__init__(self, parent)
self.exiting = True
self.slider = 0
#QtCore.pyqtSlot()
def get_data(self):
while self.exiting == False:
self.started.emit()
queue1 = Queue()
queue2 = Queue()
im1T = Thread(target=self.gauss, args=(misc.ascent(),queue1))
im2T = Thread(target=self.gauss, args=(misc.lena(),queue2))
slider_val = self.slider
im1T.start()
im2T.start()
im1T.join()
im2T.join()
im1_data = queue1.get()
im2_data = queue2.get()
self.new_pixel_array.emit(im1_data, im2_data)
if slider_val == self.slider:
self.done.emit()
print 'Slider Value: ', self.slider
break
def gauss(self,im,output_queue):
gaussed = ndimage.gaussian_filter(im,self.slider)
output_queue.put(gaussed)
def main():
app = QtGui.QApplication(sys.argv)
form = ApplicationWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()