mottosso / Qt.py

Minimal Python 2 & 3 shim around all Qt bindings - PySide, PySide2, PyQt4 and PyQt5.
MIT License
906 stars 252 forks source link

Should there be a wrapper around sip/shiboken? #53

Closed fredrikaverpil closed 10 months ago

fredrikaverpil commented 8 years ago

Originally asked by Pierre Augeard:

What would be the wrapper around sip/shiboken?

For example:

mottosso commented 8 years ago

Originally asked by Pierre Augeard:

Where? :O

fredrikaverpil commented 8 years ago

Where?

Over at the Maya beta forums, but now I see he's interacting here. Ping @pipoun

fredrikaverpil commented 8 years ago

@pipoun - could you please provide a use case for this request/question?

pipoun commented 8 years ago
from Qt import QtGui, binder
import maya.OpenMayaUI

def get_maya_mainwindow():
    """Get the main Maya window

    :return: :class:`QtGui.QMainWindow` instance of the top level Maya window
    """
    ptr = maya.OpenMayaUI.MQtUtil.mainWindow()
    if ptr is not None:
        # wrap_instance (pep8), wrapInstance (shiboken) or wrapinstance (sip)
        return binder.wrap_instance(long(ptr), QtGui.QMainWindow)
fredrikaverpil commented 8 years ago

@pipoun do you ever use this for anything except returning the main Maya window when you want to parent your UI to Maya?

Would you consider this approach or do you feel QtWidgets.QApplication.activeWindow() is too unreliable?

mottosso commented 8 years ago

A portable approach would be preferable. However..

Would you consider this approach

This returns the script editor for me.

from Qt import QtWidgets
window = QtWidgets.QApplication.activeWindow().objectName()
print(window)

Unless I have another window active, in which case that window is returned.

There is this.

from Qt import QtWidgets
window = {w.objectName(): w for w in QtWidgets.qApp.topLevelWidgets()}["MayaWindow"]
print(window)
pipoun commented 8 years ago

I just wanted to focus on the fact that sip.wrapinstance and shiboken.wrapInstance have not the same spelling nor the exact same behavior and it should be considered to have a wrapper to unify all of this ?

@pipoun do you ever use this for anything except returning the main Maya window when you want to parent your UI to Maya?

There are other use cases than getting a main window as a QMainWindow instance.

