pyQt5 matplotlib on key press image slices - python

Matplotlib does not respond to key presses while embedded in PyQt5. I have verified that key presses work with Tkinter. This is my first PyQt5 experience. I am not sure what I'm doing wrong here. I have tried to set the focus on canvas by doing. All this is to display 3D volumetric data, which you can think of as a stack of images. Together, they describe a 3D structure. For example, magnetic resonance imaging (MRI)
self.canvas.setFocusPolicy( QtCore.Qt.ClickFocus )
self.canvas.setFocus()
however that returns an error:
self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
AttributeError: 'InputCanvas' object has no attribute 'canvas'
This is the code I have:
import sys
from PyQt5 import QtCore
from skimage import io
from PyQt5.QtWidgets import QMainWindow, QSizePolicy
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from PyQt5.QtWidgets import QApplication
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as
FigureCanvas
class App(QMainWindow):
def __init__(self):
super().__init__()
self.left = 10
self.top = 10
self.title = 'PYQT'
self.width = 600
self.height = 400
self.initialize_ui()
def initialize_ui(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
m = InputCanvas(self, width=3, height=3)
m.move(200, 20)
self.show()
class InputCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
self.canvas.setFocus()
fig.canvas.mpl_connect('key_press_event', self.process_key)
self.input_plot()
def input_plot(self):
volume = io.imread("14.mha", plugin='simpleitk')
ax = self.figure.add_subplot(111)
ax.volume = volume
ax.index = volume.shape[0] // 2
ax.set_title('Ground Truth Image')
plt.set_cmap("gray")
plt.axis('off')
ax.imshow(volume[ax.index])
self.draw()
def process_key(self, event):
fig = event.canvas.figure
ax = fig.axes[0]
if event.key == 'j':
self.previous_slice(ax)
elif event.key == 'k':
self.ext_slice(ax)
self.draw()
def previous_slice(self, ax):
volume = ax.volume
ax.index = (ax.index - 1) % volume.shape[0] # wrap around using %
ax.images[0].set_array(volume[ax.index])
def next_slice(self, ax):
volume = ax.volume
ax.index = (ax.index + 1) % volume.shape[0]
ax.images[0].set_array(volume[ax.index])
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())

You forgot to tell the canvas that it should do something when a key is pressed. You may use matplotlib to do so,
self.cid = self.mpl_connect("key_press_event", self.process_key)
To get rid of the error, consider the error message. "canvas" has no attribute canvas is telling you that the canvas does not have itself as attribute. Hence
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.setFocus()

Related

Embed Matplotlib Graph in a PyQt5

