How can I show matplotlib on my PyQt5 Qwidget - python

I want to draw on my PyQt designer file.
I made 2 py file, one is Main, and another one is ui file(pyuic)
this is code of UI
self.graph_widget = QtWidgets.QWidget(self.tab_4)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graph_widget.sizePolicy().hasHeightForWidth())
self.graph_widget.setSizePolicy(sizePolicy)
self.graph_widget.setObjectName("graph_widget")
graph_widget is widget name
def show_graph(self):
self.graph_widget.fig = plt.Figure()
self.graph_widget.canvas = FigureCanvas(self.graph_widget.fig)
canvasLayout = QVBoxLayout()
canvasLayout.addStretch(1)
self.graph_widget.layout = QHBoxLayout()
self.graph_widget.layout.addLayout(canvasLayout)
ax = self.graph_widget.fig.add_subplot(1, 1, 1)
ax.grid()
self.graph_widget.canvas.draw()
This is code of Main for show graph on my widget.
I want to show graph on my widget, but it doesn't work.
just show white window as before send signal.
and doesn't print any error.
please let me know how I print it.

I think you don't understand well the concept of objects.
In your function show_graph(), you wrote self.graph_widget.fig, that means, fig is an attribute (a variable) of the object graph_widget, which is an object QWidget, so by writing self.graph_widget.fig = plt.Figure() has no sense.
I suggest you this solution:
def show_graph(self):
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas #You can put it at the beginning of your program
self.fig = plt.Figure()
self.plot = self.fig.add_subplot()
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
#Create a layout
layout = QVBoxLayout()
layout.addWidget(self.canvas)
#You can now add your layout to your QWidget()
self.graph_widget.setLayout(layout)
#You can active the grid by the following line
self.plot.yaxis.grid()
Sorry for my English, I am French.

Related

Set position on label

can you help me please with set of possiotion? I have a problem, because the labels don't move :(
Detail in screen.
label_result.move (X, Y) doesnť work.
from datetime import datetime as datetime, timedelta as timedelta
from PyQt5 import QtWidgets
app = QtWidgets.QApplication([])
# Hlavní okno
main = QtWidgets.QWidget()
main.setWindowTitle('Set tag')
main.setGeometry(60, 60, 300, 600)
# Layout 1
layout = QtWidgets.QHBoxLayout()
main.setLayout(layout)
# Nápis
label = QtWidgets.QLabel('Číslo sezení')
layout.addWidget(label)
#Input
input_session = QtWidgets.QLineEdit()
input_session.move(0,0)
layout.addWidget(input_session)
# Tlačítko
button = QtWidgets.QPushButton('Zaznamenat tag')
layout.addWidget(button)
#Sesion Result
label_result = QtWidgets.QLabel('Výsledky \n')
label_result.move (20, 20)
layout.addWidget(label_result)
button.clicked.connect(lambda: test(input_session.text()))
# Spuštění
main.show()
app.exec()
Thanks a lot.
Using layout managers means that all positioning and sizing of widgets is left to it, so any attempt to manually set the geometry of widget is ignored everytime the widget on which the layout is set is resized, which is what happens at least once as soon as the widget is mapped (shown) the first time.
If you want to position widgets in a different layouts, then you need to understand how layout(s) could be structured in order to achieve the desired result. In your case, it could be done in at least 3 slightly different ways:
using a main vertical boxed layout, then adding a horizontal layout for the first three widgets, another one for the last label, and finally a stretch (which is an empty layout item that tries to expand itself up to the maximum available extent);
again a main vertical layout, then a grid layout and again a stretch;
a grid layout with an expanding spacer at the bottom;
I'll show a possible implementation of the first, leaving the others up to you. Note that I will use a subclass of QWidget, which gives a better code/object structure and allows better implementation.
from PyQt5 import QtWidgets
class MyWidget(QtWidgets.QWidget):
def __init__():
super().__init__()
self.setWindowTitle('Set tag')
mainLayout = QtWidgets.QVBoxLayout(self)
topLayout = QtWidgets.QHBoxLayout()
mainLayout.addLayout(topLayout)
label = QtWidgets.QLabel('Číslo sezení')
topLayout.addWidget(label)
input_session = QtWidgets.QLineEdit()
topLayout.addWidget(input_session)
button = QtWidgets.QPushButton('Zaznamenat tag')
topLayout.addWidget(button)
midLayout = QtWidgets.QHBoxLayout()
mainLayout.addLayout(midLayout)
label_result = QtWidgets.QLabel('Výsledky')
midLayout.addWidget(label_result)
mainLayout.addStretch()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
myWidget = MyWidget()
myWidget.show()
sys.exit(app.exec_())

