How to modify a figure properties, after placing it in a canvas - python

I have embedded a matplotlib figure inside a plot canvas. The idea is to be able to change the figure and axes properties. But, I couldn't modify the axis after initializing. Below is a simple example:
import sys
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
super(MplCanvas, self).__init__(self.fig)
class MainWindow(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.layout = QtWidgets.QVBoxLayout(self)
self.sc = MplCanvas(self, width=5, height=4, dpi=100)
self.sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])
self.layout.addWidget(self.sc)
button = QtWidgets.QPushButton('change figure properties')
button.clicked.connect(self.changeProp)
self.layout.addWidget(button)
self.show()
def changeProp(self):
# here is to change prop, e.g., tickslabel, size, color, colorbar, legend, position, ...etc
"I have tried the following to change the ticks, but doesn't work"
self.sc.fig.axes[0].set_xticklabels(self.sc.fig.axes[0].get_xticklabels(), fontsize=10, color='blue', rotation=90)
"I have also tried this, but not working"
self.sc.fig.axes[0].xaxis.set_tick_params(fontsize=10, color='blue', rotation=90)
"""
UserWarning: FixedFormatter should only be used together with FixedLocator
self.sc.axes.set_xticklabels(self.sc.axes.get_xticklabels(), fontsize=10, color='blue', rotation=90)
"""
"What is the best API to solve this "
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()

Apparently the 'font size' parameter in set_tick_params no, i removed it.
[tick_params][1]
Also did a redraw after the update. But, I failed to remove the warning
''User Warning: FixedFormatter should only be used together with Fixed Locator'.
def changeProp(self):
self.sc.fig.axes[0].set_xticklabels(self.sc.fig.axes[0].get_xticklabels(), fontsize=10, color='red',
rotation=90)
self.sc.fig.axes[0].xaxis.set_tick_params(color='green', width= 5, rotation=45)
self.sc.draw()

Related

Update lat/lon axes when zooming on cartopy plot

I am plotting some data in cartopy. I would like to be able to zoom in on a region of the map and have the latitude/longitude axes update to reflect the zoomed in region. Instead, they just dissapear altogether when I zoom in. How do I fix this?
Here is my code for generating the axes
plt.figure()
ax = plt.axes(projection=cartopy.crs.PlateCarree())
ax.add_feature(cartopy.feature.LAND, edgecolor='black')
gl = ax.gridlines(crs=cartopy.crs.PlateCarree(), draw_labels=True,
linewidth=2, color='gray', alpha=0.5, linestyle='--')
# plot some stuff here
It is possible to update the cartopy gridliners in interactive mode, but you need to subclass the Navigation toolbar.
In this example below I have used a PySide/QT5 example code that allows me to substitute a subclassed toolbar, then merged in the gridliner example code. The overloaded toolbar callbacks recreate the gridlines everytime zoom/pan/home is used.
I used python3.8, matplotlib-3.4.2, cartopy-0.20
import sys
from PySide2 import QtWidgets
from PySide2.QtWidgets import QVBoxLayout
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import cartopy.crs as ccrs
class CustomNavigationToolbar(NavigationToolbar):
toolitems = [t for t in NavigationToolbar.toolitems if t[0] in ('Home', 'Pan', 'Zoom', 'Save')]
def __init__(self, canvas, parent, coordinates=True, func_recreate_gridlines=None):
print('CustomNavigationToolbar::__init__')
super(CustomNavigationToolbar, self).__init__(canvas, parent, coordinates)
self.func_recreate_gridlines = func_recreate_gridlines
def home(self, *args):
print('CustomNavigationToolbar::home')
super(CustomNavigationToolbar, self).home(*args)
if self.func_recreate_gridlines is not None:
self.func_recreate_gridlines()
def release_pan(self, event):
print('CustomNavigationToolbar::release_pan')
super(CustomNavigationToolbar, self).release_pan(event)
if self.func_recreate_gridlines is not None:
self.func_recreate_gridlines()
def release_zoom(self, event):
print('CustomNavigationToolbar::release_zoom')
super(CustomNavigationToolbar, self).release_zoom(event)
if self.func_recreate_gridlines is not None:
self.func_recreate_gridlines()
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
print('ApplicationWindow::__init__')
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
self.layout = QVBoxLayout(self._main)
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
self.toolbar = CustomNavigationToolbar(self.canvas, self,
coordinates=True,
func_recreate_gridlines=self.recreate_gridlines)
self.layout.addWidget(self.canvas)
self.addToolBar(self.toolbar)
# figure setup taken from gridlines example at
# https://scitools.org.uk/cartopy/docs/latest/matplotlib/gridliner.html
projection = ccrs.RotatedPole(pole_longitude=120.0, pole_latitude=70.0)
self.ax = self.canvas.figure.add_subplot(1, 1, 1, projection=projection)
self.ax.set_extent([-6, 3, 48, 58], crs=ccrs.PlateCarree())
self.ax.coastlines(resolution='10m')
self._gl = None
self.recreate_gridlines()
def recreate_gridlines(self):
print('ApplicationWindow::recreate_gridlines')
print(' remove old gridliner artists')
if self._gl is not None:
for artist_coll in [self._gl.xline_artists, self._gl.yline_artists, self._gl.xlabel_artists, self._gl.ylabel_artists]:
for a in artist_coll:
a.remove()
self.ax._gridliners = []
print(' self.ax.gridlines()')
self._gl = self.ax.gridlines(crs=ccrs.PlateCarree(),
draw_labels=True, dms=True, x_inline=False, y_inline=False)
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()

