Embedding a window from another process with Enaml

July 31, 2018

Since DeclaraCAD attempts to render changes to your models in realtime it can lead to performance issues. One solution would be rendering in a thread however this still has issues. If the model has an invalid definition it can cause a segfault crashing the whole app and sometimes preventing the application from restarting.

I decided to instead use Qt's Window composition technique to embed the 3D model renderer into the DeclaraCAD application as a separate process, it's rather simple with enaml.

First, break the app into two pieces, the main app, and what you want embedded into the main app. In my case, I'm using a basic 3D viewer as the embedded app and will be displaying this as a dock item within the DeclaraCAD application.

Next, have the embedded app either print, or somehow notify the parent app it's main window ID.

Finally, create a custom RawWidget to display the embedded window using it's window ID, like so.

from atom.api import Int, set_default
from enaml.core.declarative import d_
from enaml.widgets.api import RawWidget
from enaml.qt.QtGui import QWindow
from enaml.qt.QtWidgets import QWidget

class EmbeddedWindow(RawWidget):
    """ Create a widget that embeds the window from another application.
    This allows you to run expensive operations (ex 3D rendering) without
    blocking the main UI.

    #: Expand by default
    hug_width = set_default('ignore')
    hug_height = set_default('ignore')

    #: Window ID of embedded application
    window_id = d_(Int())

    def create_widget(self, parent):
        window = QWindow.fromWinId(self.window_id)
        return QWidget.createWindowContainer(window, parent=parent)

We can use this like any other widget, in our main view use:

enamldef Main(Window): window:
    attr window_id
    attr pid
            text = "Main PID: {}".format(pid)
            window_id << window.window_id

The startup script needs to first spawn the child process, read the window_id, and then show the main view. Qt handle's the rest. Enaml's layout system will handle resizing for you.

Here's the result

Even though rendering the part takes a few seconds the UI is no longer blocked and you're free to keep editiing code, open dialogs, etc!