Plotting data from the file kills GUI

I try to draw a graph from the data I have in the file. For example, a set of data with several chart points is no problem and it is drawn. However, the amount of data I have to draw is constantly growing and it is currently about 15000 points. When I try to load and draw them, the application interface crashes. My code is below. The data file is here: testdata.txt Could you please tell me how to deal with it?
import sys
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
class Window(QDialog):
def __init__(self):
super().__init__()
title = "Wykresy"
self.setWindowTitle(title)
# a figure instance to plot on
self.figure = plt.figure()
# 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 = QPushButton('Plot')
# adding action to the button
self.button.clicked.connect(self.plot)
# creating a Vertical Box layout
layout = QVBoxLayout()
# adding tool bar to the layout
layout.addWidget(self.toolbar)
# adding canvas to the layout
layout.addWidget(self.canvas)
# adding push button to the layout
layout.addWidget(self.button)
# setting layout to the main window
self.setLayout(layout)
self.showMaximized()
def plot(self):
with open('testdata.txt') as f:
lines = f.readlines()
x = [line.split('\t')[0] for line in lines]
y = [line.split('\t')[1] for line in lines]
# clearing old figure
self.figure.clear()
# create an axis
ax = self.figure.add_subplot(111)
# plot data
ax.plot(x, y, c = 'r', label = 'temperature')
self.figure.autofmt_xdate()
# refresh canvas
self.canvas.draw()
# driver code
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
# loop
sys.exit(app.exec_())
The main bottleneck seems to be the autofmt_xdate() call, which adds a date label for every one of those 15k points. This happens because your x labels aren't actually dates; as far as pyplot is concerned, they're just arbitrary strings, so it doesn't know which ones to keep and which ones to throw away. Something similar is happening for the y labels too.
To parse x into datetime objects and y into floats:
from datetime import datetime
...
x = [datetime.strptime(line.split('\t')[0], '%Y-%m-%d %H:%M:%S') for line in lines]
y = [float(line.split('\t')[1]) for line in lines]
Now I get a single tick per hour on the x axis, and one per 2.5 degrees on the y axis. Rendering is nearly instantaneous.
You should also consider downsampling your data before trying to plot it. 15000 points is way more than fits in the horizontal resolution of a typical computer screen anyway.
To add to #Thomas' answer, you could use pandas to read the file, which may be faster than looping through the content.
(...)
def plot(self):
df = pd.read_csv('testdata.txt', sep='\t', header=None, parse_dates=[0])
(...)
# plot data
ax.plot(df[0], df[1], c='r', label='temperature')

PYQT5 QMainWindow setCentralWidget consisting of multiple box layouts

