QMediaPlayer state not stopped when status changed to end of media - python

I have a problem using QMediaPlayer.mediaStatusChanged.
According to Qt5.7 documentation, when media status is changed to EndOfMedia, the QMediaPlayer state should be StoppedState:
Playback has reached the end of the current media. The player is in the StoppedState.
Qt5.7
However, the state is not stopped. Here is a sample that reproduces the issue:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import QUrl
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
class MediaPlayer(QMediaPlayer):
default = 'test.mp3'
def __init__(self):
super(MediaPlayer, self).__init__()
self.mediaStatusChanged[QMediaPlayer.MediaStatus].connect(self.media_status_changed)
self.setup_media(self.default)
def setup_media(self, media):
url = QUrl.fromLocalFile(media)
self.setMedia(QMediaContent(url))
def media_status_changed(self, status):
if status == QMediaPlayer.EndOfMedia:
print(self.state() == QMediaPlayer.StoppedState) # I get False
# self.state() is QMediaPlayer.PlayingState
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
m = MediaPlayer()
m.play()
sys.exit(app.exec_())
Does anyone face the same problem?
I can fix the problem with a workaround but I think it may be a Qt problem.

I reported the issue to Qt as it seems to be a Windows only bug :
Possible workarounds to fix the problem :
Force stop before processing
def media_status_changed(self, status):
if status == QMediaPlayer.EndOfMedia:
super(MediaPlayer, self).stop()
# process
Poll until getting StoppedState
def media_status_changed(self, status):
if status == QMediaPlayer.EndOfMedia:
while not (self.state() == QMediaPlayer.StoppedState):
time.sleep(0.1)
# process
I will add here any update regarding the Qt issue.
EDIT: issue updated and fixed in Qt v5.10.1

Related

why mne raw.plot doesn't block in a Qt Application on macOS?

