Retrieve Main Window in PyQt/PySide for DCC Apps

If you’re developing within a DCC and you create a new GUI element in PyQt/PySide, such as a QFrame, QMainWindow, or QDialog, you’re going to want the main window of the DCC app as the parent of the new GUI element. This ensures that when we close the main app, the new widget closes as well. Here are the ways I’ve gotten the main window for the most recent DCCs I’ve used:

3DS MAX 2020

reference

from PySide2 import QtWidgets


def get_qmax_main_window():
    """Get the 3DS MAX main window.
    
    Returns:
        PySide2.QtWidgets.QMainWindow: 'QMainWindow' 3DS MAX main window.
    """
    for w in QtWidgets.QApplication.topLevelWidgets():
        if w.inherits('QMainWindow') and 
                w.metaObject().className() == 'QmaxApplicationWindow':
            return w
    raise RuntimeError('Count not find QmaxApplicationWindow instance.')

Houdini 18

reference

import hou

def getHoudiniMainWindow():
    """Get the Houdini main window.
    
    Returns:
        PySide2.QtWidgets.QWidget: 'QWidget' Houdini main window.
    """
    return hou.qt.mainWindow()

Katana 3

from UI4.App import MainWindow

def getKatanaMainWindow():
    """Get the Katana main window.

    Returns:
        UI4.App.MainWindow.KatanaWindow: 'KatanaWindow' Katana main window.
    """
    return MainWindow.GetMainWindow()

Mari 4

from PySide2.QtWidgets import QApplication

import mari

def getMariMainWindow():
    """Get the Mari main window.
    
    Returns:
        PySide2.QtWidgets.QWidget: 'MriMainWindow' Mari main window.
    """

    # Set Mari main window to be in focus.
    mari.app.activateMainWindow()

    # Returns the window that has focus.
    return QApplication.activeWindow()

Maya 2020

from PySide2 import QtWidgets
import shiboken2

import maya.OpenMayaUI as apiUI

def getMayaMainWindow():
    """Get the Maya main window.
    
    Returns: 
        PySide2.QtWidgets.QWidget:  'TmainWindow' Maya main window.
    """

    ptr = apiUI.MQtUtil.mainWindow()
    if ptr is not None:
        return shiboken2.wrapInstance(long(ptr), QtWidgets.QWidget)

Nuke 12

from PySide2 import QtWidgets

def getNukeMainWindow():
    """Get the Nuke main window.

    Returns:
        PySide2.QtWidgets.QMainWindow: 'DockMainWindow' Nuke 
            main window.
    """
    for w in QtWidgets.QApplication.topLevelWidgets():
        if w.inherits('QMainWindow') and w.metaObject().className() == \
                'Foundry::UI::DockMainWindow':
            return w
    raise RuntimeError('Could not find DockMainWindow instance')

A simple “QApplication.activeWindow()” would also probably work for Nuke, but if there are multiple instances of QMainWindow, it might get confused. As far as I know, there is only one “Foundry::UI::DockMainWindow“, so it’s best to check for that.

Regex "AND"

On one occasion we had a PySide QListView with a QSortFilterProxyModel and we wanted to use regex for the filtering. QSortFilterProxyModel does accept regex for filtering so we figured this wouldn’t be a problem. One of the requirements was the filtered text would be space delineated. For example, if the user typed “red blue”, it would now have 2 search terms: “red” and “blue”.

Next to the filter text box were 2 radio buttons denoting “AND” and “OR”. The user would use one of these button to let the filter know what they expect from the returned items; if the user selected “OR”, the filter would return all items found with “red”, all items found with “blue”, and all items found with both “red” and “blue”; if the user selected “AND”, the filter would ONLY return all items found with BOTH “red” and “blue”.

Lucking for us, regex supports OR right out of the box. It’s denoted by “|”.

But AND… is not. So we have to build it. The code for regex AND is:

(?=.*'first term')(?=.*'second term').*

You can use as many terms as you want as long as they’re contained in their own parentheses. So the next step would be to get all of your key words in a list and build your regex using string “join” and “format” methods.

search_strings = str(filter_text).split()

pattern = QtCore.QRegExp()
if search_strings:
    if filter_type == AND:
        # AND
        # regex pattern = (?=.*'first term')(?=.*'second term').*
        pattern = QtCore.QRegExp(
            '{}.*'.format(
                ''.join(
                    [
                        '(?=.*{})'.format(search_string)
                        for search_string in search_strings
                    ]
                )
            ),
            QtCore.Qt.CaseInsensitive
        )
    else:
        # OR
        # regex pattern = 'first term'|'second term'
        pattern = QtCore.QRegExp(
            '|'.join(search_strings),
            QtCore.Qt.CaseInsensitive,
        )

sortfilterproxy.setFilterRegExp(pattern)