threading a class with arguments pyqt5 [duplicate] - python

This question already has an answer here:
msgbox error while threading , GUI blocks [closed]
(1 answer)
Closed 2 years ago.
In the following code I've tried to thread the calculation class that needs x as an argument, the thread works perfectly when there's no x. but once i put an argument the things missed up, can i know why?
from PyQt5 import QtCore, QtGui, QtWidgets
from mainwindow2 import Ui_Form
import time
class Calculation(QtCore.QThread):
def __init__(self,x):
super().__init__()
self.x = x
def run(self):
for i in range(10):
if i!=5:
time.sleep(1)
print(i+self.x)
class MainWindow(QtWidgets.QMainWindow, Ui_Form):
def __init__(self):
super().__init__()
self.setupUi(self)
self.pushButton.pressed.connect(self.threadingc)
def threadingc(self):
# create the thread with the main window as a parent, this is possible
# since QMainWindow also inherits from QObject, and this also ensures
# that python will not delete it if you want to start another thread
self.pushButton.setEnabled(False)
thread = Calculation(5)
thread.start()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
mainwindow2
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(150, 110, 93, 28))
self.pushButton.setObjectName("pushButton")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.pushButton.setText(_translate("Form", "PushButton"))

You need to provide the parent variable to the thread.
from PyQt5 import QtCore, QtGui, QtWidgets
import time
class Calculation(QtCore.QThread):
def __init__(self, parent, x):
# I have added the parent variable.
super().__init__(parent)
self.x = x
def run(self):
for i in range(10):
if i != 5:
time.sleep(1)
print(i + self.x)
class MainWindow(QtWidgets.QMainWindow, Ui_Form):
...
def threadingc(self):
# You need to provide a reference to the gui or make the thread a class variable.
thread = Calculation(self, 5)
thread.start()
# If you don't provide a parent or make the thread a class variable the function scope will
# end here and the thread variable will be terminated.
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())

Related

Pyqt 5 Application crashes without Error Message when changing current Widget of QStackedWidget

