Reading frames from a folder and display in QGraphicsView in pyqt4 - pyqt4

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_())

Related

PyQt5 shows black window without widgets while moviepy is concatenating videos

When concatenating two video files, the PyQt window is completely black with no widgets. The background color changes to grey and the widget shows up only after the videos have been concatenated.
I would like to see a grey window and my widget while the videos are being concatenated.
Here is the script:
import os
import sys
from PyQt5.QtWidgets import *
from moviepy.editor import VideoFileClip, concatenate_videoclips
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Using Labels")
self.setGeometry(50,50,350,350)
self.UI()
def UI(self):
text1=QLabel("Hello Python",self)
text1.move(50,50)
self.show()
def make_movie():
sep = os.sep
directory_stub = '.' + sep + 'src' + sep + "assets" + sep
clip1 = VideoFileClip(directory_stub + 'introoutro' + sep + "intro.mp4")
clip2 = VideoFileClip(directory_stub + 'introoutro' + sep + "outro.mp4")
final_clip = concatenate_videoclips([clip1,clip2])
final_clip.write_videofile("my_concatenation.mp4")
def main():
App = QApplication(sys.argv)
window=Window()
make_movie()
sys.exit(App.exec_())
if __name__ == '__main__':
main()
While the videos are being put together I see this:
I want to see this during the video processing but I only see it 1. after the processing is done or 2. if I edit the code to never call make_movie():
Thanks for any help.
Concatenation is a very time consuming task so it should not be run on the main thread as it can freeze the GUI:
import os
import sys
import threading
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
from moviepy.editor import VideoFileClip, concatenate_videoclips
class QMovie(QObject):
started = pyqtSignal()
finished = pyqtSignal()
def concatenate(self, inputs, output):
threading.Thread(
target=self._concatenate, args=(inputs, output), daemon=True
).start()
def _concatenate(self, inputs, output):
self.started.emit()
clips = []
for input_ in inputs:
clip = VideoFileClip(input_)
clips.append(clip)
output_clip = concatenate_videoclips(clips)
output_clip.write_videofile(output)
self.finished.emit()
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Using Labels")
self.setGeometry(50, 50, 350, 350)
self.UI()
def UI(self):
text1 = QLabel("Hello Python", self)
text1.move(50, 50)
self.show()
def main():
App = QApplication(sys.argv)
window = Window()
directory_stub = os.path.join(".", "src", "assets")
in1 = os.path.join(directory_stub, "introoutro", "intro.mp4")
in2 = os.path.join(directory_stub, "introoutro", "outro.mp4")
qmovie = QMovie()
qmovie.concatenate([in1, in2], "my_concatenation.mp4")
sys.exit(App.exec_())
if __name__ == "__main__":
main()

How can I access images from qrc.py into reportlab?

I have converted "image_fonts.qrc" into image_fonts_rc.py file. It has one image named as "image.png"
How can I use an image into reportlab PDF in Python from qrc.py file.
File image_fonts.qrc
<RCC>
<qresource prefix="image_fonts">
<file>image.png</file>
<file>logo.png</file>
</qresource>
</RCC>
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/image_fonts/logo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
I used above the lines, but I get an error. Please find the below error.
TypeError: expected str, bytes or os.PathLike object, not QIcon
Minimal Example:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5 import uic
import sys
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, PageTemplate, TableStyle, Paragraph, Image, Spacer, Frame, Paragraph, Flowable
import image_fonts_rc
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi("test_images.ui", self)
self.show()
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/image_fonts/logo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
doc = SimpleDocTemplate("images.pdf", pagesize=A4, rightMargin=40, leftMargin=40, topMargin=20, bottomMargin=20, title ="Images")
width, height = A4
document = []
logo = icon
imgw = imgh = 80
im = (Image(logo, width=imgw, height=imgh))
document.append(im)
doc.build(document)
app = QApplication(sys.argv)
window = UI()
app.exec_()
It is not necessary to use QPixmap or QIcon but you must get the bytes from the image as in my previous answer:
from io import BytesIO
from PyQt5 import QtCore
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Image
import image_fonts_rc
def convert_qrc_to_bytesio(filename):
file = QtCore.QFile(filename)
if not file.open(QtCore.QIODevice.ReadOnly):
raise RuntimeError(file.errorString())
return
f = BytesIO(file.readAll().data())
return f
doc = SimpleDocTemplate(
"images.pdf",
pagesize=A4,
rightMargin=40,
leftMargin=40,
topMargin=20,
bottomMargin=20,
title="Images",
)
width, height = A4
document = []
logo = convert_qrc_to_bytesio(":/image_fonts/logo.png")
imgw = imgh = 80
im = Image(logo, width=imgw, height=imgh)
document.append(im)
doc.build(document)

Kivy strange behavior when it updates Image Texture

