MediaPlayer not showing in Layout PyQt5 [duplicate] - python

I am trying to overlay some graphics(QtGraphicsView) on top of video player(QVideoWidget). i have already tried setting QtGraphicsView subclass stylesheets to transparent and background brush and none is working.
#self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30, 3)))
#self.setStyleSheet("background-color:rgba(0,0,0,0)")
#self.setStyleSheet("background:rgba(0,0,0,0)")
self.setStyleSheet("background:transparent")
self.setStyleSheet("background-color:transparent")
self.setStyleSheet("background-color:rgba(30,30,30,3)")
self.setStyleSheet("background:rgba(30,30,30,3)")
Actual Intentions is to easily crop a video (visual way). All other work is done regarding capturing events , doing math etc. this image explains situation pretty well. . At this point it feels, surely i am doing it wrong way, there must be some easier way in QtMultiMedia Components to draw on top of them. Any Ideas really appreciated.

One possible solution is to use QGraphicsVideoItem instead of QVideoWidget and embed it in the QGraphicsView, then the other items can be made child of the QGraphicsVideoItem so that it is on top, besides the position of the new items will be related to the QGraphicsVideoItem.
import os
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self._scene = QtWidgets.QGraphicsScene(self)
self._gv = QtWidgets.QGraphicsView(self._scene)
self._videoitem = QtMultimediaWidgets.QGraphicsVideoItem()
self._scene.addItem(self._videoitem)
self._ellipse_item = QtWidgets.QGraphicsEllipseItem(QtCore.QRectF(50, 50, 40, 40), self._videoitem)
self._ellipse_item.setBrush(QtGui.QBrush(QtCore.Qt.green))
self._ellipse_item.setPen(QtGui.QPen(QtCore.Qt.red))
self._player = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
self._player.stateChanged.connect(self.on_stateChanged)
self._player.setVideoOutput(self._videoitem)
file = os.path.join(os.path.dirname(__file__), "small.mp4")
self._player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file)))
button = QtWidgets.QPushButton("Play")
button.clicked.connect(self._player.play)
self.resize(640, 480)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self._gv)
lay.addWidget(button)
#QtCore.pyqtSlot(QtMultimedia.QMediaPlayer.State)
def on_stateChanged(self, state):
if state == QtMultimedia.QMediaPlayer.PlayingState:
self._gv.fitInView(self._videoitem, QtCore.Qt.KeepAspectRatio)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Related

`QPixmap` and `QLabel` size slightly increases when reloading

