PyQt5 figure canvas doesnt stretch - python

Problem is my FigureCanvas isnt taking all possible height, it is only reisizing its width. The problem occurs after resizing main window. I would like to let canvas take maximum available height.
This is how it looks now
This is minimal reproducible example:
import sys
import os
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtGui import (QPixmap, QPainter, QBrush, QPen, QColor)
from PyQt5.QtCore import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from PyQt5.QtWidgets import *
import matplotlib
from matplotlib import pyplot as plt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Distributions")
self.setMinimumSize(480, 320)
# Layout
mainLayout = QVBoxLayout()
dirLayout = QHBoxLayout()
radioLayout = QHBoxLayout()
canvasLayout = QVBoxLayout()
# Setting main Widget
self.mainWidget = QWidget()
self.setCentralWidget(self.mainWidget)
self.mainWidget.setLayout(mainLayout)
# Setting canvas to plot
mainLayout.addLayout(canvasLayout)
figure = plt.figure()
canvas = FigureCanvas(figure)
toolbar = NavigationToolbar(canvas, self)
canvasLayout.addWidget(toolbar)
canvasLayout.addWidget(canvas)
plotButton = QPushButton('Plot')
canvasLayout.addWidget(plotButton,
alignment=QtCore.Qt.AlignCenter)
plotButton.setMaximumSize(QSize(80, 60))
app = QApplication(sys.argv)
dialogi = MainWindow()
dialogi.show()
sys.exit(app.exec_())

You have to set the stretch factor when you add the widget to the layout:
canvasLayout.addWidget(canvas, stretch=1)

Related

PyQt, Matplotlib: Can't set-up matplotlib's navigation toolbar, Unexpected Type

I'm trying to make an app where the matplotlib's tab widget is embedded inside another widget
import os
import matplotlib
import matplotlib.pyplot as pp
import numpy as np
from PySide2 import QtGui
from PySide2.QtCore import QTimer
from PySide2.QtWidgets import *
from layout import Ui_Form
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import sys
graphed = False
res = 180
class VentanaPrincipal(QDialog):
def __init__(self, parent=None):
super(VentanaPrincipal, self).__init__(parent)
self.ventana = Ui_Form()
self.ventana.setupUi(self)
self.timer = QTimer()
self.timer.timeout.connect(self.mainloop)
self.timer.start(20)
self.ancho = self.size().height()
self.alto = self.size().height()
self.ventana.pshBtnCalcularOrbita.clicked.connect(self.truer)
self.ventana.pshBtnCalcularOrbita.clicked.connect(self.graph)
self.oldsize = self.size()
self.figure = Figure()
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
self.button = QPushButton('Plot')
self.button.clicked.connect(self.plot)
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def truer(self):
global graphed
graphed = True
def graph(self):
self.ventana.graph()
def resizeEvent(self,event):
self.oldsize = event.size()
def mainloop(self):
self.ancho = self.size().width()
self.alto = self.size().height()
if self.size() != self.oldsize:
if graphed:
self.graph()
if __name__ == '__main__':
app = QApplication(sys.argv)
aplicacion = VentanaPrincipal()
aplicacion.show()
sys.exit(app.exec_())
Gives me:
TypeError: arguments did not match any overloaded call:
QToolBar(str, parent: QWidget = None): argument 1 has unexpected type 'VentanaPrincipal'
QToolBar(parent: QWidget = None): argument 1 has unexpected type 'VentanaPrincipal'
The Ui_Form is a .py file compiled from the original .ui file from QtDesigner
I've seen an identical example onto another question, the example i grabbed onto was:
import sys
from PySide2.QtWidgets import QDialog,QApplication,QPushButton, QVBoxLayout
from PySide2 import QtGui
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import random
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = Figure()
print(self)
# 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)
print(type(self))
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
ax.clear()
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
There are some prints i put inside the code to check which kind of object is accepted by the QToolbar init line inside Matplotlib's backend

How to plot graphs in QScrollArea without shrinking them?