I'm currently writing an PYQT5 GUI Application, where it is possible to switch between multiple widgets in the same Main Window. I'm using a QStackedWidget for that, where I've added all the other Widgets. For switching between them, I just set the current Widget of the QStackedWidget. The QStackedWidget is the central Widget of the Main Window.
One of the Widgets available in the QStackedWidget has a QListWidget, where I can add multiple custom objects.
The Problem now is, if i add more than one object to the list and then try to change the current Widget of the QStackedWidget, the Application crashes with no error Message.
I'm using Pyqt 5 in Python 3.9.13.
Also I've written a code example, in which the described problem occures.
I would really appreciated, if somebody could help me!
Reproduce the Error:
1.) Press the pushButton --> a new View shows up
2.) Add one Item to the List with the Button at the Bottom
3.) Press the pushButton at the Top --> FirstView shows up, everything works fine
4.) Press again the pushButton in the Center
5.) Add further Objects to the List
6.) Press the Button at the Top again --> the Application crashes with no error...
My example Code to reproduce the Error:
App
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import *
class App(QApplication):
def __init__(self, argv):
super().__init__(argv)
self.setApplicationName("Test")
self.setOrganizationName("Kai Industries")
Controller View 1
from PyQt5 import *
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from Ui_view1 import Ui_View1
from controller_view2 import Controller_View2
class Controller_View1(QWidget, Ui_View1):
def __init__(self, main, model,previous):
super().__init__(main)
QWidget.__init__(self)
Ui_View1.__init__(self)
self.setupUi(self)
self._main = main
self._model = model
self._previous = previous
self._main._controller_view2=Controller_View2(self._main,self._model,self)
self._main._central_widget.addWidget(self._main._controller_view2)
self.pushButton_forward.clicked.connect(self.execute_forward)
def execute_forward(self):
self._main._central_widget.setCurrentWidget(self._main._controller_view2)
Controller View 2
from PyQt5 import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from Ui_view2 import Ui_View2
from submodel import Submodel
from controller_widget_submodel import Controller_Widget_Submodel
class Controller_View2(QWidget, Ui_View2):
def __init__(self, main, model,previous):
super().__init__(main)
QWidget.__init__(self)
Ui_View2.__init__(self)
self.setupUi(self)
self._main = main
self._model = model
self._previous = previous
self.pushButton_add.clicked.connect(self.execute_add)
self.pushButton_back.clicked.connect(self.execute_back)
def execute_add(self):
submodels = self._model.get_submodels()
submodel=Submodel()
submodel.set_value1=1
submodel.set_value2=2
submodels.insert(len(submodels),submodel)
self.actualize_list()
def execute_back(self):
self._main._central_widget.setCurrentWidget(self._previous)
def actualize_list(self):
self.listWidget.clear()
submodels = self._model.get_submodels()
i=0
for submodel in submodels:
list_widget_item = QListWidgetItem(self.listWidget)
controller_widget_submodel = Controller_Widget_Submodel(self._main,submodel)
list_widget_item.setSizeHint(controller_widget_submodel.sizeHint())
self.listWidget.insertItem(i,list_widget_item)
self.listWidget.setItemWidget(list_widget_item,controller_widget_submodel)
i=i+1
Controller widget for ListWidgetItem
from PyQt5 import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from Ui_widget_submodel import Ui_widget_submodel
class Controller_Widget_Submodel(QWidget, Ui_widget_submodel):
def __init__(self, main, model):
super().__init__(main)
QWidget.__init__(self)
Ui_widget_submodel.__init__(self)
self.setupUi(self)
self._main = main
self._model = model
self.label_value1.setText(self._model.get_value1())
self.label_value2.setText(self._model.get_value2())
Controller Main Window
from PyQt5 import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from controller_view1 import Controller_View1
from model import Model
class Controller_Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.showFullScreen()
self.setFixedSize(QSize(800,480))
self._model=Model()
self._central_widget = QStackedWidget()
self._controller_view1 = Controller_View1(self,self._model,self)
self._central_widget.addWidget(self._controller_view1)
self._central_widget.setCurrentWidget(self._controller_view1)
self.setCentralWidget(self._central_widget)
self.show()
Main
import sys
from PyQt5.QtCore import *
from app import *
from controller_window import Controller_Main
import cgitb
cgitb.enable(format = 'text')
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
if __name__ == "__main__":
sys.excepthook = lambda exctype, value, traceback: sys.__excepthook__(exctype, value, traceback)
app = App(sys.argv)
#if frozen: _MEIPASS path and every error as messagebox
try:
controller_main = Controller_Main()
sys.exit(app.exec())
except Exception as e:
QMessageBox.critical(None , "Error", "An Error occured: " + str(e))
Model
from submodel import Submodel
class Model():
def __init__(self):
self._submodels = list()
def add_submodel(self,submodel:Submodel):
self._submodels.append(submodel)
def get_submodels(self):
return self._submodels
Submodel
class Submodel():
def __init__(self):
self._value1="Test1"
self._value2="Test2"
def set_value1(self,value:int):
self._value1 = value
def set_value2(self,value:int):
self._value2 = value
def get_value2(self):
return self._value2
def get_value1(self):
return self._value1
Ui View 1
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_View1(object):
def setupUi(self, View1):
View1.setObjectName("View1")
View1.resize(400, 300)
self.verticalLayout = QtWidgets.QVBoxLayout(View1)
self.verticalLayout.setObjectName("verticalLayout")
self.pushButton_forward = QtWidgets.QPushButton(View1)
self.pushButton_forward.setObjectName("pushButton_forward")
self.verticalLayout.addWidget(self.pushButton_forward)
self.retranslateUi(View1)
QtCore.QMetaObject.connectSlotsByName(View1)
def retranslateUi(self, View1):
_translate = QtCore.QCoreApplication.translate
View1.setWindowTitle(_translate("View1", "Form"))
self.pushButton_forward.setText(_translate("View1", "PushButton"))
Ui View 2
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_View2(object):
def setupUi(self, View2):
View2.setObjectName("View2")
View2.resize(400, 300)
self.verticalLayout = QtWidgets.QVBoxLayout(View2)
self.verticalLayout.setObjectName("verticalLayout")
self.pushButton_back = QtWidgets.QPushButton(View2)
self.pushButton_back.setObjectName("pushButton_back")
self.verticalLayout.addWidget(self.pushButton_back)
self.listWidget = QtWidgets.QListWidget(View2)
self.listWidget.setObjectName("listWidget")
self.verticalLayout.addWidget(self.listWidget)
self.pushButton_add = QtWidgets.QPushButton(View2)
self.pushButton_add.setObjectName("pushButton_add")
self.verticalLayout.addWidget(self.pushButton_add)
self.retranslateUi(View2)
QtCore.QMetaObject.connectSlotsByName(View2)
def retranslateUi(self, View2):
_translate = QtCore.QCoreApplication.translate
View2.setWindowTitle(_translate("View2", "Form"))
self.pushButton_back.setText(_translate("View2", "Back"))
self.pushButton_add.setText(_translate("View2", "Add"))
Ui submodel
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_widget_submodel(object):
def setupUi(self, widget_submodel):
widget_submodel.setObjectName("widget_submodel")
widget_submodel.resize(120, 33)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(widget_submodel)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_value1 = QtWidgets.QLabel(widget_submodel)
self.label_value1.setObjectName("label_value1")
self.horizontalLayout.addWidget(self.label_value1)
self.label_value2 = QtWidgets.QLabel(widget_submodel)
self.label_value2.setObjectName("label_value2")
self.horizontalLayout.addWidget(self.label_value2)
self.horizontalLayout_2.addLayout(self.horizontalLayout)
self.retranslateUi(widget_submodel)
QtCore.QMetaObject.connectSlotsByName(widget_submodel)
def retranslateUi(self, widget_submodel):
_translate = QtCore.QCoreApplication.translate
widget_submodel.setWindowTitle(_translate("widget_submodel", "Form"))
self.label_value1.setText(_translate("widget_submodel", "TextLabel"))
self.label_value2.setText(_translate("widget_submodel", "TextLabel"))
Ui Main Window
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

