Repeatedly create/delete a PyQt6.QtWebEngineWidgets.QWebEngineView widget
-
I would like to be able to create a QWebEngineView widget, interact with it, close it, and later in the script, do it again. Inbetween these create/interact/close cycles, I would like to be able to run the event loop of another toolkit, say Gtk. So I need to completely discard the QApplication after closing the QWebEngineView widget (as an example, Matplotlib refuses to use the GtkAgg backend if a QApplication instance exists). I know this usecase sounds odd, but please refrain from making answers such as "what would anyone want to do this anyway", or "this is complete non-sense". I know that the normal way of developing a Qt app is to use QApplication.instance(), and to keep it until the end of the app lifetime; but I don't want to build a Qt app. I just want to use periodically the capabilities of QWebEngineView and the capabilities of some libraries potentially using another GUI toolkit in a non-graphical script or interactive session. So please regard my question as the technical challenge of making the following code work, if possible (see runnable example later on):
[... some preamble...] for _ in range(2): application = QApplication(sys.argv) widget = QWebEngineView() widget.setHtml([...some html content...]) del application
Note that the same code with a QLabel works (the 2 "widget" lines being replaced with widget = QLabel("Label")). Also note that, although playing with complex objects like QApplication and QWebEngineView, the loop body is conceptually simple: create object A, create object B that needs object A, delete object A (with the implicit, but clearly wrong with QWebEngineView, assumption that deleting object A will trigger the proper deletion of object B). Finally, I know the widget is not shown and the event loop is not run. I just wanted to write the shortest possible code for my question.
What I would greatly appreciate as an answer to this technical challenge is either "This is illegal with the Qt toolkit, and here is the Qt documentation page which explains why" or "Here is how to fix your code" (my preferred answer of course). Now the runnable example with comments about where the error occurs:import gc import sys from PyQt6.QtWebEngineWidgets import QWebEngineView as html_t from PyQt6.QtWidgets import QApplication as application_t from PyQt6.QtWidgets import QLabel as label_t html = """ <!DOCTYPE html> <html> <body> <p>Content.</p> </body> </html> """ def CreateAndDelete(widget_t, n_loops): print(f"--- Widget: {widget_t.__name__}, Loop(s): {n_loops}") for idx in range(n_loops): print(f" Loop {idx + 1} starts...") print(" Creating app") application = application_t(sys.argv) print(" Creating widget") if widget_t is label_t: widget = widget_t("Label") else: print(" Instantiation") widget = widget_t() print(" Content setting") widget.setHtml(html) # Crashes here on second loop with # error: Segmentation fault (core dumped). # When running: # gdb --args python <name_of_script>.py # then typing "run" at gdb prompt, the error is: # Thread 1 "python" received signal SIGSEGV, Segmentation fault. # 0x0000ffffe1ba5914 in QWebEngineProfile::settings() const () # from [...]/lib64/python3.13/site-packages/PyQt6/Qt6/lib/libQt6WebEngineCore.so.6 # (path of lib64 has been replaced with [...].) # Full console output at the end of the file. # "Make sure" application is "deleted" (quotes=whatever that means) # so that a new one can be instantiated right away if needed. print(" Deleting app") del application gc.collect() print(" Loop is done.") CreateAndDelete(label_t, 1) # Check that QApplication can be deleted. CreateAndDelete(label_t, 2) # Check that QApplication can be instantiated/deleted repeatedly. CreateAndDelete(html_t, 2) # Situation of interest: do the above with a QWebEngineView. """ Full console output: --- Widget: QWebEngineView, Loop(s): 2 Loop 1 starts... Creating app [New Thread 0xffffd473f180 (LWP 7957)] [New Thread 0xffffd4f4f180 (LWP 7958)] Creating widget Instantiation Content setting [New Thread 0xffffd15bb180 (LWP 7959)] [New Thread 0xffffb414f180 (LWP 7964)] [Thread 0xffffb414f180 (LWP 7964) exited] [New Thread 0xffffb414f180 (LWP 7966)] [Detaching after fork from child process 7967] [Detaching after fork from child process 7968] [Detaching after fork from child process 7969] [New Thread 0xffffb83a3180 (LWP 7972)] [New Thread 0xffffb7b93180 (LWP 7973)] [New Thread 0xffffb7383180 (LWP 7974)] [New Thread 0xffffb5d6f180 (LWP 7975)] [New Thread 0xffffb555f180 (LWP 7976)] [New Thread 0xffffb4d4f180 (LWP 7977)] [New Thread 0xffffb393f180 (LWP 7982)] [New Thread 0xffffb312f180 (LWP 7983)] [New Thread 0xffffb210f180 (LWP 7985)] [New Thread 0xffffb291f180 (LWP 7984)] [New Thread 0xffffb18ff180 (LWP 7986)] [New Thread 0xffffb10ef180 (LWP 7987)] [New Thread 0xffffb08df180 (LWP 7988)] [New Thread 0xffff7bfff180 (LWP 7989)] [New Thread 0xffff7b7ef180 (LWP 7992)] [New Thread 0xffff7afdf180 (LWP 7993)] Deleting app [New Thread 0xffff7a7cf180 (LWP 8004)] [New Thread 0xffff79fbf180 (LWP 8006)] [Thread 0xffff79fbf180 (LWP 8006) exited] [Thread 0xffffb10ef180 (LWP 7987) exited] [Thread 0xffffb210f180 (LWP 7985) exited] [Thread 0xffffb08df180 (LWP 7988) exited] [Thread 0xffffb4d4f180 (LWP 7977) exited] [Thread 0xffffb7b93180 (LWP 7973) exited] [Thread 0xffff7bfff180 (LWP 7989) exited] [Thread 0xffffd15bb180 (LWP 7959) exited] [Thread 0xffffd473f180 (LWP 7957) exited] [Thread 0xffffd4f4f180 (LWP 7958) exited] Loop is done. Loop 2 starts... Creating app [New Thread 0xffffd4f4f180 (LWP 8007)] [New Thread 0xffffd473f180 (LWP 8008)] Creating widget Instantiation Content setting Thread 1 "python" received signal SIGSEGV, Segmentation fault. 0x0000ffffe1ba5914 in QWebEngineProfile::settings() const () from [...]/lib64/python3.13/site-packages/PyQt6/Qt6/lib/libQt6WebEngineCore.so.6 """ """ Note that the following threads of the first loop did not exit before the start of the second loop: 7966 # created after "Content setting" 7972 # ... 7974 # ... 7975 # ... 7976 # ... 7982 # ... 7983 # ... 7984 # ... 7986 # ... 7992 # ... 7993 # created after "Content setting" 8004 # created after "Deleting app" """
-
I would like to be able to create a QWebEngineView widget, interact with it, close it, and later in the script, do it again. Inbetween these create/interact/close cycles, I would like to be able to run the event loop of another toolkit, say Gtk. So I need to completely discard the QApplication after closing the QWebEngineView widget (as an example, Matplotlib refuses to use the GtkAgg backend if a QApplication instance exists). I know this usecase sounds odd, but please refrain from making answers such as "what would anyone want to do this anyway", or "this is complete non-sense". I know that the normal way of developing a Qt app is to use QApplication.instance(), and to keep it until the end of the app lifetime; but I don't want to build a Qt app. I just want to use periodically the capabilities of QWebEngineView and the capabilities of some libraries potentially using another GUI toolkit in a non-graphical script or interactive session. So please regard my question as the technical challenge of making the following code work, if possible (see runnable example later on):
[... some preamble...] for _ in range(2): application = QApplication(sys.argv) widget = QWebEngineView() widget.setHtml([...some html content...]) del application
Note that the same code with a QLabel works (the 2 "widget" lines being replaced with widget = QLabel("Label")). Also note that, although playing with complex objects like QApplication and QWebEngineView, the loop body is conceptually simple: create object A, create object B that needs object A, delete object A (with the implicit, but clearly wrong with QWebEngineView, assumption that deleting object A will trigger the proper deletion of object B). Finally, I know the widget is not shown and the event loop is not run. I just wanted to write the shortest possible code for my question.
What I would greatly appreciate as an answer to this technical challenge is either "This is illegal with the Qt toolkit, and here is the Qt documentation page which explains why" or "Here is how to fix your code" (my preferred answer of course). Now the runnable example with comments about where the error occurs:import gc import sys from PyQt6.QtWebEngineWidgets import QWebEngineView as html_t from PyQt6.QtWidgets import QApplication as application_t from PyQt6.QtWidgets import QLabel as label_t html = """ <!DOCTYPE html> <html> <body> <p>Content.</p> </body> </html> """ def CreateAndDelete(widget_t, n_loops): print(f"--- Widget: {widget_t.__name__}, Loop(s): {n_loops}") for idx in range(n_loops): print(f" Loop {idx + 1} starts...") print(" Creating app") application = application_t(sys.argv) print(" Creating widget") if widget_t is label_t: widget = widget_t("Label") else: print(" Instantiation") widget = widget_t() print(" Content setting") widget.setHtml(html) # Crashes here on second loop with # error: Segmentation fault (core dumped). # When running: # gdb --args python <name_of_script>.py # then typing "run" at gdb prompt, the error is: # Thread 1 "python" received signal SIGSEGV, Segmentation fault. # 0x0000ffffe1ba5914 in QWebEngineProfile::settings() const () # from [...]/lib64/python3.13/site-packages/PyQt6/Qt6/lib/libQt6WebEngineCore.so.6 # (path of lib64 has been replaced with [...].) # Full console output at the end of the file. # "Make sure" application is "deleted" (quotes=whatever that means) # so that a new one can be instantiated right away if needed. print(" Deleting app") del application gc.collect() print(" Loop is done.") CreateAndDelete(label_t, 1) # Check that QApplication can be deleted. CreateAndDelete(label_t, 2) # Check that QApplication can be instantiated/deleted repeatedly. CreateAndDelete(html_t, 2) # Situation of interest: do the above with a QWebEngineView. """ Full console output: --- Widget: QWebEngineView, Loop(s): 2 Loop 1 starts... Creating app [New Thread 0xffffd473f180 (LWP 7957)] [New Thread 0xffffd4f4f180 (LWP 7958)] Creating widget Instantiation Content setting [New Thread 0xffffd15bb180 (LWP 7959)] [New Thread 0xffffb414f180 (LWP 7964)] [Thread 0xffffb414f180 (LWP 7964) exited] [New Thread 0xffffb414f180 (LWP 7966)] [Detaching after fork from child process 7967] [Detaching after fork from child process 7968] [Detaching after fork from child process 7969] [New Thread 0xffffb83a3180 (LWP 7972)] [New Thread 0xffffb7b93180 (LWP 7973)] [New Thread 0xffffb7383180 (LWP 7974)] [New Thread 0xffffb5d6f180 (LWP 7975)] [New Thread 0xffffb555f180 (LWP 7976)] [New Thread 0xffffb4d4f180 (LWP 7977)] [New Thread 0xffffb393f180 (LWP 7982)] [New Thread 0xffffb312f180 (LWP 7983)] [New Thread 0xffffb210f180 (LWP 7985)] [New Thread 0xffffb291f180 (LWP 7984)] [New Thread 0xffffb18ff180 (LWP 7986)] [New Thread 0xffffb10ef180 (LWP 7987)] [New Thread 0xffffb08df180 (LWP 7988)] [New Thread 0xffff7bfff180 (LWP 7989)] [New Thread 0xffff7b7ef180 (LWP 7992)] [New Thread 0xffff7afdf180 (LWP 7993)] Deleting app [New Thread 0xffff7a7cf180 (LWP 8004)] [New Thread 0xffff79fbf180 (LWP 8006)] [Thread 0xffff79fbf180 (LWP 8006) exited] [Thread 0xffffb10ef180 (LWP 7987) exited] [Thread 0xffffb210f180 (LWP 7985) exited] [Thread 0xffffb08df180 (LWP 7988) exited] [Thread 0xffffb4d4f180 (LWP 7977) exited] [Thread 0xffffb7b93180 (LWP 7973) exited] [Thread 0xffff7bfff180 (LWP 7989) exited] [Thread 0xffffd15bb180 (LWP 7959) exited] [Thread 0xffffd473f180 (LWP 7957) exited] [Thread 0xffffd4f4f180 (LWP 7958) exited] Loop is done. Loop 2 starts... Creating app [New Thread 0xffffd4f4f180 (LWP 8007)] [New Thread 0xffffd473f180 (LWP 8008)] Creating widget Instantiation Content setting Thread 1 "python" received signal SIGSEGV, Segmentation fault. 0x0000ffffe1ba5914 in QWebEngineProfile::settings() const () from [...]/lib64/python3.13/site-packages/PyQt6/Qt6/lib/libQt6WebEngineCore.so.6 """ """ Note that the following threads of the first loop did not exit before the start of the second loop: 7966 # created after "Content setting" 7972 # ... 7974 # ... 7975 # ... 7976 # ... 7982 # ... 7983 # ... 7984 # ... 7986 # ... 7992 # ... 7993 # created after "Content setting" 8004 # created after "Deleting app" """
Hi, and welcome
@Eric-DBV said in Repeatedly create/delete a PyQt6.QtWebEngineWidgets.QWebEngineView widget:
I need to completely discard the QApplication
Destroying and re-constructing QApplication is legal and supported in general...
...after closing the QWebEngineView widget
...but unfortunately, it is "not supported when Qt WebEngine is involved, due to a limitation in Chromium's design" (https://bugreports.qt.io/browse/QTBUG-128345 )
-
Hi, and welcome
@Eric-DBV said in Repeatedly create/delete a PyQt6.QtWebEngineWidgets.QWebEngineView widget:
I need to completely discard the QApplication
Destroying and re-constructing QApplication is legal and supported in general...
...after closing the QWebEngineView widget
...but unfortunately, it is "not supported when Qt WebEngine is involved, due to a limitation in Chromium's design" (https://bugreports.qt.io/browse/QTBUG-128345 )
-