For a project in programming class we need to develop an application and have to use PYQT5 for the GUI.
I have a class based on QMainWindow. There are some lines of code I don't quite understand. Below is the initialization:
# Snippet 1
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
# self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setWindowTitle("Main Window")
Then, the following variables/GUI elements are defined:
# Snippet 2
self.main_widget = QtWidgets.QWidget(self)
l = QtWidgets.QVBoxLayout(self.main_widget)
sc = MyStaticMplCanvas(width=5, height=4, dpi=100) # just some graph
dc = MyDynamicMplCanvas(width=5, height=4, dpi=100) # another graph
l.addWidget(sc)
l.addWidget(dc)
Then, I tried to add a horizontal box layout with the following content:
# Snippet 3
x = QtWidgets.QHBoxLayout(self.main_widget) # new
b1 = QtWidgets.QPushButton("Test1") # new
b2 = QtWidgets.QPushButton("Test2") # new
x.addWidget(p1) # new
x.addWidget(p2) # new
Lastly, the central widget is generated:
# Snippet 4
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
The program itself produces no error and works properly. But only the two graphs in snippet 2 are displayed in the window. If you delete
self.main_widget
from
l = QtWidgets.QVBoxLayout(self.main_widget)
and leave the parenthesis empty, only the buttons in snippet 3 are displayed.
What is the meaning behind the following assignment?
self.main_widget = QtWidgets.QWidget(self)
Are you able to combine the vertical and horizontal box layout into a single one and therefore display the two buttons as well as the two graphs as the central widget?
This was my original plan and I tried some things with the addLayout() and setLayout() options and wanted to add these layouts to the setCentralWidget() but was not successful.
Do you have an idea on how to display a combination of multliple box layouts as the central widget in a class based on QMainWindow?
Thank you very much. :)
You can nest layouts using addLayout() on a layout; the inner layout then becomes a child of the layout it is inserted into.
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setWindowTitle("Main Window")
self.main_widget = QtWidgets.QWidget(self)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
l = QtWidgets.QVBoxLayout(self.main_widget)
sc = MyStaticMplCanvas(width=5, height=4, dpi=100) # just some graph
dc = MyDynamicMplCanvas(width=5, height=4, dpi=100) # another graph
l.addWidget(sc)
l.addWidget(dc)
# Snippet 3
x = QtWidgets.QHBoxLayout() # self.main_widget) # new
b1 = QtWidgets.QPushButton("Test1") # new
b2 = QtWidgets.QPushButton("Test2") # new
x.addWidget(b1) # new + b1
x.addWidget(b2) # new + b2
l.addLayout(x) # <----
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = ApplicationWindow()
MainWindow.show()
sys.exit(app.exec_())

How to plot different graphs in different canvas in PyQt4 in a window?

