【问题标题】:Why are my QThreads consistently crashing Maya?为什么我的 QThreads 总是让 Maya 崩溃?
【发布时间】:2019-10-07 02:04:18
【问题描述】:

我有一个 UI,我想在 Maya 内部使用线程。这样做的原因是我可以运行 Maya.cmds 而无需挂起/冻结 UI,同时使用进度条等更新 UI。

我从 StackOverflow 中阅读了一些示例,但我的代码每运行一次就会崩溃。我遵循的示例是herehere

import maya.cmds as cmds
from PySide2 import QtWidgets, QtCore, QtGui, QtUiTools
import mainWindow #Main window just grabs the Maya main window and returns the object to use as parent.

class Tool(QtWidgets.QMainWindow):
    def __init__(self, parent=mainWindow.getMayaMainWindow()):
        super(Tool, self).__init__(parent)

        UI = "pathToUI/UI.ui"
        loader = QtUiTools.QUiLoader()
        ui_file = QtCore.QFile(UI)
        ui_file.open(QtCore.QFile.ReadOnly)
        self.ui = loader.load(ui_file, self)

        #Scans all window objects and if one is open with the same name as this tool then close it so we don't have two open.
        mainWindow.closeUI("Tool")      

        ###HERE'S WHERE THE THREADING STARTS###
        #Create a thread
        thread = QtCore.QThread()
        #Create worker object
        self.worker = Worker()
        #Move worker object into thread (This creates an automatic queue if multiples of the same worker are called)
        self.worker.moveToThread(thread)

        #Connect buttons in the UI to trigger a method inside the worker which should run in a thread
        self.ui.first_btn.clicked.connect(self.worker.do_something)
        self.ui.second_btn.clicked.connect(self.worker.do_something_else)
        self.ui.third_btn.clicked.connect(self.worker.and_so_fourth)

        #Start the thread
        thread.start()

        #Show UI
        self.ui.show()

class Worker(QtCore.QObject):
    def __init__(self):
        super(Worker, self).__init__() #This will immediately crash Maya on tool launch
        #super(Worker).__init__() #This works to open the window but still gets an error '# TypeError: super() takes at least 1 argument (0 given)'

    def do_something(self):
        #Start long code here and update progress bar as needed in a still active UI.
        myTool.ui.progressBar.setValue(0)
        print "doing something!"
        myTool.ui.progressBar.setValue(100)

    def do_something_else(self):
        #Start long code here and update progress bar as needed in a still active UI.
        myTool.ui.progressBar.setValue(0)
        print "doing something else!"
        myTool.ui.progressBar.setValue(100)

    def and_so_fourth(self):
        #Start long code here and update progress bar as needed in a still active UI.
        myTool.ui.progressBar.setValue(0)
        print "and so fourth, all in the new thread in a queue of which method was called first!"
        myTool.ui.progressBar.setValue(100)

#A Button inside Maya will import this code and run the 'launch' function to setup the tool
def launch():
    global myTool
    myTool = Tool()

我希望 UI 保持活动状态(未锁定)并且线程运行 Maya cmds 而不会在更新 UI 进度条时完全使 Maya 崩溃。

对此的任何见解都会令人惊叹!

【问题讨论】:

    标签: python python-2.7 maya qthread pyside2


    【解决方案1】:

    据我所知,它有以下错误:

    • thread 是一个局部变量,当构造函数完成执行时会被删除,导致在主线程中执行不希望的操作,解决方案是延长生命周期,为此有几种解决方案:1)制作类属性,2)将父级传递给它们由父级管理的生命周期。在这种情况下,请使用第二种解决方案。

    • 您不应该从另一个线程修改 GUI,如果您从另一个线程修改了 progressBar,在 Qt 中您必须使用信号。

    • 您必须在另一个线程中执行的方法中使用@Slot 装饰器。

    • 您表示要修改myTool,但您没有声明它,所以将myTool 设置为要删除的局部变量,global myTool 将不起作用。解决方案是声明myTool:myTool = None 综合以上,解决办法是:

    import maya.cmds as cmds
    from PySide2 import QtWidgets, QtCore, QtGui, QtUiTools
    import mainWindow  # Main window just grabs the Maya main window and returns the object to use as parent.
    
    
    class Tool(QtWidgets.QMainWindow):
        def __init__(self, parent=mainWindow.getMayaMainWindow()):
            super(Tool, self).__init__(parent)
    
            UI = "pathToUI/UI.ui"
            loader = QtUiTools.QUiLoader()
            ui_file = QtCore.QFile(UI)
            ui_file.open(QtCore.QFile.ReadOnly)
            self.ui = loader.load(ui_file, self)
    
            # Scans all window objects and if one is open with the same name as this tool then close it so we don't have two open.
            mainWindow.closeUI("Tool")
    
            # Create a thread
            thread = QtCore.QThread(self)
            # Create worker object
            self.worker = Worker()
            # Move worker object into thread (This creates an automatic queue if multiples of the same worker are called)
            self.worker.moveToThread(thread)
    
            # Connect buttons in the UI to trigger a method inside the worker which should run in a thread
            self.ui.first_btn.clicked.connect(self.worker.do_something)
            self.ui.second_btn.clicked.connect(self.worker.do_something_else)
            self.ui.third_btn.clicked.connect(self.worker.and_so_fourth)
    
            self.worker.valueChanged.connect(self.ui.progressBar.setValue)
    
            # Start the thread
            thread.start()
    
            # Show UI
            self.ui.show()
    
    
    class Worker(QtCore.QObject):
        valueChanged = QtCore.Signal(int)
    
        @QtCore.Slot()
        def do_something(self):
            # Start long code here and update progress bar as needed in a still active UI.
            self.valueChanged.emit(0)
            print "doing something!"
            self.valueChanged.emit(100)
    
        @QtCore.Slot()
        def do_something_else(self):
            # Start long code here and update progress bar as needed in a still active UI.
            self.valueChanged.emit(0)
            print "doing something else!"
            self.valueChanged.emit(100)
    
        @QtCore.Slot()
        def and_so_fourth(self):
            # Start long code here and update progress bar as needed in a still active UI.
            self.valueChanged.emit(0)
            print "and so fourth, all in the new thread in a queue of which method was called first!"
            self.valueChanged.emit(100)
    
    myTool = None
    
    def launch():
        global myTool
        myTool = Tool()
    

    【讨论】:

    • 哇,这太完美了!如果您不介意,我想问一些后续问题:您为什么将 self 传递给此? thread = QtCore.QThread(self) 我可以使用这些来发送其他变量,如列表、字典或字符串吗? @QtCore.Slot() QtCore.Signal(int) 为什么要添加这个?我的工具 = 无
    • 在一般情况下,除非在文档中说明,否则从另一个主线程访问或修改任何 Maya 节点是不保存的。线程保存 Maya 节点是个例外。如果您需要从另一个线程修改 maya,您应该使用空闲回调和线程保存队列系统,并从主线程进行任何修改。
    • @haggikrey 我也读过这个。因此,我正在考虑让 UI 在另一个线程中运行并且所有 Maya cmds 在主线程中运行的想法。我也可能把事情复杂化了。我真的只是希望 Maya cmds 工作并且 UI 完全活跃。
    猜你喜欢
    • 2011-05-31
    • 1970-01-01
    • 2011-08-08
    • 2012-12-14
    • 2020-07-28
    • 2023-03-24
    • 2011-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多