When I'm trying to make my app, I stumbled upon this unexpected behavior where when I re-display a new QPixmap in a QLabel. I tried to simplify the code and ended up with the code below. I also attached the video of the behavior.
I provided here a replicable example (It just needs some .jpg file in the same directory):
import sys
import os
import random
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel, QSizePolicy
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
class AppDemo(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(200, 200, 400, 400)
current_working_dir = os.path.abspath('')
dir_files = os.listdir(current_working_dir)
# Saving .jpg from the dir
self.picture = []
for file in dir_files:
if file.endswith(".jpg"):
self.picture.append(file)
self.label = QLabel()
self.label.setStyleSheet("border: 1px solid black;") # <- for the debugging
self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding)
self.label.setPixmap(self.random_picture_selector())
button = QPushButton("Reload Picture")
button.clicked.connect(self.reload_picture)
layout = QVBoxLayout(self)
layout.addWidget(button)
layout.addWidget(self.label)
def reload_picture(self):
self.label.setPixmap(self.random_picture_selector())
def random_picture_selector(self):
rnd_picture = random.choice(self.picture)
pixmap = QPixmap(rnd_picture)
pixmap = pixmap.scaledToWidth(self.label.width(), Qt.SmoothTransformation)
# pixmap = pixmap.scaled(self.label.width(), self.label.height(), Qt.KeepAspectRatio) # <- even this is not working
return pixmap
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = AppDemo()
demo.show()
sys.exit(app.exec_())
Additional Infos:
When simplifying the code I realized that the problem disappears when I removed these following lines. (although I'm not very sure that these part of the code really causes the problem)
pixmap = pixmap.scaledToWidth(self.label.width(), Qt.SmoothTransformation)
# pixmap = pixmap.scaled(self.label.width(), self.label.height(), Qt.KeepAspectRatio) # <- even this is not working
I really have no idea what causes the problem even after looking for the Docs of QPixmap and QLabel.
The problem is caused by the stylesheet border. If you just print the pixmap and label size after setting the pixmap, you'll see that the label width is increased by 2 pixels, which is the sum of the left and right border.
You either remove the border, or you use the contentsRect():
width = self.label.contentsRect().width()
pixmap = pixmap.scaledToWidth(width, Qt.SmoothTransformation)
Read more about the Box Model in the Qt style sheet documentation.

Removing focus border from QSlider in PyQt

I am looking for a solution to remove a terrible looking focus rectangle over a QSlider.
In addition that it looks terrible, it covers the ticks and is not entirely drawn.
This is an ancient issue; I remember stumbling into it many years ago. The workarounds
mentioned (e.g. Removing dotted border without setting NoFocus in Windows PyQt) do not work for a slider on my Linux system.
Setting outline to none or using the Qt.WA_MacShowFocusRect does not work on Linux.
slider.setStyleSheet('QSlider { outline:none; }')
slider.setAttribute(Qt.WA_MacShowFocusRect, 0)
The only thing that 'works' is setting the focus policy to Qt.NoFocus, which is not a real
solution, since one might want to work with keyboard.
slider.setFocusPolicy(Qt.NoFocus)
I am on Linux and the issue is on all available themes. Source code:
#!/usr/bin/python
from PyQt5.QtWidgets import (QWidget, QSlider, QHBoxLayout,
QLabel, QApplication)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
import sys
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout()
sld = QSlider(Qt.Horizontal, self)
# sld.setFocusPolicy(Qt.NoFocus)
# sld.setStyleSheet('QSlider { outline:none; padding: 0 2 0 2;}')
# sld.setAttribute(Qt.WA_MacShowFocusRect, 0)
sld.setTickPosition(QSlider.TicksAbove)
sld.setRange(0, 100)
sld.setPageStep(5)
sld.valueChanged.connect(self.changeValue)
self.label = QLabel("0", self)
self.label.setMinimumWidth(80)
hbox.addWidget(sld)
hbox.addSpacing(15)
hbox.addWidget(self.label)
self.setLayout(hbox)
self.setGeometry(300, 300, 350, 250)
self.setWindowTitle('QSlider')
self.show()
def changeValue(self, value):
self.label.setText(str(value))
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Screenshot:

Background picture in QMainwindow PyQt5

I'm trying to get a background image to my mainwindow but i can't get it to work properly.
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QLabel
from PyQt5.QtGui import QIcon
from PyQt5 import QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSvg import *
from PyQt5.QtWidgets import *
from abc import abstractmethod
class App(QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent=parent)
self.title = 'Title'
self.left = 500
self.top = 500
self.width = 440
self.height = 280
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
# ...
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
#view = TableScene(ex)
ex.show()
sys.exit(app.exec_())
I've tried different kinds of methods but none of them works as it should.
I found the following code as a solution from another topic but it just gives me a black background and the rest of the widgets get laggy.
oImage = QImage("table.png")
sImage = oImage.scaled(QSize(440, 280))
palette = QPalette()
palette.setBrush(QPalette.Window, QBrush(sImage))
self.setPalette(palette)
I don't know if the whole window gets laggy or what really happens but the picture below is a screenshot of a part of the window using the code above, and as you can see it gets all black and the slider shows all the previous position it has been on, sort of laggy anyways.
I've also tried the setStyleSheet but I don't know if it's my syntax that's wrong or if it's a faulty way of doing it. Does anyone know a way of doing it correctly?
EDIT
This is my current window:
This is the picture I'm trying to implement as a background to my current window, the picture called "table.png" :
This is a visualization of what I'm trying to do, and this is made in paint since I don't know how to do it correctly:
And this is what I get if i use the code from the other topic:
One of the possible reasons why a black background appears is that QImage is null. And a QImage is null because the image is invalid or because the image path is incorrect. In this case I think it is the second case since the OP uses a relative path that is prone to errors. The solution is to build the absolute path using the script information such as its location:
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class App(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent=parent)
self.initUI()
def initUI(self):
self.setWindowTitle("Title")
self.setGeometry(500, 500, 440, 280)
oImage = QtGui.QImage(os.path.join(CURRENT_DIR, "table.png"))
sImage = oImage.scaled(QtCore.QSize(440, 280))
palette = QtGui.QPalette()
palette.setBrush(QtGui.QPalette.Window, QtGui.QBrush(sImage))
self.setPalette(palette)
pushbutton = QtWidgets.QPushButton("test", self)
pushbutton.move(100, 100)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
Note: The image provided by the OP has extension .jpg but the one indicated by code is .png, maybe "imgur" has changed the extension.
Note: If the window is resized manually, the following behavior will be observed:
So for this there are 2 possible solutions depending on the developer's criteria:
Set a fixed size: self.setFixedSize(440, 280)
Adapt the image to the size of the window:
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class App(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent=parent)
self.initUI()
def initUI(self):
self.setWindowTitle("Title")
self.setGeometry(500, 500, 440, 280)
pushbutton = QtWidgets.QPushButton("test", self)
pushbutton.move(100, 100)
self.oImage = QtGui.QImage(os.path.join(CURRENT_DIR, "table.png"))
# or QPixmap
# self.oPixmap = QtGui.QPixmap(os.path.join(CURRENT_DIR, "table.png"))
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawImage(self.rect(), self.oImage)
# or QPixmap
# painter.drawPixmap(self.rect(), self.oPixmap)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
or
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class App(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent=parent)
self.initUI()
def initUI(self):
self.setWindowTitle("Title")
self.setGeometry(500, 500, 440, 280)
pushbutton = QtWidgets.QPushButton("test", self)
pushbutton.move(100, 100)
self.setStyleSheet(
"""
QMainWindow{
border-image: url(%s) 0 0 0 0 stretch stretch
}
"""
% os.path.join(CURRENT_DIR, "table.png")
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())

PyQt video frame update signal (Trigger function after each video frame)

I am creating a video player and I need to draw some polygons on top of it. I am using a QGraphicsScene to create this and I need to update the polygons on screen after each frame. I am currently using the QMediaPlayer paired up with a QGraphicsVideoItem to create this. The problem I am having is that the QMediaPlayer doesn't have a signal that activates on each frame. It has positionChanged(), but this only seems to trigger once every second.
I tried using QMovie since it does send updates on every frame, but it did not display anything. This is the code I used to implement this.
video_view = QGraphicsView()#view to hold video
video_item = QGraphicsVideoItem()#video item for scene
video_scene = QGraphicsScene()#scene for Qgraphics view
video_view.setScene(video_scene)
label = QLabel()
movie = QMovie(self.video_paths[index]) #contains file path
label.setMovie(movie)
video_scene.addWidget(label)
self.vlayout_main_video.addWidget(video_view)
The video file I am using is a .avi file and it is 72Mb large.
I would really appreciate it if somebody could point me in the right direction on how I could do this. I am currently using PyQt5.
Thank you
There are 2 options:
positionChanged is emited every second because the notifyInterval property of QMediaPlayer is set in that period. So you can change that property, for example to 60 ms.
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.video_view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(self.video_view)
self.player = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
self.video_item = QtMultimediaWidgets.QGraphicsVideoItem()
self.player.setVideoOutput(self.video_item)
scene.addItem(self.video_item)
file = "/path/of/video"
self.player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file)))
self.player.positionChanged.connect(self.on_positionChanged)
self.player.setNotifyInterval(60)
self.player.play()
#QtCore.pyqtSlot('qint64')
def on_positionChanged(self, p):
print(p, QtCore.QTime.currentTime().toString("hh:mm:ss.zzz"))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Use the VideoFrameProbed signal from QVideoProbe:
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.video_view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(self.video_view)
self.player = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
self.video_item = QtMultimediaWidgets.QGraphicsVideoItem()
self.player.setVideoOutput(self.video_item)
scene.addItem(self.video_item)
file = "/path/of/video"
self.player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file)))
self.player.play()
probe = QtMultimedia.QVideoProbe(self)
probe.videoFrameProbed.connect(self.on_videoFrameProbed)
probe.setSource(self.player)
#QtCore.pyqtSlot()
def on_videoFrameProbed(self):
print(QtCore.QTime.currentTime().toString("hh:mm:ss.zzz"))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

How do I set layout's fixed height in PyQt5?

I am trying to set a fixed height for QHBoxLayout. To elaborate more on that, I need to set a specific height for my Horizontal layout. However, I cannot find the proper way to do so. What should I do to make this happen?
hbox1 = QHBoxLayout()
As noted by #ekhumuro in QHBoxLayout you can not set the fixed height, you must do that to the widget where it will be contained as I show below:
import random
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setFixedHeight(300)
lay = QtWidgets.QHBoxLayout(self)
for letter in "ABCDEFG":
label = QtWidgets.QLabel(letter, alignment=QtCore.Qt.AlignCenter)
color = QtGui.QColor(*[random.randint(0, 255) for _ in range(3)])
label.setStyleSheet("background-color: {}".format(color.name()))
lay.addWidget(label)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Categories

Resources