I have a set of routines that essentially mirror the solution found here, except that I have my plot function in its own class that I import. I do this instead of leaving the code as a method within the MyApp class as the solution has in the given link. I make the code to plot the data in its own file because the actual code is very verbose.
My goal: I am trying to make it such that clicking on the graph will change a label in the GUI.
I've tried researching signals/slots and inheritance, but can't grasp how they can connect or apply when a module load is involved. Keep in mind I'm creating the GUI in PyQtDesigner.
Here is my code
main.py
import sys
from PyQt5.QtWidgets import QMainWindow
from PyQt5 import QtWidgets
from timeseries import plotData
import design
class MyApp(QMainWindow, design.Ui_MainWindow, plotData):
def __init__(self, parent=None):
super(self.__class__, self).__init__()
self.setupUi(self)
self.x = [1,2,3,4,5]
self.y = [1,2,3,4,5]
# Button click plots the timeseries in the first timeseries plot widget
self.pushButton_plotData.clicked.connect(lambda: self.plot())
if __name__ == '__main__':
# Create GUI application
app = QtWidgets.QApplication(sys.argv)
form = MyApp()
form.show()
app.exec_()
mplwidget.py
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
import matplotlib
# Ensure using PyQt5 backend
matplotlib.use('QT5Agg')
# Matplotlib canvas class to create figure
class MplCanvas(Canvas):
def __init__(self):
self.fig = Figure(dpi=100)
self.ax = self.fig.add_subplot(111)
Canvas.__init__(self, self.fig)
Canvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
Canvas.updateGeometry(self)
# Call when user clicks in the plot window
Canvas.mpl_connect(self, s='button_press_event', func=self.get_coord)
def get_coord(self, event):
if event.button == 1:
# Get the x coordinate of the mouse cursor in data coordinates
ix = event.xdata
print(ix)
# GOAL: Want to be able to execute:
# self.textBrowser_X1.setText(str(ix[0]))
# where textBrowser_X1 is the name of a line edit widget
# Matplotlib widget
class MplWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) # Inherit from QWidget
self.canvas = MplCanvas() # Create canvas object
self.vbl = QtWidgets.QVBoxLayout() # Set box for plotting
self.vbl.addWidget(self.canvas)
self.setLayout(self.vbl)
timeseries.py
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
class plotData(FigureCanvas):
def __init__(self, parent=None):
super().__init__()
def plot(self):
self.plotWidget.canvas.ax.plot(self.x, self.y)
self.plotWidget.canvas.draw()
design.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1216, 379)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton_plotData = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_plotData.setGeometry(QtCore.QRect(216, 20, 96, 33))
font = QtGui.QFont()
font.setPointSize(14)
self.pushButton_plotData.setFont(font)
self.pushButton_plotData.setObjectName("pushButton_plotData")
self.plotWidget = MplWidget(self.centralwidget)
self.plotWidget.setGeometry(QtCore.QRect(20, 75, 1177, 234))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.plotWidget.sizePolicy().hasHeightForWidth())
self.plotWidget.setSizePolicy(sizePolicy)
self.plotWidget.setObjectName("plotWidget")
self.textBrowser_X1 = QtWidgets.QTextBrowser(self.centralwidget)
self.textBrowser_X1.setGeometry(QtCore.QRect(132, 21, 75, 30))
self.textBrowser_X1.setObjectName("textBrowser_X1")
self.label_X1 = QtWidgets.QLabel(self.centralwidget)
self.label_X1.setGeometry(QtCore.QRect(25, 22, 170, 25))
font = QtGui.QFont()
font.setPointSize(14)
self.label_X1.setFont(font)
self.label_X1.setObjectName("label_X1")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1216, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton_plotData.setText(_translate("MainWindow", "Plot Data"))
self.label_X1.setText(_translate("MainWindow", "X position 1"))
from mplwidget import MplWidget
I see that there are many redundant things. For example because it is necessary that PlotWidget is a QWidget that only has the canvas if the canvas is a QWidget, another example is the class plotData that inherits unnecessarily from FigureCanvas in addition to doing a redundant task.
Considering the above, the solution is:
mplwidget.py
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
import matplotlib
# Ensure using PyQt5 backend
matplotlib.use("QT5Agg")
class MplWidget(Canvas):
def __init__(self, parent=None):
Canvas.__init__(self, Figure(dpi=100))
self.setParent(parent)
self.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
)
self.updateGeometry()
self.ax = self.figure.add_subplot(111)
main.py
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
import design
class MyApp(QMainWindow, design.Ui_MainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setupUi(self)
self.x = [1, 2, 3, 4, 5]
self.y = [1, 2, 3, 4, 5]
self.pushButton_plotData.clicked.connect(self.plot)
self.plotWidget.mpl_connect(s="button_press_event", func=self.get_coord)
def plot(self):
self.plotWidget.ax.plot(self.x, self.y)
self.plotWidget.draw()
def get_coord(self, event):
if event.button == 1:
ix = event.xdata
self.textBrowser_X1.setText(str(ix))
if __name__ == "__main__":
# Create GUI application
app = QApplication(sys.argv)
form = MyApp()
form.show()
app.exec_()
Related
Goal: display and save plot in mp4 at the same time
I am able to save the video but when it finishes saving, it starts to override canvases which leads to crashes and doesnt show the plot in gui.
I imagine i have to use some sort of threads to run animation but i am stuck how to do that. I can also imagine a lot flaws in my code.
main.py
import matplotlib.animation as animation
from matplotlib import style
import csv
import os.path
from os import path
import matplotlib.animation as animation
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import datetime
import serial.tools.list_ports
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import random
from numpy import diff
import numpy as np
LARGE_FONT = ("Verdana", 12)
import pandas as pd
style.use('fivethirtyeight')
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (QTreeWidget, QTreeWidgetItem, QPushButton, QLabel, QDialog, QVBoxLayout, QApplication, QLineEdit)
from PyQt5.QtWidgets import (QPushButton, QDialog, QTreeWidget,
QTreeWidgetItem, QVBoxLayout,
QHBoxLayout, QFrame, QLabel, QComboBox,
QApplication, QTreeWidgetItemIterator, QMessageBox, QProxyStyle)
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
import matplotlib
from matplotlib.figure import Figure
matplotlib.use('QT5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
plt.rcParams['animation.ffmpeg_path'] = 'C:/ffmpeg/bin/ffmpeg.exe'
from anim import Ui_MainWindow
plt.ion()
writer = animation.FFMpegWriter()
plt.show()
class Test(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super().__init__(parent)
QtWidgets.QMainWindow.__init__(self, parent)
self.i = 0
self.ui = Ui_MainWindow()
self.ui.setupUi(parent)
vlay1 = QVBoxLayout()
self.plotWidget1 = FigureCanvas(Figure())
toolbar1 = NavigationToolbar(self.plotWidget1, self)
openButton = QtWidgets.QPushButton("Open a file")
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(openButton.sizePolicy().hasHeightForWidth())
openButton.setSizePolicy(sizePolicy)
dateTimeEdit = QtWidgets.QDateTimeEdit()
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(dateTimeEdit.sizePolicy().hasHeightForWidth())
dateTimeEdit.setSizePolicy(sizePolicy)
vlay1.addWidget(openButton)
vlay1.addWidget(dateTimeEdit)
vlay1.addWidget(toolbar1)
vlay1.addWidget(self.plotWidget1)
self.ui.gridLayout.addLayout(vlay1, 0, 0, 1, 1)
openButton.clicked.connect(self.openFile)
self.ax1 = self.plotWidget1.figure.subplots()
self.line, = self.ax1.plot([], [], marker='o')
self.ax1.set_xlabel("Real")
self.ax1.set_ylabel("Imaginary")
self.ax1.relim()
self.ax1.autoscale_view(True, True, True)
self.ax1.set_title(str(self.i))
self.plotWidget1.figure.tight_layout()
self.openFile()
anim = animation.FuncAnimation(self.plotWidget1.figure, self.animate, init_func=self.init,
frames=self.len-1, interval=1, blit=True)
anim.save('plot.mp4', writer=writer, dpi=300, )
def readFile(self):
df = pd.read_excel(self.fileName)
self.Real1 = df["Real 1"]
self.Real2 = df["Real 2"]
self.Real3 = df["Real 3"]
self.Real4 = df["Real 4"]
self.Real5 = df["Real 5"]
self.Img1 = df["Imaginary 1 "]
self.Img2 = df["Imaginary 2"]
self.Img3 = df["Imaginary 3"]
self.Img4 = df["Imaginary 4"]
self.Img5 = df["Imaginary 5"]
self.len = len(self.Img1)
def getDataX(self):
self.i += 1
if self.i == self.len - 1:
print("complete")
return [self.Real1[self.i], self.Real2[self.i], self.Real3[self.i], self.Real4[self.i], self.Real5[self.i]]
def getDataY(self):
return [abs(self.Img1[self.i]), abs(self.Img2[self.i]), abs(self.Img3[self.i]), abs(self.Img4[self.i]), abs(self.Img5[self.i])]
def openFile(self):
dirAndName = QtWidgets.QFileDialog.getOpenFileName(self,'Open a File', "*.xlsx")
if dirAndName[0] != []:
self.fileName = dirAndName[0]
self.readFile()
print(dirAndName)
def init(self):
self.line.set_data([], [])
return self.line,
def animate(self, i):
x = self.getDataX()
y = self.getDataY()
x = np.asarray(x)
y = np.asarray(y)
self.ax1.set_title(str(self.i))
self.line.set_data(x, y)
self.ax1.relim()
self.ax1.autoscale_view(True, True, True)
return self.line,
def Home():
f = QtWidgets.QMainWindow()
#f.resize(1699, 980)
c = Test(f)
f.show()
r = qApp.exec_()
if __name__ == "__main__":
qApp = QtWidgets.QApplication(sys.argv)
qApp.setStyle(QStyleFactory.create('Fusion'))
proxy = QProxyStyle(qApp.style())
qApp.setStyle(proxy)
Home()
Anim.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth())
self.pushButton.setSizePolicy(sizePolicy)
self.pushButton.setObjectName("pushButton")
self.verticalLayout.addWidget(self.pushButton)
self.dateTimeEdit = QtWidgets.QDateTimeEdit(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.dateTimeEdit.sizePolicy().hasHeightForWidth())
self.dateTimeEdit.setSizePolicy(sizePolicy)
self.dateTimeEdit.setObjectName("dateTimeEdit")
self.verticalLayout.addWidget(self.dateTimeEdit)
self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Open File"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
*** I am using Anim.py for other reasons, even though my main.py is creating qpushbutton and qtimeedit again***
Disclaimer: I am not going to use the OP's code as it has unnecessary elements that only distract as well as useless imports so I am going to show an example focused only on the required functionality.
If you want to save and show the GUI then you should not use the save() method as this is blocking, instead you should directly use the writer (FFMpegWriter), using the setup() method to configure and then the grab_frame() method to record the frames. It should be noted that when recording the speed of the animation decreases since recording a frame consumes time making the eventloop busy.
import sys
import numpy as np
from PyQt5 import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
from matplotlib import animation
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.animation_button = QtWidgets.QPushButton("Start", checkable=True)
self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
self.ax = self.canvas.figure.subplots()
t = np.linspace(0, 10, 101)
x, y = self.generate_data(0)
(self._line,) = self.ax.plot(x, y)
self.writer = animation.FFMpegWriter()
self.writer.setup(self.canvas.figure, outfile="plot.mp4", dpi=300)
self.anim = animation.FuncAnimation(
self.canvas.figure,
self.callback_animation,
frames=200,
interval=20,
blit=True,
)
self.animation_button.toggled.connect(self.handle_toggled)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
layout = QtWidgets.QVBoxLayout(central_widget)
layout.addWidget(self.canvas)
layout.addWidget(self.animation_button)
def callback_animation(self, i):
x, y = self.generate_data(i)
self._line.set_data(x, y)
if self.animation_button.isChecked():
self.writer.grab_frame()
return (self._line,)
def generate_data(self, i):
x = np.linspace(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
return x, y
def handle_toggled(self):
self.animation_button.setText(
"Stop" if self.animation_button.isChecked() else "Start"
)
def closeEvent(self, event):
super().closeEvent(event)
self.writer.finish()
if __name__ == "__main__":
qapp = QtWidgets.QApplication.instance()
if not qapp:
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
app.activateWindow()
app.raise_()
qapp.exec_()
Using Python , PYQT5 I want to draw a Polygon on top a Image, which is in a Qlabel widget. I used a simple Qmainwindow with a label widget generated in QT designer (code is below).
I am aware that there are several informations out abaut drawing in a Qmainwindow like here:
PyQT5: How to interactively paint on image within QLabel Widget? - which has no solution within a Qlabel widget.
Draw over image in a QLabel with PyQt5 - marked solution is unclear because also painting on top of Qlabel is not solved
PYQT5 drawing line - paintevent on Qlabel is working, but not on top of the image
Painting in a QLabel with paintEvent - but also not solved to draw on top of an image.
Let me know, if you have a solution for this problem.
import sys
from PyQt5.QtCore import Qt
from PyQt5 import QtWidgets, uic, QtCore, QtGui
from PyQt5.QtGui import QPixmap, QPainter, QPolygon, QPen, QBrush
from PyQt5.QtCore import QPoint
from polygon_ui import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
pixmap = QPixmap("img.png")
self.label.setPixmap(pixmap)
self.label.mousePressEvent = self.getPixel
self.pol = []
def getPixel(self, event):
x = event.pos().x()
y = event.pos().y()
self.pol.append(QPoint(int(x),int(y)))
print(x,y, self.pol)
def paintEvent(self, event):
painter = QPainter(self)
#painter.drawPixmap(self.rect(), self.image)
painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
painter.setBrush(QBrush(Qt.red, Qt.VerPattern))
#points = QPolygon([ QPoint(10,10), QPoint(10,100),
# QPoint(100,10), QPoint(100,100)])
points = QPolygon(self.pol)
painter.drawPolygon(points)
def mouseMoveEvent(self, event):
pass
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
the code for polygon_ui is here - was simply generated by QT-Designer using Mainwindow + Qlabel:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(621, 641)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(10, 10, 600, 600))
self.label.setObjectName("label")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 621, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "TextLabel"))
If you want to add elements such as polygons, lines, circles, etc. on an image then do not complicate yourself with a QLabel since for example with your current code you are painting in the window that is below the QLabel so it will not be seen , a possible solution using with QLabel is to get the QPixmap and paint it on top.
A better alternative is to use the Qt Graphics Framework, where the image is set to a QGraphicsPixmapItem, and a polygon as a child of the QGraphicsPixmapItem as I show below:
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.setScene(scene)
self._pixmap_item = QtWidgets.QGraphicsPixmapItem()
scene.addItem(self.pixmap_item)
self._polygon_item = QtWidgets.QGraphicsPolygonItem(self.pixmap_item)
self.polygon_item.setPen(QtGui.QPen(QtCore.Qt.black, 5, QtCore.Qt.SolidLine))
self.polygon_item.setBrush(QtGui.QBrush(QtCore.Qt.red, QtCore.Qt.VerPattern))
#property
def pixmap_item(self):
return self._pixmap_item
#property
def polygon_item(self):
return self._polygon_item
def setPixmap(self, pixmap):
self.pixmap_item.setPixmap(pixmap)
def resizeEvent(self, event):
self.fitInView(self.pixmap_item, QtCore.Qt.KeepAspectRatio)
super().resizeEvent(event)
def mousePressEvent(self, event):
sp = self.mapToScene(event.pos())
lp = self.pixmap_item.mapFromScene(sp)
poly = self.polygon_item.polygon()
poly.append(lp)
self.polygon_item.setPolygon(poly)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
view = GraphicsView()
self.setCentralWidget(view)
view.setPixmap(QtGui.QPixmap("img.png"))
self.resize(640, 480)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Update:
If you want to use the OP design, the implementation is trivial.
Option1:
Create a file called graphicsview.py where the GraphicsView logic is implemented:
graphicsview.py
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.setScene(scene)
self._pixmap_item = QtWidgets.QGraphicsPixmapItem()
scene.addItem(self.pixmap_item)
self._polygon_item = QtWidgets.QGraphicsPolygonItem(self.pixmap_item)
self.polygon_item.setPen(QtGui.QPen(QtCore.Qt.black, 5, QtCore.Qt.SolidLine))
self.polygon_item.setBrush(QtGui.QBrush(QtCore.Qt.red, QtCore.Qt.VerPattern))
#property
def pixmap_item(self):
return self._pixmap_item
#property
def polygon_item(self):
return self._polygon_item
def setPixmap(self, pixmap):
self.pixmap_item.setPixmap(pixmap)
def resizeEvent(self, event):
self.fitInView(self.pixmap_item, QtCore.Qt.KeepAspectRatio)
super().resizeEvent(event)
def mousePressEvent(self, event):
sp = self.mapToScene(event.pos())
lp = self.pixmap_item.mapFromScene(sp)
poly = self.polygon_item.polygon()
poly.append(lp)
self.polygon_item.setPolygon(poly)
Replace QLabel with QGraphicsView in polygon_ui:
polygon_ui.py
from PyQt5 import QtCore, QtGui, QtWidgets
from graphicsview import GraphicsView
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(621, 641)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = GraphicsView(self.centralwidget)
self.label.setGeometry(QtCore.QRect(10, 10, 600, 600))
self.label.setObjectName("label")
# ...
Restore the main.py
main.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from polygon_ui import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
pixmap = QtGui.QPixmap("img.png")
self.label.setPixmap(pixmap)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Option2:
Another approach is to promote the widget, and in SO there are many examples of that type so I will obviate showing the procedure:
How to insert video in ui file which made at qt designer?
Clear QLineEdit on click event
Create a widget to embed into QMainWindow
where do I write the class for a single promoted QWidget from Qt designer
Option3:
Another simpler alternative is to use QLabel as a container and set the GraphicsView with a layout:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from polygon_ui import Ui_MainWindow
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.setScene(scene)
self._pixmap_item = QtWidgets.QGraphicsPixmapItem()
scene.addItem(self.pixmap_item)
self._polygon_item = QtWidgets.QGraphicsPolygonItem(self.pixmap_item)
self.polygon_item.setPen(QtGui.QPen(QtCore.Qt.black, 5, QtCore.Qt.SolidLine))
self.polygon_item.setBrush(QtGui.QBrush(QtCore.Qt.red, QtCore.Qt.VerPattern))
#property
def pixmap_item(self):
return self._pixmap_item
#property
def polygon_item(self):
return self._polygon_item
def setPixmap(self, pixmap):
self.pixmap_item.setPixmap(pixmap)
def resizeEvent(self, event):
self.fitInView(self.pixmap_item, QtCore.Qt.KeepAspectRatio)
super().resizeEvent(event)
def mousePressEvent(self, event):
sp = self.mapToScene(event.pos())
lp = self.pixmap_item.mapFromScene(sp)
poly = self.polygon_item.polygon()
poly.append(lp)
self.polygon_item.setPolygon(poly)
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.graphicsview = GraphicsView()
lay = QtWidgets.QVBoxLayout(self.label)
lay.addWidget(self.graphicsview)
pixmap = QtGui.QPixmap("img.png")
self.graphicsview.setPixmap(pixmap)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Using Python3 and PyQt5, I want to have a GUI that has a button on it that, when pressed, opens another widget which also has buttons and other controls. In the future, the master GUI will have many buttons that will open many additional widgets. I'm looking at having one widget with an embedded matplotlib.pyplot. At the moment, I am struggling to open a second widget.
Main Program
import sys
from PyQt5 import QtWidgets
from gui import Ui_MainWindow as Ui_MainWindow1
from gui2 import Ui_MainWindow as Ui_MainWindow2
class Window(QtWidgets.QMainWindow, Ui_MainWindow1):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.ui_gui = Ui_MainWindow1()
self.ui_gui.setupUi(self)
self.pb.clicked.connect(self.on_pb_clicked)
self.graph = Graph(self)
def on_pb_clicked(self):
print('pb clicked')
self.graph.show()
class Graph(QtWidgets.QMainWindow, Ui_MainWindow2):
def __init__(self, parent=None):
super(Graph, self).__init__(parent)
self.ui_graph = Ui_MainWindow2()
self.ui_graph.setupUi(self)
if __name__ == "__main__":
#import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow1()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
gui = gui2. Both look like the below.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(282, 219)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.pb = QtWidgets.QPushButton(self.centralWidget)
self.pb.setGeometry(QtCore.QRect(100, 60, 75, 23))
self.pb.setObjectName("pb")
MainWindow.setCentralWidget(self.centralWidget)
self.menuBar = QtWidgets.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 282, 21))
self.menuBar.setObjectName("menuBar")
MainWindow.setMenuBar(self.menuBar)
self.mainToolBar = QtWidgets.QToolBar(MainWindow)
self.mainToolBar.setObjectName("mainToolBar")
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
self.statusBar = QtWidgets.QStatusBar(MainWindow)
self.statusBar.setObjectName("statusBar")
MainWindow.setStatusBar(self.statusBar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pb.setText(_translate("MainWindow", "pb"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I want to click the button on gui and have the gui2 appear. NB gui will not equal gui2 in the future.
Since Window and Graph inherit from Ui_Window1 and Ui_Window2, respectively, you should call self.setupUi(self) in Window.__init__ and Graph.__init__ instead of creating separate instances of Ui_Window1 and Ui_Window2, i.e.
class Window(QtWidgets.QMainWindow, Ui_MainWindow1):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.setupUi(self)
self.pb.clicked.connect(self.on_pb_clicked)
self.graph = Graph(self)
# just to see the two windows side-by-side
self.move(500, 400)
self.graph.move(self.x()+self.width()+20, self.y())
def on_pb_clicked(self):
print('pb clicked')
self.graph.show()
class Graph(QtWidgets.QMainWindow, Ui_MainWindow2):
def __init__(self, parent=None):
super(Graph, self).__init__(parent)
self.setupUi(self)
The main part of the program should then be something like
if __name__ == "__main__":
#import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = Window()
MainWindow.show()
app.exec()
You use QMetaObject::connectSlotsByName() to enable the automatic calling of the on_pb_clicked() slot.
You do not need to use self.pb.clicked.connect(self.on_pb_clicked)
You need #QtCore.pyqtSlot()
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from gui_1 import Ui_MainWindow as Ui_MainWindow1
from gui_2 import Ui_MainWindow as Ui_MainWindow2
class Graph(QtWidgets.QMainWindow, Ui_MainWindow2):
def __init__(self, parent=None):
super(Graph, self).__init__(parent)
self.setupUi(self)
self.setWindowTitle("window Graph")
self.setGeometry(QtCore.QRect(850, 260, 282, 219))
# self.pb.clicked.connect(self.on_pb_clicked) # ---
#QtCore.pyqtSlot() # +++
def on_pb_clicked(self):
print('pb clicked -> Graph')
class Window(QtWidgets.QMainWindow, Ui_MainWindow1):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowTitle("main Window")
# self.pb.clicked.connect(self.on_pb_clicked) # ---
self.graph = Graph(self)
#QtCore.pyqtSlot() # +++
def on_pb_clicked(self):
print('pb clicked -> "main Window')
self.graph.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
I used QtDesigner to create all of my windows, the main window uses mdiArea so that other windows fit inside of it when called. My main problem is when I close a window inside the mdiArea it disappears and I can't reopen it, id either like the window to not have a exit button or make a window with buttons that will open the files if its not there.
Output Screenshot
The window that is minimized is ValveSimulator and if I close it it doesn't exist anymore and I can't open it
my main code: CreateWindow makes the window with the buttons and CreateValveSimulator is my main program that I want to open from the button when I close it
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableWidget,QWidget, QMdiSubWindow
import ValveSim
import MainWindow
import Window
class Win1(QMainWindow):
def __init__(self):
QWidget.__init__(self)
self.vtn = MainWindow.Ui_MainWindow()
self.vtn.setupUi(self)
self.subwindow = QMdiSubWindow()
self.CreateValveSimulator()
self.CreateWindow()
def CreateValveSimulator(self):
widget = QMainWindow()
self.VSim_subwindow = ValveSim.Ui_ValveSim()
self.VSim_subwindow.setupUi(widget)
self.subwindow = QMdiSubWindow(self.vtn.mdiArea)
widget.setParent(self.subwindow)
self.subwindow.setWidget(widget)
self.vtn.mdiArea.addSubWindow(self.subwindow)
widget.show()
self.subwindow.show()
self.subwindow.widget().show()
def CreateWindow(self):
widget = QMainWindow()
self.win_subwindow = Window.Ui_MainWindow()
self.win_subwindow.setupUi(widget)
self.subwindow = QMdiSubWindow(self.vtn.mdiArea)
widget.setParent(self.subwindow)
self.subwindow.setWidget(widget)
self.vtn.mdiArea.addSubWindow(self.subwindow)
widget.show()
self.subwindow.show()
self.subwindow.widget().show()
if __name__=='__main__':
app =QApplication(sys.argv)
win = Win1()
win.show()
sys.exit(app.exec_())
I connected the self.pushbutton to a function called openvs which I hope to open the Valve Simulator window when its closed as shown in my main code,however it just crashes when I click the button
Table with buttons code:
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableWidget, QWidget, QMdiSubWindow, QAction
import Untitled
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setWindowModality(QtCore.Qt.WindowModal)
MainWindow.resize(351, 442)
MainWindow.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(85, 125, 75, 23))
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QtCore.QRect(85, 195, 75, 23))
self.pushButton_2.setObjectName("pushButton_2")
self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_3.setGeometry(QtCore.QRect(200, 125, 75, 23))
self.pushButton_3.setObjectName("pushButton_3")
MainWindow.setCentralWidget(self.centralwidget)
self.pushButton.clicked.connect(self.openvs)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Open Valve Simulator"))
self.pushButton_2.setText(_translate("MainWindow", "Open Module Program"))
self.pushButton_3.setText(_translate("MainWindow", "Open Metal Sizing"))
def openvs(self):
Untitled.Win1.CreateValveSimulator()
You must create the windows independently, and add them to each QMdiSubWindow. When the widget is displayed in the subwindow this will cause the subwindow to be displayed. To open normally with the button you must use the clicked signal and call the showNormal function.
The complete code is here.
class HomeWindow(QMainWindow, Ui_HomeWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent=parent)
self.setupUi(self)
class ValveSim(QMainWindow, Ui_ValveSim):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent=parent)
self.setupUi(self)
class Win1(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent=parent)
self.setupUi(self)
self.vs = ValveSim()
self.hw = HomeWindow()
self.CreateValveSimulator()
self.CreateWindow()
def CreateValveSimulator(self):
subwindow = QMdiSubWindow()
subwindow.setWidget(self.vs)
self.mdiArea.addSubWindow(subwindow)
subwindow.setFixedSize(500, 500)
# self.subwindow.close()
def CreateWindow(self):
self.hw.pushButton.clicked.connect(self.vs.showNormal)
subwindow = QMdiSubWindow()
subwindow.setWindowFlags(Qt.CustomizeWindowHint | Qt.Tool)
subwindow.setWidget(self.hw)
self.mdiArea.addSubWindow(subwindow)
I am facing a problem interacting with the MatplotlibWidget that I creater via Qt Designer. I am unable to change the axes labels, scale, provide titles or anything. Am i doing anything wrong?
This is a sample UI code generated using Qt Designer:
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.mplwidget = MatplotlibWidget(self.centralwidget)
self.mplwidget.setGeometry(QtCore.QRect(70, 50, 400, 300))
self.mplwidget.setObjectName("mplwidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
from matplotlibwidget import MatplotlibWidget
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
QtGui.QMainWindow.__init__(self, parent, f)
self.setupUi(self)
This is the python code i wrote to interact with the UI python Code:
from PyQt4 import QtGui, QtCore
from TestUI2 import MainWindow
class Window(MainWindow):
def __init__(self):
MainWindow.__init__(self)
x=[0,10,100]
y=[3,4,5]
self.mplwidget.axes.set_xscale('log') # Nothing Happens
self.mplwidget.axes.set_title('GRAPH') # Nothing Happens
self.mplwidget.axes.plot(x,y)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
You need to define your own class for MatplotlibWidget first (in your main script, not the UI code) and then instantialize it in Window() definintion. To do that you also need to import FigureCanvas from _qt4agg matplotlib backend module and Figure class:
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as Canvas
from matplotlib.figure import Figure
Then in the Matplotlib class definition you create instances of the Figure subplots and Figure canvas, that can then be used to interact with your plot from the main window.
So using your code this would look like this:
from PyQt4 import QtGui, QtCore
from TestUI2 import MainWindow
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as Canvas
from matplotlib.figure import Figure
class Window(MainWindow):
def __init__(self):
MainWindow.__init__(self)
x=[0,10,100]
y=[3,4,5]
self.mplwidget = MatplotlibWidget(self.centralwidget)
self.mplwidget.setGeometry(QtCore.QRect(70, 50, 600, 500))
self.mplwidget.setObjectName("mplwidget")
self.mplwidget.plotDataPoints(x,y)
class MatplotlibWidget(Canvas):
def __init__(self, parent=None, title='Title', xlabel='x label', ylabel='y label', dpi=100, hold=False):
super(MatplotlibWidget, self).__init__(Figure())
self.setParent(parent)
self.figure = Figure(dpi=dpi)
self.canvas = Canvas(self.figure)
self.theplot = self.figure.add_subplot(111)
self.theplot.set_title(title)
self.theplot.set_xlabel(xlabel)
self.theplot.set_ylabel(ylabel)
def plotDataPoints(self, x, y):
self.theplot.plot(x,y)
self.draw()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
And the Ui script:
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
QtGui.QMainWindow.__init__(self, parent, f)
self.setupUi(self)
And then if you want to interact with the mpl plot, for example to change a title you do it from within the Window class, say:
self.mplwidget.theplot.set_title("New Title")
Hope that helps.
You only need to add the line:
self.mplwidget.draw()
in your:
class Window(MainWindow):
def __init__(self):
There is no need to manually define the class, you can just use the MatplotlibWidget you created with the Qt Designer.
Based on embert's answer, regarding to the code in the original question, the following is tested and worked:
Either
x=[0,10,100]
y=[3,4,5]
self.matplotlibwidget.axes.plot(x,y)
self.matplotlibwidget.axes.set_xscale('log')
self.matplotlibwidget.axes.set_title('GRAPH')
Or
x=[0,10,100]
y=[3,4,5]
self.matplotlibwidget.axes.set_xscale('log')
self.matplotlibwidget.axes.set_title('GRAPH')
self.matplotlibwidget.axes.hold(True)
self.matplotlibwidget.axes.plot(x,y)
You call self.mplwidget.axes.plot(x,y) after you set the properties.
hold (False): if False, figure will be cleared each time plot is called
Therefore change
self.mplwidget = MatplotlibWidget(self.centralwidget)
to
self.mplwidget = MatplotlibWidget(self.centralwidget, hold=True)
or set the properties after calling plot