I'm trying to create a simple GUI that has an embedded mplot3d widget for viewing STL files. I'm currently using PyQt4 with Qt Designer for creating the layout and python 3.6.2. I've used this post as a starting point and I have been able to successfully implement a custom mplot3d widget. However, I would like to be able to dynamically update the mplot3d widget (eg. Click a button to load a new stl file and update the figure/widget). I know this is possible for 2D matplotlib figures using
manager = matplotlib.get_current_fig_manager()
manager.canvas.draw()
to update a regular matplotlib figure. But is this possible for an embedded matplotlib widget? Or a mplot3d custom widget?
Should I write I new method in the QtMplCanvas(FigureCanvas) class? I can't seem to figure out from the
FigureCanvasQTAgg
docs how to update a widget from matplotlibs backend. My code is somewhat long but I'll try to include the relevant bits:
from PyQt4 import QtCore, QtGui
import os
import sys
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4 import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
from stl import mesh
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
plt.switch_backend('Qt4Agg')
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(1007, 607)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.gridLayout = QtGui.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.line = QtGui.QFrame(self.centralwidget)
self.line.setFrameShape(QtGui.QFrame.VLine)
self.line.setFrameShadow(QtGui.QFrame.Sunken)
self.line.setObjectName(_fromUtf8("line"))
self.gridLayout.addWidget(self.line, 2, 1, 1, 1)
# STL VIEWER
self.graphicsView_STL = MPL_WIDGET_3D(self.centralwidget)
self.graphicsView_STL.setObjectName(_fromUtf8("graphicsView_STL"))
self.gridLayout.addWidget(self.graphicsView_STL, 2, 0, 1, 1)
# File load btn
self.import_tlbtn = QtGui.QToolButton(self.centralwidget)
self.import_tlbtn.setObjectName(_fromUtf8("import_tlbtn"))
self.gridLayout.addWidget(self.import_tlbtn, 1, 0, 1, 1)
#... More Widgets, layouts, etc.
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
self.import_tlbtn.setText(_translate("MainWindow", "...", None))
self.label_slice.setText(_translate("MainWindow", "Slice", None))
self.label_import.setText(_translate("MainWindow", "Import STL File", None))
... Code from Link
.... New file for connecting methods..
from PyQt4 import QtCore, QtGui
import UI_DED
import sys
import os
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from stl import mesh
''' CONNECTING METHODS HERE '''
def open_file():
# Open a new file selection window
widget_f = QtGui.QWidget()
widget_f.resize(320, 240)
widget_f.setWindowTitle("Select a file")
filename = QtGui.QFileDialog.getOpenFileName(widget_f, 'Open File', '/')
if filename == '':
pass
else:
your_mesh = mesh.Mesh.from_file(filename)
stl_update(your_mesh)
# Some sort of method for updating the widget
def stl_update():
pass
# Some method for updating canvas....
if __name__ == "__main__":
plt.switch_backend('Qt4Agg')
# Set window from UI_DED
app = QtGui.QApplication(sys.argv)
MainWindow = UI_DED.QtGui.QMainWindow()
ui = UI_DED.Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
# Connect widgets here
# Import STL File
ui.import_tlbtn.clicked.connect(open_file)
# Exit
sys.exit(app.exec_())
Related
I am trying to plot in real-time to a tkinter window in python3.
I am attempting to wrap my window in a class.
my code shows the graph, but data is not being plotted. Here is the code:
#! /usr/bin/python3
# -*- coding: utf-8 -*-
import sys
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import tkinter
import time
import numpy as np
matplotlib.use("TkAgg")
class Window(tkinter.Frame):
def __init__(self, master = None):
tkinter.Frame.__init__(self, master)
self.master = master
self.master.grid()
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax = self.fig.add_subplot(111)
self.t, self.y = [], []
self.init_window()
self.ani = animation.FuncAnimation(self.fig, self.animate, interval=1000)
def init_window(self):
# set window title
self.master.title("PlotSomething")
# run show_graph
self.show_graph()
def animate(self):
self.t.append(time.time())
self.y.append(np.random.random())
self.ax.clear()
self.ax.plot(self.t,self.y)
def show_graph(self):
canvas = FigureCanvasTkAgg(self.fig, self.master)
canvas.get_tk_widget().grid(row=1, column=1)
canvas.draw()
def client_exit(self):
sys.exit()
def main(args):
root = tkinter.Tk()
app = Window(root)
root.mainloop()
return 0
if __name__=="__main__":
sys.exit(main(sys.argv[1:]))
Please let me know what the issue is.
Bonus if you can help me get the real-time plot stuff into a separate class that I can call with my tkinter window.
I obviously need some learning on how to create and call classes using tkinter.
As always thank you for your support!
I have a data collection software that requires camera feeds from two different camera sources one is a Brio webcam & the other one is an IP webcam connected via USB tethering.
Now when I edited the code for streaming two multiple videos it was showing just one & not from the other.
The code is given below:
import sys
import cv2
#from gsp import GstreamerPlayer
import datetime
from pyfirmata import util, Arduino
from PyQt5 import QtCore, QtGui
import openpyxl
from openpyxl import load_workbook
from PyQt5.QtCore import pyqtSlot, QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QLayout, QDialog, QApplication, QMainWindow, QFileDialog, QPushButton, QWidget, QLabel
from PyQt5.uic import loadUi
import xlrd
from xlutils.copy import copy
import serial
import xlsxwriter
from xlwt import Workbook
sys.setrecursionlimit(15000)
# For the camera feed
class Thread(QThread):
changePixmap = pyqtSignal(QImage)
def run(self):
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if ret:
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QImage.Format_RGB888)
p = convertToQtFormat.scaled(256, 181)
self.changePixmap.emit(p)
class Thread1(QThread):
changePixmap = pyqtSignal(QImage)
def run(self):
cap = cv2.VideoCapture('http://192.168.42.129:8080/video')
while True:
ret, frame = cap.read()
if ret:
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QImage(
rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QImage.Format_RGB888)
p1 = convertToQtFormat.scaled(111, 181)
self.changePixmap.emit(p1)
The calling functions in the main are as follows:
#pyqtSlot(QImage)
def setImage(self, image):
self.webcam.setPixmap(QPixmap.fromImage(image))
#pyqtSlot(QImage)
def setImage1(self, image):
self.webcam_2.setPixmap(QPixmap.fromImage(image))
def initUI(self):
th = Thread(self)
th1 = Thread1(self)
th1.changePixmap1.connect(self.setImage1)
th.changePixmap.connect(self.setImage)
th.start()
I am new to python programming can anyone tell me what I am doing wrong here? I tried the other approach of doing the streaming in a function & setting streams up but this was not a conventional approach as my application kept crashing due to the while loop*( I guess)*.
If I use one source at a time it works but I can't seem to get them working at a time.
I got it worked around as I wasn't adding
thread.start()
function before.
I have tried adding the pdf.js viewer files in my project and it works in browsers like Chrome, Mozilla, Safari, etc, but it's not loading some pages in node-webkit and PyQt webkit.
I am trying to load the file using an iframe, like this:
<iframe src="/test/?file=/assets/pdf/example.pdf#page=3"> </iframe>
Below are some more up-to-date demo scripts for using pdf.js with PyQt4/QtWebKit or PyQt5/QtWebEngine. To try these, first download the latest stable version of pdf.js and unpack the zip file into a suitable location. (NB: if you're on Linux your distro may already have a pdf.js package, so that could be installed instead).
UPDATE:
As of Qt-5.13.0, it is also possible to use the built-in Chromium PDF Viewer with QWebEngineView:
import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
PDF = 'file://path/to/my/sample.pdf'
class Window(QtWebEngineWidgets.QWebEngineView):
def __init__(self):
super().__init__()
self.settings().setAttribute(
QtWebEngineWidgets.QWebEngineSettings.PluginsEnabled, True)
self.settings().setAttribute(
QtWebEngineWidgets.QWebEngineSettings.PdfViewerEnabled, True)
self.load(QtCore.QUrl.fromUserInput(PDF))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 800, 600)
window.show()
sys.exit(app.exec_())
PyQt5/QtWebEngine pdfjs script:
UPDATE:
NB: as of Aug 2022, it may be necessary to use the legacy build of pdfjs (i.e. the build for "older browsers" on the download page) to keep things working with PyQt5. The stable build should work okay with PyQt6, though.
import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
PDFJS = 'file:///path/to/pdfjs-1.9.426-dist/web/viewer.html'
# PDFJS = 'file:///usr/share/pdf.js/web/viewer.html'
PDF = 'file:///path/to/my/sample.pdf'
class Window(QtWebEngineWidgets.QWebEngineView):
def __init__(self):
super().__init__()
self.load(QtCore.QUrl.fromUserInput('%s?file=%s' % (PDFJS, PDF)))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 800, 600)
window.show()
sys.exit(app.exec_())
PyQt4/QtWebKit pdfjs script:
import sys
from PyQt4 import QtCore, QtGui, QtWebKit
PDFJS = 'file:///path/to/pdfjs-1.9.426-dist/web/viewer.html'
# PDFJS = 'file:///usr/share/pdf.js/web/viewer.html'
PDF = 'file:///path/to/my/sample.pdf'
class Window(QtWebKit.QWebView):
def __init__(self):
super().__init__()
self.load(QtCore.QUrl.fromUserInput('%s?file=%s' % (PDFJS, PDF)))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 800, 600)
window.show()
sys.exit(app.exec_())
I've found this thread over at the Qt Forums, where thebeast44 posted a snippet of Qt code answering your question. My translation to python is below.
You'll also need to unpack the res folder from the author's original code, I think he just modified the viewer... I've also attached said code here.
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4 import QtNetwork
from PyQt4 import QtWebKit
class PDFViewer(QtWebKit.QWebView):
pdf_viewer_page = 'res/pdf-viewer.html'
def __init__(self, parent=None):
super().__init__(parent)
self.settings = QtWebKit.QWebSettings.globalSettings()
self.settings.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessFileUrls, True )
self.settings.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True )
self.settings.setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True )
nam = QtNetwork.QNetworkAccessManager()
page = QtWebKit.QWebPage(self)
page.setNetworkAccessManager(nam)
self.setPage(page)
self.loadFinished.connect(self.onLoadFinish)
self.setUrl(QtCore.QUrl(self.pdf_viewer_page))
def onLoadFinish(self, success):
if success:
self.page().mainFrame().evaluateJavaScript("init();")
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
viewer = PDFViewer(parent=None)
viewer.show()
sys.exit(app.exec_())
From PyQt5 v5.13 you can load PDF files with the chromium API. According to the documentation https://doc.qt.io/qt-5/qtwebengine-features.html#pdf-file-viewing this option is by default enabled.
This minimal example is adapted from Simple Browser
import sys
from pathlib import Path
from PyQt5 import QAxContainer
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLineEdit, QApplication
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.main_layout = QVBoxLayout(self)
self.qlineedit = QLineEdit()
self.qlineedit.returnPressed.connect(self.go_action)
self.main_layout.addWidget(self.qlineedit)
self.read_btn = QPushButton('Test')
self.read_btn.clicked.connect(self.go_action)
self.main_layout.addWidget(self.read_btn)
self.WebBrowser = QAxContainer.QAxWidget(self)
self.WebBrowser.setFocusPolicy(Qt.StrongFocus)
self.WebBrowser.setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}")
self.main_layout.addWidget(self.WebBrowser)
def go_action(self):
# convert system path to web path
f = Path(self.qlineedit.text()).as_uri()
# load object
self.WebBrowser.dynamicCall('Navigate(const QString&)', f)
if __name__ == "__main__":
a = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(a.exec_())
This example:
I am trying to read frame for a folder and display in QGraphics view.
import os
import PyQt4
from PyQt4 import QtCore, QtGui
dir = "frames"
for file in os.listdir(dir):
grview = QtGui.QGraphicsView()
scene = QtGui.QGraphicsScene()
#pixmap = QtGui.QPixmap(os.path.join(dir, file))
pixmap = QtGui.QPixmap(file)
item = QtGui.QGraphicsPixmapItem(pixmap)
self.scene.addItem(item)
grview.setScene()
grview.show()
#self.scene.update()
The folder named "frames" contains jpg files and is in the same folder as the code. But still I am not able to read the frames.
I have also tried general display without graphicsview to check if it works using the code below but it too doesn't work.
import cv2
import os
import PyQt4
from PyQt4 import QtGui,QtCore
import matplotlib.pyplot as plt
from PIL import Image
def load_images_from_folder(folder):
images = []
for filename in os.listdir(folder):
img = cv2.imread(os.path.join(folder,filename))
if img is not None:
images.append(img)
return images
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
img = load_images_from_folder('/frames')
image = Image.open(img)
image.show()
I am not able to understand what am I missing. I want to display the images sequentially in QGraphicsView resembling a video and in some particular frame I have to select the object of interest by draw a rectangle around it which has to hold in the subsequent frames as well. Is the task i am attempting possible? if yes any help is appreciated
Sorry, but I have PyQt5.
Try it:
import os
import sys
from PyQt5 import Qt
#from PyQt4 import QtCore, QtGui
class MainWindow(Qt.QMainWindow):
def __init__(self, parent=None):
Qt.QMainWindow.__init__(self, parent)
self.scene = Qt.QGraphicsScene()
self.grview = Qt.QGraphicsView(self.scene)
self.setCentralWidget(self.grview)
dir = "frames"
self.listFiles = os.listdir(dir)
self.timer = Qt.QTimer(self)
self.n = 0
self.timer.timeout.connect(self.on_timeout)
self.timer.start(1000)
self.setGeometry(700, 150, 300, 300)
self.show()
def on_timeout(self):
if self.n < len(self.listFiles):
self.scene.clear()
file = self.listFiles[self.n]
pixmap = Qt.QPixmap("frames\{}".format(file)) # !!! "frames\{}"
item = Qt.QGraphicsPixmapItem(pixmap)
self.scene.addItem(item)
self.grview.setScene(self.scene) # !!! (self.scene)
self.n += 1
else:
self.timer.stop()
app = Qt.QApplication(sys.argv)
GUI = MainWindow()
sys.exit(app.exec_())
Im trying to build a temperature monitor using pyqt5 on a raspberry pi 3 with ds18b20 sensors attached. I am pretty new to python and pyqt5.
The Gui file (frameless.py) was developed with QT 4 designer. The Gui should serve as a basic example for a more enviromental sensors (e.g pH EC etc.) to be implemented. The actual code (runframeless.py) is shown below.
I am well aware that is is best to use threads (or Multithreading) if more senosor data are to be read at the same time, but currently I am stuck in a pretty simple problem.
Problem:
The temperature is shown in the textlabel, but the temperature value in def read_temp(self) is not updated.
Question:
Can anyone explain why it is not updated and help me getting the code right so that the temperature value shown in the gui is changed when the temperature acually changes?
runframeless.py
# -*- coding: utf-8 -*-
# file: runframeless.py)
#-create a skeleton class(es) for Raspberry Pi GUI-
# need this
import sys
import time
import datetime
import os
import glob
#----This gets the Qt stuff------------------------
from PyQt5 import QtGui
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QLabel
# Import QtCreator/qtdesigner file
import frameless
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '10*')[0]
device_file = device_folder + '/w1_slave'
#-------------------------------------------------
#----class(es) for our Raspberry Pi GUI-----------
#-------------------------------------------------
class MainWindow(QMainWindow, frameless.Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self) # gets defined in the UI file
self.label.setText(str(self.read_temp()))
self.timer = QtCore.QTimer(self)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.read_temp)
self.timer.start()
def read_temp(self):
f = open(device_file, 'r')
lines = f.readlines()
f.close()
time.sleep(.1)
while lines[0].strip()[-3:] != 'YES':
time.sleep(.1)
#lines = self.read_temp()
equals_pos = lines[1].find('t=')
if equals_pos != -1:
temp_string = lines[1][equals_pos+2:]
temp_c = float(temp_string) / 1000.0
return temp_c
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
file: frameless.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'frameless.ui'
#
# Created: Tue Feb 27 17:31:49 2018
# by: PyQt5 UI code generator 5.3.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.setWindowFlags(Qt.FramelessWindowHint) # frameless
MainWindow.resize(800, 480)
MainWindow.setStyleSheet("background:rgb(0, 0, 0);")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setStyleSheet("")
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(660, 430, 101, 31))
self.pushButton.setStyleSheet("background: rgb(255, 255, 255) ")
self.pushButton.setObjectName("pushButton")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(10, 170, 751, 41))
self.widget.setObjectName("widget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_2 = QtWidgets.QLabel(self.widget)
self.label_2.setStyleSheet("background: rgb(255, 255, 255) ")
self.label_2.setObjectName("label_2")
self.horizontalLayout.addWidget(self.label_2)
self.label = QtWidgets.QLabel(self.widget)
self.label.setStyleSheet("background: rgb(255, 255, 255) ")
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
self.pushButton.clicked.connect(MainWindow.close)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "exit"))
self.label_2.setText(_translate("MainWindow", "Ds18B20-sensor"))
self.label.setText(_translate("MainWindow", "TextLabel"))
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_())
When debugging code, it pays to logically step through how the program works.
Firstly, this line self.label.setText(str(self.read_temp())) runs once as it is in the __init__ method. This calls the self.read_temp() method which returns the temperature and places it in the text box.
Next, you create a QTimer in the __init__ method, which calls self.read_temp() every second. So when the timer triggers every second, self.read_temp is run, the temperature is read out, and returned to whatever internal code the QTimer uses to call your method.
At this point the QTimer throws away the return value because it doesn't want it, can't use it, etc.
As you can probably now see, the reason why it doesn't update is because you have never told it to update.
I would suggest adding the line self.label.setText(str(temp_c)) prior to the return temp_c line in order to update the label from the code that runs every second.
I made a repository in github that contain a code in python and PyQt5 for temperature control (monitoring) whith raspberry Pi and ds18b20 sensor
You can use that by following link
https://github.com/salarshekari/ds18b20-raspberry-pyqt5
please do not forget star if you like that