Simple popup dialog opens too long with Chaco - python

I need the small popup dialog to appear at cursor position when user clicks my Chaco plot. But when there are many points in the plot (~100.000) the popup window loads too slow (1-2 seconds) and not responsive in the beginning. So the application becomes not very interactive.. I have no idea how I can speed it up. Can you suggest any solution, workaround or advice something?
Code example:
from chaco.api import ArrayPlotData, Plot, BaseTool
from enable.component_editor import ComponentEditor
from traits.api import HasTraits, Instance
from traitsui.api import View, Item
import numpy as np
from PyQt4 import QtGui, QtCore
# simple popup with button and lineedit
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setWindowFlags(QtCore.Qt.Popup)
self.resize(100, 50)
self.layout = QtGui.QVBoxLayout(self)
self.lineedit = QtGui.QLineEdit()
self.button = QtGui.QPushButton('button')
self.layout.addWidget(self.button)
self.layout.addWidget(self.lineedit)
# new Tool to process right click over plot
class RightClickTool(BaseTool):
def __init__(self, *args, **kw):
self.dlg = Dialog()
super(RightClickTool, self).__init__(*args, **kw)
def normal_right_down(self, event):
self.dlg.show() # show the dialog on right click
event.handled = True
class MyPlot(HasTraits):
plot = Instance(Plot)
traits_view = View(Item('plot', editor=ComponentEditor(), show_label=False))
def __init__(self):
super(MyPlot, self).__init__()
# data to plot
x = np.linspace(0, 1, 10**6)
y = np.random.rand(10**6)
plotdata = ArrayPlotData(x=x, y=y)
plot = Plot(plotdata)
plot.tools.append(RightClickTool(plot))
plot.plot(('x', 'y'))
self.plot = plot
lineplot = MyPlot()
lineplot.configure_traits()

Related

How to remove Matplotlib colorbar upon subsequent replots?