QtWidgets.qApp.topLevelWidgets() is very slow in some cases compared to MQtUtil.mainWindow (when there are many Qt widgets init'd), see the implementation : http://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/kernel/qapplication.cpp#n1776

mottosso commented 8 years ago

Thanks for the added info @pipoun.

Would it be possible to show us some more examples? I haven't used this particular feature myself, so it's difficult to know what and how to wrap.

fredrikaverpil commented 8 years ago

To me, it sounds like there's a need for a convenience function "Qt.wrap_instance()", like this one, by Nathan Horne, previously mentioned:

def wrapinstance(ptr, base=None):
    """
    Utility to convert a pointer to a Qt class instance (PySide/PyQt compatible)

    :param ptr: Pointer to QObject in memory
    :type ptr: long or Swig instance
    :param base: (Optional) Base class to wrap with (Defaults to QObject, which should handle anything)
    :type base: QtGui.QWidget
    :return: QWidget or subclass instance
    :rtype: QtGui.QWidget
    """
    if ptr is None:
        return None
    ptr = long(ptr) #Ensure type
    if globals().has_key('shiboken'):
        if base is None:
            qObj = shiboken.wrapInstance(long(ptr), QtCore.QObject)
            metaObj = qObj.metaObject()
            cls = metaObj.className()
            superCls = metaObj.superClass().className()
            if hasattr(QtGui, cls):
                base = getattr(QtGui, cls)
            elif hasattr(QtGui, superCls):
                base = getattr(QtGui, superCls)
            else:
                base = QtGui.QWidget
        return shiboken.wrapInstance(long(ptr), base)
    elif globals().has_key('sip'):
        base = QtCore.QObject
        return sip.wrapinstance(long(ptr), base)
    else:
        return None

I've personally only used this to successfully fetch the Maya main window:

def maya_main_window():
    '"""Returns the main Maya window"""
    main_window_ptr = omui.MQtUtil.mainWindow()
    return wrapinstance(long(main_window_ptr), QtGui.QWidget)

But for this particular thing, your code is much much nicer, @mottosso. I second @mottosso's request to see additional use cases.

mottosso commented 8 years ago

Yeah, a feature, encompassing 14% of the entire codebase (32/230 LOC), for a single usecase isn't worth it.

Once we find more usecases, I'd be happy to reconsider this.

innerhippy commented 7 years ago

Sometimes it's useful to access sip.delete() / shiboken.delete(), especially when redefining layouts, selection models, or running complex unit tests. Admittedly it's only really needed for PyQt4 but it would be useful to abstract this as something like C_binding.

mottosso commented 7 years ago

Hi @innerhippy,

Would it be possible to share an example of where sip could be useful? That would really help determine how to best see it realised.

fredrikaverpil commented 7 years ago

Admittedly it's only really needed for PyQt4

Just a suggestion, would this work for you then?

if "PyQt4" in QtCompat.__binding__:
    sip.delete()
    shiboken.delete()
mottosso commented 7 years ago

What I'm interested in, is whether there is an alternative to using sip.delete() and friends in the first place, that is cross-compatible. But to do that, we'll need examples of where sip is used.

innerhippy commented 7 years ago

Thanks @fredrikaverpil , that would certainly work and similar to what I was thinking. I guess the consideration is if this should be provided by the Qt module itself. We use it to delete layouts and for some unit tests where the destruction order of models gets a bit messed up (in PyQt4 anyway). I agree that ideally this shouldn't be necessary, and maybe refactoring the code would solve the problem, but sometimes it's a question of having to just making stuff work. I'll post some code snippets on Monday but have to get clearance first.

boglin commented 7 years ago

Just wanted to add another voice here.

I found this request when looking at porting our stack over to Maya2017 and doing the Qt4->Qt5 shuffle. We have upwards of 20 projects which use wrapinstance - our previous (inhouse) PyQt4/PySide shim provided access to the underlying binding where we used a couple of very specific methods, e.g. wrapInstance, delete, signature/methodsignature where behaviour was comparable.

Common with others on this thread the largest use is wrapInstance generally against the Maya API: MQtUtil Class Reference, where *QObject/QWidget is returned.

We could ask people to port to pymel: uitypes.py within Maya-specific code, which is already providing this switching logic. But the API brings us back to a specific PyQt/PySide implementations which is what makes Qt.py so attractive.

I guess the reason why we see so much of this in the codebase is that Maya's examples are driving people towards wrapInstance: Autodesk Docs: Working with PySide in Maya I would love for us to be able to produce a translation of that document as "Working with Qt.py in Maya".

If people are happy for it to be added - I'd be happy to help put together a patch.

mottosso commented 7 years ago

I would love for us to be able to produce a translation of that document as "Working with Qt.py in Maya".

Good idea. Perhaps as an extension of Fredrik's document.

To include pros and cons of these binding-specific methods. Could also be elsewhere, even in the README of this project since it maybe relevant enough.

innerhippy commented 7 years ago

To follow up on my post from 1st Oct, here's an example of where we use the delete function:

def deleteLayout(widget, layout=None, deleteTopLevelLayout=True):
   """ Function to delete a widget's layout. This needs to be done in
        reverse order of each item in the layout as the index is changed
        for each itemAt() call.
        If deleteTopLevelLayout==True, the layout is deleted using the sip 
        module to ensure that all underlying C++ destructors are called,
        otherwise the layout will have stale widgets from the previous
        setLayout() calls
    """"
    if widget is not None and layout is None:
        layout = widget.layout()

    if layout is not None:
        for i in xrange(layout.count()-1, -1, -1):
            item = layout.itemAt(i)

            if isinstance(item, QtGui.QWidgetItem):
                item.widget().setParent(None)
            elif isinstance (item, QtGui.QLayoutItem):
                # recursive call if the item is a layout
                deleteLayout(item)
        if deleteTopLevelLayout:
            import sip
            sip.delete(layout)
mottosso commented 7 years ago

That is perfect @innerhippy! Thanks for that!

fredrikaverpil commented 7 years ago

I guess the reason why we see so much of this in the codebase is that Maya's examples are driving people towards wrapInstance: Autodesk Docs: Working with PySide in Maya I would love for us to be able to produce a translation of that document as "Working with Qt.py in Maya".

If people are happy for it to be added - I'd be happy to help put together a patch.

In case you decide to start doing this, you can fork this project and make a pull request with the translation. If anyone has the time he/she can join in and help out by making pull requests to your fork. This way it could become a collaborative process.

Good idea. Perhaps as an extension of Fredrik's document.

To include pros and cons of these binding-specific methods. Could also be elsewhere, even in the README of this project since it maybe relevant enough.

If this were to be created, I think it would be useful to keep it as a markdown file (linked from the README) here in the repository.

glm-nchaverou commented 7 years ago

Hi guys, Sorry to jump in the discussion. First thanks for Qt.py, it's been a huge lifesaver here at Golaem !! I've ported all our code to it but there's still a function which relies around wrap_instance that I can't convert (yet). We're having a findLayout function which relies on Nathan Ortiz one (http://nathanhorne.com/embedding-a-maya-widget-into-a-pyqt-ui/). This allows us fetching the Attribute Editor layout of a specific node and override it with our own QT controllers (such as this one:

image

For sure a wrapInstance binding will be awesome but I let this decision up to you :) Let me know if you need more info