How can I filter events for objects in a different thread? [duplicate]

I have a GUI made in Designer (pyqt5). A function in my main class needs to work on a separate thread. I also catch the stdout on a QtextEdit LIVE during operations. Everything so far works.
Right now I'm trying to implement a ProgressBar onto my main GUI form. The bar needs to show live progression just like it does on the textEdit.
The example code below works on Linux without any warnings. But on Windows I get the error:
QObject::setParent: Cannot set parent, new parent is in a different thread
I know that this is due to me having a ui element modification within my threaded function. I did my research but all the answers point to using QThreads (just when I started to understand basic threading!). I would prefer a way to update my GUI without having to change the current threading system below.
Here is the example code:
import sys
import threading
import time
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtGui import QTextCursor
from ui_form import Ui_Form
class EmittingStream(QObject):
textWritten = pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class Form(QMainWindow):
finished = pyqtSignal()
def __init__(self, parent=None):
super(Form, self).__init__(parent)
# Install the custom output stream
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
self.ui = Ui_Form()
self.ui.setupUi(self)
self.ui.pushButton_run.clicked.connect(self.start_task)
self.finished.connect(self.end_task)
def start_task(self):
self.thread = threading.Thread(target=self.run_test)
self.thread.start()
self.ui.pushButton_run.setEnabled(False)
def end_task(self):
self.ui.pushButton_run.setEnabled(True)
def __del__(self):
# Restore sys.stdout
sys.stdout = sys.__stdout__
def normalOutputWritten(self, text):
"""Append text to the QTextEdit."""
cursor = self.ui.textEdit.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.insertText(text)
self.ui.textEdit.setTextCursor(cursor)
self.ui.textEdit.ensureCursorVisible()
def run_test(self):
for i in range(100):
per = i + 1
self.ui.progressBar.setValue(per)
print("%%%s" % per)
time.sleep(0.15) # simulating expensive task
print("Task Completed!")
time.sleep(1.5)
self.ui.progressBar.reset()
self.finished.emit()
def main():
app = QApplication(sys.argv)
form = Form()
form.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
the ui:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'form.ui'
#
# Created: Mon Apr 30 13:43:19 2018
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(Form)
self.centralwidget.setObjectName("centralwidget")
self.pushButton_run = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_run.setGeometry(QtCore.QRect(40, 20, 311, 191))
self.pushButton_run.setObjectName("pushButton_run")
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setGeometry(QtCore.QRect(40, 230, 721, 241))
self.textEdit.setObjectName("textEdit")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(40, 490, 721, 23))
self.progressBar.setObjectName("progressBar")
Form.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(Form)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
self.menubar.setObjectName("menubar")
Form.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(Form)
self.statusbar.setObjectName("statusbar")
Form.setStatusBar(self.statusbar)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "MainWindow"))
self.pushButton_run.setText(_translate("Form", "RUN"))
Somehow I need to -instantly- inform the gui thread (from my running thread) that the progress bar value is changing (a process that could take up minutes to complete).
Define a custom signal that sends updates to the progress-bar:
class Form(QMainWindow):
finished = pyqtSignal()
updateProgress = pyqtSignal(int)
def __init__(self, parent=None):
super(Form, self).__init__(parent)
...
self.updateProgress.connect(self.ui.progressBar.setValue)
def run_test(self):
for i in range(100):
per = i + 1
self.updateProgress.emit(per)
...

