PDA

View Full Version : Mathplotlib chart not working in PyQt5 GUI without layouts



kiskivancsi
5th February 2021, 04:35
I'm making small python app for myself, but I've been stuck for days.
So I have simple non-resizeable GUI (PyQt5) and there is a live matplotlib chart inside a QVBoxLayout. I would like to remove this last layout and declare my gui layout free, but once I do, the chart disappears. Any help or advice is appreciated.

Here is a greatly shortened (working) version of my code:


import sys
from random import randrange
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from threading import Thread
from PyQt5 import QtTest

xdata = []
ydata = []

def background_task():
while True:
global xdata
global ydata

cp = randrange(10)
ct = randrange(10)

if len(xdata) > 21:
xdata = xdata[-21:]
ydata = ydata[-21:]
else:
pass

xdata.append(str(cp))
ydata.append(str(ct))

QtTest.QTest.qWait(1000)


bg = Thread(target=background_task, daemon = True)
bg.start()

class MplCanvas(FigureCanvas):

def __init__(self, parent=None, width=7, height=3, dpi=80):
fig = plt.figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super(MplCanvas, self).__init__(fig)


class MainWindow(QMainWindow):

def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setFixedSize(600, 400)

self.canvas = MplCanvas(self, dpi=80)
self.canvas.setFixedSize(QSize(600, 250))
self.update_plot()
self.timer = QtCore.QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.update_plot)
self.timer.start()

central_widget = QWidget()
self.setCentralWidget(central_widget)

layout = QVBoxLayout(central_widget)
layout.addWidget(self.canvas)

def update_plot(self):
self.canvas.axes.cla()
self.canvas.axes.plot(xdata, ydata, 'r')
plt.xticks(rotation = 45)
plt.tight_layout()
every_nth = 3
for n, label in enumerate(plt.gca().xaxis.get_ticklabels()):
if n % every_nth != 0:
label.set_visible(False)
plt.tight_layout()
self.canvas.draw()


app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

d_stranz
5th February 2021, 16:47
This post was initially marked as "Moderated", which made it invisible to Qt Centre members. I have removed that status.


the chart disappears

Well, probably not really, The widget containing the plot is probably created with zero size. When you place it in a layout for the main window's central widget, the layout will resize the widget along with the main window. Without the layout, there is nothing to cause the widget to resize when the main window does.

The solution is to add a resizeEvent override for the main window. In that event, you set the size of the central widget to match the size passed in.

kiskivancsi
5th February 2021, 17:58
Thank you for the reply.
How would you go about using the resizeEvent, tried googling it, but no matter what I try I get errors or just simply nothing happens. I'm sure it's not that complicated but I'm new to PyQt or Python for that matter. :(

d_stranz
5th February 2021, 19:03
I am not PyQt expert, but you would add the event handler to your MainWindow class, something like this:



def resizeEvent(self, event):
# Resize the main widget
self.centralWidget.resize(event.size())
QMainWindow.resizeEvent(self, event)


I don't know what type of object MplCanvas is. If it is a QWidget, then you may be able to replace the QWidget you now are setting as the central widget with the MplCanvas instance itself.

kiskivancsi
6th February 2021, 00:39
I've managed to make it work, the solution was super simple: self.setCentralWidget(self.canvas)
Now my next problem is, I can't move this widget. For example self.canvas.move(50 ,50) does nothing. Do you have any suggestion for this?

d_stranz
6th February 2021, 02:16
The whole point of the QMainWindow and the central widget is that the main window takes charge of whatever is inside it. And if you use layouts, then the layout is in charge of sizing and positioning the widgets inside it.