I'm creating an interface where I want to plot graphs in another tab by clicking in a button. I want the graphs to be plotted side-by-side in a scroll area. The problem is that when I plot the graphs, instead of the Scroll Bar working on the visualization, the graphs actually shrink to fit the widget
Here's a minimal reproducible example
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QPushButton,
QLabel, QVBoxLayout, QHBoxLayout, QMessageBox,
QLineEdit,QComboBox, QAction, QTabWidget, QScrollArea)
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from PyQt5.QtCore import Qt
from PyQt5 import QtGui
class MainWindow(QMainWindow):
#view (GUI)
def __init__(self, parent = None):
#initalizer
super(MainWindow, self).__init__(parent)
#Window
self.setWindowTitle("DataVisualizationPrototype")
self.setGeometry(400, 200, 900, 800)
self.activateWindow()
self.raise_()
self.tab_widget = TabWidget(self)
self.setCentralWidget(self.tab_widget)
class TabWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout =QVBoxLayout(self)
self.tabs = QTabWidget()
self.tab1 = QWidget()
self.tab2 = QWidget()
self.tabs.addTab(self.tab1, "Home")
self.tabs.addTab(self.tab2, "Comparison")
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
#HOME PAGE
#==========================================
#GeneralLayout
self.tab1layout = QVBoxLayout()
self.tab1.setLayout(self.tab1layout)
self.tab1layout.sizeHint()
plotButton = QPushButton()
self.tab1layout.addWidget(plotButton)
plotbutton.clicked.connect(onclick2)
#COMPARISON TAB
#==========================================
#GeneralLayout
self.tab2layout = QVBoxLayout()
self.tab2.setLayout(self.tab2layout)
self.tab2layout.sizeHint()
self.compGraphLayout = QHBoxLayout()
self.compGraphLayout.addStretch()
self.compScroll = QScrollArea()
self.compScroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.compScroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scrollWidget = QWidget()
self.compScroll.setWidget(self.scrollWidget)
self.compScroll.setWidgetResizable(True)
self.tab2layout.addWidget(self.compScroll)
self.scrollWidget.setLayout(self.compGraphLayout)
def onclick2(self, event):
self.plotLayout = QVBoxLayout()
self.fig = plt.figure()
self.plotLayout.addWidget(FigureCanvas(self.fig))
self.plotLayout.addWidget(NavigationToolbar
(FigureCanvas(self.fig),
self.scrollWidget))
self.compGraphLayout.addLayout(self.plotLayout)
#Plot MRE
self.fig.plt.plot([1, 2, 3, 4])
self.fig.plt.ylabel('some numbers')
FigureCanvas(self.fig).adjustSize()
def main():
pt1 = QApplication(sys.argv)
view = MainWindow()
view.show()
sys.exit(pt1.exec())
if __name__ == '__main__':
main()
The code provided by the OP is messy, redundant and has some typos so I will avoid pointing out the cause of the error.
My solution propose a simple and clear way to achieve the goal of displaying the plots horizontally. On the other hand, the canvas does not have a suitable sizeHint, so the widgets will be compressed making the scrollbar not visible, so a possible option is to set a minimum width.
import sys
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QPushButton,
QVBoxLayout,
QHBoxLayout,
QTabWidget,
QScrollArea,
)
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# Window
self.setWindowTitle("DataVisualizationPrototype")
self.setGeometry(400, 200, 900, 800)
self.activateWindow()
self.raise_()
self.tab_widget = TabWidget()
self.setCentralWidget(self.tab_widget)
class TabWidget(QTabWidget):
def __init__(self, parent=None):
super(TabWidget, self).__init__(parent)
self.tab1 = QWidget()
self.plot_button = QPushButton("Add plot")
lay = QVBoxLayout(self.tab1)
lay.addWidget(self.plot_button)
self.tab2 = QWidget()
self.scroll_area = QScrollArea()
self.scroll_container = QWidget()
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setWidget(self.scroll_container)
self.scroll_layout = QHBoxLayout(self.scroll_container)
lay = QVBoxLayout(self.tab2)
lay.addWidget(self.scroll_area)
self.addTab(self.tab1, "Home")
self.addTab(self.tab2, "Comparison")
self.plot_button.clicked.connect(self.plot)
def plot(self):
canvas = FigureCanvas(Figure())
ax = canvas.figure.add_subplot(111)
toolbar = NavigationToolbar(canvas, self)
container = QWidget()
lay = QVBoxLayout(container)
lay.addWidget(canvas)
lay.addWidget(toolbar)
self.scroll_layout.addWidget(container)
container.setMinimumWidth(400)
ax.plot([1, 2, 3, 4])
ax.set_ylabel("some numbers")
def main():
app = QApplication(sys.argv)
view = MainWindow()
view.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()

