1

我正在一个项目中使用修改后的QWebView. 我收到此错误:

Traceback (most recent call last):
  File "/home/jorge/coders/universal-scraper/src/customwebview.py", line 63, in mouseMoveEvent            
    hittestresult = self.currentframe.hitTestContent(event.pos())
RuntimeError: Internal C++ object (PySide.QtWebKit.QWebFrame) already deleted.

我已经阅读了有关PySide 陷阱的信息,并且我一直将该QtWebKit.QWebFrame对象保存为我修改后的属性,QWebView该方法setframeafterloadfinished使用页面完成加载时调用的方法,在我修改后的一些专业更改发生后引发了问题QWebView,之前那,一切都很顺利。

功能性和最小的示例在这里(只需确保webelementinfo.py在运行测试之前将文件放在与此代码相同的目录中):

#!/usr/bin/env python2
# coding: utf-8
#                        VENI, SANCTE SPIRITUS

from PySide.QtWebKit import QWebView
from PySide import QtCore, QtGui
try:
    from . import webelementinfo
except ValueError:
    import webelementinfo


class CustomQWebView(QWebView):

    def __init__(self, *args, **kwargs):
        """ Init the custom class
        """
        super(CustomQWebView, self).__init__(*args, **kwargs)
        self.colors = {0: QtGui.QColor(255, 165, 0, 127),
                       1: QtGui.QColor(135, 206, 235, 127),
                       2: QtGui.QColor(135, 235, 164, 127),
                       3: QtGui.QColor(235, 135, 206, 127),
                       4: QtGui.QColor(235, 164, 135, 127)}
        self.color = None
        self.currentframe = None
        self.element = None
        self.loadFinished.connect(self.setframeafterloadfinished)
        self.selectCommentsArea()

    @QtCore.Slot()
    def selectCommentsArea(self):
        """ For selecting the comment area
        """
        self.setup_rectcolor_area(0)

    @QtCore.Slot(QtGui.QMouseEvent)
    def mouseMoveEvent(self, event):
        super(CustomQWebView, self).mouseMoveEvent(event)

        if self.drawrects:
            if self.currentframe:
                hittestresult = self.currentframe.hitTestContent(event.pos())
                element = webelementinfo.WebElement(
                    hittestresult, self.color, self)
                if not self.element:
                    self.element = element
                elif self.element != element:
                    self.element = element

                # FIXME: self.update should draw rects from WebElements too.
                self.update()

    @QtCore.Slot(QtGui.QPaintEvent)
    def paintEvent(self, event):
        # draw the content first
        super(CustomQWebView, self).paintEvent(event)

        if self.drawrects:
            # then the rectangle
            if self.element:
                self.element.update()

    def setframeafterloadfinished(self):
        self.currentframe = self.page().mainFrame()

    def setup_rectcolor_area(self, forarea):
        """Called when we want to select certain area of a web site

        This method set-up the painter to a giving color so web elements are
        drawn with a rect on top. Also activates the flag to allow painting
        inside CustomQWebView.

        :param int forarea: For which area we are going to set the painter\\
        valid values are: 0 for Comments area, 1 for comment box, 2 for\\
        commentator's user name, 3 for comment date and time, 4 for\\
        commentary text.
        """
        self.drawrects = True
        self.color = self.colors[forarea]

        # defines what we are looking to select
        self.selecttype = forarea

if __name__ == "__main__":
    app = QtGui.QApplication([])
    mainwn = QtGui.QMainWindow()
    mainwn.resize(800, 696)
    centralwidget = QtGui.QWidget(mainwn)
    centralwidget.resize(800, 600)
    gridlayout = QtGui.QGridLayout(centralwidget)
    web = CustomQWebView(parent=centralwidget)
    gridlayout.addWidget(web, 0, 0, 1)
    web.setUrl(QtCore.QUrl("http://duckduckgo.com"))
    mainwn.show()

    app.exec_()

WebElement这是另一个包含我编写并开始使用的新类定义的文件:

#!/usr/bin/env python2
# coding: utf-8
#                        VENI, SANCTE SPIRITUS

from PySide.QtWebKit import QWebElement, QWebHitTestResult
from PySide import QtGui
from PySide import QtCore