updating a label value on the GUI which gets updated value from temperature sensor all the time

my project is to design a GUI window to get Temperature from temp sensor and display it on a label widget on the GUI. i was able to display the value but it's not updating on the label
please help. i was looking about how to emit signal in pyqt5 and connect it to the label widget
from PyQt5 import QtCore, QtGui, QtWidgets
from w1thermsensor import W1ThermSensor
from PyQt5.QtCore import Qt, QThread, pyqtSignal, pyqtSlot
sensor = (W1ThermSensor())
class Ui_Form(object):
def setupUi(self, parent=None):
Form.setObjectName("Form")
Form.resize(400, 300)
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(60, 40, 141, 71))
self.label.setObjectName("label")
self.label.setNum(int(sensor.get_temperature()))
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
self.threadclass = ThreadClass()
self.threadclass.start()
self.signals(self)
def signals(self):
self.threadclass.connect(self.updateLabel)
self.threadclass.connect(pyqtSignal(val))
self.pyqtSignal(val).connect(self.updateLabel)
def updateLabel(self,val):
val = int(sensor.get_temperature())
self.label.setNum(val)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
class ThreadClass(QtCore.QThread):
change_value = pyqtSignal(int)
def setupUi(self, parent=None):
super(ThreadClass, self).__init__(parent)
def run(self):
while 1:
val = int(sensor.get_temperature())
self.change_value.emit(val)
print (val)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
ui.signals()
Form.show()
sys.exit(app.exec_())
There are various problems with your code:
you are calling self.signals(self), but signals does not accept arguments;
connections are between signals and slots, but you're trying to connect to the threadclass instance, which is neither a signal nor a slot, but a QThread object;
self.threadclass.connect(pyqtSignal(val)) does not make much sense: pyqtSignal is a function used when a class is constructed, and can only be used as such; also, val is never declared in the scope of signals();
self.pyqtSignal(val).connect(self.updateLabel) doesn't work either: self is the Ui_Form instance, and has no pyqtSignal attribute; again, no val is declared;
the python files generated with pyuic should never be edited, but used as imported modules only and all program implementation should happen in another class, not in the "ui" objects it creates (read more about using Designer);
Recreate the GUI if you don't have it anymore, and generate again the ui with pyuic. Then try to do something like this (supposing you've created a file named ui_form.py) in a separate file:
from PyQt5 import QtCore, QtWidgets
from ui_form import Ui_Form
class MyTest(QtWidgets.QWidget, Ui_Form):
def __init__(self, parent=None):
super(MyTest, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.threadclass.change_value.connect(self.updateLabel)
self.threadclass.start()
def updateLabel(self, value):
self.label.setNum(value)
class ThreadClass(QtCore.QThread):
change_value = pyqtSignal(int)
def setupUi(self, parent=None):
super(ThreadClass, self).__init__(parent)
def run(self):
while 1:
val = int(sensor.get_temperature())
# I suppose
self.change_value.emit(val)
print (val)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
test = MyTest
test.show()
sys.exit(app.exec_())

How to fix attribute Error for module in PyQt5 [duplicate]

This question already has answers here:
PyQt4 to PyQt5 how?
(1 answer)
PyQt5 failing import of QtGui
(1 answer)
Closed 4 years ago.
Hi :) please can someone assist me. I have to do an assignment where i add a LCDNumber widget to the dialog in Qtdesigner, convery the ui file to py and then create a separate script to import the code to invoke the UI design and display, it also must include a timer to keep updating the LCD display.
This is the Error i get
Traceback (most recent call last):
File "C:\PythonPrograms\showtime.pyw", line 4, in <module>
class MyForm(QtGui.QDialog):
AttributeError: 'module' object has no attribute 'QDialog'
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.lcdNumber = QtWidgets.QLCDNumber(Dialog)
self.lcdNumber.setGeometry(QtCore.QRect(70, 20, 241, 151))
self.lcdNumber.setObjectName("lcdNumber")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
Next is file showtime.pyw which is suppose to import the code from disptime.py to invoke the UI design and display
import sys
from disptime import *
class MyForm(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.__init__(self, parent)
self.ui =Ui_Dialog()
self.ui.setupUi(self)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.showlcd)
timer.start(1000)
self.showlcd()
def showlcd(self):
time = QtCore.QTime.currentTime()
text = time.toString('hh:mm')
self.ui.lcdNumber.display(text)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
Try it:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets # +++
from disptime import Ui_Dialog # * <-> Ui_Dialog
#class MyForm(QtGui.QDialog): # ---
# def __init__(self, parent=None): # +++
# QtGui.__init__(self, parent) # ---
class MyForm(QtWidgets.QDialog): # +++
def __init__(self): # +++
super().__init__() # +++
self.ui = Ui_Dialog()
self.ui.setupUi(self)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.showlcd)
timer.start(1000)
self.showlcd()
def showlcd(self):
time = QtCore.QTime.currentTime()
text = time.toString('hh:mm')
self.ui.lcdNumber.display(text)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())