I'm trying to do something pretty straight forward in Matplotlib, but hitting a ton of hurdles; after googling for hours, I'm thinking this takes some very specific magic. I just want my GUI to start on a blank screen and then import a file that needs a colorbar - if the file is good, a colorbar is added. If the file is added again (or a different one), the colorbar is removed and a new one is plotted. If the file is bad, we reset to initial conditions (no colorbars or plot). I really hope the below code makes sense for what I'm getting at here, thank you so much for your help and time in advance:
import sys
import os
import random
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QGridLayout, QFileDialog, QPushButton
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
MyMplCanvas.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = MyMplCanvas.fig.add_subplot(111)
self.compute_initial_figure()
FigureCanvas.__init__(self, MyMplCanvas.fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyDynamicMplCanvas(MyMplCanvas):
def __init__(self, *args, **kwargs):
MyMplCanvas.__init__(self, *args, **kwargs)
def compute_initial_figure(self):
C=np.random.rand(500).reshape((20,25))
S=np.random.rand(500).reshape((20,25))
dc = self.function(S,C)
im = self.axes.imshow(dc, alpha = 0)
MyMplCanvas.fig.patch.set_facecolor('k')
self.axes.patch.set_facecolor('k')
# don't want to show the cb initially (imagine GUI starts on a blank screen)
#cb = MyMplCanvas.fig.colorbar(im, ax=self.axes,
#orientation='vertical')#,use_gridspec=True)??
def update_figure(self):
# need something like the below to delete subsequent colorbars if they exist:
#if MyMplCanvas.fig.axes[1] is not None:
# MyMplCanvas.fig.delaxes(MyMplCanvas.fig.axes[1]) #remove colorbar
self.axes.cla()
if P1.df: #datafile is good
MyMplCanvas.fig.patch.set_facecolor('w')
self.axes.patch.set_facecolor('w')
C=np.random.rand(500).reshape((20,25))
S=np.random.rand(500).reshape((20,25))
dc = self.function(S,C)
im = self.axes.imshow(dc)
cb = MyMplCanvas.fig.colorbar(im, ax=self.axes,
orientation='vertical')#,use_gridspec=True)??
else: #datafile was bad, or they pressed cancel
C=np.random.rand(500).reshape((20,25))
S=np.random.rand(500).reshape((20,25))
dc = self.function(S,C)
im = self.axes.imshow(dc, alpha = 0)
MyMplCanvas.fig.patch.set_facecolor('k')
self.axes.patch.set_facecolor('k')
self.show()
self.draw()
def function(self,s,c):
return s*2+c
class P1(QtWidgets.QWidget):
df = False
def __init__(self, parent=None):
super(P1, self).__init__(parent)
layout = QGridLayout(self)
self.button_browse1 = QPushButton('Browse Good', self)
self.button_browse1.clicked.connect(self.browseFile1)
layout.addWidget(self.button_browse1, 1, 1, 1, 1)
self.button_browse1.show()
self.button_browse2 = QPushButton('Browse Bad', self)
self.button_browse2.clicked.connect(self.browseFile2)
layout.addWidget(self.button_browse2, 2, 1, 1, 1)
self.button_browse2.show()
self.dc = MyDynamicMplCanvas(self, width=5, height=4, dpi=100)
layout.addWidget(self.dc, 3, 1, 1, 1)
def browseFile1(self):
P1.df = True
self.dc.update_figure()
def browseFile2(self):
P1.df = False
self.dc.update_figure()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.stack = QtWidgets.QStackedWidget(self)
P1f = P1(self)
self.stack.addWidget(P1f)
self.setCentralWidget(self.stack)
if __name__ == '__main__':
qApp = QtWidgets.QApplication(sys.argv)
aw = MainWindow()
aw.show()
sys.exit(qApp.exec_())
Okay, so for anyone else stumbling on this, the trick is to first define your colorbar the very first time you plot (initial figure) via the top Class its in, i.e., MyMplCanvas.cbar = MyMplCanvas.fig.colorbar(im). Its very important to define it up front even if you didn't want to even use it yet.
Then at the top of the update plot function, simply call MyMplCanvas.cbar.remove().

Plotting with a for loop in matplotlib, with lists

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.

Variables across classes to scale plot in PyQt GUI

I'm making a GUI which is to have a couple user input boxes and a plot which will use factors in the input boxes to scale data. The GUI will need an apply button and an export button. I am using PyQt5 for the GUI and Matplotlib for the plotting. My approach has been to create separate QWidgets for the plot and the input boxes and tie them together in a third QMainWindow.
I have the GUI appearing correctly
How can I get the apply button to send the 3 variables across to the main class and to the plotting class? Would it be possible to have all of this happen within one class to simplify how my variables will work?
import sys
import matplotlib
matplotlib.use("Qt5Agg")
from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
from PyQt5.QtWidgets import *
from numpy import arange, sin, pi
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class AppForm(QWidget):
def __init__(self):
# Initialize the object as a QWidget and
# set its title and minimum width
QWidget.__init__(self)
self.setWindowTitle('Input')
self.setMinimumWidth(400)
# Create the QVBoxLayout that lays out the whole form
self.layout = QVBoxLayout()
# Create the form layout that manages the labeled controls
self.form_layout = QFormLayout()
self.aFactor = QLineEdit(self)
self.mFactor = QLineEdit(self)
self.cZone = QLineEdit(self)
self.form_layout.addRow('AFactor', self.aFactor)
self.form_layout.addRow('MFactor', self.mFactor)
self.form_layout.addRow('CZone', self.cZone)
self.layout.addLayout(self.form_layout)
self.button_box = QHBoxLayout()
self.button_box.addStretch(1)
self.apply_button = QPushButton("Apply", self)
self.output_button = QPushButton("Output", self)
self.button_box.addWidget(self.apply_button)
self.button_box.addWidget(self.output_button)
self.layout.addLayout(self.button_box)
self.setLayout(self.layout)
self.apply_button.clicked.connect(self.applyButton)
def applyButton(self):
self.af = self.aFactor
self.mf = self.mFactor
self.cz = self.cZone
print self.af.text(), self.mf.text(), self.cz.text()
class MyMplCanvas(FigureCanvas):
"""Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
def __init__(self, parent=None, width=5, height=4, dpi=100, title='title'):
self.title = title
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
fig.suptitle(title)
# We want the axes cleared every time plot() is called
self.axes.hold(False)
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyStaticMplCanvas(MyMplCanvas):
"""Simple canvas with a sine plot."""
def compute_initial_figure(self):
t = arange(0.0, 3.0, 0.01)
s = sin(2*pi*t)
self.axes.plot(t, s)
self.axes.set_ylabel('label1')
self.axes.set_xlabel('label')
self.axes.grid(True)
class ApplicationWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setWindowTitle("application main window")
self.setMinimumWidth(800)
self.setMinimumHeight(600)
self.file_menu = QMenu('&File', self)
self.file_menu.addAction('&Quit', self.fileQuit,
QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
self.menuBar().addMenu(self.file_menu)
self.help_menu = QMenu('&Help', self)
self.menuBar().addSeparator()
self.menuBar().addMenu(self.help_menu)
self.help_menu.addAction('&About', self.about)
self.main_widget = QWidget(self)
l = QVBoxLayout(self.main_widget)
form = AppForm()
sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100, title='Title 1')
l.addWidget(form)
l.addWidget(sc)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
#self.statusBar().showMessage("Cool", 2000
def fileQuit(self):
self.close()
def closeEvent(self, ce):
self.fileQuit()
def about(self):
QMessageBox.about(self, "About",)
if __name__ == '__main__':
app = QApplication(sys.argv)
aw = ApplicationWindow()
aw.setWindowTitle("PyQt5 Matplot Example")
aw.show()
#sys.exit(qApp.exec_())
app.exec_()
There are several options here:
(a) Peform the work in the main class:
Instead of connecting the button in the AppForm to a method in AppForm, do the same in the ApplicationWindow.
self.form = AppForm()
self.sc = MyStaticMplCanvas(....)
self.form.apply_button.clicked.connect(self.applyButton)
def applyButton(self):
tx = self.form.aFactor.text()
# do something with tx
# e.g. call a method from MplCanvas
self.sc.someMethod(tx)
(b) Use signals and slots:
Create a signla in AppForm. Once the button is clicked, applyButton may emit this signal with the relevant content. In ApplicationWindow connect that signal to a method which can use the supplied data in some way. You can also connect it directly to a method of MplCanvas.
class AppForm(QWidget):
mysignal = pyqtSignal(object)
def __init__(self):
....
self.apply_button.clicked.connect(self.applyButton)
def applyButton(self):
self.mysignalemit((self.aFactor.text(),self.mFactor.text(), self.cZone()) )
class ApplicationWindow(QMainWindow):
def __init__(self):
....
self.form = AppForm()
self.sc = MyStaticMplCanvas(....)
self.form.mysignal.connect(self.useButtonResults)
self.form.mysignal.connect(self.sc.someMethod)
def useButtonResults(self, obj):
a,b,c = obj
# do something with a, b, c
(c) Use a single class to do everything:
Just put everything in a single class and do whatever you like.

Python 2.7 Qt Matplotlib : subplot ID reference from event

My goal is to identify which subplot has been clicked on by the user. More precisely in the matplotlib class, I can identify the subplot using event.inaxes. Great. But I cannot get that event in the Qt widget class.
I am definitely missing something ...
Here is the code with my latest "awkward" attempt. Any suggestion on how to procceed ?
I am no Python expert. Python 2.7 has to be used (no choice)
from __future__ import print_function
from __future__ import division
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from matplotlib.figure import Figure
from matplotlib.backend_bases import key_press_handler
from matplotlib.backend_bases import Event
from matplotlib.backends.backend_qt4agg import (
FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QT as NavigationToolbar)
#Connect InventoryChartsWidget to ChartFigure: QT
class TheConnector(QtCore.QObject):
selecteddataregion=pyqtSignal(name='selecteddataregion')
def emitsignal(self,xmin,xmax,ymin,ymax):
self.selecteddataregion.emit()
#Chart including events: MATPLOTLIB
class ChartFigure(Figure):
def onclick(self,event):
#MAIN ISSUE
#HOW TO RETURN THE subplot axes to class InventoryChartsWidget?
if event.button==1 :
self.ConnSbPlt.emitsignal(1.0,1.0,2.0,2.0)
print('OK: Axes is ... ', event.inaxes)
def __init__(self,Conn):
#Init the Matplotlib
Figure.__init__(self) #initialize the orginal class, see also super()
super(ChartFigure, self).__init__()
self.canvas=FigureCanvas(self)
self.ConnSbPlt=Conn
#Chart including events: QT
class InventoryChartsWidget(QtGui.QDialog):
def __init__(self, parent=None,xlimlow=0,xlimhigh=100,ylimlow=0,ylimhigh=100, G_array=[], N_array=[], ddom_array=[], hdom_array=[], speciesDict={}):
QMainWindow.__init__(self, parent)
#Fake stupid data
self.mG_array = [2] * 10
self.mHdom_array = [0.5] * 10
#jte to make sur I have it
self.xdata_start=-1.0
#fake plot to get a member of type subplot: UGLY!
#Attempt to create a member object "axes"
self.tabFake = QtGui.QWidget()
self.tabFake = self.create_tab(self.tabFake)
self.tabFake.plots = []
self.subPlotFake = self.tabFake.fig.add_subplot(111)
print("here is OK; it exists ...", self.subPlotFake)
self.create_main_frame()
self.setModal(False)
self.setVisible(True)
self.show()
def create_main_frame(self):
#Associate a Qwidget with the InventoryChartsWidget widget
print("OK here too; it exists ... ",self.subPlotFake)
self.main_frame = QtGui.QWidget()
LesTabs = QtGui.QTabWidget()
self.tabG = QtGui.QWidget()
#Fill the tab with Matplotlib object and draw the charts
self.tabG=self.create_tab(self.tabG)
self.on_draw_G(self.tabG)
self.tabG.fig.subplots_adjust(left=0.02,bottom=0.05,right=1,top=0.95,wspace=0.2,hspace=0.2)
LesTabs.addTab(self.tabG,"Chart")
grid = QGridLayout()
grid.addWidget(LesTabs, 0, 0)
self.main_frame.setLayout(grid)
self.setLayout(grid)
self.layout().addWidget(self.main_frame)
def UpdatePlot_DataSelection(self):
#SLOT
print("Get connected here process the data in the subplot XX...")
def on_draw_G(self,tab):
#Juts one subplot for test purpose
tab.fig.clear()
tab.plots = []
subPlot = tab.fig.add_subplot(111)
subPlot.hold(False)
tab.plots.append(subPlot)
self.PlotData_G(subPlot,self.mG_array,self.mHdom_array)
subPlot = tab.fig.add_subplot(122)
subPlot.hold(False)
tab.plots.append(subPlot)
self.PlotData_G(subPlot,self.mG_array,self.mHdom_array)
tab.canvas.draw()
def create_tab(self,tab):
#Create the tab widget, associated with Matplotlib plot
print("OK member exists ... ", self.xdata_start)
print("OK member exists ",self.tabFake)
#ISSUE HERE: don't understand
#print("NOT OK !!! member does not exist Why ? ",self.subPlotFake)
Conn=TheConnector()
#MATPLOTLIB
tab.fig = ChartFigure(Conn)
tab.canvas = FigureCanvas(tab.fig)
tab.canvas.setParent(tab)
tab.canvas.setFocusPolicy(Qt.StrongFocus)
tab.canvas.setFocus()
#connect signal to slot
Conn.selecteddataregion.connect(self.UpdatePlot_DataSelection)
tab.mpl_toolbar = NavigationToolbar(tab.canvas, tab)
vbox = QVBoxLayout()
vbox.addWidget(tab.canvas)
vbox.addWidget(tab.mpl_toolbar)
tab.setLayout(vbox)
tab.canvas.mpl_connect('button_press_event', tab.fig.onclick)
return tab
def on_key_press(self, event):
#Keyboard input: standard mpl key press
key_press_handler(event, self.canvas, self.mpl_toolbar)
def PlotData_G(self, plot, G_array, hdom_array):
# Plot G
plot.hold(False)
plot.scatter(x=hdom_array, y=G_array, marker='+',linewidths=1.5)
plot.set_autoscaley_on(True)
plot.tick_params(labelsize=8)
def main():
app = QApplication(sys.argv)
form = InventoryChartsWidget(xlimlow=0,xlimhigh=60,ylimlow=0,ylimhigh=80)
form.show()
app.exec_()
if __name__ == "__main__":
main()
There are 3 classes:
TheConnector is a Signal/Slot QT class
ChartFigure is matplotlib class (including the desired mouse Event)
InventoryChartsWidget is the main widget (Qt; here I need the ID of the subplot)
Any help would be greatly appreciated. Thank you.
Here is a working solution. The true limit was my thinking in python (to be more specific the lack of properly declared variables ... can't get use to it)
from __future__ import print_function
from __future__ import division
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from matplotlib.figure import Figure
from matplotlib.backend_bases import key_press_handler
from matplotlib.backend_bases import Event
from matplotlib.backends.backend_qt4agg import (
FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QT as NavigationToolbar)
#Connect InventoryChartsWidget to ChartFigure: QT
class TheConnector(QtCore.QObject):
selecteddataregionARG=pyqtSignal(object,name='selecteddataregionIN')
def emitsignalEvent(self,TheEvent):
self.selecteddataregionARG.emit(TheEvent)
#Chart including events: MATPLOTLIB
class ChartFigure(Figure):
def onclick(self,event):
#MAIN ISSUE
#HOW TO RETURN THE subplot axes to class InventoryChartsWidget class?
if event.button==1 :
print('Event: Axes is ... ', event.inaxes)
self.ConnSbPlt.emitsignalEvent(event.inaxes)
def __init__(self,Conn):
#Init the Matplotlib
Figure.__init__(self) #initialize the orginal class, see also super()
super(ChartFigure, self).__init__()
self.canvas=FigureCanvas(self)
self.ConnSbPlt=Conn
#Chart including events: QT
class InventoryChartsWidget(QtGui.QDialog):
def __init__(self, parent=None,xlimlow=0,xlimhigh=100,ylimlow=0,ylimhigh=100, G_array=[], N_array=[], ddom_array=[], hdom_array=[], speciesDict={}):
QMainWindow.__init__(self, parent)
#Fake stupid data
self.mG_array = [2] * 10
self.mHdom_array = [0.5] * 10
#jte to make sur I have it
self.xdata_start=-1.0
self.create_main_frame()
self.setModal(False)
self.setVisible(True)
self.show()
def create_main_frame(self):
#Associate a Qwidget with the InventoryChartsWidget widget
self.main_frame = QtGui.QWidget()
LesTabs = QtGui.QTabWidget()
self.tabG = QtGui.QWidget()
#Fill the tab with Matplotlib object and draw the charts
self.tabG=self.create_tab(self.tabG)
self.on_draw_G(self.tabG)
self.tabG.fig.subplots_adjust(left=0.02,bottom=0.05,right=1,top=0.95,wspace=0.2,hspace=0.2)
LesTabs.addTab(self.tabG,"Chart")
grid = QGridLayout()
grid.addWidget(LesTabs, 0, 0)
self.main_frame.setLayout(grid)
self.setLayout(grid)
self.layout().addWidget(self.main_frame)
def UpdatePlot_DataSelection_withArg(self,TheEvent):
#SLOT
print("WITH ARG : Get connected here process the data in the subplot XX...",TheEvent)
def on_draw_G(self,tab):
#Juts one subplot for test purpose
tab.fig.clear()
tab.plots = []
subPlot = tab.fig.add_subplot(111)
subPlot.hold(False)
tab.plots.append(subPlot)
self.PlotData_G(subPlot,self.mG_array,self.mHdom_array)
subPlot = tab.fig.add_subplot(122)
subPlot.hold(False)
tab.plots.append(subPlot)
self.PlotData_G(subPlot,self.mG_array,self.mHdom_array)
tab.canvas.draw()
def create_tab(self,tab):
#Create the tab widget, associated with Matplotlib plot
Conn=TheConnector()
#MATPLOTLIB
tab.fig = ChartFigure(Conn)
tab.canvas = FigureCanvas(tab.fig)
tab.canvas.setParent(tab)
tab.canvas.setFocusPolicy(Qt.StrongFocus)
tab.canvas.setFocus()
#connect signal to slot
Conn.selecteddataregionARG.connect(lambda who="Three": self.UpdatePlot_DataSelection_withArg(who))
tab.mpl_toolbar = NavigationToolbar(tab.canvas, tab)
vbox = QVBoxLayout()
vbox.addWidget(tab.canvas)
vbox.addWidget(tab.mpl_toolbar)
tab.setLayout(vbox)
tab.canvas.mpl_connect('button_press_event', tab.fig.onclick)
return tab
def on_key_press(self, event):
#Keyboard input: standard mpl key press
key_press_handler(event, self.canvas, self.mpl_toolbar)
def PlotData_G(self, plot, G_array, hdom_array):
# Plot G
plot.hold(False)
plot.scatter(x=hdom_array, y=G_array, marker='+',linewidths=1.5)
plot.set_autoscaley_on(True)
def main():
app = QApplication(sys.argv)
form = InventoryChartsWidget(xlimlow=0,xlimhigh=60,ylimlow=0,ylimhigh=80)
form.show()
app.exec_()
if __name__ == "__main__":
main()
Maybe it can help someone ...
We can of course argue if the 3 classes make sense but this is another topic.
Here is how you could transfer the event to the main class:
from __future__ import print_function
from __future__ import division
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from matplotlib.figure import Figure
from matplotlib.backend_bases import key_press_handler
from matplotlib.backends.backend_qt4agg import (
FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QT as NavigationToolbar)
#Connect InventoryChartsWidget to ChartFigure: QT
class TheConnector(QtCore.QObject):
selecteddataregion=QtCore.pyqtSignal(object, name='selecteddataregion')
def emitsignal(self,xmin,xmax,ymin,ymax, event):
self.selecteddataregion.emit((xmin,xmax,ymin,ymax, event))
#Chart including events: MATPLOTLIB
class ChartFigure(Figure):
def onclick(self,event):
#MAIN ISSUE
#HOW TO RETURN THE subplot axes to class InventoryChartsWidget?
if event.button==1 :
print('OK: Axes is ... ', event.inaxes)
self.ConnSbPlt.emitsignal(1.0,1.0,2.0,2.0, event)
def __init__(self,Conn):
#Init the Matplotlib
Figure.__init__(self) #initialize the orginal class, see also super()
super(ChartFigure, self).__init__()
self.canvas=FigureCanvas(self)
self.ConnSbPlt=Conn
#Chart including events: QT
class InventoryChartsWidget(QtGui.QDialog):
def __init__(self, parent=None,xlimlow=0,xlimhigh=100,ylimlow=0,ylimhigh=100, G_array=[], N_array=[], ddom_array=[], hdom_array=[], speciesDict={}):
QtGui.QMainWindow.__init__(self, parent)
#Fake stupid data
self.mG_array = [2] * 10
self.mHdom_array = [0.5] * 10
#jte to make sur I have it
self.xdata_start=-1.0
#fake plot to get a member of type subplot: UGLY!
#Attempt to create a member object "axes"
self.tabFake = QtGui.QWidget()
self.tabFake = self.create_tab(self.tabFake)
self.tabFake.plots = []
self.subPlotFake = self.tabFake.fig.add_subplot(111)
print("here is OK; it exists ...", self.subPlotFake)
self.create_main_frame()
self.setModal(False)
self.setVisible(True)
self.show()
def create_main_frame(self):
#Associate a Qwidget with the InventoryChartsWidget widget
print("OK here too; it exists ... ",self.subPlotFake)
self.main_frame = QtGui.QWidget()
LesTabs = QtGui.QTabWidget()
self.tabG = QtGui.QWidget()
#Fill the tab with Matplotlib object and draw the charts
self.tabG=self.create_tab(self.tabG)
self.on_draw_G(self.tabG)
self.tabG.fig.subplots_adjust(left=0.02,bottom=0.05,right=1,top=0.95,wspace=0.2,hspace=0.2)
LesTabs.addTab(self.tabG,"Chart")
grid = QtGui.QGridLayout()
grid.addWidget(LesTabs, 0, 0)
self.main_frame.setLayout(grid)
self.setLayout(grid)
self.layout().addWidget(self.main_frame)
def UpdatePlot_DataSelection(self, transfer_object):
#SLOT
xmin,xmax,ymin,ymax, event = transfer_object
print ("Axes are now in the InventoryChartsWidget: ", event.inaxes)
def on_draw_G(self,tab):
#Juts one subplot for test purpose
tab.fig.clear()
tab.plots = []
subPlot = tab.fig.add_subplot(111)
#subPlot.hold(False)
tab.plots.append(subPlot)
self.PlotData_G(subPlot,self.mG_array,self.mHdom_array)
subPlot = tab.fig.add_subplot(122)
#subPlot.hold(False)
tab.plots.append(subPlot)
self.PlotData_G(subPlot,self.mG_array,self.mHdom_array)
tab.canvas.draw()
def create_tab(self,tab):
#Create the tab widget, associated with Matplotlib plot
print("OK member exists ... ", self.xdata_start)
print("OK member exists ",self.tabFake)
#ISSUE HERE: don't understand
#print("NOT OK !!! member does not exist Why ? ",self.subPlotFake)
# reason: self.subPlotFake does not yet exist
Conn=TheConnector()
#MATPLOTLIB
tab.fig = ChartFigure(Conn)
tab.canvas = FigureCanvas(tab.fig)
tab.canvas.setParent(tab)
tab.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
tab.canvas.setFocus()
#connect signal to slot
Conn.selecteddataregion.connect(self.UpdatePlot_DataSelection)
tab.mpl_toolbar = NavigationToolbar(tab.canvas, tab)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(tab.canvas)
vbox.addWidget(tab.mpl_toolbar)
tab.setLayout(vbox)
tab.canvas.mpl_connect('button_press_event', tab.fig.onclick)
return tab
def on_key_press(self, event):
#Keyboard input: standard mpl key press
key_press_handler(event, self.canvas, self.mpl_toolbar)
def PlotData_G(self, plot, G_array, hdom_array):
# Plot G
#plot.hold(False) #axes.hold is deprecated.
plot.scatter(x=hdom_array, y=G_array, marker='+',linewidths=1.5)
plot.set_autoscaley_on(True)
plot.tick_params(labelsize=8)
def main():
app = QtGui.QApplication(sys.argv)
form = InventoryChartsWidget(xlimlow=0,xlimhigh=60,ylimlow=0,ylimhigh=80)
form.show()
app.exec_()
if __name__ == "__main__":
main()

Expediting matplotlib's scatter function in a pyqt wrapper

I am trying to plot real-time data using matplotlib's scatter function (~200 fps) with a pyqt wrapper. Each frame consists of around 1000 points. However, I get a maximum of around 7 or 8 frames per second. My code is as follows:
import sys
import random
import types
import re
import sys
import os
import matplotlib.pyplot as plt
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib import pyplot as plt
from time import sleep
class Window(QtGui.QDialog):
def __init__(self, increment=10, nSteps=500, timestep=0.0004, parent=None):
super(Window, self).__init__(parent)
# frame increment
self.increment = increment
self.nSteps = nSteps
self.timestep = timestep # in seconds
# a figure instance to plot on
self.figure = plt.figure()
self.ax1 = self.figure.add_subplot(1, 1, 1)
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QtGui.QPushButton('Plot')
self.button.clicked.connect(self.start)
# the current frame
self.index = 0
# set the layout
layout = QtGui.QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def start(self):
# connects timer to dynamic plot
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.updatePlot)
self.timer.start(100)
def updatePlot(self):
if self.index > self.increment*self.nSteps:
self.timer.stop()
return
data = [["ghost_points.dat", "green"], ["wall_points.dat", "red"], [("mps.%s.out") % self.index, "blue"]]
self.index += self.increment
self.ax1.cla() # clear axes
for i in data:
<gets x and y data and color>
self.ax1.scatter(x, y, c=color)
self.canvas.draw()
def mpsPlot():
app = QtGui.QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
if __name__ == "__main__":
mpsPlot()
I've looked at several other sources online, but none of them have provided substantial answers that have helped me in my goal. Is there any way to speed up my code to reach ~250 fps? If not, are there any alternatives to matplotlib that will allow me to reach this speed?

Categories