class WebElement(QtCore.QObject):

    """ Holds information of webelements
    """

    def __eq__(self, other):
        if isinstance(other, WebElement):
            return (self.web_element == other.web_element and
                    self.getrect() == other.getrect())
        else:
            raise ValueError("Not same objects")

    def __ne__(self, other):
        if isinstance(other, WebElement):
            return (self.web_element != other.web_element and
                    self.getrect() != other.getrect())
        else:
            raise ValueError("Not same objects")

    def __init__(self, hittestresult, color, parent=None):
        super(WebElement, self).__init__(parent)

        if (not isinstance(hittestresult, QWebHitTestResult) and
                not isinstance(hittestresult, QWebElement)):
            raise ValueError(
                "Argument passed for 'hittestresult' is not"
                " QtWebkit.QWenHitTestResult or QtWebkit.QWebElement instance"
            )
        if not isinstance(color, QtGui.QColor):
            raise ValueError(
                "Argument passed for 'color' is not QtGui.QColor instance"
            )

        try:
            self.frame = hittestresult.frame()
        except AttributeError:
            self.frame = hittestresult.webFrame()

        self.frame_scroll_x = self.frame.scrollPosition().x()
        self.frame_scroll_y = self.frame.scrollPosition().y()

        try:
            rect = hittestresult.boundingRect()
        except AttributeError:
            rect = hittestresult.geometry()

        self.element_rect_x = rect.x()
        self.element_rect_y = rect.y()
        self.element_rect_w = rect.width()
        self.element_rect_h = rect.height()

        try:
            self.web_element = hittestresult.element()
        except AttributeError:
            self.web_element = hittestresult

        self.color = color
        self.color_darker = color.darker()
        self.color_darker.setAlpha(255)
        self.pen = QtGui.QPen(self.color_darker)
        self.pen.setWidth(2)
        #self.painter = QtGui.QPainter(self.parent)
        self.painter = QtGui.QPainter()
        self.painter.setPen(self.pen)

    def update(self):
        """ draw the rect for this element in the CustomQWebView
        """
        rect = self.getrect()
        rectf = QtCore.QRectF(rect)
        self.painter.fillRect(rectf, self.color)
        self.painter.drawRect(rectf)

    def getrect(self):
        """ Return the rect for this WebElement
        """
        self.frame_scroll_x = self.frame.scrollPosition().x()
        self.frame_scroll_y = self.frame.scrollPosition().y()
        rect = QtCore.QRect()
        rect.setRect(self.element_rect_x - self.frame_scroll_x,
                     self.element_rect_y - self.frame_scroll_y,
                     self.element_rect_w, self.element_rect_h)
        return rect

我的项目应该可以正常工作,就好像我没有更改任何内容一样,但是,这些更改并没有。我究竟做错了什么?我错过了一些关于QWebFrames 的东西吗?

4

1 回答 1

2

首先,您的示例不是最小的:文件“webelementinfo.py”是不相关的(以及您的类的许多其他部分),导致问题的原因是QWebHitTestResult.frame()方法。足以发生错误的代码如下:

@QtCore.Slot(QtGui.QMouseEvent)
def mouseMoveEvent(self, event):
    if self.currentframe:
        hittestresult = self.currentframe.hitTestContent(event.pos())
        hittestresult.frame() # <- will cause the crash on next mouseMoveEvent

正如ekhumoro所指出的,这看起来像是 PySide 中的一个错误,与对象所有权有关。您需要避免调用该frame()方法 - 它似乎在您的代码中并不是真正重要的。将 的构造函数更改WebElement为:

def __init__(self, frame, hittestresult, color, parent=None):

接着:

#try:
#    self.frame = hittestresult.frame()     <-- DO NOT CALL THIS
#except AttributeError:
#    self.frame = hittestresult.webFrame()

self.frame = frame

并明确传递框架:

hittestresult = self.currentframe.hitTestContent(event.pos())

element = webelementinfo.WebElement(
    self.currentframe, 
    hittestresult,
    self.color,
    self)

通过这些修复,“内部 C++ 对象 (PySide.QtWebKit.QWebFrame) 已被删除。” 不会发生错误。

于 2014-11-08T01:32:33.983 回答