matplotlib, How to save subplot with scrollbar

Hi I searched nice module to save subplot with scrollbar. but this module can only show plot not save file showing with 'Segmentation fault (core dumped)' message...
I don't know why.. Can you help me..? Actually when I saved only one plot, It was working well. but When I saved multiple plot with for loop function, that message show and block script running..
```python````
import matplotlib.pyplot as plt
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class ScrollableWindow(QtGui.QMainWindow):
def __init__(self, fig, savefile):
self.qapp = QtGui.QApplication([])
QtGui.QMainWindow.__init__(self)
self.widget = QtGui.QWidget()
self.setCentralWidget(self.widget)
self.widget.setLayout(QtGui.QVBoxLayout())
self.widget.layout().setContentsMargins(0,0,0,0)
self.widget.layout().setSpacing(0)
self.fig = fig
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
self.scroll = QtGui.QScrollArea(self.widget)
self.scroll.setWidget(self.canvas)
self.nav = NavigationToolbar(self.canvas, self.widget)
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.scroll)
#self.show()
plt.savefig(savefile)
#exit()
#exit(self.qapp.exec_())
# create a figure and some subplots
fig, axes = plt.subplots(ncols=4, nrows=5, figsize=(16,16))
for ax in axes.flatten():
ax.plot([2,3,5,1])
# pass the figure to the custom window
a = ScrollableWindow(fig,'test.png')

Creating and updating a plot using a QPushButton in PyQt5

I am pretty new to PyQt5. I would like to have a button that, when clicked, generates and shows a plot. I cannot seem to find a way to use the QPushButton to call the PlotCanvas function successfully.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import QtWidgets, Qt
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import random
import numpy as np
import math
class GUI_Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.plot_button = QtWidgets.QPushButton(self)
self.plot_button.setText('Plot')
self.plot_button.setToolTip('Plots the function')
self.plot_button.move(50,240)
self.plot_button.clicked.connect(self.updater)
def updater(self):
PlotCanvas(self, inputparam=5)
print('Function has been called.')
The code above does not work (PlotCanvas doesn't plot) even though I receive the 'function has been called' printout.
However if I simply call PlotCanvas in initUI, it plots it as expected.
def initUI(self):
self.plot_button = QtWidgets.QPushButton(self)
self.plot_button.setText('Plot')
self.plot_button.setToolTip('Plots the function')
self.plot_button.move(50,240)
self.plot_button.clicked.connect(self.updater)
PlotCanvas(self, inputparam=5)
The above code plots the function as expected.
Furthermore, if I simply call the updater function without the pushbutton (shown below), the plot is shown.
def initUI(self):
self.plot_button = QtWidgets.QPushButton(self)
self.plot_button.setText('Plot')
self.plot_button.setToolTip('Plots the function')
self.plot_button.move(50,240)
#self.plot_button.clicked.connect(self.updater)
self.updater()
def updater(self):
PlotCanvas(self, inputparam=5)
print('Function has been called.')
I sense I do not know how 'self' works and by passing a clicked signal I am somehow messing things up. Any help/insight would be appreciated.
class PlotCanvas(FigureCanvas):
def __init__(self, parent=None, width=1.5, height=2, dpi=100, inputparam=0):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
self.LLA_Plotter(inputparam)
# This function performs calculations and outputs x,y data points to plot
ax = self.figure.add_subplot(111)
for i in range(0,90):
ax.scatter(x[i], y[i], color='b', s=0.6)
ax.set_title('testing plot')
plt.show()

python - Embedding Matplolib/Basemap in PyQt application

I am attempting to write a simple application which reads KML files and plots the data onto a Matplotlib/Basemap - sort of "poor's man Google Earth" which can be used offline for a quick view of the distribution of data over geographic space.
Currently, my problem is in embedding the Basemap into the user interface. The code below creates the application but instead of displaying the map, it only displays a pair of axes.
import warnings
import sys
import numpy as np
from PyQt4 import QtCore, QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from mpl_toolkits.basemap import Basemap
warnings.filterwarnings("ignore")
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
m = Basemap()
m.drawcoastlines(color='#777799')
m.drawcountries(color='#ccccee')
m.drawmapboundary()
m.bluemarble()
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.fileMenu = QtGui.QMenu("&File", self)
self.fileMenu.addAction("Open...", self.fileOpen,
QtCore.Qt.CTRL + QtCore.Qt.Key_O)
self.fileMenu.addSeparator()
self.fileMenu.addAction("&Quit", self.fileQuit,
QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
self.menuBar().addMenu(self.fileMenu)
self.statusBar().setSizeGripEnabled(True)
self.statusBar().showMessage("Ready")
self.items = QtGui.QDockWidget("Layers", self)
self.items.setFloating(False)
self.items.setFeatures(self.items.NoDockWidgetFeatures)
self.listWidget = QtGui.QListWidget()
self.listWidget.addItem("file1")
self.listWidget.addItem("file2")
self.listWidget.addItem("file3")
self.items.setWidget(self.listWidget)
self.main_widget = QtGui.QWidget(self)
l = QtGui.QVBoxLayout(self.main_widget)
sc = MyMplCanvas(self.main_widget, width=5, height=4, dpi=100)
l.addWidget(sc)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
self.setGeometry(100,100,650,350)
self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.items)
self.setWindowTitle("Poor's Man KML Viewer")
self.show()
def fileOpen(self):
filename = unicode(QtGui.QFileDialog.getSaveFileName(self,
"Open file", "",
"KML files (*.kml)"))
if filename:
self.listWidget.addItem(filename)
def fileQuit(self):
self.close()
def closeEvent(self, ce):
self.fileQuit()
def main():
app = QtGui.QApplication(sys.argv)
main = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
There are good examples of embedding Matplotlib plots into PyQt applications, but I could not find any considering Basemap.
Can anyone give me a hand?
Thanks in advance!
I would say that you are missing to tell the basemap in which axes it should reside:
m = Basemap(..., ax=self.axes)
I would also suggest not to call a varaible by the name of a python function. I.e. use m instead of map.
While this is unproblematic here, it is an easily overseen problem in other cases.
Without the use of the ax argument, Basemap would create its own figure or take the available matplotlib axes inside pyplot. Since in the embedded case, you do not want to use pyplot at all, a specific axes needs to be specified for the basemap to live in.

PYQT and embedding matplotlib: Graph not showing

Hello im trying to add a custom graph to the pyqt interface I have. Its not showing any data, but the placeholder matplotlib graph is showing. Any help?! Also If i plot just the graph data without putting it into PYQT, it shows up. Thanks!
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):
fig = Figure(figsize=(width, height), dpi=dpi)
#self.axes = fig.add_subplot(111)
# 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,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyStaticMplCanvas(MyMplCanvas):
def mystatic(self, parent=None):
super(MyStaticMplCanvas, self).__init__(parent)
rate,data = read('test.wav') # reading
subplot(411)
self.plot(range(len(data)),data)
subplot(412)
self.specgram(data, NFFT=128, noverlap=0) # small window
subplot(413)
self.specgram(data, NFFT=512, noverlap=0)
subplot(414)
self.specgram(data, NFFT=1024, noverlap=0) # big window
self.show()
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
l = QtGui.QVBoxLayout(self.main_widget)
sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100)
l.addWidget(sc)
In the code sample you provided, there is no call to the mystatic method that you use to plot your data. Therefore, nothing is plotted on your figure.
Moreover, It seems you are using the pyplot interface for plotting your data by calling directly subplot and plot for example in mystatic. When embedding a mpl figure in an application, it is recommended from the mpl documentation to stick to the object oriented API.
I've produced a minimal working example from the code you've provided that follows the guidelines provided in the aforementioned documentation. Hope it helps.
from PyQt4 import QtGui
import sys
import numpy as np
import matplotlib as mpl
mpl.use('Qt4Agg')
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = mpl.figure.Figure(figsize=(width, height), dpi=dpi)
fig.set_tight_layout('tight')
super(MyMplCanvas, self).__init__(fig)
class MyStaticMplCanvas(MyMplCanvas):
def mystatic(self, parent=None):
x, y = np.random.rand(50), np.random.rand(50)
ax1 = self.figure.add_subplot(411)
ax1.plot(x,y, '.')
ax2 = self.figure.add_subplot(412)
ax2.plot(x,y, '.')
ax3 = self.figure.add_subplot(413)
ax3.plot(x,y, '.')
ax4 = self.figure.add_subplot(414)
ax4.plot(x,y, '.')
if __name__ == '__main__' :
app = QtGui.QApplication(sys.argv)
w = MyStaticMplCanvas(width=5, height=6, dpi=100)
w.mystatic()
w.show()
sys.exit(app.exec_())
which results in:

Categories