mottosso commented 7 years ago

Hi @glm-nchaverou! Thanks for the kind words, and you are most welcome to join the discussion.

I can see the use of wrapping sip/shiboken, but I'm still out the lookout for reason enough to warrant it. So far, the uses I have seen and been shown have had a non-sip equivalent; the most prominent being parenting to the main Maya window.

Let me know if you need more info

If you could provide me with an example of doing what you're looking to do, in this case modifying the Attribute Editor, using sip/shiboken, then I'd be happy to try and find a non-sip/shiboken equivalent.

If there is none, then that would be the best of reasons to start looking at wrapping enough of it to facilitate what you are looking to do.

mottosso commented 7 years ago

I couldn't find how to get the Attribute Editor, but if it's similar to finding the Channel Box, then maybe this could be of help.

Shiboken

import shiboken
import maya.OpenMayaUI as apiUI
from Qt import QtWidgets
channel_box = apiUI.MQtUtil.findControl("mainChannelBox")
channel_box = shiboken.wrapInstance(long(channel_box), QtWidgets.QTableView)
channel_box.setStyleSheet("QWidget {background: red}")

Pure Qt

from Qt import QtWidgets
app = QtWidgets.QApplication.instance()
window = {o.objectName(): o for o in app.topLevelWidgets()}["MayaWindow"]
channel_box = window.findChild(QtWidgets.QTableView, "mainChannelBox")
channel_box.setStyleSheet("QWidget {background: green}")
glm-nchaverou commented 7 years ago

Hey,

Ok here's how we hacked the AttributeEditor (can't find who we inspired this first... sorry)

In the AEMyNodeTemplate.mel file:

global proc AEMyNodeTemplate( string $nodeName )
{
    ...
    editorTemplate -callCustom "melbuildQT" "melUpdateQT" "";
    ...
}

melBuildQT( string $nodeName )
{
    string $parent = `setParent -q`;
    python("buildQT('" + $parent + "')");
}

And on the Python side:

def buildQT(layout):
    mayaLayoutPtr = omui.MQtUtil.findLayout(layout)
    mayaLayout = wrapInstance(long(mayaLayoutPtr), QtWidgets.QWidget)
    mayaLayoutObj = mayaLayout.findChild(QtWidgets.QBoxLayout)
    #add the widget to the layout
    mayaLayoutObj.insertWidget(0,widget)

It should highlight eveything :) Let me know if not

boglin commented 7 years ago

I can see the use of wrapping sip/shiboken, but I'm still out the lookout for reason enough to warrant it.

Our teams have a reasonable volume of code to port from Qt4 to Qt5. For those porting it might be their first time working with this project and certainly some of the original reasoning/intended behavior is perhaps lost in the mists of time.

The first pass is a like for like replacement - with much of the work performed by a conversion script.

Locally, we've extended Qt.py using something along the lines of:

        c_binding = imp.new_module("c_binding")
        c_binding.delete = shiboken2.delete
        c_binding.wrapInstance = shiboken2.wrapInstance
        _add(PySide2, "c_binding", c_binding)

We're waiting feedback on how well this works in the field so YMMV. I started to put together a pull request, but haven't had the opportunity to complete as other priorities have overtaken me. If it is of any use, the initial work in progress is here: https://github.com/boglin/Qt.py/tree/expose_binding

ben-hearn-sb commented 7 years ago

Sorry to jump in on this but I also have use for the sip.wrapInstance setup. We get the statusline in Maya to place our tools so they are easily accessible:

def getMayaStatusLine(): gStatusLine = mel.eval('$temp=$gStatusLine') ptr = apiUI.MQtUtil.findControl(gStatusLine) if ptr is not None: return sip.wrapinstance(long(ptr), QtWidgets.QWidget)

