PyQt5 Cant link slider to the playing audio in my mp3 player - python

I am making an Mp3 Player, and I am almost done, the only thing left is the time slider.
I managed to make the slider so that it can change the position/time of the audio, but I want the vice-versa now, the slider to be moved by the audio progress.
(Ignore this pls, they are asking for more details but I got none other to give, thanks)
My MRE:
import os
import vlc # version 3.0.10114
from PyQt5 import QtCore, QtGui, QtWidgets
from tkinter import Tk, filedialog # 8.6
from time import sleep
from pygame import mixer # version 1.9.6
class UiMainWindow:
def __init__(self, main_window):
self.main_window = main_window
mixer.init() # For the volume
self.vlc_instance = vlc.Instance()
media = self.vlc_instance.media_new('')
self.player = self.vlc_instance.media_player_new()
self.player.set_media(media)
self.main_window.setStyleSheet('background-color: gray')
self.main_window.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(self.main_window)
# Initiating the buttons and labels
self.add_new_song_button = QtWidgets.QPushButton(self.centralwidget)
self.add_new_song_button.setGeometry(QtCore.QRect(690, 50, 80, 24))
self.add_new_song_button.clicked.connect(self.add_song)
self.time_slider = QtWidgets.QSlider(self.centralwidget)
self.time_slider.setMinimum(0)
self.time_slider.setMaximum(10000)
self.time_slider.setValue(0)
self.time_slider.setSingleStep(1)
self.time_slider.setOrientation(QtCore.Qt.Horizontal)
self.time_slider.sliderMoved.connect(self.slider_moved)
# self.time_slider.valueChanged.connect(self.slider_changed)
self.time_slider.setGeometry(QtCore.QRect(200, 80, 400, 20))
# Initiating the lists and music
self.ui_song_list = QtWidgets.QListWidget(self.centralwidget)
self.ui_song_list.setGeometry(QtCore.QRect(10, 120, 780, 480))
self.ui_song_list.setEnabled(True)
self.ui_song_list.setStyleSheet('background-color: lightblue;')
self.current_audio = '' # To prevent errors when trying to play nothing
self.audio_paths = {}
self.ui_song_list.itemClicked.connect(self.play_song)
self.retranslate_ui(self.main_window)
QtCore.QMetaObject.connectSlotsByName(self.main_window)
def config_audio(self, audio=''): # Changes the song of the player
if not audio:
media = self.vlc_instance.media_new(audio)
else:
media = self.vlc_instance.media_new(self.audio_paths[audio])
self.player = self.vlc_instance.media_player_new()
self.player.set_media(media)
def play_song(self, song): # This is called when a song is clicked
self.current_audio = song.text()
self.player.stop()
self.config_audio(audio=self.current_audio)
self.player.play()
def add_song(self):
Tk().withdraw() # Creating the interface for choosing songs
filetypes = [('mp3 files', '*.mp3'), ('wav files', '*.wav')] # Only audio should pe added
list_of_chosen_audio = filedialog.askopenfilenames(title='Choose audio files', filetypes=filetypes)
for audio_path in list_of_chosen_audio:
audio_name = audio_path[:-4].split('/')[-1] # taking only the audio name without mp3 and audio_paths
self.audio_paths[audio_name] = audio_path
self.ui_song_list.addItem(audio_name)
self.all_songs = self.ui_song_list.findItems('', QtCore.Qt.MatchContains)
def slider_moved(self):
try:
self.player.set_position(self.time_slider.value()/10000) # / 10000 because the slider didnt work if lowered the maximum value
except Exception as e:
print(e)
def slider_changed(self):
if self.player.is_playing():
print(round(self.player.get_position()*10000, 2))
self.time_slider.setValue(round(self.player.get_position()*10000, 2))
self.player.set_position(self.time_slider.value()/10000)
def retranslate_ui(self, main_window): # Setting the text for all the buttons and labels
main_window.setCentralWidget(self.centralwidget)
main_window.setWindowTitle("MP3 Player")
self.add_new_song_button.setText("Add Songs")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = UiMainWindow(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Thanks in advance!

The slider has a setValue to set the slider position (0-1). To update the slider, add a timer to the app.
Update your code with the following changes:
class UiMainWindow:
def __init__(self, main_window):
..........
# timer to update slide bar
timer = QTimer(self.main_window)
timer.timeout.connect(self.time_hit)
timer.start(500) # 1/2 second
def time_hit(self):
if self.player.is_playing():
self.time_slider.setValue(self.player.get_position()*10000) # update slide bar

Related

how to add command paramaters in QPushButton - PyQt5

I am using a simple GUI with PyQt5 in which I have 3 buttons to record, stop and play the audio. I have some know-how of the Tkinter GUI, but I am totally confused here in PyQt5. In Tkinter Buttons, I am using commands something like this:
record_btn = Button(voice_rec, text="Record Audio", command=lambda m=1: threading_rec(m))
# Stop button
stop_btn = Button(voice_rec, text="Stop Recording", command=lambda m=2: threading_rec(m))
# Play button
play_btn = Button(voice_rec, text="Play Recording", command=lambda m=3: threading_rec(m))
I tried this in Pyqt5 Buttons, but still not working:
self.record_btn = QPushButton('Record', self)
self.record_btn.clicked.connect(lambda: self.threading_rec(x=1))
self.record_btn.move(100, 70)
self.stop_btn = QPushButton('Stop', self)
self.stop_btn.clicked.connect(lambda: self.threading_rec(x=2))
self.stop_btn.move(200, 70)
self.play_btn = QPushButton('Play', self)
self.play_btn.clicked.connect(lambda: self.threading_rec(x=3))
self.play_btn.move(300, 70)
Here is my complete code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
import sounddevice as sd
import queue
import soundfile as sf
import threading
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'Voice Recorder'
self.left = 400
self.top = 300
self.width = 500
self.height = 200
self.initUI()
# Create a queue to contain the audio data
q = queue.Queue()
# Declare variables and initialise them
recording = False
file_exists = False
# Fit data into queue
def callback(indata, frames, time, status):
q.put(indata.copy())
def threading_rec(x):
if x == 1:
# If recording is selected, then the thread is activated
t1 = threading.Thread(target=record_audio)
t1.start()
elif x == 2:
# To stop, set the flag to false
global recording
recording = False
messagebox.showinfo(message="Recording finished")
elif x == 3:
# To play a recording, it must exist.
if file_exists:
# Read the recording if it exists and play it
data, fs = sf.read("trial.wav", dtype='float32')
sd.play(data, fs)
sd.wait()
else:
# Display and error if none is found
messagebox.showerror(message="Record something to play")
def record_audio(self):
# Declare global variables
global recording
# Set to True to record
recording = True
global file_exists
# Create a file to save the audio
messagebox.showinfo(message="Recording Audio. Speak into the mic")
with sf.SoundFile("trial.wav", mode='w', samplerate=44100,
channels=2) as file:
# Create an input stream to record audio without a preset time
with sd.InputStream(samplerate=44100, channels=2, callback=callback):
while recording == True:
# Set the variable to True to allow playing the audio later
file_exists = True
# write into file
file.write(q.get())
# Button to record audio
record_btn = Button(voice_rec, text="Record Audio", command=lambda m=1: threading_rec(m))
# Stop button
stop_btn = Button(voice_rec, text="Stop Recording", command=lambda m=2: threading_rec(m))
# Play button
play_btn = Button(voice_rec, text="Play Recording", command=lambda m=3: threading_rec(m))
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
record_btn = QPushButton('Record', self)
record_btn.move(100, 70)
stop_btn = QPushButton('Stop', self)
stop_btn.move(200, 70)
play_btn = QPushButton('Play', self)
play_btn.move(300, 70)
button.clicked.connect(self.on_click)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Someone please help me with this code!
I'll try to answer this in a simple way.
Let's say you have a button like this:
def init_UI(self):
self.sum_button = QPushButton(text='sum')
self.sum_button.clicked.connect(lambda: self.get_sum(int_a, int_b))
def get_sum(self, int_a, int_b):
return int_a + int_b

How to display a live OpenCV video feed using a PyQt5

I'm developing an application with PyQt5 and QtDesigner. For one of the pages (page 2), I'm trying to embed a live video stream from a camera with OpenCV. The code has a thread running and I confirmed that it is sending good frames. The problem I'm facing is dynamically updating a QLabel with the OpenCV frame.
The program currently crashes when this line of code (in the MainWindow class) is uncommented: Why?
self.ui.Worker1.ImageUpdate.connect(self.ui.ImageUpdateSlot)
Below is the main code
# by: reevve
# Import Modules
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import cv2
# Import UI files
from ui_main import Ui_MainWindow
from ui_splashscreen import Ui_SplashScreen
# Global Variables
counter = 0
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# add page btn click functionality
self.ui.btn_page_1.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_1))
self.ui.btn_page_2.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_2))
self.ui.btn_page_3.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_3))
# set up the video feed
self.ui.CancelBTN.clicked.connect(lambda: self.ui.CancelFeed)
self.ui.Worker1 = Worker1()
self.ui.Worker1.start()
# the line below is causing the program to crash
#self.ui.Worker1.ImageUpdate.connect(self.ui.ImageUpdateSlot)
def ImageUpdateSlot(self, Image):
print('recieve frames')
self.ui.FeedLabel.setPixmap(QPixmap.fromImage(Image))
def CancelFeed(self):
print('cancel feed')
self.ui.Worker1.stop()
class SplashScreen(QMainWindow):
def __init__(self):
super(SplashScreen,self).__init__()
self.ui = Ui_SplashScreen()
self.ui.setupUi(self)
# remove title bar
self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
# drop shadow effect
self.shadow = QGraphicsDropShadowEffect(self)
self.shadow.setBlurRadius(20)
self.shadow.setXOffset(0)
self.shadow.setYOffset(0)
self.shadow.setColor(QColor(0, 0, 0, 60))
self.ui.dropShadowFrame.setGraphicsEffect(self.shadow)
# start timer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.progress)
# specify duration of launcher
self.timer.start(15)
# initial text
self.ui.label_description.setText("<strong>UD ASAE</strong> Ground Station GUI")
# change texts during loading process
QtCore.QTimer.singleShot(1500, lambda: self.ui.label_description.setText("<strong>LOADING</strong> the good stuff"))
QtCore.QTimer.singleShot(3000, lambda: self.ui.label_description.setText("<strong>GATHERING</strong> remaining braincells"))
# show main window
self.show()
def progress(self):
global counter
self.ui.progressBar.setValue(counter)
# close splash screen and open main gui
if counter > 100:
self.timer.stop()
self.main = MainWindow()
self.main.show()
self.close()
counter += 1
# FPV thread
class Worker1(QThread):
ImageUpdate = pyqtSignal(QImage)
def run(self):
print('\nrun feed')
self.ThreadActive = True
Capture = cv2.VideoCapture(0)
while self.ThreadActive:
ret, frame = Capture.read()
if ret:
Image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
ConvertToQtFormat = QImage(Image.data, Image.shape[1], Image.shape[0], QImage.Format_RGB888)
Pic = ConvertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.ImageUpdate.emit(Pic)
print('send good frames')
def stop(self):
print('stop feed')
self.ThreadActive = False
self.quit()
def window():
app = QApplication(sys.argv)
win = SplashScreen()
sys.exit(app.exec_())
window()
Again, the Worker1 thread seems to be sending good frames (confirmed with print statement), but I'm having trouble updating my QLabel (called FeedLabel) as the frames come in.
I did not attach the supporting .ui files to this post.
I changed a bunch of things in your code, indicated in the comments. Essentially the methods of your program were defined in a strange way and you stored many things in self.ui instead of self.
I made myself a minimal UI to be able to test the changes and it works. Below you can see the back of the sticky note I put on my laptop's camera:
Here is your modified code:
class MainWindow(QMainWindow):
def __init__(self):
super().__init__() # (optional) removed the args of `super`
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# add page btn click functionality
...
# set up the video feed
self.ui.CancelBTN.clicked.connect(self.CancelFeed) # removed `lambda` and `.ui`
self.Worker1 = Worker1() # (optional) removed `.ui` because your thread should be an attr of the program, not of the ui. This is a matter of preference though.
self.Worker1.start() # (optional) removed `.ui`
self.Worker1.ImageUpdate.connect(self.ImageUpdateSlot) # removed `.ui`
#pyqtSlot(QImage) # (optional) decorator to indicate what object the signal will provide.
def ImageUpdateSlot(self, Image): # Unindented by 4 spaces.
print('recieve frames')
self.ui.FeedLabel.setPixmap(QPixmap.fromImage(Image))
def CancelFeed(self): # Unindented by 4 spaces.
print('cancel feed')
self.Worker1.stop() # (optional) removed `.ui`