SEGV_MAPERR for PyQt5 object

Why does the following demo code produce a SEGV_MAPERR? How would one fix it?
Hint: Once one deletes the line annotated with "# Ref1", no error is produced. This is not easily done in the production code the problem is abstracted from.
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebChannel
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.webView = QtWebEngineWidgets.QWebEngineView(self.centralwidget)
self.webView.setHtml("")
self.gridLayout.addWidget(self.webView, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
page = self.webView.page() # REF1
for i in range(2):
self.init_page()
def init_page(self):
class EditObject(QtCore.QObject):
#QtCore.pyqtSlot(str)
def edit(self, s):
print("test")
editObject = EditObject(self.webView.page())
poChannel = self.webView.page().webChannel()
print(1)
if poChannel is None:
poChannel = QtWebChannel.QWebChannel()
self.webView.page().setWebChannel(poChannel)
print(2)
objects = poChannel.registeredObjects()
print(objects)
poChannel.registerObject("editObject", editObject)
self.webView.setHtml("")
from PyQt5 import QtWebEngineWidgets
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
This is similar to Issues with PyQt5==5.10 on Ubuntu 18, but with example code. For the example code, https://gist.github.com/gioditalia/03c9fd5d793aeccbe065fea45d842431 is adapted.
The problem is that poChannel is a local variable that will be deleted after executing init_page, so in the second iteration the poChannel reference will point to an incorrect memory address. So the solution is to extend its cycle to that of the view, so we take advantage of the Qt property and pass it as a parent to self.webView.
poChannel = QtWebChannel.QWebChannel(self.webView)
Although as PyQt points out in the docs and the generated file: # WARNING! All changes made in this file will be lost!, it is not convenient to modify the class generated by the .ui, instead you must create another class that inherits from the appropriate widget and use the interface provided by Qt Designer.
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets, QtWebChannel
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.webView = QtWebEngineWidgets.QWebEngineView(self.centralwidget)
self.gridLayout.addWidget(self.webView, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
class EditObject(QtCore.QObject):
#QtCore.pyqtSlot(str)
def edit(self, s):
print("test")
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
for i in range(2):
self.init_page()
def init_page(self):
editObject = EditObject(self.webView.page())
poChannel = self.webView.page().webChannel()
if poChannel is None:
poChannel = QtWebChannel.QWebChannel(self)
self.webView.page().setWebChannel(poChannel)
objects = poChannel.registeredObjects()
poChannel.registerObject("editObject", editObject)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

Categories