[PySide] Internal C++ object (PySide.QtWebKit.QWebFrame) already deleted; but I'm saving it as an attribute to avoid this exact issue

Jorge Araya Navarro elcorreo at deshackra.com
Thu Nov 6 06:04:41 CET 2014


I need some help figuring out how to solve this error. As I described it on SO[1]:

        I'm working on a project where I use a modified `QWebView`. I'm getting this error:

            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.

        I have read about [PySide pitfalls](http://qt-project.org/wiki/PySide_Pitfalls) already, and I've been
        saving that `QtWebKit.QWebFrame` as an attribute of my modified `QWebView` **from the beggining of the
        project** with the method `setframeafterloadfinished` which is called when the page finishes loading,
        the problem raised after some majors changes on my modified `QWebView`.

        Here is the source code showing what I changed:

            diff -r 916f0091fee8 src/customwebview.py
            --- a/src/customwebview.py	Thu Oct 30 19:54:57 2014 -0600
            +++ b/src/customwebview.py	Mon Nov 03 20:22:51 2014 -0600
            @@ -4,6 +4,7 @@

             from PySide.QtWebKit import QWebView
             from PySide import QtCore, QtGui
            +from . import webelementinfo


             class CustomQWebView(QWebView):
            @@ -12,19 +13,16 @@
                     """ Init the custom class
                     """
                     super(CustomQWebView, self).__init__(*args, **kwargs)
            -        self.colors = {0: QtGui.QColor(255, 165, 0),
            -                       1: QtGui.QColor(135, 206, 235),
            -                       2: QtGui.QColor(135, 235, 164),
            -                       3: QtGui.QColor(235, 135, 206),
            -                       4: QtGui.QColor(235, 164, 135)}
            -        self.colorfill = None
            +        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.lastelement = None
            -        self.lastelementboundingrect = None
            +        self.element = None
                     self.loadFinished.connect(self.setframeafterloadfinished)
            -        self.pen = QtGui.QPen()
            -        self.pen.setWidth(2)
            -        self.drawrects = False
            +        self.selectCommentsArea()

                 @QtCore.Slot()
                 def selectCommentsArea(self):
            @@ -63,11 +61,15 @@
                     if self.drawrects:
                         if self.currentframe:
                             hittestresult = self.currentframe.hitTestContent(event.pos())
            -                element = hittestresult.element()
            -                if self.lastelement != element:
            -                    self.lastelement = element
            -                    self.lastelementboundingrect = hittestresult.boundingRect()
            -                    self.update()
            +                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):
            @@ -76,22 +78,8 @@

                     if self.drawrects:
                         # then the rectangle
            -            if self.lastelementboundingrect:
            -                painter = QtGui.QPainter(self)
            -                painter.setPen(self.defaultpen)
            -                # This rectangles takes into account any offset of the scroll
            -                # bar so the rectangle can be drawn correctly
            -                rect = QtCore.QRect()
            -                rect.setRect(self.lastelementboundingrect.x() -
            -                             self.currentframe.scrollPosition().x(),
            -                             self.lastelementboundingrect.y() -
            -                             self.currentframe.scrollPosition().y(),
            -                             self.lastelementboundingrect.width(),
            -                             self.lastelementboundingrect.height())
            -                # painter.drawRect(QtCore.QRectF(self.lastelementcurrectrect))
            -                painter.drawRect(QtCore.QRectF(rect))
            -                painter.fillRect(
            -                    QtCore.QRectF(rect), self.colorfill)
            +            if self.element:
            +                self.element.update()

                 def setframeafterloadfinished(self):
                     self.currentframe = self.page().mainFrame()
            @@ -109,15 +97,7 @@
                     commentary text.
                     """
                     self.drawrects = True
            -        self.colorfill = self.colors[forarea]
            -        self.colorfill.setAlpha(128)
            -
            -        colorborder = self.colors[forarea]
            -        self.pen.setColor(colorborder)
            +        self.color = self.colors[forarea]

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

        and here it is again but complete, **and functional** (just be sure to put the file
        `webelementinfo.py` in the same directory as this code before running a test):

            #!/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_()

        Here is the other file that have the definition of that new class `WebElement` that I write and start
        to use:

            #!/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

        My project should work right as if I didn't change anything, however, with these changes it
        doesn't. What am I doing wrong? Am I missing something about `QWebFrame`s?

[1]: http://stackoverflow.com/q/26726878/2020214

-- 
Pax et bonum.
Jorge Araya Navarro.
ES: DiseƱador Publicitario, Programador Python y colaborador en Parabola GNU/Linux-libre
EN: Ads Designer, Python programmer and contributor Parabola GNU/Linux-libre
EO: Anonco grafikisto, Pitino programalingvo programisto kai kontribuanto en Parabola GNU/Linux-libre
https://es.gravatar.com/shackra



More information about the PySide mailing list