How do you display a figure in matplotlib using PySide2?

I am trying to learn matplotlib and I tried testing the following code.
import matplotlib
from matplotlib.figure import Figure
import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import sys
from PySide2 import QtGui
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
wid = QtGui.QWidget()
wid.resize(250, 150)
grid = QtGui.QGridLayout()
wid.setLayout(grid)
fig = Figure(figsize=(7,5), dpi=65, facecolor=(1,1,1), edgecolor=(0,0,0))
canvas = FigureCanvas(fig)
grid.addWidget(canvas)
wid.show()
However it seems like it doesn't recognize the signature of Figure.
TypeError: 'PySide2.QtGui.QGridLayout.addWidget' called with wrong
argument types:
PySide2.QtGui.QGridLayout.addWidget(FigureCanvasQTAgg) Supported
signatures:
PySide2.QtGui.QGridLayout.addWidget(PySide2.QtGui.QWidget, int, int,
PySide2.QtCore.Qt.Alignment = 0)
PySide2.QtGui.QGridLayout.addWidget(PySide2.QtGui.QWidget, int, int,
int, int, PySide2.QtCore.Qt.Alignment = 0)
PySide2 is a binding of Qt5 so you must use backend_qt5agg instead ofbackend_qt4agg, on the other hand it is advisable to import the backend (PySide2 in this case) before matplotlib so that matplotlib configures it internally, in addition to QApplication, QWidget and QGridLayout belong to QtWidgets since Qt5 separates them from QtGui and finally when you add a widget to QGridLayout you must indicate its position in the following parameters.
Considering the above the solution is
import sys
from PySide2 import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
wid = QtWidgets.QWidget()
wid.resize(250, 150)
grid = QtWidgets.QGridLayout(wid)
fig = Figure(figsize=(7, 5), dpi=65, facecolor=(1, 1, 1), edgecolor=(0, 0, 0))
canvas = FigureCanvas(fig)
grid.addWidget(canvas, 0, 0)
wid.show()
sys.exit(app.exec_())

Plotting with matplotlib after changing rows in qlistwidget

