Drawing a 3D object in a separate widget (QuarterWidget segfaults)

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
suzanne.soy
Posts: 54
Joined: Sat Dec 19, 2020 11:55 pm

Drawing a 3D object in a separate widget (QuarterWidget segfaults)

Post by suzanne.soy »

I'm trying to draw a FreeCAD object in a widget that is separate from the default MDI tab 3D view.

I tried this code, but it segfaults on the qw.setSceneGraph() call:

Code: Select all

import pivy
import pivy.coin
import pivy.quarter

# Create Quarter 3D view
qw = pivy.quarter.QuarterWidget()
qw.setSceneGraph(FreeCADGui.ActiveDocument.ActiveView.getSceneGraph())
qw.show() # might need qw.setGeometry(…) too
I also tried with a fresh scene graph, which also segfaults on the qw.setSceneGraph() call:

Code: Select all

import pivy
import pivy.coin
import pivy.quarter

# Create red cube
col = pivy.coin.SoBaseColor()
col.rgb = (1, 0, 0)
trans = pivy.coin.SoTranslation()
trans.translation.setValue([0, 0, 0])
cub = pivy.coin.SoCube()
myCustomNode = pivy.coin.SoSeparator()
myCustomNode.addChild(col)
myCustomNode.addChild(trans)
myCustomNode.addChild(cub)

# Create Quarter 3D view
qw = pivy.quarter.QuarterWidget()
qw.setSceneGraph(myCustomNode)
qw.show() # might need qw.setGeometry(…) too
I can however alter the scene graph of the default MDI tab 3D view, like so:

Code: Select all

import pivy
import pivy.coin
import pivy.quarter

# Create red cube
col = pivy.coin.SoBaseColor()
col.rgb = (1, 0, 0)
trans = pivy.coin.SoTranslation()
trans.translation.setValue([0, 0, 0])
cub = pivy.coin.SoCube()
myCustomNode = pivy.coin.SoSeparator()
myCustomNode.addChild(col)
myCustomNode.addChild(trans)
myCustomNode.addChild(cub)

# Change scene graph of existing 3D view
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
sg.removeAllChildren()
# This works:
sg.addChild(myCustomNode)
How can I create a 3D view and set its scene graph? Thanks :)
Last edited by suzanne.soy on Tue Sep 14, 2021 12:08 pm, edited 1 time in total.
:?: Please mark your posts as [solved] :!:
:idea: If the answer you got is a good fit for the wiki, you can edit it!
FreeCAD modelling & coding workbenches+macros: twitch GitHub
suzanne.soy
Posts: 54
Joined: Sat Dec 19, 2020 11:55 pm

Re: Drawing a 3D object in a separate widget (QuarterWidget segfaults)

Post by suzanne.soy »

:roll: I forgot to include my FreeCAD version details halfway through writing the post…

OS: Ubuntu 20.04.2 LTS (wayfire)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.24291 (Git) AppImage
Build type: Release
Branch: (HEAD detached at 0.19.2)
Hash: 7b5e18a0759de778b74d3a5c17eba9cb815035ac
Python version: 3.8.8
Qt version: 5.12.9
Coin version: 4.0.0
OCC version: 7.4.0
Locale: English/United Kingdom (en_GB)
:?: Please mark your posts as [solved] :!:
:idea: If the answer you got is a good fit for the wiki, you can edit it!
FreeCAD modelling & coding workbenches+macros: twitch GitHub
suzanne.soy
Posts: 54
Joined: Sat Dec 19, 2020 11:55 pm

Re: Drawing a 3D object in a separate widget (QuarterWidget segfaults)

Post by suzanne.soy »

I found here https://forum.freecadweb.org/viewtopic.php?t=16180 that FreeCAD uses its own modified fork of Quarter, and therefore the one that ships with pivy doesn't work. Instead, we have to rely on FreeCAD to create the view for us, using Gui.createViewer. I tried the following but:
  • it loses the focus (due to the temporary creation of a new tab in the MDI, the focus is lost, I'm trying to display a preview of an object in a drop-down under a search field, so this makes the search field lose focus :| )
  • Despite my efforts, it still segfaults very often when the object gets garbage collected.

Code: Select all

class SafeViewer:
  """FreeCAD uses a modified version of QuarterWidget, so the import pivy.quarter one will cause segfaults.
     FreeCAD's FreeCADGui.createViewer() puts the viewer widget inside an MDI window, and detaching it without causing segfaults on exit is tricky.
     This class contains some kludges to extract the viewer as a standalone widget and destroy it safely."""
  def __init__(self, parent = None):
    from PySide import QtGui
    self.viewer = FreeCADGui.createViewer()
    self.graphicsView = self.viewer.graphicsView()
    self.oldGraphicsViewParent = self.graphicsView.parent()
    self.oldGraphicsViewParentParent = self.oldGraphicsViewParent.parent()
    self.oldGraphicsViewParentParentParent = self.oldGraphicsViewParentParent.parent()

    # Avoid segfault but still hide the undesired window by moving it to a new hidden MDI area.
    self.hiddenQMDIArea = QtGui.QMdiArea()
    self.hiddenQMDIArea.addSubWindow(self.oldGraphicsViewParentParentParent)

    self.widget = self.oldGraphicsViewParent
    self.widget.setParent(parent)

    import weakref
    def finalizer(slf):
      # Cleanup in an order that doesn't cause a segfault:
      slf.widget.setParent(self.oldGraphicsViewParentParent)
      slf.oldGraphicsViewParentParentParent.close()
      slf.oldGraphicsViewParentParentParent = None
      slf.oldGraphicsViewParentParent = None
      slf.oldGraphicsViewParent = None
      slf.graphicsView = None
      slf.viewer = None
      #slf.parent = None
      slf.hiddenQMDIArea = None

    weakref.finalize(self, finalizer, self)

# Example use:
from PySide import QtGui
import pivy
w = QtGui.QMainWindow()
sv = SafeViewer(w)
w.show()
col = pivy.coin.SoBaseColor()
col.rgb = (1, 0, 0)
trans = pivy.coin.SoTranslation()
trans.translation.setValue([0, 0, 0])
cub = pivy.coin.SoCube()
myCustomNode = pivy.coin.SoSeparator()
myCustomNode.addChild(col)
myCustomNode.addChild(trans)
myCustomNode.addChild(cub)
sv.viewer.getViewer().setSceneGraph(myCustomNode)
sv.viewer.fitAll()
Is there any easier way to get a proper Quarter widget using FreeCAD's forked version of the library?

Thanks :D
Attachments
20210919_00h12m51s_grim.png
20210919_00h12m51s_grim.png (402.45 KiB) Viewed 1045 times
:?: Please mark your posts as [solved] :!:
:idea: If the answer you got is a good fit for the wiki, you can edit it!
FreeCAD modelling & coding workbenches+macros: twitch GitHub
Post Reply