I am trying to embed matplotlib graph into PqQt5, I have a ready function to call them, however, I have no success with it. The code for the graph is as follow:
import re
import os
import sys
import json
import numpy as np
import pylab
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_DIR = BASE_DIR + os.path.sep + 'data'
if not os.path.exists(DATA_DIR ):
popup_critical('Грешен път до данните')
with open('./data/71161.json', 'r') as f:
data = json.load(f)
for distro in data:
downwelling_radiance = np.array(distro['spectra']['ld']['data'])
irradiance_reflectance = np.array(distro['spectra']['r0minuscorr']['data'])
downwelling_irradiance = np.array(distro['spectra']['ed']['data'])
upwelling_radiance = np.array(distro['spectra']['lu']['data'])
wavelength = np.array(distro['spectra']['wavelength']['data'])
reflectance = np.array(distro['spectra']['r0minus']['data'])
date = np.array(distro['meta']['date'])
name = np.array(distro['meta']['id'])
def radiance_plot():
fig = plt.figure(figsize=(14, 8))
plt.title("Plot")
plt.xlabel('Wavelength [nm]')
plt.ylabel('Radiance [W/(m^2xnmxsr)]')
ax=plt.gca()
ax.set_xlim(400,800)
plt.grid(True, color='skyblue')
plt.plot(
wavelength, downwelling_radiance, 'orange',
wavelength, upwelling_radiance, 'burlywood',
lw=3,
)
print(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance) >
max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02))
print(list(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance) >
max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02)))
plt.ylim(0.00)
plt.yticks(list(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance)
> max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02)))
plt.legend(['Upwelling radiance', 'Downwelling radiance'], loc='upper left')
plt.twinx(ax = None)
plt.plot(
wavelength, downwelling_irradiance, 'c',
lw=3,
)
plt.grid(True)
plt.ylabel('Irradiance [W/(m^2xnm)]')
plt.yticks(list(np.arange(0, max(downwelling_irradiance + 0.2), 0.2)))
plt.legend(['Downwelling irradiance'], loc='upper right')
fig.tight_layout()
return plt.show()
def reflectance_plot():
fig = plt.figure(figsize=(14, 8))
plt.title("Plot")
plt.xlabel('Wavelength [nm]')
plt.ylabel('Reflectance [-]')
ax=plt.gca()
ax.set_xlim(400,800)
plt.grid(True)
plt.plot(
wavelength, reflectance, 'burlywood',
lw=3,
)
plt.ylim(0.00)
plt.yticks(list(np.arange(0, max(reflectance + 0.02), step=0.01)))
plt.legend(['Radiance'], loc='upper right')
fig.tight_layout()
return plt.show()
def date_print():
return date
def name_print():
return name
#radiance_plot()
#reflectance_plot()
#name_print()
#date_print()
The code above prints the graphs, now I want to print the graphs in PyQt5 so that when the user presses button 1 it prints the first graph and the second button prints the second graph. There is the code for the PyQt5
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QListWidget,
QMainWindow, QPushButton
import sys
from PyQt5.QtGui import QIcon, QFont
from read import date_print, name_print, radiance_plot, reflectance_plot
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(200, 200, 400, 300)
self.setWindowTitle("Python and Matplotlib")
self.create_list()
self.myUI()
#chart = Canvas(self)
def create_list(self):
vbox = QVBoxLayout()
self.list_widget = QListWidget()
self.list_widget.insertItem(0, "File1")
self.list_widget.insertItem(1, "File2")
self.list_widget.insertItem(2, "file3")
self.list_widget.setStyleSheet("background-color:red")
self.list_widget.setFont(QFont("Sanserif", 15))
self.list_widget.clicked.connect(self.item_clicked)
self.label = QLabel("")
self.setFont(QFont("Sanserif", 14))
self.label.setStyleSheet("color:green")
vbox.addWidget(self.list_widget)
vbox.addWidget(self.label)
self.setLayout(vbox)
def item_clicked(self):
item = self.list_widget.currentItem()
self.label.setText("You have selected file:" + str(name_print()) + "; Date: " +
str(date_print()))
def myUI(self):
#canvas = Canvas(self, width=8, height=4)
#canvas.move(0,0)
butt = QPushButton("Click Me", self)
butt.move(100, 100)
butt2 = QPushButton("Click Me 2", self)
butt2.move(250, 100)
"""
class Canvas(FigureCanvas):
def __init__(self, parent):
fig, self.ax = plt.subplots(figsize=(5,4), dpi = 50)
super().__init__(fig)
self.setParent(parent)
self.plot()
def plot(self):
ref = self.figure.add_subplot(111)
ref.pie(reflectance_plot(), labels=labels)
"""
App = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(App.exec_())
I tried to connect them however I was not able to print anything. I found a way to print a graph however it does not work in my case
import sys
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from read import wavelength, downwelling_radiance, upwelling_radiance
import pandas as pd
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super(MplCanvas, self).__init__(fig)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
# Create the maptlotlib FigureCanvas object,
# which defines a single set of axes as self.axes.
sc = MplCanvas(self, width=10, height=5, dpi=100)
# Create our pandas DataFrame with some simple
# data and headers.
df = pd.DataFrame([
[0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
], columns=['A', 'B'])
# plot the pandas DataFrame, passing in the
# matplotlib Canvas axes.
df.plot(ax=sc.axes)
# Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
toolbar = NavigationToolbar(sc, self)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(toolbar)
layout.addWidget(sc)
# Create a placeholder widget to hold our toolbar and canvas.
widget = QtWidgets.QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.show()
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()
Any idea how to connect them
I’ve created this minimal example for you, showing how to embed matplotlib plotting into your pyqt5 app (works for pyside2 just the same).
I saw in your code, that you kept calling to matplotlib.pyplot. You’re not supposed to talk to pyplot when embedding in your gui backend.
The example also illustrates, how you can set your grid, legend, etc. via calling methods on the axis/figure object and not via plt.
There are 3 buttons, play around with it, and you’ll quickly get how to embed matplotlib and how to handle your “custom plot widget”.
EDIT: With keyboard keys 1, 2, 3 you can call the plot methods as well.
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
# in case of pyside: just replace PyQt5->PySide2
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT
from matplotlib.figure import Figure
class MplWidget(qtw.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
fig = Figure(figsize=(5, 5))
self.can = FigureCanvasQTAgg(fig)
self.toolbar = NavigationToolbar2QT(self.can, self)
layout = qtw.QVBoxLayout(self)
layout.addWidget(self.toolbar)
layout.addWidget(self.can)
# here you can set up your figure/axis
self.ax = self.can.figure.add_subplot(111)
def plot_basic_line(self, X, Y, label):
# plot a basic line plot from x and y values.
self.ax.cla() # clears the axis
self.ax.plot(X, Y, label=label)
self.ax.grid(True)
self.ax.legend()
self.can.figure.tight_layout()
self.can.draw()
class MyQtApp(qtw.QWidget):
def __init__(self):
super(MyQtApp, self).__init__()
# layout
self.mpl_can = MplWidget(self)
self.btn_plot1 = qtw.QPushButton('plot1', self)
self.btn_plot2 = qtw.QPushButton('plot2', self)
self.btn_plot3 = qtw.QPushButton('scatter', self)
self.layout = qtw.QVBoxLayout(self)
self.layout.addWidget(self.mpl_can)
self.layout.addWidget(self.btn_plot1)
self.layout.addWidget(self.btn_plot2)
self.layout.addWidget(self.btn_plot3)
self.setLayout(self.layout)
# connects
self.btn_plot1.clicked.connect(self.plot1)
self.btn_plot2.clicked.connect(self.plot2)
self.btn_plot3.clicked.connect(self.plot_scatter)
def keyPressEvent(self, event):
if event.key() == qtc.Qt.Key_1: self.plot1()
elif event.key() == qtc.Qt.Key_2: self.plot2()
elif event.key() == qtc.Qt.Key_3: self.plot_scatter()
def plot1(self):
X, Y = (1, 2), (1, 2)
self.mpl_can.plot_basic_line(X, Y, label='plot1')
def plot2(self):
X, Y = (10, 20), (10, 20)
self.mpl_can.plot_basic_line(X, Y, label='plot2')
def plot_scatter(self):
X, Y = (1, 7, 9, 3, 2), (3, 6, 8, 2, 11)
self.mpl_can.ax.scatter(X, Y, label='scatter')
self.mpl_can.ax.legend()
self.mpl_can.can.draw()
if __name__ == '__main__':
app = qtw.QApplication([])
qt_app = MyQtApp()
qt_app.show()
app.exec_()
https://www.mfitzp.com/tutorials/plotting-matplotlib/ is an awesome more in depth tutorial on how to embed matplotlib into pyqt5, but I think for your purpose, above very basic example will suffice.

Read variable created with Open File Dialog Box in a function, inside another class

I'm trying to code a GUI which will allow the user to press a button "Open Simulation File", and from that create a variable "path_sim". The issue I am facing is that when I go to plot the file using this "path_sim" variable, it does not plot, as it sees the variable as undefined.
I tried passing it the function as "self.path_sim = open_dialog_box_sim()" but this causes another issue as it causes the file dialog box to open up before the rest of the GUI, which is not what I want. Is there another way to pass this "path_sim" variable into my class?
from PyQt5 import QtCore
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QFileDialog
import sys
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import pandas as pd
from scipy.interpolate import UnivariateSpline
def open_dialog_box_sim():
filename_sim = QFileDialog.getOpenFileName()
path_sim = filename_sim[0]
return path_sim
def open_dialog_box_exp():
filename_exp = QFileDialog.getOpenFileName()
path_exp = filename_exp[0]
return path_exp
def simButton_handler():
print("Sim Button Pressed")
open_dialog_box_sim()
def expButton_handler():
print("Exp Button Pressed")
open_dialog_box_exp()
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.expButton = QPushButton("Open Experimental File", self)
self.simButton = QPushButton("Open Simulation File", self)
title = "Test"
top = 0
left = 0
width = 1920
height = 1080
self.setWindowTitle(title)
self.setGeometry(top, left, width, height)
self.MyUI()
def MyUI(self):
canvas = Canvas(self, width=11, height=6)
canvas.move(300, 35)
self.simButton.setGeometry(QtCore.QRect(30, 10, 221, 61))
self.simButton.move(30, 30)
self.simButton.clicked.connect(simButton_handler)
self.expButton.setGeometry(QtCore.QRect(30, 10, 221, 61))
self.expButton.move(30, 120)
self.expButton.clicked.connect(expButton_handler)
class Canvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=5, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
# self.plot()
self.path_sim = open_dialog_box_sim() #############
self.axes = fig.add_subplot(111)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
self.plot()
def plot(self):
s = pd.read_excel(self.path_sim)
plt.scatter(s.energy, s.Simulation, label="Simulation Data") # plot the raw data
xp = np.linspace(400, 2800, 1201)
spl = UnivariateSpline(s.energy, s.Simulation) # fit the data to a spline
sply = spl(xp)
db_entry = { # puts the x's and y's together in one matrix
"energy": xp,
"Simulation": sply
}
df_spline = pd.DataFrame(db_entry) # out the values in a pandas dataframe
self.axes.plot(df_spline['energy'], df_spline['Simulation'])
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec()
You must rewrite the "plot" method to receive the filename so you can change the plot for each filename. On the other hand you must create a slot in the same class to access the variable without problems with the scope:
import sys
from PyQt5.QtCore import QRect
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QFileDialog
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import pandas as pd
from scipy.interpolate import UnivariateSpline
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.expButton = QPushButton("Open Experimental File", self)
self.simButton = QPushButton("Open Simulation File", self)
self.canvas = Canvas(self, width=11, height=6)
title = "Test"
top = 0
left = 0
width = 1920
height = 1080
self.setWindowTitle(title)
self.setGeometry(top, left, width, height)
self.MyUI()
def MyUI(self):
self.canvas.move(300, 35)
self.simButton.setGeometry(QRect(30, 10, 221, 61))
self.simButton.move(30, 30)
self.simButton.clicked.connect(self.sim_clicked)
self.expButton.setGeometry(QRect(30, 10, 221, 61))
self.expButton.move(30, 120)
def sim_clicked(self):
filename_sim, _ = QFileDialog.getOpenFileName()
if filename_sim:
self.canvas.plot(filename_sim)
class Canvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=5, dpi=100):
super().__init__(Figure(figsize=(width, height), dpi=dpi))
self.setParent(parent)
self.axes = self.figure.add_subplot(111)
def plot(self, filename):
self.axes.clear()
s = pd.read_excel(filename)
xp = np.linspace(400, 2800, 1201)
spl = UnivariateSpline(s.energy, s.Simulation) # fit the data to a spline
sply = spl(xp)
db_entry = { # puts the x's and y's together in one matrix
"energy": xp,
"Simulation": sply,
}
df_spline = pd.DataFrame(db_entry) # out the values in a pandas dataframe
self.axes.plot(df_spline["energy"], df_spline["Simulation"])
self.draw()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())