I am trying to update the Kivy Image.texture with the OpenCV image, and I am using a new thread to do it. I found some discussion that "the graphics operation should be in the main thread". But still, I want to figure out why the code below works.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
from threading import Thread
class MainApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.image = Image()
self.layout.add_widget(self.image)
Thread(target=self.calculate).start()
def build(self):
return self.layout
def calculate(self):
img = cv2.imread("test.png")
w, h = img.shape[1], img.shape[0]
img = cv2.flip(img, flipCode=0)
buf = img.tostring()
texture = Texture(0, 0, 0).create(size=(w, h), colorfmt="bgr")
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.image.texture = texture
def main():
Image(source='test.png') # remove this line will froze the app
MainApp().run()
if __name__ == "__main__":
main()
If I remove this line:
Image(source='test.png')
the app is frozen. Can someone help me to understand why decalring an Image object outside the main loop will affect the MainApp running.
The "test.png" image can be any simple image, like below. You need to put the image in the same directory as this script.
Not sure exactly why that line affects your code, but the main problem is that not only is the GUI manipulation required to be done on the main thread, but also any blit_buffer() must also be done on the main thread. So, a working version of your code (with the Image() removed) looks like this:
from functools import partial
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
from threading import Thread
class MainApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.image = Image()
self.layout.add_widget(self.image)
Thread(target=self.calculate).start()
def build(self):
return self.layout
def calculate(self):
img = cv2.imread("test.png")
w, h = img.shape[1], img.shape[0]
img = cv2.flip(img, flipCode=0)
buf = img.tostring()
Clock.schedule_once(partial(self.do_blit, buf, w, h))
def do_blit(self, buf, w, h, dt):
texture = Texture(0, 0, 0).create(size=(w, h), colorfmt="bgr")
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.image.texture = texture
def main():
# Image(source='test.png') # remove this line will froze the app
MainApp().run()
if __name__ == "__main__":
main()

How to render Altair / Vega in a PyQt widget

Is it possible to have Altair or Vega(-Lite) render to a PyQt widget, similar to Matplotlib supporting multiple backends? I know I can use a Qt WebView widget to render a web page with Vega-embed, but I want to prevent the overhead of having to serve this, even if locally.
The best option to visualize a plot with Altair is to use QWebEngineView since altair what is to create javascript code based on the instructions you set. IMHO the best solution is to obtain the html of the chart and set it in a QWebEngineView. In the following example I show how to do the above, in addition to enabling the characteristics of saving the image as svg or png, etc.
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from io import StringIO
class WebEngineView(QtWebEngineWidgets.QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent)
self.page().profile().downloadRequested.connect(self.onDownloadRequested)
self.windows = []
#QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def onDownloadRequested(self, download):
if (
download.state()
== QtWebEngineWidgets.QWebEngineDownloadItem.DownloadRequested
):
path, _ = QtWidgets.QFileDialog.getSaveFileName(
self, self.tr("Save as"), download.path()
)
if path:
download.setPath(path)
download.accept()
def createWindow(self, type_):
if type_ == QtWebEngineWidgets.QWebEnginePage.WebBrowserTab:
window = QtWidgets.QMainWindow(self)
view = QtWebEngineWidgets.QWebEngineView(window)
window.resize(640, 480)
window.setCentralWidget(view)
window.show()
return view
def updateChart(self, chart, **kwargs):
output = StringIO()
chart.save(output, "html", **kwargs)
self.setHtml(output.getvalue())
if __name__ == "__main__":
import sys
import altair as alt
from vega_datasets import data
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QMainWindow()
cars = data.cars()
chart = (
alt.Chart(cars)
.mark_bar()
.encode(x=alt.X("Miles_per_Gallon", bin=True), y="count()",)
.properties(title="A bar chart")
.configure_title(anchor="start")
)
view = WebEngineView()
view.updateChart(chart)
w.setCentralWidget(view)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Currently the only rendering backend for Altair is Vega-Embed, so rendering it in a PyQt widget will require running some Javascript engine. I suspect that Qt WebView is probably the best option.
If you're OK losing interactivity of charts, you could also use altair_saver as a backend to store a static PNG, SVG, or PDF of the chart and show it within a QtWidget.
Building on the great answer by eyllanesc, this is the version adapted for PyQt6:
from io import StringIO
from typing import Optional
from PyQt6 import QtCore
from PyQt6 import QtWidgets
from PyQt6.QtWebEngineCore import QWebEngineDownloadRequest
from PyQt6.QtWebEngineCore import QWebEnginePage
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QWidget
class WebEngineView(QWebEngineView):
def __init__(self, parent: Optional[QWidget] = None):
super().__init__(parent)
self.page().profile().downloadRequested.connect(self.onDownloadRequested)
self.windows = []
#QtCore.pyqtSlot(QWebEngineDownloadRequest)
def onDownloadRequested(self, download: QWebEngineDownloadRequest) -> None:
if (
download.state()
== QWebEngineDownloadRequest.DownloadState.DownloadRequested
):
path, _ = QtWidgets.QFileDialog.getSaveFileName(
self, self.tr("Save as"), download.downloadFileName()
)
if path:
download.setDownloadFileName(path)
download.accept()
def createWindow(
self, web_window_type: QWebEnginePage.WebWindowType
) -> Optional[QWebEngineView]:
if web_window_type == QWebEnginePage.WebWindowType.WebBrowserTab:
window = QtWidgets.QMainWindow(self)
view = QWebEngineView(window)
window.resize(640, 480)
window.setCentralWidget(view)
window.show()
return view
def set_chart(self, chart, **kwargs) -> None:
output = StringIO()
chart.save(output, "html", **kwargs)
self.setHtml(output.getvalue())

PyQt5 loading PDF file format into Qlabel or QWidget to display [duplicate]

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:

Resources