Seems like you got a good solution that suits your use case. Have you
played with the idea of making your QMessageBox parented to
your ImportFileUI, and then only having to do the connection if the active
modal widget is a child of your target UI? Maybe it doesn't matter. But I
thought it would mean if someone was using a normal file dialog, or some
other tool did a file operation that triggered your scriptJob, that you
wouldn't really care if the modal widget was some other widget as opposed
to yours. The only reason you are doing all of this extra logic is because
you are trying to delete your own known widget while it might be in the
middle of an operation.

On Thu, Sep 24, 2020 at 3:33 AM Enrico Losavio <[email protected]>
wrote:

> Hi Justin,
>
> Thank you so much for your reply.
>
> Sorry for not providing better code. It was meant mainly to recreate the
> issue rather than describe my intent. I tried to keep it as short and
> readable as possible.
>
> The original ui needs to be updated any time a new scene is
> loaded/saved/imported, and since this can also happen outside my code I
> need the callback.
> I tried also your solution of adding the *self.deleteLater()*
> instruction. It can be a good fix in some situations, but it would not
> suffice if more ui methods were called after the callback is fired.
>
> I've been researching and testing a bit more. I tried playing around with
> threading but that only led me to a deadlock. However maybe I came up with
> a solution which seems to be a bit more flexible: I've found a function
> *QApplication.activeModalWidget()* to get any existing (top-level) modal
> dialogs. So when the callback is fired I check if a modal dialog exists,
> and if so I connect its *finished* signal to the callback function. In
> this context, the *executeDeferred()* function seems to actually delay
> the execution of the callback and prevent maya from crashing.
>
>
> def create_ui():
>
>     def callback():
>         # checks whether a modal dialog exists
>         modal_dialog = QtWidgets.QApplication.activeModalWidget()
>         if modal_dialog:
>             # connects the ui's 'finished' signal to the (deferred)
> callback
>             modal_dialog.finished.connect(lambda: maya.utils.
> executeDeferred(callback))
>             return
>         # call the ui creation function (only if a modal dialog doesn't
> exist)
>         maya.utils.executeDeferred(create_ui)
>
>     if pm.control(ui_name, exists=True):
>         pm.deleteUI(ui_name)
>     ui = ImportFileUI(get_maya_window())
>     ui.show()
>
>     pm.scriptJob(e=["PostSceneRead", callback], parent=ui_name)
>
>
> I've tested also adding a bunch of instructions between the *QMessageBox*
> and the *self.close()*, and still the callback waits until all the
> commands are executed!
>
>
>
>
> def onBtnClicked(self):
>
>     pm.importFile(self.file_path, force=True)
>
>     QtWidgets.QMessageBox.information(get_maya_window(), "Info", "Scene
> was imported correctly")
>
>     for i in range(1000):
>
>         print(self.objectName, i)
>     self.close()
>
>
> To me this seems a good solution, but I'd be curious to hear any feedback!
>
> Enrico
>
>
> On Tuesday, September 22, 2020 at 8:43:42 PM UTC+2, Justin Israel wrote:
>>
>>
>>
>> On Wed, Sep 23, 2020 at 4:55 AM Enrico Losavio <[email protected]>
>> wrote:
>>
>>> Hi all,
>>>
>>> I write here because I'm dealing with an issue that makes my maya crash
>>> all the times.
>>> I have found a workaround (explained later), but I'm still curious to
>>> know if there's a better solution.
>>>
>>> Basically I want to display some messages to the user using a modal
>>> dialog (QMessageBox.information()).
>>> I have a function (called from a ui) that is used to import a file in
>>> the maya scene. This is working fine.
>>> I also have a callback attached to that piece of ui, which is meant to
>>> update it (by deleting it and recreating it) every time a file is imported.
>>> What happens is that when I click on the Import button, the file is
>>> imported, the QMessageBox is shown, the code execution is paused (because
>>> of the modal dialog), but the callback is fired anyways.
>>> Since the callback deletes (and recreates) the dialog, when the code
>>> finally reaches the ui "self.close()" instruction, the ui object was
>>> already destroyed - thus resulting in a *#* *RuntimeError: Internal C++
>>> object **(ImportFileUI) already deleted. # *or more often than not a
>>> sudden maya crash.
>>>
>>>
>>> I temporarily avoided this error by calling the QMessageBox.show()
>>> method, which does not halt the execution of the code.
>>>
>>> However, rather than finding more workarounds, I would like to find a
>>> solution and get to the bottom of this issue.
>>>
>>>
>>> Does anyone have an idea of how to make the callback "wait"? Are there
>>> better practices to deal with a similar situation?
>>> I tried to delay the execution of the callback using "evalDeferred()"
>>> and even "processIdleEvents()", but none of them really made any difference.
>>>
>>
>> Your example code may be oversimplified, leading to me missing your
>> actual intention. But based on your example it seems the question should
>> really be "how can I avoid the dialog widget crashing when it tries to
>> close after already being deleted?". If we look at the order of operations,
>> there is really no logical sense in trying to delay the scriptJob. It is
>> separate logic from the dialog which wants to happen in response to a Maya
>> event. It is a misconception that your modal information dialog is pausing
>> code execution. What it is actually doing is starting its own blocking
>> event loop at that point, which means the entire application can still
>> continue to function and process events. Only the next line in that scope
>> of the main thread is paused. As soon as you start the modal dialog, the
>> new event loop will process the scriptJob.
>> So now that we have addressed why the scriptJob callback still gets
>> executed before the end of that slot function scope, we could take a moment
>> to look at the suggestion of delaying the callback. If the callback is
>> defined outside the scope of this dialog, as in your simplified example, I
>> think it makes less sense to somehow try and defer it until after some
>> other UI logic in your dialog, because that defeats the point of the
>> callback. You might as well just not use the callback at all, and trigger
>> the create_ui function directly at the point you deem appropriate (after
>> the QMessageBox modal dialog has returned). But I will leave that
>> suggestion here for your consideration, if it makes sense to do so.
>> The simplest fix I can see to keeping the scriptJob callback as-is and
>> just preventing the close from crashing is to use deleteLater just before
>> you start the modal dialog:
>>
>>     def onBtnClicked(self):
>>         pm.importFile(self.file_path, force=True)
>>         self.deleteLater()
>>         QtWidgets.QMessageBox.information(get_maya_window(), "Info", "Scene 
>> was imported correctly")
>>
>> I don't know if this approach makes sense in the scope of your actual
>> production code. But it fixes the issue in the simplified version. I
>> figured if you already intended on destroying the dialog after the
>> QMessageBox anyways, that this would be equivalent. If this isn't
>> acceptable, then maybe you can expand your example to cover something
>> closer to your intentions.
>>
>> Justin
>>
>>
>>
>>>
>>> I have put together a few lines of codes to recreate the issue, and I
>>> tested it in Maya 2018.7 on Windows10.
>>>
>>>
>>>
>>> from PySide2 import QtCore, QtWidgets
>>> from maya.OpenMayaUI import MQtUtil
>>> from pymel import core as pm
>>> from shiboken2 import wrapInstance
>>>
>>> # this code is an overly simplified version of the original script
>>> # its only purpose is to recreate the issue described in the post above
>>> # paste it the script editor, *change the ui's file_path variable to an
>>> existing .ma file* , and run it
>>> #
>>> # the callback destroys the ui object, before the ui.close() instruction
>>> can be executed
>>> # causing a # RuntimeError: Internal C++ object (ImportFileUI) already
>>> deleted. # or often even a maya crash
>>> # how can this be prevented?
>>>
>>> ui_name = 'ImportFileUI'
>>>
>>> def get_maya_window():
>>>     pointer = MQtUtil.mainWindow()
>>>     return wrapInstance(long(pointer), QtWidgets.QMainWindow)
>>>
>>> class ImportFileUI(QtWidgets.QDialog):
>>>     def __init__(self, parent):
>>>         super(ImportFileUI, self).__init__(parent)
>>>         self.setObjectName(ui_name)
>>>
>>>         self.file_path = r"C:\example_file.ma"
>>>         self.btn = QtWidgets.QPushButton("Import File")
>>>         self.btn.clicked.connect(self.onBtnClicked)
>>>         self.setLayout(QtWidgets.QVBoxLayout())
>>>         self.layout().addWidget(self.btn)
>>>
>>>     def onBtnClicked(self):
>>>         pm.importFile(self.file_path, force=True)
>>>         QtWidgets.QMessageBox.information(get_maya_window(), "Info", "Scene
>>> was imported correctly")
>>>         self.close()
>>>
>>> def create_ui():
>>>     if pm.control(ui_name, exists=True):
>>>         pm.deleteUI(ui_name)
>>>     ui = ImportFileUI(get_maya_window())
>>>     ui.show()
>>>     pm.scriptJob(e=["PostSceneRead", create_ui], parent=ui_name)
>>>
>>> if __name__ == "__main__":
>>>     create_ui()
>>>
>>>
>>>
>>> Thank you for your help!
>>>
>>>
>>> Enrico
>>>
>>>
>>> --
>>> You received this message because you are subscribed to the Google
>>> Groups "Python Programming for Autodesk Maya" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> To view this discussion on the web visit
>>> https://groups.google.com/d/msgid/python_inside_maya/f4fcd5e0-00fa-4bb1-ad0d-feecc8504f25o%40googlegroups.com
>>> <https://groups.google.com/d/msgid/python_inside_maya/f4fcd5e0-00fa-4bb1-ad0d-feecc8504f25o%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>> --
> You received this message because you are subscribed to the Google Groups
> "Python Programming for Autodesk Maya" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/python_inside_maya/a8dc8dc2-82fd-4ac4-84bc-a6465d128124o%40googlegroups.com
> <https://groups.google.com/d/msgid/python_inside_maya/a8dc8dc2-82fd-4ac4-84bc-a6465d128124o%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/python_inside_maya/CAPGFgA2eZ2EDw8vV_ax8rMaLwJ%2BZVzy-pwcVnT7NbWL1avyFFQ%40mail.gmail.com.

Reply via email to