How to draw eye gaze data from eye tracker by PyQt while a video is playing

I am working bulid a eye gaze visualization tool just like this by PyQt5, and I also checked this post.
here is the code by modifing the above links.
turn out it's worked, but the video always get stuck sometime (the audio is normal, the the frames stuck, both video content and ellipse,just like this video),anyone can help?
import os
import time
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
import tobii_research as tr
import numpy as np
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
#first window,just have a single button for play the video
self.resize(256, 256)
self.btn_play = QtWidgets.QPushButton(self)
self.btn_play.setGeometry(QtCore.QRect(100, 100, 28, 28))
self.btn_play.setObjectName("btn_open")
self.btn_play.setText("Play")
self.btn_play.clicked.connect(self.Play_video)#click to play video
#
self._scene = QtWidgets.QGraphicsScene(self)
self._gv = QtWidgets.QGraphicsView(self._scene)
#construct a videoitem for showing the video
self._videoitem = QtMultimediaWidgets.QGraphicsVideoItem()
#add it into the scene
self._scene.addItem(self._videoitem)
# assign _ellipse_item is the gaze data, and embed it into videoitem,so it can show above the video.
self._ellipse_item = QtWidgets.QGraphicsEllipseItem(QtCore.QRectF(0, 0, 40, 40), self._videoitem)
self._ellipse_item.setBrush(QtGui.QBrush(QtCore.Qt.black))
self._ellipse_item.setPen(QtGui.QPen(QtCore.Qt.red))
#self._scene.addItem(self._ellipse_item)
self._gv.fitInView(self._videoitem)
self._player = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
self._player.setVideoOutput(self._videoitem)
file = os.path.join(os.path.dirname(__file__), "video.mp4")#video.mp4 is under the same dirctory
self._player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file)))
print(f"self._videoitem::{self._videoitem.size()}")
#get eye tracker
self.eyetrackers = tr.find_all_eyetrackers()
self.my_eyetracker = self.eyetrackers[0]
def gaze_data_callback(self, gaze_data_):
#for now, I don't know the coordinate system,just randomly assign the gaze data to test the functionality
self._ellipse_item.setPos(float(np.random.choice(range(0, 300))), float(np.random.choice(range(0, 240))))
print("time.time()::{}".format(time.time()))
def Play_video(self):
self.my_eyetracker.subscribe_to(tr.EYETRACKER_GAZE_DATA, self.gaze_data_callback, as_dictionary=True)
#size = QtCore.QSizeF(1920.0, 1080.0)#I hope it can fullscreen the video
#self._videoitem.setSize(size)
#self._gv.showFullScreen()
self._gv.resize(720,720)
self._gv.show()
self._player.play()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
cmd printout information here
According to the warning message:
QObject::startTimer: Timers can only be used with threads started with QThread
it can be deduced that the callback associated with "my_eyetracker" is executed in a secondary thread, so the position of the item from a different thread to the main thread would be updated, which could generate the problem described by the OP.
The solution is to send the callback information to the guide through signals.
import os
import time
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
import tobii_research as tr
import numpy as np
class EyeTracker(QtCore.QObject):
positionChanged = QtCore.pyqtSignal(float, float)
def __init__(self, tracker, parent=None):
super(EyeTracker, self).__init__(parent)
self._tracker = tracker
#property
def tracker(self):
return self._tracker
def start(self):
self.tracker.subscribe_to(
tr.EYETRACKER_GAZE_DATA, self._callback, as_dictionary=True
)
def _callback(self, gaze_data_):
self.positionChanged.emit(
float(np.random.choice(range(0, 300))),
float(np.random.choice(range(0, 240))),
)
print("time.time()::{}".format(time.time()))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
# first window,just have a single button for play the video
self.resize(256, 256)
self.btn_play = QtWidgets.QPushButton(self)
self.btn_play.setGeometry(QtCore.QRect(100, 100, 28, 28))
self.btn_play.setObjectName("btn_open")
self.btn_play.setText("Play")
self.btn_play.clicked.connect(self.Play_video) # click to play video
#
self._scene = QtWidgets.QGraphicsScene(self)
self._gv = QtWidgets.QGraphicsView(self._scene)
# construct a videoitem for showing the video
self._videoitem = QtMultimediaWidgets.QGraphicsVideoItem()
# add it into the scene
self._scene.addItem(self._videoitem)
# assign _ellipse_item is the gaze data, and embed it into videoitem,so it can show above the video.
self._ellipse_item = QtWidgets.QGraphicsEllipseItem(
QtCore.QRectF(0, 0, 40, 40), self._videoitem
)
self._ellipse_item.setBrush(QtGui.QBrush(QtCore.Qt.black))
self._ellipse_item.setPen(QtGui.QPen(QtCore.Qt.red))
# self._scene.addItem(self._ellipse_item)
self._gv.fitInView(self._videoitem)
self._player = QtMultimedia.QMediaPlayer(
self, QtMultimedia.QMediaPlayer.VideoSurface
)
self._player.setVideoOutput(self._videoitem)
file = os.path.join(
os.path.dirname(__file__), "video.mp4"
) # video.mp4 is under the same dirctory
self._player.setMedia(
QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file))
)
print(f"self._videoitem::{self._videoitem.size()}")
# get eye tracker
eyetrackers = tr.find_all_eyetrackers()
self.tracker = EyeTracker(eyetrackers[0])
self.tracker.positionChanged.connect(self._ellipse_item.setPos)
def Play_video(self):
self.tracker.start()
# size = QtCore.QSizeF(1920.0, 1080.0)#I hope it can fullscreen the video
# self._videoitem.setSize(size)
# self._gv.showFullScreen()
self._gv.resize(720, 720)
self._gv.show()
self._player.play()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