I'm a beginner to Pyside6 and mne-python. I want the raw.plot to block so I can interact with the figure. However, the program continue to run and print 'after window close' after the figure opened. According to mne documentation, https://mne.tools/stable/generated/mne.viz.plot_raw.html?highlight=plot_raw#mne.viz.plot_raw
I have set the parameter block to True, but it doesn't work.
import matplotlib
import mne, sys
from PySide6.QtWidgets import QMainWindow, QPushButton, QApplication
matplotlib.rcParams['font.sans-serif'] = ['Arial Unicode MS']
matplotlib.rcParams['axes.unicode_minus'] = False
matplotlib.use('qtAgg')
class Test(QMainWindow):
def __init__(self):
super(Test, self).__init__()
self.button = QPushButton('Plot raw')
self.button.clicked.connect(self.Show)
self.setCentralWidget(self.button)
def Show(self):
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = (sample_data_folder / 'MEG' / 'sample' /
'sample_audvis_filt-0-40_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file)
raw.plot(block=True)
print('after window close')
app = QApplication(sys.argv)
main = Test()
main.show()
app.exec()
sys.exit()
When the program is running, it shows some information like this:
CoreApplication::exec: The event loop is already running.
I search for it, but don't find a solution to my problem. Is this an waring or error and can anyone give me a solution?

How to debug PyQt5 threads in Visual Studio Code?

I am right now developing a PyQT5 application an use multithreading to avoid freezing of the GUI. Unfortuneately the Visual Studio Code debugger does not stop on breakpoints inside the executed thread. I tried all suggestions from the following page without fixing the problem. https://github.com/microsoft/ptvsd/issues/428. I think VS Code switched debugger from ptvsd to debugpy so all the suggestions do not hold any more. Maybe somebody has an idea how to fix this issue.
import time
import sys
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QTextEdit, QVBoxLayout, QWidget, QLabel
class Worker(QObject):
sig_msg = pyqtSignal(str) # message to be shown to user
def __init__(self):
super().__init__()
#pyqtSlot()
def work(self):
self.sig_msg.emit('Hello from inside the thread!')
result = 1 + 1
result2 = 1 + 2
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(400, 200)
self.button_start_threads = QPushButton("Start")
self.button_start_threads.clicked.connect(self.start_threads)
self.label = QLabel()
form_layout.addWidget(self.label)
form_layout.addWidget(self.button_start_threads)
QThread.currentThread().setObjectName('main')
self.__threads = None
def start_threads(self):
self.__threads = []
worker = Worker()
thread = QThread()
thread.setObjectName('thread')
self.__threads.append((thread, worker)) # need to store worker too otherwise will be gc'd
worker.moveToThread(thread)
worker.sig_msg.connect(self.label.setText)
thread.started.connect(worker.work)
thread.start()
if __name__ == "__main__":
app = QApplication([])
form = MyWidget()
form.show()
sys.exit(app.exec_())
You can reproduce the error by setting a breakpoint at self.sig_msg.emit('Hello from inside the thrad!') in my case the debugger does not stop at this position. I use VS Code Version 1.65.2. The code is taken from the post mentioned above.
I have recently experienced the same issue with VS Code and PyQt5. Following the advice at https://code.visualstudio.com/docs/python/debugging#_troubleshooting, I was able to hit the breakpoint in your example code by importing debugpy and adding debugpy.debug_this_thread() in the work method e.g.
def work(self):
debugpy.debug_this_thread()
self.sig_msg.emit('Hello from inside the thread!')
result = 1 + 1
result2 = 1 + 2
Hopefully that can help others facing the same issue.

how to restart the app with single process?

I want to restart my app, but I follow the tutorial and add systemtray icon. every time restart the app, the systemtray not disappear, I found the app not really restart by some reason.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MainWindow(QMainWindow):
EXIT_CODE_REBOOT = 122
def __init__(self):
super().__init__()
self.restart_button = QPushButton('restart')
self.restart_button.clicked.connect(self.onRestart)
self.setCentralWidget(self.restart_button)
self.systray = QSystemTrayIcon(self)
icon = self.style().standardIcon(QStyle.SP_TrashIcon)
self.systray.setIcon(icon)
self.systray.show()
def onRestart(self, checked):
QApplication.exit(self.EXIT_CODE_REBOOT)
if __name__ == '__main__':
currentExitCode = MainWindow.EXIT_CODE_REBOOT
while currentExitCode == MainWindow.EXIT_CODE_REBOOT:
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
currentExitCode = app.exec()
app = None
each time restart the app, the previous system tray always existing, it is like it start an other process, I want the only process and not consuming any resources.
I tried your code and it works fine on Linux, but I also found similar reports (like this) about persistent icon after quit on Windows.
While doing a self.systray.hide() before quitting should be fine enough, I think that deleting the object from Qt's side (not by using del) might be better:
def onRestart(self, checked):
self.systray.deleteLater()
QApplication.exit(self.EXIT_CODE_REBOOT)

Is my threading proper ? if yes then why code is not working?

I am creating an alarm clock in python using PyQt4 and in that I am using LCD display widget, which display current updating time. For that I am using threading. But I am new to it so the problem is I have no clue how to debug that thing.
This is my code
import sys
from PyQt4 import QtGui, uic
import time
import os
from threading import Thread
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('AlarmClock_UI.ui', self)
self.show()
self.comboBox.setCurrentIndex(0)
self.comboBox.currentIndexChanged.connect(self.getSelection)
self.lineEdit.setText('Please select the reminder type')
timeThread = Thread(target = self.showTime())
timeThread.start()
def getSelection(self):
if self.comboBox.currentIndex() == 1:
self.lineEdit.setText('Select the alarm time of your choice')
elif self.comboBox.currentIndex() == 2:
self.lineEdit.setText('Use those dials to adjust hour and minutes')
else:
self.lineEdit.setText('Please select the reminder type')
def showTime(self):
showTime = time.strftime('%H:%M:%S')
self.lcdNumber.display(showTime)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyWindow()
sys.exit(app.exec_())
I tried while loop in showTime() function then it was not even loading GUI just running in the background.
Thanks :)
As has been said elsewhere, you do not need to use threading for this, as a simple timer will do. Here is a basic demo script:
import sys
from PyQt4 import QtCore, QtGui
class Clock(QtGui.QLCDNumber):
def __init__(self):
super(Clock, self).__init__(8)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.showTime)
self.timer.start(1000)
self.showTime()
def showTime(self):
time = QtCore.QTime.currentTime()
self.display(time.toString('hh:mm:ss'))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Clock()
window.setWindowTitle('Clock')
window.setGeometry(500, 100, 400, 100)
window.show()
sys.exit(app.exec_())
Qt does not support doing GUI operations in threads other than the main thread. So when you call self.lcddisplay.display(showTime) from within the context of your spawned thread, that is an error and Qt will not work correctly.
As tdelaney suggested in his comment, the best way to handle this sort of thing is to use a QTimer to emit a signal at the appropriate intervals, and update your lcddisplay in the slot that signal is connected to.
(if you insist on using threads, however, e.g. as a learning exercise, then your spawned thread would need to send a message to the main thread to tell the main thread to do the display update, rather than trying to do the update itself)

Is there any solution for the QtWebKit memory leak?

Memory size of QtWebKit process increases with every new page load. Cleaning memory cache doesn't help. Does anyone know how to solve it?
This simple example crashes after some time of operation:
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebKitWidgets import QWebView
from PyQt5.QtWebKit import QWebSettings
class Crawler(QWebView):
def __init__(self):
QWebView.__init__(self)
self.settings().setMaximumPagesInCache(0)
self.settings().setObjectCacheCapacities(0, 0, 0)
self.settings().setOfflineStorageDefaultQuota(0)
self.settings().setOfflineWebApplicationCacheQuota(0)
self.settings().setAttribute(QWebSettings.AutoLoadImages, False)
self.loadFinished.connect(self._result_available)
def start(self):
self.load(QUrl('http://stackoverflow.com/'))
def _result_available(self, ok):
print('got it!')
self.settings().clearMemoryCaches() # it doesn't help
self.settings().clearIconDatabase()
self.start() # next try
if __name__ == '__main__':
app = QApplication([])
crawler = Crawler()
crawler.start()
app.exec_()
Reason of memory leak in disabled autoloading of images. It's a bug that will be fixed in next QT version. Removing this line solves the problem for example above:
self.settings().setAttribute(QWebSettings.AutoLoadImages, False)
Second possible reason which can lead to leaks is "Memory leak in GStreamer". It's in process.
Update:
I see people still looking for a solution. I've recently noticed bug with AutoLoadImages=False was not fixed in version Qt 5.2.1, nor in Qt 5.3 RC. New discussion about it has been opened. You can vote for this issue in bugtracker to increase the chances for fix in Qt 5.3.0

Categories

Resources