4

I develop an application where a lot of objects need to be added to a QGraphicsScene object in order to represent the diagram of an electric power system. When the grid is large this process takes a while and the main window remains unresponsive. Hence, I'd like to know a way to create the objects asynchronously.

The following code is an very very simplified version of the actual code. In the example I create objects and I add them to the scene using the function add_objects. This is the function that should run in a thread.

import sys
from PySide2.QtWidgets import *
from PySide2.QtCore import *


def add_objects(scene: QGraphicsScene, n=1000):
    for i in range(n):
        item = QGraphicsEllipseItem(i * 5, 10, 60, 40)
        scene.addItem(item)


class MyView(QGraphicsView):
    def __init__(self):
        QGraphicsView.__init__(self)

        self.setGeometry(QRect(100, 100, 600, 250))

        self.scene = QGraphicsScene(self)
        self.scene.setSceneRect(QRectF())

        self.setScene(self.scene)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    view = MyView()
    view.show()
    add_objects(scene=view.scene, n=100)
    sys.exit(app.exec_())

------ EDIT --------

Since I've been asked for another example, here goes the complete case:

import sys
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from GridCal.Gui.GridEditorWidget import GridEditor
from GridCal.Engine.IO.file_handler import FileOpen


class AddObjectsThreaded(QThread):

    def __init__(self, editor: GridEditor, explode_factor=1.0):
        QThread.__init__(self)

        self.editor = editor

        self.explode_factor = explode_factor

    def run(self):
        """
        run the file open procedure
        """
        # clear all
        self.editor.diagramView.scene_.clear()

        # first create the buses
        for bus in self.editor.circuit.buses:
            bus.graphic_obj = self.editor.add_api_bus(bus, self.explode_factor)

        for branch in self.editor.circuit.branches:
            branch.graphic_obj = self.editor.add_api_branch(branch)

        # figure limits
        min_x = sys.maxsize
        min_y = sys.maxsize
        max_x = -sys.maxsize
        max_y = -sys.maxsize

        # Align lines
        for bus in self.editor.circuit.buses:
            bus.graphic_obj.arrange_children()
            # get the item position
            x = bus.graphic_obj.pos().x()
            y = bus.graphic_obj.pos().y()

            # compute the boundaries of the grid
            max_x = max(max_x, x)
            min_x = min(min_x, x)
            max_y = max(max_y, y)
            min_y = min(min_y, y)

        # set the figure limits
        self.editor.set_limits(min_x, max_x, min_y, max_y)
        #  center the view
        self.editor.center_nodes()


if __name__ == '__main__':
    app = QApplication(sys.argv)

    fname = r'1354 Pegase.xlsx'  # this file you need to download from https://github.com/SanPen/GridCal/tree/master/Grids_and_profiles/grids
    circuit = FileOpen(fname).open()

    view = GridEditor(circuit)
    view.resize(600, 400)
    view.show()

    thr = AddObjectsThreaded(editor=view)
    thr.start()

    sys.exit(app.exec_())

This process crases cleanly with the following error:

Process finished with exit code -1073740791 (0xC0000409)

Santi Peñate-Vera
  • 1,053
  • 4
  • 33
  • 68
  • Does the code you provided generate this problem? I have executed it and not me. If you want help provide a real [MRE], on the other hand discard the use of threads for loading items since it is not possible, maybe an option is to load an item every T ms. – eyllanesc Sep 24 '19 at 18:02
  • Indeed the code does not reproduce any issue. I have tried threading and the way I do it (which is to run the function from a threaded class) does not work. Hence the question. Is there any way to do this threaded or so that the main window remains responsive? – Santi Peñate-Vera Sep 24 '19 at 18:10
  • See the new example – Santi Peñate-Vera Sep 25 '19 at 06:35
  • This crashes for me with : xlrd.biffh.XLRDError: Unsupported format, or corrupt file: Expected BOF record; found b'\n\n\n\n\n\n – delica Sep 30 '19 at 20:21
  • Something must be off... any other file from https://github.com/SanPen/GridCal/tree/master/Grids_and_profiles would do as well – Santi Peñate-Vera Oct 01 '19 at 05:47
  • Is there any other approach that is desirable here? – Santi Peñate-Vera Oct 03 '19 at 07:02
  • @eyllanesc According to the Qt docs: "Graphics View uses a BSP (Binary Space Partitioning) tree to provide very fast item discovery, and as a result of this, it can visualize large scenes in real-time, **even with millions of items.**" – ekhumoro Oct 06 '19 at 15:24
  • @ekhumoro Interesting, I suppose that refers to the fact that it can handle millions of items but not show them all at once, since although the loading process is slow (due to a potential for-loop) with what you point out the painting should not have problems but when I execute the code there is a lag that I suppose is the update of the painting. Maybe deactivating some flags will help but unfortunately the MRE uses an external library (GridCal) that makes it difficult to understand that flags are not necessary, or perhaps does not consider certain limitations. – eyllanesc Oct 06 '19 at 15:33
  • @ekhumoro [cont.] IMHO that makes it impossible to analyze this particular case – eyllanesc Oct 06 '19 at 15:33
  • @eyllanesc The first examples doesn't have that problem, though. I can add 10 thousand items without any delay. Really, the whole point of the Graphics View Framework is that it can handle very large numbers of 2D graphical items in a way that would be impossible when using standard widgets. – ekhumoro Oct 06 '19 at 15:45
  • @ekhumoro Well, I was just going to try what you said, but you already saved me time. So the problem is how it handles the GridCal elements, maybe it does some operation that consumes a lot of time when interacting with the items. – eyllanesc Oct 06 '19 at 15:46
  • @eyllanesc Yes - we need to see a *non-threaded* example that shows the original problem. – ekhumoro Oct 06 '19 at 15:49

1 Answers1

0

According to other similar problems, the solution could be reinstalling pyqt, see here. These answers imply that obsolete versions of pyqt are the cause of this problem. In your case, since you are using PySide2 I suggest removing whatever version you currently have and installing again, if you're using pip:

pip uninstall PySide2
pip install --upgrade --no-cache-dir PySide2

In case that doesn't help, I've also found another similar error in a question that I hope would at least point you in the right direction that has an answer (which is probably already mentioned in the comments, don't add UI elements from other threads), I'm copying it here:

STATUS_STACK_BUFFER_OVERRUN is a /GS exception. They are thrown when Windows detects 'tampering' of a security cookie protecting a return address. It is probable that you are writing something past the end of a buffer, or writing something to a pointer that is pointing to the wrong place. However it is also possible that you have some dodgy memory or otherwise faulty hardware that is tripping validation code.

One thing that you could try is to disable the /GS switch (project properties, look for C/C++ -> Code Generation -> Buffer Security Check) and recompile. Running the code again may well cause an error that you can trap and trace. I think /GS is designed not to give you any info for security reasons.

Another thing you could do is run the code as is on a different PC and see if that fails, this may point to a hardware problem if it doesn't.

Ofer Sadan
  • 11,391
  • 5
  • 38
  • 62