How to add Timer in VLC Python in PYQt5?

I have use VLC Python binding for media Player Gui and I want to add Timer to the Below Slider its like 0.00:0.00 (current video time : total duration of video) how can i add this ??
How to callback the current playing time and display as label below position slider as mention in image ?
Please help for small hint to how to do??
import sys
from PyQt5 import QtCore as qtc
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
import vlc
import os.path
class MainWindow(qtw.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
##Main framwork
# creating a basic vlc instance
self.instance = vlc.Instance()
# creating an empty vlc media player
self.mediaplayer = self.instance.media_player_new()
self.createUI()
self.isPaused = False
def createUI(self):
base_widget = qtw.QWidget()
base_widget.setLayout(qtw.QHBoxLayout())
notebook = qtw.QVBoxLayout()
base_widget.layout().addLayout(notebook)
self.setCentralWidget(base_widget)
#VideoFrame Loading
self.videoframe = qtw.QFrame()
self.videoframe.setMinimumWidth(950)
self.videoframe.setMinimumHeight(525)
self.palette = self.videoframe.palette()
self.palette.setColor (qtg.QPalette.Window,
qtg.QColor(0,0,0))
self.videoframe.setPalette(self.palette)
self.videoframe.setAutoFillBackground(True)
#Position Slider
self.positionslider = qtw.QSlider(qtc.Qt.Horizontal, self)
self.positionslider.setToolTip("Position")
self.positionslider.setMaximum(100000.0)
self.positionslider.setTickPosition(qtw.QSlider.TicksBelow)
self.positionslider.setTickInterval(2000)
self.positionslider.sliderMoved.connect(self.setPosition)
self.hbuttonbox = qtw.QHBoxLayout()
self.playbutton = qtw.QPushButton("Play")
self.hbuttonbox.addWidget(self.playbutton)
self.playbutton.clicked.connect(self.PlayPause)
#Button Box
self.stopbutton = qtw.QPushButton("Stop")
self.hbuttonbox.addWidget(self.stopbutton)
self.stopbutton.clicked.connect(self.Stop)
#Volume slider
self.hbuttonbox.addStretch(1)
self.volumeslider = qtw.QSlider(qtc.Qt.Horizontal, self)
self.volumeslider.setMaximum(100)
self.volumeslider.setValue(self.mediaplayer.audio_get_volume())
self.volumeslider.setToolTip("Volume")
self.hbuttonbox.addWidget(self.volumeslider)
self.volumeslider.valueChanged.connect(self.setVolume)
notebook.addWidget(self.videoframe)
notebook.addWidget(self.positionslider)
notebook.addLayout(self.hbuttonbox)
#Actions Code
open1 = qtw.QAction("&Open", self)
open1.triggered.connect(self.OpenFile)
exit = qtw.QAction("&Exit", self)
exit.triggered.connect(sys.exit)
menubar = self.menuBar()
filemenu = menubar.addMenu("&File")
filemenu.addAction(open1)
filemenu.addSeparator()
filemenu.addAction(exit)
self.timer = qtc.QTimer(self)
self.timer.setInterval(200)
self.timer.timeout.connect(self.updateUI)
def PlayPause(self):
"""Toggle play/pause status
"""
if self.mediaplayer.is_playing():
self.mediaplayer.pause()
self.playbutton.setText("Play")
self.isPaused = True
else:
if self.mediaplayer.play() == -1:
self.OpenFile()
return
self.mediaplayer.play()
self.playbutton.setText("Pause")
self.timer.start()
self.isPaused = False
def PausePlay(self):
if self.mediaplayer.is_playing():
self.mediaplayer.pause()
self.playbutton.setText("Play")
self.isPaused = True
def Stop(self):
"""Stop player
"""
self.mediaplayer.stop()
self.playbutton.setText("Play")
def OpenFile(self, filename=None):
"""Open a media file in a MediaPlayer
"""
if filename is None or filename is False:
print("Attempt to open up OpenFile")
filenameraw = qtw.QFileDialog.getOpenFileName(self, "Open File", os.path.expanduser('~'))
filename = filenameraw[0]
if not filename:
return
# create the media
if sys.version < '3':
filename = unicode(filename)
self.media = self.instance.media_new(filename)
# put the media in the media player
self.mediaplayer.set_media(self.media)
# parse the metadata of the file
self.media.parse()
# set the title of the track as window title
self.setWindowTitle(self.media.get_meta(0))
# print(vlc.libvlc_media_get_meta(self.media, 6))
# print(vlc.libvlc_media_get_duration(self.media))
# the media player has to be 'connected' to the QFrame
# (otherwise a video would be displayed in it's own window)
# this is platform specific!
# you have to give the id of the QFrame (or similar object) to
# vlc, different platforms have different functions for this
if sys.platform.startswith('linux'): # for Linux using the X Server
self.mediaplayer.set_xwindow(self.videoframe.winId())
elif sys.platform == "win32": # for Windows
self.mediaplayer.set_hwnd(self.videoframe.winId())
elif sys.platform == "darwin": # for MacOS
self.mediaplayer.set_nsobject(int(self.videoframe.winId()))
self.PlayPause()
def setVolume(self, Volume):
"""Set the volume """
self.mediaplayer.audio_set_volume(Volume)
def setPosition(self, position):
"""Set the position
"""
# setting the position to where the slider was dragged
self.mediaplayer.set_position(position / 100000.0)
# the vlc MediaPlayer needs a float value between 0 and 1, Qt
# uses integer variables, so you need a factor; the higher the
# factor, the more precise are the results
# (1000 should be enough)
def updateUI(self):
"""updates the user interface"""
# setting the slider to the desired position
self.positionslider.setValue(self.mediaplayer.get_position() * 100000.0)
if not self.mediaplayer.is_playing():
# no need to call this function if nothing is played
self.timer.stop()
if not self.isPaused:
# after the video finished, the play button stills shows
# "Pause", not the desired behavior of a media player
# this will fix it
self.Stop()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv) #it's required to save a referance to MainWindow
mw = MainWindow()
mw.show()
if sys.argv[1:]:
mw.OpenFile(sys.argv[1])
sys.exit(app.exec_())
#if it goes out of scope ,it will be destroyed