I want to plot charts after clicking on rows. The signal is transmitted, but the chart isn't plotted. But if I call a plotting function (self.hist()) from MyPlot.__init__, the chart is plotted.
I used this as an example, but it didn't help. How to embed matplotib in pyqt - for Dummies
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtCore import QCoreApplication, Qt
from PyQt4.QtGui import QListWidget, QListWidgetItem, QApplication, QMessageBox
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import sqlite3
matplotlib.style.use('ggplot')
import pandas as pd
import numpy as np
# my plotting functions are in this class
class Myplot(FigureCanvas):
def __init__(self, parent=None):
self.fig = Figure()
FigureCanvas.__init__(self, self.fig)
#self.hist()
#this and next functions should plot
def compute_initial_figure(self):
self.axes = self.fig.add_subplot(1,1,1)
self.axes.hold(False)
self.axes.plot(np.random.rand(400))
self.draw()
def hist(self):
tm = pd.Series(np.random.randn(500), index=pd.date_range('1/1/2005', periods=500))
self.axes = self.fig.add_subplot(1,1,1)
self.axes.hold(False)
tm = tm.cumsum()
self.axes.plot(tm)
self.draw()
#list with rows. I want to draw charts after clicking on them
class Panel (QtGui.QListWidget):
def __init__(self, parent=None):
QtGui.QListWidget.__init__(self)
self.row1 = "COMMISSIONS & FEES"
self.addItem(self.row1)
row2 = "NET LIQUIDATING VALUE"
self.addItem(row2)
self.setFixedWidth(200)
self.itemClicked.connect(self.Clicked)
#this function sends signal to the plotting functions
def Clicked(self):
mplot=Myplot()
index=self.currentRow()
if index == 0:
print '0000'
mplot.compute_initial_figure()
if index == 1:
print '1111'
mplot.hist()
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
#menu
exitAction = QtGui.QAction(QtGui.QIcon('Stuff/exit.png'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(QtGui.qApp.quit)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
self.statusBar().showMessage('Ready')
self.showMaximized()
self.setMinimumSize(600, 400)
self.setWindowTitle('ESPA')
self.setWindowIcon(QtGui.QIcon('Stuff/icon.png.'))
#setup window widgets
self.main_widget= QtGui.QWidget(self)
#lines up widgets horizontally
layout = QtGui.QHBoxLayout(self.main_widget)
mp=Myplot(self.main_widget)
p=Panel(self.main_widget)
layout.addWidget(p)
layout.addWidget(mp)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())

connection of Pyside QPushButton and matplotlib

I'm trying to develop a very simple pyside/Qt program using matplotlib. I want that a graph is draw when a button is pressed. So far, I can draw something on the constructor, but I can't connect the Pyside event with matplotlib. Is there a way to do that?
import sys
import platform
import numpy as np
import PySide
from PySide.QtGui import QApplication, QMainWindow, QTextEdit,\
QPushButton, QMessageBox, QWidget, QVBoxLayout
from PySide import QtCore
__version__ = '0.0.1'
import matplotlib
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.main_frame = QWidget()
self.figure = Figure()
self.canvas = FigureCanvas(self.figure)
self.canvas.setParent( self.main_frame )
self.axes = self.figure.add_subplot(111)
vbox = QVBoxLayout( )
vbox.addWidget( self.canvas )
self.main_frame.setLayout( vbox )
self.setCentralWidget( self.main_frame )
self.button = QPushButton('Run')
def button_pressed(self):
data1 = np.loadtxt('FStream.dat')
data2 = np.loadtxt('FShield.dat')
self.axes.plot(data1[0],data1[1],data2[0],data2[1])
print 'pressed'
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
frame = MainWindow()
frame.button.clicked.connect(frame.button_pressed)
frame.button.show()
frame.show()
app.exec_()
Thanks a lot!
EDIT: edited the code to put the draw().
EDIT 2: Separating in different functions is now looking like this:
import sys
import platform
import numpy as np
import PySide
from PySide.QtGui import QApplication, QMainWindow, QTextEdit,\
QPushButton, QMessageBox, QWidget, QVBoxLayout
from PySide import QtCore
__version__ = '0.0.1'
from ui_pygradient_uni import Ui_MainWindow
import matplotlib
import widgets.matplotlibwidget
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
#from matplotlib.figure import Figure
#from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.main_frame = widgets.matplotlibwidget.MatplotlibWidget()
self.button = QPushButton('Run')
vbox = QVBoxLayout( )
vbox.addWidget( self.main_frame.canvas )
self.main_frame.setLayout( vbox )
self.setCentralWidget( self.main_frame )
def button_pressed(self):
data1 = np.loadtxt('FStream.dat')
data2 = np.loadtxt('FShield.dat')
self.axes.plot(data1[0],data1[1],data2[0],data2[1])
self.canvas.draw()
print 'pressed'
if __name__ == '__main__':
app = QApplication(sys.argv)
frame = MainWindow()
frame.button.clicked.connect(frame.main_frame.Plot)
frame.button.show()
frame.show()
app.exec_()
And the matplotlibwidget is like this:
import matplotlib
import numpy as np
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class MatplotlibWidget(FigureCanvas):
def __init__(self, parent=None):
super(MatplotlibWidget, self).__init__(Figure())
self.setParent(parent)
self.figure = Figure()
self.canvas = FigureCanvas(self.figure)
self.axes = self.figure.add_subplot(111)
def Plot(self):
data1 = np.loadtxt('FStream.dat')
data2 = np.loadtxt('FShield.dat')
self.axes.plot(data1[0],data1[1],data2[0],data2[1])
self.canvas.draw()
Try putting in
self.canvas.draw()
after
self.axes.plot(data1[0],data1[1],data2[0],data2[1])
Your GUI code looks ok and button_pressed is being called correctly but the graph isn't being redrawn.

Categories

Resources