Почему мои QThreads постоянно сбивают Maya? - PullRequest
2 голосов
/ 07 октября 2019

У меня есть пользовательский интерфейс, с которым я хочу использовать многопоточность внутри Maya. Это делается для того, чтобы я мог запускать Maya.cmds без зависания / замораживания пользовательского интерфейса при обновлении пользовательского интерфейса с помощью индикаторов выполнения и т. Д.

Я прочитал несколько примеров из StackOverflow, но мой код падает каждую секундураз я бегу это. Вот примеры, которые я использовал: здесь и здесь

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()

Я ожидаю, что пользовательский интерфейс останется активным (не заблокированным) и потоки будут запускаться Mayacmds без сбоев Maya при обновлении индикаторов выполнения пользовательского интерфейса.

Любое понимание этого было бы удивительным!

1 Ответ

2 голосов
/ 07 октября 2019

Из того, что я вижу, есть следующие ошибки:

  • thread - это локальная переменная, которая удаляется, когда конструктор завершает выполнение, вызывая то, что выполняется вОсновной поток, который нежелателен, решение состоит в том, чтобы продлить жизненный цикл, и для этого есть несколько решений: 1) сделать атрибут класса, 2) передать родительский цикл в цикл жизни, которым они управляют родительским. В этом случае используйте второе решение.

  • Вы не должны изменять GUI из другого потока, в вашем случае вы изменили progressBar из другого потока, в Qt вы должны использовать сигналы.

  • Вы должны использовать декоратор @Slot в методах, выполняемых в другом потоке.

  • Вы указываете, что хотите изменить myTool но вы не объявили это, поэтому global myTool не будет работать, если сделать 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()
...