Any tips on how to get the status line using Qt.py, I have tried the above solution replacing "MayaWindow" with "StatusLine" but it appears there are a number of QWidgets and QMenus without object names

ben-hearn-sb commented 7 years ago

I found a good gist that gives me what I am after:

def getMayaStatusLine():
    gStatusLine = mel.eval('$temp=$gStatusLineForm')
    ptr = apiUI.MQtUtil.findControl(gStatusLine)
    print ptr
    if ptr is not None:
        statusLine = shiboken2.wrapInstance(long(ptr), QtWidgets.QWidget)
        return statusLine.children()[1].children()[1]
boglin commented 7 years ago

Hi Ben, please be aware that in your second snippet you are using shiboken2 which is the library that helps bind PySide2 to the underlying Qt (C++) library.
This will of course work in Maya2017, but you will have linked yourself directly with PySide2 - which maybe defeats the point of using Qt.py.

boglin commented 7 years ago

Given the continued interest in this topic I reimplemented my changes on the current master (see pull request #180). I'm aware that this issue is closed and would respect it if this is felt beyond the scope of this project.

With these changes, Ben should be able to do:

from Qt import c_binding

def getMayaStatusLine():
    gStatusLine = mel.eval('$temp=$gStatusLineForm')
    ptr = apiUI.MQtUtil.findControl(gStatusLine)
    print ptr
    if ptr is not None:
        statusLine = c_binding.wrapInstance(long(ptr), QtWidgets.QWidget)
        return statusLine.children()[1].children()[1]

My ability to test is somewhat limited at present, so feedback from those on this issue would be appreciated.

ben-hearn-sb commented 7 years ago

Hey guys I see your point on usiung shiboken_2. How would I build pull request 180 it to test this for boglin?

boglin commented 7 years ago

You can pick up the change from the c_binding branch of my fork (https://github.com/boglin/Qt.py/tree/c_binding)

mottosso commented 7 years ago

Hey guys, sorry for the lagging replies, I'm currently on holiday until the 10th of Feb and will address this as soon as I'm back. So far, considering the popularity of the request, it seems what's best for both the project and yourselves to include something like this.

Will comment on specifics in the current pull request.

Reopening this so that we may close it once it has been implemented

Thanks guys!

mottosso commented 7 years ago

Hi @glm-nchaverou,

I'm having trouble running your example with the Attribute Editor Template. Could you post a full template for me, along with how I use it?

mottosso commented 7 years ago

Hi @ben-hearn-sb,

Let me know if this works for you.

from maya import mel
from Qt import QtWidgets

def getMayaStatusLine():
    window = {o.objectName(): o for o in QtGui.qApp.topLevelWidgets()}["MayaWindow"]
    gStatusLine = mel.eval('$temp=$gStatusLineForm')
    statusLine = window.findChild(QtGui.QWidget, gStatusLine)
    return statusLine

statusLine = getMayaStatusLine()
statusLine.setStyleSheet("QWidget {background: red}")

Having tried a few examples at this point, the pattern seems to be that wherever there is a findLayout or findControl you replace it with window.findChild. Any reference to sip or shiboken is included in that call, as passing the superclass you are looking for. Such as QtWidgets.QLayout or QtWidgets.QWidget.

glm-nchaverou commented 7 years ago

Hey @mottosso, sorry for the late answer was stucked with work those past days Find attached a small 101 maya plugin showing how to hack the AEditor

Let me know if you have any question

mottosso commented 7 years ago

Thanks @glm-nchaverou, can't quite get it to work. Here's what I did.

  1. Unzip DummyNode.zip into $HOME/maya/scripts/AETemplates
  2. Load glmDummyNode.py through the Maya 2015 plug-in manager
  3. cmds.createNode("DummyNode")
  4. In the Attribute Editor, I'm not seeing any QLabel, and no error messages.

Sorry, I have no experience with AETemplates and Autodesk's help page haven't been very helpful.

What am I doing wrong?

glm-nchaverou commented 7 years ago

Oops sorry, assumed you're using Maya regularly :) Maya needs to know where to source scripts from. You can put both AEDummyNodeTemplate.mel and DummyNodeUI.py into a dir referenced within %MAYA_SCRIPT_PATH% On Win, it's usually C:\Users\chaverou\Documents\maya\2016\scripts :)

glm-nchaverou commented 7 years ago

Those will be sourced automatically If the AEXXTemplate is loaded, you should see the following in the AE for this node: 2017-02-15 19_21_52-autodesk maya 2016 - not for resale_ untitled_ --- dummynode1 If the python UI file is loaded correctly, you should see this label on top : 2017-02-15 19_23_02-autodesk maya 2016 - not for resale_ untitled_ --- dummynode1

mottosso commented 7 years ago

Oops sorry, assumed you're using Maya regularly :)

Haha, I do. Just not AETemplates.

If the AEXXTemplate is loaded, you should see the following in the AE for this node:

Ok, that did the trick. Thanks.

The Python file had a reference to Qtpy.Qt that I changed into Qt which got the error to stop appearing.

How can I reload the AETemplate? Do I restart Maya each time?

glm-nchaverou commented 7 years ago

Ah yes sorry !! I should have warned about Qt.py :) but, well, looks like you're used to use it :) DummyNode_updateQT gets called every time you unselect / reselect the node. Just source a function within the same name in the Script Editor to overwrite it :) The rest is static and will require you to restart Maya I'm afraid

