QtWebPageRenderer SIGILL问题 [英] QtWebPageRenderer SIGILL issue

查看:103
本文介绍了QtWebPageRenderer SIGILL问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题总结在标题中.当我在QtWebPageRenderer实例上调用方法setHtml时,将发出SIGILL信号,并且我的应用程序关闭.

My problem is summed in title. When I call method setHtml on instance of QtWebPageRenderer, SIGILL signal is emitted and my application goes down.

我知道此问题是由错误的Qt5动态库引起的,但我将其安装为:

I'm aware that this issue is caused by bad Qt5 dynamic library but I installed it with:

sudo pip install PyQt5 --only-binary PyQt5
sudo pip install PyQtWebEngine --only-binary PyQtWebEngine

所以我想我会得到正确的预编译库.当我尝试不使用--only-binary安装PyQt5时,总是以一些奇怪的编译错误结束.即使qmake之类的东西不在PATH中,我也可以从shell调用qmake.

so I thought I will get correct precompiled library. When I tried to install PyQt5 without --only-binary, I always ended with some strange compilation error. Something like qmake is not in PATH even though it is and I'm able to call qmake from shell.

所以我的问题是,如何使PyQt5在没有任何SIGILL的Fedora 31上运行.

So my question is, how to make PyQt5 running on Fedora 31 without any SIGILLs.

以下代码可以复制该问题.关于SIGILL的信息有点不准确,因为第一个信号实际上是SIGTRAP,在gdb中按continue后,我得到了SIGILL.这暗示了Qt实际上是想对我说些什么,尽管不是很直观.

Following code can replicate the issue. That information about SIGILL is little inaccurate because first signal is actually SIGTRAP, after I hit continue in gdb, I got SIGILL. This hints that Qt is actually trying to say something to me, although in not very intuitive way.

经过一番尝试之后,我发现没有线程就可以了.这是否意味着Qt强制用户使用QThread而不是python线程?还是这意味着我无法在运行事件循环的线程之外调用Qt对象的方法?

After some playing around with it, I found that without thread, its ok. Does this mean that Qt forces user to use QThread and not python threads? Or it means that I can't call methods of Qt objects outside of thread where event loop is running?

import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage


class WebView(QWebEnginePage):
   def __init__(self):
      QWebEnginePage.__init__(self)
      self.loadFinished.connect(self.on_load_finish)

   def print_result(self, data):
      print("-" * 30)
      print(data)
      with open("temp.html", "wb") as hndl:
         hndl.write(data.encode("utf-8"))

   def on_load_finish(self):
      self.toHtml(self.print_result)


class Runner(threading.Thread):
   def __init__(self, web_view):
      self.web_view = web_view
      threading.Thread.__init__(self)
      self.daemon = True

   def run(self):
      self.web_view.load(QtCore.QUrl("https://www.worldometers.info/coronavirus/"))


def main():
   signal.signal(signal.SIGINT, signal.SIG_DFL)
   app = QtWidgets.QApplication(sys.argv)
   web_view = WebView()
   runner = Runner(web_view)
   runner.start()
   app.exec_()


if __name__ == "__main__":
   main()

推荐答案

您必须有几个限制:

  • QObject不是线程安全,因此在主线程中创建"web_view"时,在辅助线程中对其进行修改是不安全的

  • A QObject is not thread-safe so when creating "web_view" in the main thread then it is not safe to modify it in the secondary thread

由于QWebEnginePage任务异步运行,因此您需要Qt事件循环.

Since the QWebEnginePage tasks run asynchronously then you need a Qt eventloop.

因此,如果要使用python的Thread类,则必须同时实现两个条件:

So if you want to use python's Thread class then you must implement both conditions:

import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage


class WebView(QWebEnginePage):
    def __init__(self):
        QWebEnginePage.__init__(self)
        self.loadFinished.connect(self.on_load_finish)

    def print_result(self, data):
        print("-" * 30)
        print(data)
        with open("temp.html", "wb") as hndl:
            hndl.write(data.encode("utf-8"))

    def on_load_finish(self):
        self.toHtml(self.print_result)


class Runner(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.daemon = True

    def run(self):
        # The QWebEnginePage was created in a new thread and 
        # that thread has an eventloop
        loop = QtCore.QEventLoop()
        web_view = WebView()
        web_view.load(QtCore.QUrl("https://www.worldometers.info/coronavirus/"))
        loop.exec_()


def main():
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    app = QtWidgets.QApplication(sys.argv)
    runner = Runner()
    runner.start()
    app.exec_()


if __name__ == "__main__":
    main()


实际上,QThreadthreading.Thread()是操作系统的本机线程处理程序,因此实际上,可以说QThread是运行事件循环的threading.Thread() + QObject在辅助线程上.


In reality QThread and threading.Thread() are native thread handlers of the OS, so in practical terms it can be said that QThread is a threading.Thread() + QObject with an eventloop running on the secondary thread.

另一方面,如果您的目标是从不属于该线程的线程中调用函数,则应使用

On the other hand, if your objective is to call a function from a thread to which it does not belong, then you should use asynchronous methods as pointed out in this answer.

在这种情况下,最简单的方法是使用pyqtSlot + QMetaObject:

In this case the simplest is to use pyqtSlot + QMetaObject:

import signal
import sys
import threading

from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage


class WebView(QWebEnginePage):
    def __init__(self):
        QWebEnginePage.__init__(self)
        self.loadFinished.connect(self.on_load_finish)

    def print_result(self, data):
        print("-" * 30)
        print(data)
        with open("temp.html", "wb") as hndl:
            hndl.write(data.encode("utf-8"))

    def on_load_finish(self):
        self.toHtml(self.print_result)

    @QtCore.pyqtSlot(QtCore.QUrl)
    def load(self, url):
        QWebEnginePage.load(self, url)


class Runner(threading.Thread):
    def __init__(self, web_view):
        self.web_view = web_view
        threading.Thread.__init__(self)
        self.daemon = True

    def run(self):
        url = QtCore.QUrl("https://www.worldometers.info/coronavirus/")
        QtCore.QMetaObject.invokeMethod(
            self.web_view,
            "load",
            QtCore.Qt.QueuedConnection,
            QtCore.Q_ARG(QtCore.QUrl, url),
        )


def main():
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    app = QtWidgets.QApplication(sys.argv)
    web_view = WebView()
    runner = Runner(web_view)
    runner.start()
    app.exec_()


if __name__ == "__main__":
    main()

或functools.partial()+ QTimer

Or functools.partial() + QTimer

from functools import partial
import signal
import sys
import threading

from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage


class WebView(QWebEnginePage):
    def __init__(self):
        QWebEnginePage.__init__(self)
        self.loadFinished.connect(self.on_load_finish)

    def print_result(self, data):
        print("-" * 30)
        print(data)
        with open("temp.html", "wb") as hndl:
            hndl.write(data.encode("utf-8"))

    def on_load_finish(self):
        self.toHtml(self.print_result)


class Runner(threading.Thread):
    def __init__(self, web_view):
        self.web_view = web_view
        threading.Thread.__init__(self)
        self.daemon = True

    def run(self):
        wrapper = partial(
            self.web_view.load,
            QtCore.QUrl("https://www.worldometers.info/coronavirus/"),
        )
        QtCore.QTimer.singleShot(0, wrapper)


def main():
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    app = QtWidgets.QApplication(sys.argv)
    web_view = WebView()
    runner = Runner(web_view)
    runner.start()
    app.exec_()


if __name__ == "__main__":
    main()

这篇关于QtWebPageRenderer SIGILL问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