I'm having a hard time finding a way to reference class instances in a decorator function.
import json
import time
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from main_UI import Ui_ApplicationWindow
from slack import RTMClient
class WorkerThread(QThread):
finished = pyqtSignal(str)
def __init__(self):
QThread.__init__(self)
self.rtm_client = RTMClient(token="xoxp...")
def run(self):
self.rtm_client.start()
#RTMClient.run_on(event="message")
def say_hello(**payload):
data = payload['data']
if (len(data) != 0):
if "text" in data:
text = data['text']
self.finished.emit(str(text))
class ApplicationWindow(QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_ApplicationWindow()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.start_rtm)
def start_rtm(self):
self.thread = WorkerThread()
self.thread.finished.connect(self.update)
self.thread.start()
#pyqtSlot(str)
def update(self, data):
self.ui.label.setText(data)
if __name__ == "__main__":
app = QApplication(sys.argv)
myWindow = ApplicationWindow()
myWindow.show()
app.exec_()
So in say_hello since it can't take self as an argument, I'm not able to use self.finished.emit(text) at the end of the function.
How can I reference a class instance/function using self in say_hello?
No, You can not. Instead of using the #RTMClient.run_on() decorator, use the RTMClient.on() function to register it.
import threading
import asyncio
from slack import RTMClient
from PyQt5 import QtCore, QtWidgets
class SlackClient(QtCore.QObject):
textChanged = QtCore.pyqtSignal(str)
def start(self):
RTMClient.on(event="message", callback=self.say_hello)
threading.Thread(target=self._start_loop, daemon=True).start()
def _start_loop(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
slack_token = "xoxb-...."
rtm_client = RTMClient(token=slack_token)
rtm_client.start()
def say_hello(self, **payload):
data = payload["data"]
if data:
if "text" in data:
text = data["text"]
self.textChanged.emit(text)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
client = SlackClient()
button = QtWidgets.QPushButton("Start")
textedit = QtWidgets.QPlainTextEdit()
button.clicked.connect(client.start)
client.textChanged.connect(textedit.appendPlainText)
w = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(w)
lay.addWidget(button)
lay.addWidget(textedit)
w.show()
sys.exit(app.exec_())
Update:
import sys
import threading
import asyncio
from slack import RTMClient
from PyQt5 import QtCore, QtWidgets
from main_UI import Ui_ApplicationWindow
class SlackClient(QtCore.QObject):
textChanged = QtCore.pyqtSignal(str)
def start(self):
RTMClient.on(event="message", callback=self.say_hello)
threading.Thread(target=self._start_loop, daemon=True).start()
def _start_loop(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
slack_token = "xoxb-...."
rtm_client = RTMClient(token=slack_token)
rtm_client.start()
def say_hello(self, **payload):
data = payload["data"]
if data:
if "text" in data:
text = data["text"]
self.textChanged.emit(text)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_ApplicationWindow()
self.ui.setupUi(self)
self.client = SlackClient()
# connections
self.ui.pushButton.clicked.connect(self.client.start)
self.client.textChanged.connect(self.ui.label.setText)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
myWindow = ApplicationWindow()
myWindow.show()
sys.exit(app.exec_())
Actually you cannot self since that is a global variable, not a class one.
from slack import RTMClient
class WorkerThread(QThread):
finished = pyqtSignal(dict)
def __init__(self):
QThread.__init__(self)
self.rtm_client = RTMClient(token="xoxp-....")
def run(self):
self.rtm_client.start()
#RTMClient.run_on(event="message")
def say_hello(**payload):
data = payload['data']
if (len(data) != 0):
if "text" in data:
text = data['text']
WorkerThread.finished.emit(text) <--- using self impossible
I suggest that you make such variable private by appending two underscores at the beginning (__my_private_var)
Related
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import time
import sys
import numpy as np
class Mainthread(QThread):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.running = None
self.mutex = QMutex()
def run(self):
while self.running:
self.mutex.lock()
print ("test")
time.sleep(1)
self.mutex.unlock()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.mainthread = Mainthread(self)
self.mainthread.running = True
self.mainthread.start()
self.mainthread1 = Mainthread(self)
self.mainthread1.running = True
self.mainthread1.start()
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
app.exec_()
I have this code where I run two instances of the same MainThread class.
What I was expecting was that mainthread's message (which is "test") would print, then wait for a sec and then mainthread1's would be printed. Instead, it seems like both threads are running at the same time. Is there something I'm missing?
In your code, each thread creates its own separate mutex, so no relation is enforced between the two. Create a single mutex first, and pass it to the theads:
import time
import sys
from PyQt5.QtCore import QThread, QMutex
from PyQt5.QtWidgets import QMainWindow, QApplication
class Mainthread(QThread):
def __init__(self, mutex, parent):
super().__init__(parent)
self.parent = parent
self.running = None
self.mutex = mutex
def run(self):
while self.running:
self.mutex.lock()
print ("test")
time.sleep(1)
self.mutex.unlock()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
mutex = QMutex()
self.mainthread = Mainthread(mutex, self)
self.mainthread.running = True
self.mainthread.start()
self.mainthread1 = Mainthread(mutex, self)
self.mainthread1.running = True
self.mainthread1.start()
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
app.exec_()
Note: I don't have PyQt5 installed (and doing so on my architecture is tricky), but I tested this in PySide6 and as far as I know the behavior should be consistent.
when clicking a button in the PyQt5 Ui iam starting a mitmproxy. When the proxy is started, i try to change a listWidget with Data from the Proxy.
main.py
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from MainWindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.pushButton.clicked.connect(self.Start)
def Start(self):
x = threading.Thread(target=self.StartMITM)
x.start()
def StartMITM(self):
os.system("mitmweb -s mitmproxy.py -q --no-web-open-browser")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
mitmproxy.py - this is the error part
from mitmproxy import http
from main import MainWindow
def response(flow):
MainWindow.listWidget.addItem(flow.request.pretty_url)
Can I connect to the Widgets from another File?
It has 2 independent processes: The GUI and the mitmproxy script. And communicating both processes does not imply importing modules since you would be creating another widget, also objects should not be accessed through classes (I recommend you check your basic python notes).
In this the solution is to use some Inter process communication (IPC), in this case you can use Qt Remote Objects (QtRO):
main.py
from functools import cached_property
from PyQt5 import QtCore, QtRemoteObjects, QtWidgets
class Bridge(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
#QtCore.pyqtSlot(str)
def add_message(self, message):
self.messageChanged.emit(message)
class MitmwebManager(QtCore.QObject):
logChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
# self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self.process.readyReadStandardOutput.connect(self.handle_log)
self.process.setProgram("mitmweb")
#cached_property
def process(self):
return QtCore.QProcess()
def start(self, arguments):
self.process.setArguments(arguments)
self.process.start()
def stop(self):
self.process.kill()
def handle_log(self):
data = self.process.readAllStandardOutput()
codec = QtCore.QTextCodec.codecForName("UTF-8")
message = codec.toUnicode(data)
self.logChanged.emit(message)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.button = QtWidgets.QPushButton("Start", checkable=True)
self.listwidget = QtWidgets.QListWidget()
self.logview = QtWidgets.QPlainTextEdit(readOnly=True)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QGridLayout(central_widget)
lay.addWidget(self.button, 0, 0, 1, 2)
lay.addWidget(self.listwidget, 1, 0)
lay.addWidget(self.logview, 1, 1)
self.register_node = QtRemoteObjects.QRemoteObjectRegistryHost(
QtCore.QUrl("local:registry")
)
self.source_node = QtRemoteObjects.QRemoteObjectHost(
QtCore.QUrl("local:replica"), QtCore.QUrl("local:registry")
)
self.source_node.enableRemoting(self.bridge, "bridge")
self.button.toggled.connect(self.handle_toggled)
self.mitmweb.logChanged.connect(self.handle_log_changed)
self.bridge.messageChanged.connect(self.handle_message_changed)
self.resize(640, 480)
#cached_property
def mitmweb(self):
return MitmwebManager()
#cached_property
def bridge(self):
return Bridge()
def handle_toggled(self, checked):
if checked:
self.mitmweb.start(["-s", "script.py", "--no-web-open-browser"])
self.button.setText("Stop")
else:
self.mitmweb.stop()
self.button.setText("Start")
def handle_log_changed(self, message):
self.logview.insertPlainText(message)
def closeEvent(self, event):
super().closeEvent(event)
self.mitmweb.stop()
def handle_message_changed(self, message):
item = QtWidgets.QListWidgetItem(message)
self.listwidget.addItem(item)
def main():
app = QtWidgets.QApplication([])
view = MainWindow()
view.show()
app.exec_()
if __name__ == "__main__":
main()
script.py
from mitmproxy import http
from PyQt5 import QtCore, QtRemoteObjects
class Bridge(QtCore.QObject):
def __init__(self, bridge, parent=None):
super().__init__(parent)
self._message = ""
self._bridge = bridge
self.bridge.initialized.connect(self.handle_initialized)
#property
def bridge(self):
return self._bridge
#property
def message(self):
return self._message
def send_message(self, message):
self._message = message
#QtCore.pyqtSlot()
def handle_initialized(self):
self.bridge.add_message(self._message)
QtCore.QTimer.singleShot(0, QtCore.QCoreApplication.quit)
def send_qt(message):
qt_app = QtCore.QCoreApplication([])
replica_node = QtRemoteObjects.QRemoteObjectNode(QtCore.QUrl("local:registry"))
replica_bridge = replica_node.acquireDynamic("bridge")
bridge = Bridge(replica_bridge)
bridge.send_message(message)
qt_app.exec_()
def response(flow):
send_qt(flow.request.pretty_url)
I want to capture image from camera.I got a way to do it.But it works on PyQt5 successfully.failed on PySide2.
Here's the PySide2 code
import sys
from PySide2.QtWidgets import QWidget, QApplication
from PySide2.QtMultimedia import QCamera, QAbstractVideoSurface, QVideoSurfaceFormat, QVideoFrame
class CameraFrameGrabber(QAbstractVideoSurface):
def __init__(self, parent=None):
super(CameraFrameGrabber, self).__init__(parent)
print('init')
def supportedPixelFormats(self, type):
print("supportedPixelFormats() called")
print("supportedPixelFormats() finished")
return [QVideoFrame.Format_ARGB32, QVideoFrame.Format_ARGB32_Premultiplied,
QVideoFrame.Format_RGB32, QVideoFrame.Format_RGB24, QVideoFrame.Format_RGB565,
QVideoFrame.Format_RGB555, QVideoFrame.Format_ARGB8565_Premultiplied,
QVideoFrame.Format_BGRA32, QVideoFrame.Format_BGRA32_Premultiplied, QVideoFrame.Format_BGR32,
QVideoFrame.Format_BGR24, QVideoFrame.Format_BGR565, QVideoFrame.Format_BGR555,
QVideoFrame.Format_BGRA5658_Premultiplied, QVideoFrame.Format_AYUV444,
QVideoFrame.Format_AYUV444_Premultiplied, QVideoFrame.Format_YUV444,
QVideoFrame.Format_YUV420P, QVideoFrame.Format_YV12, QVideoFrame.Format_UYVY,
QVideoFrame.Format_YUYV, QVideoFrame.Format_NV12, QVideoFrame.Format_NV21,
QVideoFrame.Format_IMC1, QVideoFrame.Format_IMC2, QVideoFrame.Format_IMC3,
QVideoFrame.Format_IMC4, QVideoFrame.Format_Y8, QVideoFrame.Format_Y16,
QVideoFrame.Format_Jpeg, QVideoFrame.Format_CameraRaw, QVideoFrame.Format_AdobeDng]
# def isFormatSupported(self, format):
# print("isFormatSupported() called")
# def start(self, format):
# print("start() called")
def present(self, frame):
print('call present')
print(frame)
return True
# def stop(self):
# print("stop() called")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.show()
w.setWindowTitle("Hello PyQt5")
the_camera = QCamera()
the_grabber = CameraFrameGrabber()
the_camera.setViewfinder(the_grabber)
the_camera.start()
sys.exit(app.exec_())
Here's the PyQt5 code
import sys
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtMultimedia import QCamera, QAbstractVideoSurface, QVideoSurfaceFormat, QVideoFrame
class CameraFrameGrabber(QAbstractVideoSurface):
def __init__(self, parent=None):
super(CameraFrameGrabber, self).__init__(parent)
print('init')
def supportedPixelFormats(self, type):
print("supportedPixelFormats() called")
print("supportedPixelFormats() finished")
return [QVideoFrame.Format_ARGB32, QVideoFrame.Format_ARGB32_Premultiplied,
QVideoFrame.Format_RGB32, QVideoFrame.Format_RGB24, QVideoFrame.Format_RGB565,
QVideoFrame.Format_RGB555, QVideoFrame.Format_ARGB8565_Premultiplied,
QVideoFrame.Format_BGRA32, QVideoFrame.Format_BGRA32_Premultiplied, QVideoFrame.Format_BGR32,
QVideoFrame.Format_BGR24, QVideoFrame.Format_BGR565, QVideoFrame.Format_BGR555,
QVideoFrame.Format_BGRA5658_Premultiplied, QVideoFrame.Format_AYUV444,
QVideoFrame.Format_AYUV444_Premultiplied, QVideoFrame.Format_YUV444,
QVideoFrame.Format_YUV420P, QVideoFrame.Format_YV12, QVideoFrame.Format_UYVY,
QVideoFrame.Format_YUYV, QVideoFrame.Format_NV12, QVideoFrame.Format_NV21,
QVideoFrame.Format_IMC1, QVideoFrame.Format_IMC2, QVideoFrame.Format_IMC3,
QVideoFrame.Format_IMC4, QVideoFrame.Format_Y8, QVideoFrame.Format_Y16,
QVideoFrame.Format_Jpeg, QVideoFrame.Format_CameraRaw, QVideoFrame.Format_AdobeDng]
# def isFormatSupported(self, format):
# print("isFormatSupported() called")
# def start(self, format):
# print("start() called")
def present(self, frame):
print('call present')
print(frame)
return True
# def stop(self):
# print("stop() called")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.show()
w.setWindowTitle("Hello PyQt5")
the_camera = QCamera()
the_grabber = CameraFrameGrabber()
the_camera.setViewfinder(the_grabber)
the_camera.start()
sys.exit(app.exec_())
Both code run successfully.
present function called in PyQt5, not called in PySide2.
Is there any difference between the usage of PySide2 and PyQt5.
Who can tell me why it seem like this?Thanks.
I'm trying to change style of button during action.
When button is pressed, I want to make them blue, then read rfid card and then by the ID change color of button to green or red (in code is just red to make it easier).
The problem is that changing style of button to blue do nothing (green and red works fine).
It is waiting to finish 'clicked()' method?
How to tell to PyQt5 "Do it now!" ?
Edit: I modified the pseudocode to reproducible Example.
#!/usr/bin/python3
import sys
import time
from PyQt5.QtWidgets import *
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black") # set default style
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
uid = self.readCard() # reading ID from rfid card
self.changeButtonColor("red")
def readCard(self):
#return rfid.read() # in real case
time.sleep(2)
return "12345678"
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
This static function calls a slot after a given time interval.
It is very convenient to use this function because you do not need to bother with a timerEvent or create a local QTimer object.
import sys
import time
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black")
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
# time.sleep(2) # reading ID from rfid card
# self.changeButtonColor("red")
QtCore.QTimer.singleShot(2000, lambda: self.changeButtonColor("red")) # <---
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
Update
import sys
import time
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
class Worker(QtCore.QObject): # +++
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
data = QtCore.pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.running = False
self.stop = 5
#QtCore.pyqtSlot()
def read_data_from_sensor(self):
self.started.emit()
time.sleep(1) # We simulate the blocking process
while self.running and self.stop:
dt = time.strftime("%Y-%m-%d %H:%M:%S")
self.data.emit(dt)
time.sleep(1) # We simulate the blocking process
self.stop -= 1
self.finished.emit()
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT Start")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black")
self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
self.label_data.setText('Pending')
self.grid = QGridLayout(self)
self.grid.addWidget(self.label_data)
self.grid.addWidget(self.button)
self.show()
### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self._worker = Worker()
self._worker.started.connect(self.on_started)
self._worker.finished.connect(self.on_finished)
self._worker.data.connect(self.update_label)
self._thread = QtCore.QThread(self)
self._thread.start()
self._worker.moveToThread(self._thread)
#QtCore.pyqtSlot()
def on_started(self):
self.label_data.setText("Start to read")
self.button.setText("TEXT Stop")
self.button.setEnabled(True)
self._worker.stop = 5
#QtCore.pyqtSlot()
def on_finished(self):
self.label_data.setText("Pending")
self.button.setText("TEXT Start")
self.button.setEnabled(True)
self.changeButtonColor("red") # <---
self._worker.running = False
self._worker.stop = 5
#QtCore.pyqtSlot(str)
def update_label(self, data):
self.label_data.setText(data)
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
# time.sleep(2) # reading ID from rfid card
# self.changeButtonColor("red")
# QtCore.QTimer.singleShot(2000, lambda: self.changeButtonColor("red")) # <---
### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
if self._worker.running:
self._worker.running = False
else:
self._worker.running = True
QtCore.QTimer.singleShot(0, self._worker.read_data_from_sensor)
self.button.setEnabled(False)
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
install quamash and qtawesome
#!/usr/bin/python3
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QSize
import asyncio
from quamash import QEventLoop
import qtawesome as qt
class MainScreen(QWidget):
oop = None
def __init__(self, _loop):
try:
QWidget.__init__(self)
self.loop = _loop
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.synced_click)
self.button.setStyleSheet("color: black;")
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
except Exception as e:
print(e)
def synced_click(self):
try:
asyncio.ensure_future(self.clicked(), loop=self.loop)
except Exception as e:
print('error')
print(e)
#asyncio.coroutine
async def clicked(self):
try:
spin_icon = qt.icon('fa5s.spinner', color='red', animation=qt.Spin(self.button))
self.loop.call_soon_threadsafe(self.button.setIconSize, QSize(12, 12))
self.loop.call_soon_threadsafe(self.button.setIcon, spin_icon)
self.loop.call_soon_threadsafe(self.button.setText, "progress")
self.loop.call_soon_threadsafe(self.button.setStyleSheet, "color: red;")
await asyncio.sleep(3)
tick_icon = qt.icon('fa5s.check-circle', color='blue')
self.loop.call_soon_threadsafe(self.button.setStyleSheet, "color:blue;")
self.loop.call_soon_threadsafe(self.button.setText, "done")
self.loop.call_soon_threadsafe(self.button.setIcon, tick_icon)
await asyncio.sleep(1)
except Exception as e:
print('error')
print(e)
if __name__ == "__main__":
import sys
loop = QEventLoop(QApplication(sys.argv))
asyncio.set_event_loop(loop=loop)
with loop:
window = MainScreen(loop)
loop.run_forever()
This is my first time to ask question, if there are something I get wrong, please tell me, I will be appreciate.
I am using QWebEngineUrlSchemeHandler for a custom url, and I want to use QFile to open a javascript file for testing.
First, if I just use
QFile("ken.js")
The window could open the javascript, but if I use my custom QFile,
I have no idea how to process the data after I read from QIODevice.read().
I want to know what I need to do to make the window could open the javascript after I read the data from QIODevice.read().
Please give me some suggests, thank.
Here is my full code.
class TestQFile(QtCore.QFile):
def __init__(self, fileName):
super().__init__()
self.setFileName(fileName)
self.open(self.ReadOnly)
self.data = b''
while True:
receivedData = self.read(10)
self.data += receivedData
if receivedData == b'':
break
class TestHandler(QWebEngineUrlSchemeHandler):
def requestStarted(self, request):
self._dev = TestQFile("ken.js")
request.reply(b'text/javascript', self._dev)
class TestWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._view = QWebEngineView(self)
self._handler = TestHandler() # Must keep ref
self._view.page().profile().installUrlSchemeHandler(b'myuri', self._handler)
self._view.setHtml('<html><head><title>Test</title></head><body><div id="d1"></div><script src="myuri://test/ken.js"></script></body></html>')
self.setCentralWidget(self._view)
self.show()
self.raise_()
if __name__ == '__main__':
app = QtWidgets.QApplication([])
win = TestWindow()
app.exec_()
Actually if the class inherits from QFile you do not have to do anything since it already has implemented the methods that QWebEngineUrlRequestJob requires since it will use the methods that every class that inherits from QIODevice must implement as readData(), writeData(), atEnd(), etc.
from PyQt5 import QtCore, QtWidgets, QtWebEngineCore,QtWebEngineWidgets
class TestQFile(QtCore.QFile):
def __init__(self, fileName):
super().__init__()
self.setFileName(fileName)
class TestHandler(QtWebEngineCore.QWebEngineUrlSchemeHandler):
def requestStarted(self, request):
self.file = TestQFile("ken.js")
request.reply(b'text/javascript', self.file)
class TestWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._view = QtWebEngineWidgets.QWebEngineView(self)
self._handler = TestHandler() # Must keep ref
self._view.page().profile().installUrlSchemeHandler(b'myuri', self._handler)
self._view.setHtml('<html><head><title>Test</title></head><body><div id="d1"></div><script src="myuri://test/ken.js"></script></body></html>')
self.setCentralWidget(self._view)
self.show()
self.raise_()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
win = TestWindow()
sys.exit(app.exec_())