QClipboard::dataChanged() was not emitted in QtWebEngine context with pyqt6 or pyside6, but it works fine with qt5/pyqt5, how to solve it ?
Environment:
Python: 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)]
Flavor: Unknown
Executable: C:\Users\Oscar\AppData\Local\Programs\Python\Python39\python.exe
OS: Windows
Arch: x86_64
WindowsRelease: 10
>>> from PyQt6.QtCore import *
>>> QT_VERSION_STR
'6.2.3'
>>> PYQT_VERSION_STR
'6.2.3'
>>>
from PyQt6.QtCore import *
from PyQt6.QtCore import pyqtSlot as Slot
from PyQt6.QtCore import pyqtSignal as Signal
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
from PyQt6.QtWebEngineWidgets import *
from PyQt6.QtWebEngineCore import *
import sys
class WebEngineView(QWebEngineView): #
def __init__(self, parent=None):
super().__init__(parent)
self.page().settings().setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard, True)
self.load(QUrl('https://doc.qt.io/qt-6/qwebenginesettings.html'))
self.clipboard = QGuiApplication.clipboard()
self.clipboard.dataChanged.connect(self.clipboardTextChanged)
#Slot()
def clipboardTextChanged(self):
self.copiedText = self.clipboard.text()
print(self.sender(), self.copiedText)
if __name__ == "__main__":
app = QApplication(sys.argv)
webEngineView = WebEngineView()
webEngineView.show()
sys.exit(app.exec())
I find the solution here
The sandbox somehow blocks the WM_CLIPBOARDUPDATE message (
https://learn.microsoft.com/en-us/windows/win32/dataxchg/wm-clipboardupdate
) and QPA can't notify about clipboard changes.
The sandbox on windows is enabled since
https://codereview.qt-project.org/c/qt/qtwebengine/+/276060
neosettler, as a workaround you can disable the sandbox by the
--no-sandbox command line argument or by setting the QTWEBENGINE_DISABLE_SANDBOX environment variable.
Then I changed my code to:
from PyQt6.QtCore import *
from PyQt6.QtCore import pyqtSlot as Slot
from PyQt6.QtCore import pyqtSignal as Signal
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
from PyQt6.QtWebEngineWidgets import *
from PyQt6.QtWebEngineCore import *
import sys
import os
class WebEngineView(QWebEngineView): #
def __init__(self, parent=None):
super().__init__(parent)
self.page().settings().setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard, True)
self.load(QUrl('https://doc.qt.io/qt-6/qwebenginesettings.html'))
self.clipboard = QGuiApplication.clipboard()
self.clipboard.dataChanged.connect(self.clipboardTextChanged)
#Slot()
def clipboardTextChanged(self):
self.copiedText = self.clipboard.text()
print(self.copiedText)
if __name__ == "__main__":
sys.argv.append('--no-sandbox')
print(sys.argv)
app = QApplication(sys.argv)
webEngineView = WebEngineView()
webEngineView.show()
sys.exit(app.exec())
Related
I created a Windows .exe file from a python script using PyInstaller but when I launch the executable I get the error: AttributeError: 'ApplicationLogin' object has no attribute 'img_location'. However, running the same script from the terminal it works without issues.
Below is the piece of the code where the error is detected. The python version I used is 3.9.10
import os
from PyQt5.QtWidgets import QWidget, QDialog, QMessageBox
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QPixmap
from preventivo_config_file import Preventivo_Config
from password_edit import PasswordEdit
from gestione_utenti import UserMng
from menu_preventivo import PreventivoMenu
from preventivo_login_dialogUi import Ui_f_login_dialog
class ApplicationLogin(QDialog, Ui_f_login_dialog):
closed = pyqtSignal()
login = pyqtSignal()
def __init__(self):
super(ApplicationLogin, self).__init__()
self.read_configFile()
self.setup_login()
def read_configFile(self):
prev_config=Preventivo_Config("preventivo_config")
parametri=prev_config.preventivo_config_records()
if not parametri:
return
self.img_location=parametri['images']
self.icons_location=parametri['icons']
def setup_login(self):
self.setupUi(self)
pixmap = self.caricamento_immagini("mx1.png")
I tried to look on the web for similar issue but I didn't find any hint
Running this code:
from PySide6 import QtWidgets as qtw
from PySide6 import QtMultimedia as qtm
from PySide6 import QtMultimediaWidgets as qtmw
from PySide6 import QtCore as qtc
app = qtw.QApplication()
path = "video.mp4"
video_widget = qtmw.QVideoWidget()
video_widget.show()
media_player = qtm.QMediaPlayer()
media_player.setVideoOutput(video_widget)
media_player.mediaStatusChanged.connect(media_player.play)
media_player.setSource(path)
qtc.QTimer.singleShot(2000, lambda: media_player.setSource(''))
app.exec()
, you will notice that when media_player.setSource('') is called after 2000 milliseconds, the GUI becomes unresponsive for a good second or so. And it seems like it is not possible to move only the setSource() call to a separate thread, I get a warning saying QObject::killTimer: Timers cannot be stopped from another thread (you can replicate this by replacing qtc.QTimer... call with qtc.QThreadPool.globalInstance().start(lambda: media_player.setSource(''))). So, after hours of pondering, as a work around, I moved the entire QMediaPlayer instance and all objects related to it to a thread of its own and used QVideoSink's videoFrameChanged signal to update the video on the video widget:
from PySide6 import QtWidgets as qtw
from PySide6 import QtMultimedia as qtm
from PySide6 import QtMultimediaWidgets as qtmw
from PySide6 import QtCore as qtc
path = "video.mp4"
class VideoPlayer(qtmw.QVideoWidget):
_stop_worker_signal = qtc.Signal()
_initialize_media_player_signal = qtc.Signal()
_set_media_source_signal = qtc.Signal(str)
_play_media_signal = qtc.Signal()
_stop_media_signal = qtc.Signal()
_pause_media_signal = qtc.Signal()
def __init__(self) -> None:
super().__init__()
self._worker = Worker()
self._worker_thread = qtc.QThread()
self._worker.moveToThread(self._worker_thread)
self._stop_worker_signal.connect(self._worker.stop)
self._initialize_media_player_signal.connect(self._worker.create_media_player)
self._worker.video_sink_frame_changed_signal.connect(lambda frame: self.videoSink().setVideoFrame(frame))
self._set_media_source_signal.connect(self._worker.set_media_source)
self._stop_media_signal.connect(self._worker.stop_media)
self._worker_thread.start()
self._initialize_media_player_signal.emit()
self._set_media_source_signal.emit(path)
self.show()
def keyPressEvent(self, event):
if event.key() == qtc.Qt.Key.Key_Q:
self._set_media_source_signal.emit('')
def closeEvent(self, event: qtc.QEvent):
loop = qtc.QEventLoop()
self._worker.stopped_signal.connect(loop.exit)
self._stop_worker_signal.emit()
loop.exec()
self._worker_thread.exit()
class Worker(qtc.QObject):
video_sink_frame_changed_signal = qtc.Signal(qtm.QVideoFrame)
stopped_signal = qtc.Signal()
def create_media_player(self):
self._audio_output = qtm.QAudioOutput(qtm.QMediaDevices.defaultAudioOutput())
self._video_sink = qtm.QVideoSink()
self._video_sink.videoFrameChanged.connect(self.video_sink_frame_changed_signal)
self._media_player = qtm.QMediaPlayer()
self._media_player.setAudioOutput(self._audio_output)
self._media_player.setVideoSink(self._video_sink)
self._media_player.mediaStatusChanged.connect(self._media_player.play)
def set_media_source(self, source: str) -> None:
self._media_player.setSource(source)
def stop_media(self) -> None:
if not self._media_player.playbackState() == qtm.QMediaPlayer.PlaybackState.StoppedState:
self._media_player.stop()
def stop(self):
self._media_player.stop()
self._audio_output.deleteLater()
self._video_sink.deleteLater()
self._media_player.deleteLater()
self.stopped_signal.emit()
app = qtw.QApplication()
vp = VideoPlayer()
app.exec()
Now, there's no unresponsiveness in the GUI when setSource() is called but, as one can see, it's a pretty convoluted way to do this. Is there a better way?
from PySide6.__feature__ import snake_case, true_property
from PySide6.QtWidgets import QMainWindow, QWidget, QHBoxLayout, QApplication
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.main_layout = QHBoxLayout()
self.container = QWidget()
self.container.set_layout(self.main_layout)
if __name__ == '__main__':
app = QApplication([])
main_window = MainWindow()
main_window.show()
app.exec()
I got an error message when trying to run the above code:
ModuleNotFoundError: No module named 'PySide6.__feature__'
If I break the first line into
import PySide6
from __feature__ import snake_case, true_property
the code could run but PyCharm complains that
Unresolved reference: __feature__
How could I fix it?
Thanks for your help.
PyQt 5.6.2, 5.9.1, Anaconda, vscode, Windows 10 x64.
Setting QtCore.Qt.WindowStaysOnTopHint in super().__init__() or via self.setWindowFlags() does nothing. With Qt 4.8.7 (PyQt 4.11.4) the flag works as expected.
I can fix this using Windows API (HWND_TOPMOST flag):
import sys
try:
from PyQt5 import QtCore, QtWidgets
except ImportError:
from PyQt4 import QtCore, QtGui as QtWidgets
class Form1(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.pushButton1 = QtWidgets.QPushButton("Push", self)
self.pushButton1.clicked.connect(self.clicked)
self.ontop = False
def clicked(self):
self.ontop = not self.ontop
self.windowStaysOnTopHint(self.ontop)
def windowStaysOnTopHint(self, b=True):
try:
import win32gui, win32con
flag = win32con.HWND_TOPMOST if b else win32con.HWND_NOTOPMOST
win32gui.SetWindowPos(self.winId(), flag, 0, 0, 0, 0, win32con.SWP_NOSIZE | win32con.SWP_NOMOVE)
except ModuleNotFoundError:
pass
if b:
flag = self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint
else:
flag = self.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint
self.setGeometry(self.geometry()) # `setWindowFlags` resets size if setGeometry is never called
self.setWindowFlags(flag)
self.show()
print(QtCore.QT_VERSION_STR)
app = QtWidgets.QApplication(sys.argv)
form1 = Form1()
form1.show()
app.exec_()
I can use win32gui.SetWindowPos and remove setWindowFlags block at all. But I really want to know what's the reason of this behaviour.
I tried to establish a network connection with PySide (Ubuntu 15.04, Python3.4, PySide 1.2.4). I used the example code from the documentation.
The QNetworkAccessManager does not send the request and I recieve no answer. I checked the Network state with QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()).State() but it says the State is invalid. This seems to make no sense since I am on a desktop pc with a network connection via ethernet cable.
My complete example for the test is the following code:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PySide.QtGui import QApplication
from PySide.QtCore import QUrl
from PySide.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkSession, QNetworkConfigurationManager
def replyFinished(reply):
print(reply)
if __name__ == "__main__":
app = QApplication(sys.argv)
manager = QNetworkAccessManager()
manager.finished.connect(replyFinished)
print(QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()).State())
print("Sending request")
print(manager.get(QNetworkRequest(QUrl("http://www.heise.de/ct/"))))
This prints
PySide.QtNetwork.QNetworkSession.State.Invalid
Sending request
<PySide.QtNetwork.QNetworkReply object at 0x7f4b59c9af08>
but it should display the PySide.QtNetwork.QNetworkReply object twice.
My example was too small. I realized this because of the comment of Pavel Strakhov. I extended it to display a window with a button. By clicking the button it connects successfully. QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()).State() is still invalid but it works.
This is the working code:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PySide.QtGui import QApplication, QWidget, QBoxLayout, QPushButton
from PySide.QtCore import QUrl
from PySide.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkSession, QNetworkConfigurationManager
class Window(QWidget):
def __init__(self):
super().__init__()
self.manager = QNetworkAccessManager()
self.manager.finished.connect(self.reply_finished)
layout = QBoxLayout(QBoxLayout.TopToBottom)
button = QPushButton("connect")
button.clicked.connect(self.network_connect)
layout.addWidget(button)
self.setLayout(layout)
self.setWindowTitle("Network test")
self.setGeometry(100, 100, 200, 150)
self.show()
def network_connect(self):
print(QNetworkSession(QNetworkConfigurationManager().defaultConfiguration()).State())
request = QNetworkRequest(QUrl("http://example.org"))
print("Sending request")
self.manager.get(request)
def reply_finished(self, reply):
print(reply)
print(reply.readData(500))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
app.exec_()