I am trying to code with python3 a GUI that plots 4 different graph in 4 respective layout : speed, height, coordinates and the angle. Right now I am able to draw the figure in each respective layout. However, I have no idea how to plot different function into each graph. I used a method to randomly generate 10 points to plot. When the method is called, it plots the same graph into each 4 canvas.
So my question is **if there is anyway to plot different function respectively to a figure(one plot per graph)?**I am pretty new to python3 and would be grateful for any help provided.
If possible, I would like to avoid using many subplots in a figure since a layout for each figures exist already.
Here is what the current GUI looks like when I call the test method that generates random points, you can see that
the same graph are plotted in each canvas
I will also mention, if it adds any constraints, that this code will eventually be used to plots graph that will update with data coming from another thread.
And here's the code:
from PyQt4 import QtGui
from .flight_dataUI import Ui_Dialog
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import random
class FlightData(QtGui.QDialog, Ui_Dialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
self.figure = plt.figure() # FlightData.figure = matplotlib.pyplot.figure()
# Creates a figure widget self.name = FigureCanvas(self.figure)
self.speedGraph = FigureCanvas(self.figure)
self.heightGraph = FigureCanvas(self.figure)
self.mapGraph = FigureCanvas(self.figure)
self.angleGraph = FigureCanvas(self.figure)
# -------------------------------------------------------------
self.speedLayout.addWidget(self.speedGraph) # insert widget "speedGraph" in speedLayout
self.heightLayout.addWidget(self.heightGraph) # insert widget "heightGraph" in heightLayout
self.mapLayout.addWidget(self.mapGraph) # insert widget "mapGraph" in mapLayout
self.angleLayout.addWidget(self.angleGraph) # insert widget "angleGraph" in angleLayout
self.ax = self.figure.add_subplot(111)
self.ax.hold(False)
self.init_widgets()
def init_widgets(self):
self.analyseButton.clicked.connect(self.open_analysedata)
self.draw_plot()
def open_analysedata(self):
self.done(2) # Closes and delete Dialog window et and return the int 2 as a results in main_window.py
def draw_plot(self):
data = [random.random() for i in range(10)]
self.ax.plot(data, '-*')
If embedding do not import pyplot, it's global state will only cause you trouble. You are using the same figure to initialize all 4 FigureCanvas objects. You want to do something like:
from matplotlib.figure import Figure
class FlightData(QtGui.QDialog, Ui_Dialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
self.figs = {}
self.canvas = {}
self.axs = {}
plot_names = ['speed', 'height', 'map', 'angle']
for pn in plot_names:
fig = Figure()
self.canvas[pn] = FigureCanvas(fig)
ax = fig.add_subplot(1, 1, 1)
self.figs[pn] = fig
self.axs[pn] = ax
# -------------------------------------------------------------
self.speedLayout.addWidget(self.canvas['speed'])
self.heightLayout.addWidget(self.canvas['height'])
self.mapLayout.addWidget(self.canvas['map'])
self.angleLayout.addWidget(self.canvas['angle'])
def draw_plot(self, target, data):
self.axs[target].plot(data, '-*')
self.canvas[target].draw_idle()

PyQt4 - add a text edit area animation example

I have realized a python simple application, without any animation on it.
Now I want to add a simple animation, triggered by a signal (a button click for example), which on trigger enlarges the width of the windows and shows a new text area with some text in it.
Honestly, I am quite new to python/pyqt4, and I do not know much about the animation framework.
I tried to add this to my class code, for example in a method called clicking on the about menu :) :
self.anim = QPropertyAnimation(self, "size")
self.anim.setDuration(2500)
self.anim.setStartValue(QSize(self.width(), self.height()))
self.anim.setEndValue(QSize(self.width()+100, self.height()))
self.anim.start()
and this enlarge my window as I want.
Unfortunately I have no idea how to insert a new text area, avoiding the widgets already present to fill the new space (actually, when the window enlarge, the widgets use
all the spaces, thus enlarging themselves)
Could someone help me knowing how to add the text area appearance animation?
Any help is appreciated...really...
One way to achieve this is to animate the maximumWidth property on both the window and the text-edit.
The main difficulty is doing it in a way that plays nicely with standard layouts whilst also allowing resizing of the window. Avoiding flicker during the animation is also quite tricky.
The following demo is almost there (the animation is slightly jerky at the beginning and end):
from PyQt4 import QtGui, QtCore
class Window(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
self._offset = 200
self._closed = False
self._maxwidth = self.maximumWidth()
self.widget = QtGui.QWidget(self)
self.listbox = QtGui.QListWidget(self.widget)
self.button = QtGui.QPushButton('Slide', self.widget)
self.button.clicked.connect(self.handleButton)
self.editor = QtGui.QTextEdit(self)
self.editor.setMaximumWidth(self._offset)
vbox = QtGui.QVBoxLayout(self.widget)
vbox.setContentsMargins(0, 0, 0, 0)
vbox.addWidget(self.listbox)
vbox.addWidget(self.button)
layout = QtGui.QHBoxLayout(self)
layout.addWidget(self.widget)
layout.addWidget(self.editor)
layout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize)
self.animator = QtCore.QParallelAnimationGroup(self)
for item in (self, self.editor):
animation = QtCore.QPropertyAnimation(item, 'maximumWidth')
animation.setDuration(800)
animation.setEasingCurve(QtCore.QEasingCurve.OutCubic)
self.animator.addAnimation(animation)
self.animator.finished.connect(self.handleFinished)
def handleButton(self):
for index in range(self.animator.animationCount()):
animation = self.animator.animationAt(index)
width = animation.targetObject().width()
animation.setStartValue(width)
if self._closed:
self.editor.show()
animation.setEndValue(width + self._offset)
else:
animation.setEndValue(width - self._offset)
self._closed = not self._closed
self.widget.setMinimumSize(self.widget.size())
self.layout().setSizeConstraint(QtGui.QLayout.SetFixedSize)
self.animator.start()
def handleFinished(self):
if self._closed:
self.editor.hide()
self.layout().setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize)
self.widget.setMinimumSize(0, 0)
self.setMaximumWidth(self._maxwidth)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.move(500, 300)
window.show()
sys.exit(app.exec_())

Categories

Resources