How do I draw on a Qlabel with QPainter when multithreading with QThreadpool?

Please excuse me if my description isn't perfect, I'm still pretty new at PyQt and also Python in general. If you have recommendations on how to improve the question, please let me know.
I'm trying to draw on a Pixmap-QLabel, which is part of a QMainWindow, with QPainter. The QPainter is called in a loop, because the drawing is updated after a fixed duration. Drawing on the Pixmap works as intended, the problem I have is that the label always opens in a new window, instead of being placed on the QLabel inside the original QMainWindow.
I suspect that the reason for that is that I'm calling the QPainter from a Worker-class-object which is created by the QThreadpool-object. If I call the QPainter from inside the initialization of the GUI, the Pixmap-label is created as part of the QMainWindow as intended. Unfortunately the multithreading is necessary so the GUI stays responsive while the QLabel is updating.
The GUI itself is created with QtCreator, and simply loaded into the script.
Here's my code:
import os
import sys
import time
from PyQt5 import QtWidgets, QtCore, uic
from PyQt5.QtWidgets import QLabel, QPushButton, QMainWindow
from PyQt5.QtGui import QPixmap, QPainter, QPen, QPaintEvent
from PyQt5.QtCore import *
class Ui(QMainWindow):
def __init__(self):
super(Ui, self).__init__()
self.counter = 0
# load ui which can be designed with Qt Creator
uic.loadUi("ui/paintEvent_Loop.ui", self)
# find the QLabel where the picture should be placed
self.pixmap_label = self.findChild(QtWidgets.QLabel, "pixmap_label")
# creating the pixmap-label here works as intended
'''self.draw_label = PixmapLabel(self.pixmap_label)
self.draw_label.setGeometry(130, 50, 911, 512)
self.draw_label.show()'''
self.label = self.findChild(QLabel, "label")
# find the button with the name "cancel_button"
self.cancel_button = self.findChild(QtWidgets.QPushButton, "cancel_button")
self.cancel_button.clicked.connect(self.close_application)
# find the start_button button
self.start_button = self.findChild(QtWidgets.QPushButton, "start_button")
self.start_button.clicked.connect(self.start_loop)
self.pause_cont_button = self.findChild(QPushButton, "pause_cont_button")
self.pause_cont_button.clicked.connect(self.toggle_pause_continue)
self.pause_cont_button.hide()
# create the QThreadPool object to manage multiple threads
self.threadpool = QThreadPool()
print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
self.run_loop = True
# show application
self.show()
def close_application(self):
app.quit()
def toggle_pause_continue(self):
"""
changes the value of boolean run_loop to pause and continue the loop through the samples in the chosen scene
:return:
"""
if self.run_loop:
self.run_loop = False
else:
self.run_loop = True
def start_loop(self):
# hide start_button and show pause_cont_button
self.start_button.hide()
self.pause_cont_button.show()
self.pause_cont_button.setCheckable(True)
# start one further thread managed by threadpool
worker = Worker()
self.threadpool.start(worker)
class PixmapLabel(QLabel):
def __init__(self, parent=None):
super(PixmapLabel, self).__init__(parent=parent)
def paintEvent(self, a0: QPaintEvent) -> None:
# initiate QPainter instance
painter = QPainter(window.draw_label)
# open image
picture = QPixmap(os.getcwd() + '/test-image.png')
myPicturePixmap = picture.scaled(self.size(), QtCore.Qt.KeepAspectRatio)
self.setPixmap(myPicturePixmap)
# draw red box on it
painter.drawPixmap(self.rect(), myPicturePixmap)
pen = QPen(Qt.red, 3)
painter.setPen(pen)
painter.drawRect(10, 10, 100, 100)
class Worker(QRunnable):
# worker thread
def __init__(self):
super().__init__()
#pyqtSlot()
def run(self):
print("Thread start")
for self.i in range(0, 50):
# create pixmap_label with drawings
# FIXME: make pixmap-label part of original GUI
window.draw_label = PixmapLabel(window.pixmap_label)
window.draw_label.setGeometry(130, 50, 911, 512)
window.draw_label.show()
window.label.setText(str(self.i))
while window.run_loop == False:
time.sleep(0.05)
# show image for 0.5 seconds, then update image
time.sleep(0.5)
window.draw_label.destroy()
time.sleep(0.05)
# print in terminal to know that we are finished
print("Thread complete")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
The image I'm using:

Categories