mottosso commented 7 years ago

Ok, here we go.

from Qt import QtWidgets, QtCore

# Cache main window so that it is only computed once.
app = QtWidgets.QApplication.instance()
window = {o.objectName(): o for o in app.topLevelWidgets()}["MayaWindow"]

def buildQT(layout, node):
    """Build QT Widget"""

    ae = window.findChild(QtCore.QObject, "MainAttributeEditorLayout")
    form = ae.findChild(QtCore.QObject, "AEcontrolFormLayout")

    # The last element of the AE layout is expected to differ across runs
    # or opening/closing of the AE, so we grab the last entry.
    mayaLayout = form.findChild(QtCore.QObject, layout.rsplit("|", 1)[-1])

    mayaLayoutObj = mayaLayout.findChild(QtWidgets.QBoxLayout)

    widget = QtWidgets.QLabel()
    widget.setText('my uber awesome label')

    mayaLayoutObj.insertWidget(0, widget)

def updateQT(layout, node):
    pass

No other change to the template or plug-in.

The trick here was to look at the value of layout, which was this.

MayaWindow|MainAttributeEditorLayout|formLayout2|AEmenuBarLayout|AErootLayout|AEStackLayout|AErootLayoutPane|AEbaseFormLayout|AEcontrolFormLayout|AttrEdDummyNodeFormLayout|scrollLayout2|columnLayout4|frameLayout29|columnLayout5|columnLayout6

It's the location of the Qt widget in the widget hierarchy. With this information, we spot a few key members, such as MainAttributeEditor and right before getting to DummyNode specifically, a layout called AEcontrolFormLayout.

Assuming these are fixed and consistent across versions of Maya and user sessions, the only remaining thing to do was to grab the layout, which is expected to have a unique name within this narrow space. The rest is noise.

Let me know what you think.

glm-nchaverou commented 7 years ago

Looks good enough to me. I'll have to check that it's consistent through all existing versions of Maya and upcoming ones :) Thanks a ton for the handy tip

mottosso commented 7 years ago

If you wanted to play it safe, you could traverse the hierarchy as given. I'd imagine this is what Maya's findLayout is doing under the hood.

This runs in the Maya Script Editor, though odds are the final element will differ because of the dynamic generation of Maya's Attribute Editor. The concept however is sound.

from Qt import QtWidgets, QtGui, QtCore

hierarchy = "MayaWindow|MainAttributeEditorLayout|formLayout2|AEmenuBarLayout|AErootLayout|AEStackLayout|AErootLayoutPane|AEbaseFormLayout|AEcontrolFormLayout|AttrEdDummyNodeFormLayout|scrollLayout2|columnLayout4|frameLayout29|columnLayout5|columnLayout6"
window = {o.objectName(): o for o in QtWidgets.QApplication.instance().topLevelWidgets()}["MayaWindow"]

# Previous method
ae = window.findChild(QtCore.QObject, "MainAttributeEditorLayout")
form = ae.findChild(QtCore.QObject, "AEcontrolFormLayout")
layout = form.findChild(QtCore.QObject, hierarchy.rsplit("|", 1)[-1])

# Safe method
parent = window
for child in hierarchy.split("|")[1:]:
  parent = parent.findChild(QtCore.QObject, child)

# Prove that the results are identical
assert parent == layout

The final child is guaranteed to exist, as it is given by Maya. As your code runs synchronously, there is no chance of the hierarchy changing whilst it runs.

Having said that, I'm sure there is a better way of achieving your goal if we aren't trying to mimic code written specifically for use with shiboken/sip.

glm-nchaverou commented 7 years ago