Python scatter updating using Qt4 - Qslider

I am trying to write a GUI in Python3 using PyQt4.
For data visualization, I need to isolate a specific point on a curve plotted by the function whole_plot(). To do so, I am currently using a slider that let the GUI user choose a point of interest. When the slider_value is changed, the point is selected and plotted by calling the function point_plot().
Regarding some previous answers, I am now trying to update my graph through matplotlib.animation (cf. post python matplotlib update scatter plot from a function). But for some reasons, I still got the wrong updating, can someone help me figure out what is the problem in my code?
import sys
import numpy as np
from PyQt4 import QtGui
from PyQt4 import QtCore
import matplotlib.pyplot as plt
import matplotlib.animation
#
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
#%%
# some Arbitrary data
nbr_points = 500
my_X_data = np.linspace(-10,10,nbr_points)
my_Y_data = my_X_data**3 + 100*np.cos(my_X_data*5)
class MyWidget(QtGui.QWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(600,300,1000,600)
grid = QtGui.QGridLayout()
self.setLayout(grid)
self.figure_1 = plt.figure(figsize=(15,5))
self.canvas_1 = FigureCanvas(self.figure_1)
self.toolbar = NavigationToolbar(self.canvas_1, self)
grid.addWidget(self.canvas_1, 2,0,1,2)
grid.addWidget(self.toolbar, 0,0,1,2)
# Slider
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.slider.setMinimum(0)
self.slider.setMaximum(nbr_points-1)
self.slider.setTickInterval(1)
self.slider.valueChanged.connect(self.point_plot)
# Slider info box
self.label = QtGui.QLabel(self)
grid.addWidget(self.label,4,0)
# +1 / -1 buttons
btn_plus_one = QtGui.QPushButton('+1', self)
btn_plus_one.clicked.connect(self.value_plus_one)
btn_minus_one = QtGui.QPushButton('-1', self)
btn_minus_one.clicked.connect(self.value_minus_one)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(btn_minus_one)
hbox.addWidget(self.slider)
hbox.addWidget(btn_plus_one)
grid.addLayout(hbox, 3,0,1,3)
self.whole_plot()
self.point_plot()
self.show()
def whole_plot(self):
ax1 = self.figure_1.add_subplot(111)
ax1.clear()
ax1.cla()
#
ax1.plot(my_X_data,my_Y_data,'b-')
#
ax1.set_xlim([-10,10])
ax1.set_ylim([-1000,1000])
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
#
self.canvas_1.draw()
def point_plot(self):
ax1 = self.figure_1.add_subplot(111)
#
X_point, Y_point = [],[]
scat = ax1.scatter(X_point,Y_point, s=100,c='k')
def animate(i):
index_slider_value = self.slider.value()
X_point = my_X_data[index_slider_value,]
Y_point = my_Y_data[index_slider_value,]
scat.set_offsets(np.c_[X_point,Y_point])
anim = matplotlib.animation.FuncAnimation(self.figure_1,animate, frames=my_X_data, interval=200, repeat=True)
self.canvas_1.draw()
def value_plus_one(self):
# slider +1
if self.slider.value() < (my_X_data.shape[0]-1):
index_slider_value = self.slider.value() + 1
self.slider.setValue(index_slider_value)
def value_minus_one(self):
# slider -1
if self.slider.value() > 0:
index_slider_value = self.slider.value() - 1
self.slider.setValue(index_slider_value)
def main():
app = QtGui.QApplication(sys.argv)
MyWidget()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You have to learn what is better to reuse than create, in this case you just need to create a scatter and update the data with set_offsets() when the value of the slider changes, so FuncAnimation is not necessary.
import numpy as np
from PyQt4 import QtCore, QtGui
import matplotlib.pyplot as plt
import matplotlib.animation
#
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
#%%
# some Arbitrary data
nbr_points = 500
my_X_data = np.linspace(-10,10,nbr_points)
my_Y_data = my_X_data**3 + 100*np.cos(my_X_data*5)
class MyWidget(QtGui.QWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(600,300,1000,600)
self.figure_1 = plt.figure(figsize=(15,5))
self.canvas = FigureCanvas(self.figure_1)
self.toolbar = NavigationToolbar(self.canvas, self)
# Slider
self.slider = QtGui.QSlider(minimum=0,
maximum= nbr_points-1,
orientation=QtCore.Qt.Horizontal,
tickInterval=1)
self.slider.valueChanged.connect(self.on_valueChanged)
# Slider info box
self.label = QtGui.QLabel()
# +1 / -1 buttons
btn_plus_one = QtGui.QPushButton('+1')
btn_plus_one.clicked.connect(self.value_plus_one)
btn_minus_one = QtGui.QPushButton('-1')
btn_minus_one.clicked.connect(self.value_minus_one)
grid = QtGui.QGridLayout(self)
grid.addWidget(self.canvas, 2, 0, 1, 2)
grid.addWidget(self.toolbar, 0, 0, 1, 2)
grid.addWidget(self.label, 4, 0)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(btn_minus_one)
hbox.addWidget(self.slider)
hbox.addWidget(btn_plus_one)
grid.addLayout(hbox, 3, 0, 1, 3)
self.whole_plot()
def whole_plot(self):
ax = self.figure_1.add_subplot(111)
ax.clear()
ax.plot(my_X_data,my_Y_data,'b-')
ax.set_xlim([-10,10])
ax.set_ylim([-1000,1000])
ax.set_xlabel('X')
ax.set_ylabel('Y')
self.canvas.draw()
X_point, Y_point = [],[]
self.scat = ax.scatter(X_point, Y_point, s=100,c='k')
# set initial
self.on_valueChanged(self.slider.value())
#QtCore.pyqtSlot(int)
def on_valueChanged(self, value):
X_point = my_X_data[value,]
Y_point = my_Y_data[value,]
self.scat.set_offsets(np.c_[X_point,Y_point])
self.canvas.draw()
#QtCore.pyqtSlot()
def value_plus_one(self):
self.slider.setValue(self.slider.value() + 1)
#QtCore.pyqtSlot()
def value_minus_one(self):
self.slider.setValue(self.slider.value() - 1)
def main():
import sys
app = QtGui.QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
On the other hand QSlider will not update the value if it is less than minimum or greater than maximum

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().

Matplotlib: Relating Canvas Click Event to Closest Axes

is there any way to relate a click on the Canvas but outside of a plot axes to the closest axes of the click? I have a canvas with x number of subplots and I'm trying to find out the closest subplot near the mouse click. Ultimately, this would help me create a zoom-in-rectangle that allows the user to zoom in the area of selected subplots (the navigation toolbar only zoom in one subplot).
#import os
#os.environ['QT_API'] = 'pyside'
from PyQt4 import QtGui, QtCore
import sys
import matplotlib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import numpy as np
class Canvas(FigureCanvasQTAgg):
def __init__(self, parent=None):
self.figure = Figure()
super(Canvas, self).__init__(self.figure)
self.ax1 = self.figure.add_subplot(1,1,1)
self.figure.subplots_adjust(left = 0.05, bottom = 0.02, right = 0.98, top = 0.99)
self.setMinimumWidth(1000)
self.ax1.plot([1,2,3])
self.draw()
def add_subplot(self, data=[]):
rows = len(self.figure.axes) + 1
for index, axes in enumerate(self.figure.axes, start=1):
axes.change_geometry(rows, 1, index)
ax = self.figure.add_subplot(rows, 1, index+1)
x = np.arange(1000,1)
y = np.arange(1000,1)
ax.step(x,y)
ax.patch.set_facecolor('None')
self.figure.set_figheight(self.figure.get_figheight()*self.figScalingfactor)
def figScaling(self, numSubplot):
self.figScalingfactor = round(1.1729*pow(numSubplot, -0.028),3)
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.showMaximized()
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(5)
self.canvas = Canvas(self)
self.scroll = QtGui.QScrollArea(self.widget)
self.scroll.setWidget(self.canvas)
self.scroll.setWidgetResizable(False)
self.nav = NavigationToolbar(self.canvas, self.widget)
self.numSubplots = 30
self.canvas.figScaling(self.numSubplots)
for x in range(self.numSubplots):
self.canvas.add_subplot()
self.canvas.adjustSize()
self.canvas.draw_idle()
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.scroll)
self.showVline = False
self.hoveringLine = None
self.canvas.mpl_connect("scroll_event", self.scrolling)
self.canvas.mpl_connect("button_press_event", self.onClick)
self.canvas.mpl_connect("motion_notify_event", self.onMove)
def scrolling(self, event):
val = self.scroll.verticalScrollBar().value()
if event.button =="down":
self.scroll.verticalScrollBar().setValue(val+100)
else:
self.scroll.verticalScrollBar().setValue(val-100)
def onClick(self, event):
if event.dblclick and self.showVline == False:
self.background = self.canvas.copy_from_bbox(self.canvas.figure.bbox)
self.showVline = True
self.hoveringLine = self.canvas.ax1.axvline(x=event.xdata, ymin=-1.2*self.numSubplots, ymax=1.2,
lw=2, zorder=0, clip_on=False)
elif event.dblclick and self.showVline:
self.showVline = False
self.hoveringLine = None
self.canvas.ax1.axvline(x=event.xdata, ymin=-1.2*self.numSubplots, ymax=1.2,
lw=2, zorder=0, clip_on=False)
self.canvas.draw()
else:
print(event.xdata)
print(event.ydata)
def onMove(self, event):
if (self.showVline):
self.canvas.restore_region(self.background)
self.hoveringLine.set_xdata(event.xdata)
self.canvas.ax1.draw_artist(self.hoveringLine)
self.canvas.blit(self.canvas.figure.bbox)
def main():
app = QtGui.QApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
GUI = Window()
GUI.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Categories