Hi, Jumping back on this again (yeah I know, sorry :)) While the solution proposed worked efficiently for docked main attribute editor, it doesn't for floating Attribute Editors... Maya allows to use the Copy Tab button of an AE to create a dupplicated floating AE window

Thus a Layout which is named..

MayaWindow|MainAttributeEditorLayout|formLayout87|AEmenuBarLayout|AErootLayout|AEStackLayout|AErootLayoutPane|AEbaseFormLayout|AEcontrolFormLayout|AttrEdCrowdChOpSensorInputFormLayout|scrollLayout2|columnLayout5|frameLayout37|columnLayout8|columnLayout9

..in the Main AE is now named..

MayaWindow|nexFloatWindow|paneLayout1|scrollLayout1|formLayout46|formLayout48|frameLayout9|frameLayout10|formLayout49

...in a floating one.

OpenMayaUi findLayout works like a charm in this situation.

Editor note: Formatted long layout names

mottosso commented 7 years ago

Jumping back on this again (yeah I know, sorry :))

Not a problem! This isn't over until we've found at least 1 case where a cross-compatible solution is not possible.

If you could post an example of what doesn't work, I'd be happy to take a closer look.

dgovil commented 7 years ago

Just wanted to add my 2c. I think it would be great if there was an abstraction to wrapInstance as we use it in a few tools especially when we mix Python/C++ Qt, and it's not just limited to Maya.

There are actually quite a few tools that are useful in sip/shiboken unfortunately they don't keep those consistent which is annoying. But wrapInstance/wrapinstance would definitely get the most mileage.

I think @boglin had the best implementation for this: https://github.com/boglin/Qt.py/tree/c_binding We might merge that into our internal copy of SpiQt because it would definitely be handy.

mottosso commented 7 years ago

Thanks @dgovil.

To everyone, please let me know if this is just me.

So far, there are 46 comments in this thread. Half of them is requests for wrapInstance with Qt.py, and the other half is me saying no.

Now I would be most delighted to include something I felt was worth including, but that worth can't be built on anything but communication. The request you are making is nonsensical and unnecessary.

So tell me, what would you do?

Against my better judgement and due to the overwhelming number of requests, I've made a PR just now with a cross-compatible solution that I hope will live up to your expectations.

dgovil commented 7 years ago

I agree with you on that ideally Qt.py should not have to handle it, since like you said there are other ways to achieve the same.

Unfortunately the other methods require some form of traversal of hierarchies or knowing what elements are called (which may change in the future) whereas if there's a guaranteed way to get a pointer from c++ or Python (for example with Maya's getMainWindow function) , the directness would be worth it.

There's also the matter of education. The majority of reference and experience so far online says to use wrapInstance. While alternatives exist like you suggested, people will end up using sip/shiboken directly which kind of negates some of the benefits of qt.py in not having to worry about what binding is being used.

So yes I totally agree with you on that there are better ways to do it, but I think it's just so common that it would be beneficial to have it here. I realize that potentially opens the floodgate of "well what about this feature?" But I think that the rest of sip/shiboken are so incompatible it won't happen.

Your PR looks like it hits the sweet spot of giving the most bang for the buck.

ben-hearn-sb commented 7 years ago

Hello all,

Sorry for the late replies we have jumped back onto Maya2017 integration here at the studio and I am ready to give my 2cents back to the discussion. I am testing the proposed solutions now. They work for the statusline and the main maya window although @mottosso suggestion does not work for me at the moment. Is it absolutely necessary to replace the Shiboken stuff since I am only using it for the statusline?

I am getting the main Maya window and storing it like so:

def storeMayaWindow():
  ptr = apiUI.MQtUtil.mainWindow()
  if ptr is not None:
    app = QtWidgets.QApplication.instance()
    global_config.mayaMainWindow = {o.objectName(): o for o in app.topLevelWidgets()}["MayaWindow"]

and returning my statusline like so:

def getMayaStatusLine():
  gStatusLine = mel.eval('$temp=$gStatusLineForm')
  ptr = apiUI.MQtUtil.findControl(gStatusLine)
  if ptr is not None:
    statusLine = shiboken2.wrapInstance(long(ptr), QtWidgets.QWidget)
    #statusLine = c_binding.wrapInstance(long(ptr), QtWidgets.QWidget)
    return statusLine.children()[1].children()[1]
mottosso commented 7 years ago

Hi @ben-hearn-sb,

@mottosso suggestion does not work for me at the moment.

Could you let me know what exactly isn't working?