Графический интерфейс PyQt зависает до тех пор, пока потоки не будут завершены при многопоточности в Python

Код довольно тяжелый, поэтому я просто опубликую несколько фрагментов.

class Program(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Program, self).__init__(parent)

...

def main():   
    app = QtGui.QApplication(sys.argv)
    global ex
    ex = Program()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

А между ними у меня есть куча меток, текстовых полей, кнопок и 4 QListWidgets внутри вложенных макетов, эти вложенные макеты добавляются в сетку.

Я запускаю переменное количество потоков в зависимости от ввода. Обычно 4 потока. Они запускаются в собственном классе:

class myThread(Thread):
    def __init__(self, arguments, more_arguments):
        threading.Thread.__init__(self)

    def work_it(self, argument, more_arguments):
        Program.some_function_in_Program_class(ex, arguments)

И оттуда я вызываю функции внутри класса Program(), чтобы внести фактические изменения в графический интерфейс.

Program.generate_results(ex, arguments, arguments2, more_arguments)

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

my_listbox.addItem(item)

Он замораживает графический интерфейс до тех пор, пока все 4 потока не завершат просмотр списка. Тогда все результаты отображаются вместе, а не по одному.

Я сделал это в Tkinter, и я мог видеть, что один за другим элемент списка появляется в виджетах ListBox динамически, без зависания графического интерфейса.

Что касается управления потоками, то я делаю итерацию по списку и в зависимости от его длины создаю несколько потоков:

threadlist = []
for i in self.results:
    sub_thread = myThread(i, self.results[i])
    self.threadlist.append(sub_thread)
swapAndWaitThread = spawnAndWaitThreadsThread(self.threadlist)
swapAndWaitThread.start()

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

class spawnAndWaitThreadsThread(Thread):
    def __init__(self, threadlist):
        threading.Thread.__init__(self)
        self.threadlist = threadlist

    def run(self):
        for thread in self.threadlist:
            thread.start()

        for thread in self.threadlist:
            thread.join()

    print "threads finished.. do something"

Что я делаю не так?


person Onedot618    schedule 18.02.2015    source источник
comment
Не уверен, знаете ли вы это или нет, но простого наследования от Thread недостаточно, чтобы сделать фрагмент кода многопоточным. Например, для потоков, унаследованных от threading.thread, необходимо поместите нужный код в его метод run, а затем в какой-то момент вызовите start. Код, который появляется внутри, скажем, метода __init__, все равно будет выполняться в основном потоке.   -  person Kevin    schedule 18.02.2015
comment
Спасибо за ответ. Это немного сложнее. Я обновил свой вопрос, чтобы ответить на ваш комментарий. Сначала я составляю список потоков в моей функции класса Program. Затем я отправляю этот список в класс потока, который запускает и объединяет потоки, чтобы я мог узнать, когда все они будут завершены. И когда эти потоки запускаются (класс myThread), они снова вызывают некоторую функцию в классе Program, откуда я хочу обновить свой графический интерфейс.   -  person Onedot618    schedule 18.02.2015
comment
Возможно, я неправильно вызываю функции Program() из функций Thread()? Я боролся, потому что сначала я пытался вызвать их просто Program.some_function(аргументы).. но продолжал получать сообщение об ошибке о том, что экземпляр программы не передается. Поэтому мне пришлось придумать грязное (?) исправление, объявив ex глобальной переменной и передав ее в качестве аргумента экземпляра Program(). Это правильный способ сделать это?   -  person Onedot618    schedule 18.02.2015
comment
Официальный способ получить экземпляр программы для ваших потоков — добавить параметр в определение потока run и запустить потоки как thread.start(args = (my_program_instance,)), но глобальная переменная тоже должна работать. В целом ваш spawnAndWaitThreadsThread выглядит нормально. Я думаю, что следующее место, где я буду искать проблемы, это внутри myThread   -  person Kevin    schedule 18.02.2015
comment
Итак, если вы просматриваете сотни и сотни файлов (os.path.listdir), фильтруете их по некоторым расширениям, вытаскиваете их временные метки, объединяете результаты в строку, а затем пытаетесь написать одну за другой эти сотни строк на лету. для нескольких QListWidgets программа не должна зависать, верно?   -  person Onedot618    schedule 18.02.2015


Ответы (1)


Я решил эту проблему, добавив очень простую функцию в основной класс Program(), у которой есть только одна задача — запустить очень простой поток, который, в свою очередь, возвращается к классу Program() и вызывает там функцию, которая затем начинает создавать мои потоки. list, отправляя их в другой класс потоков и т. д.

В результате графический интерфейс больше не зависает.

person Onedot618    schedule 19.02.2015
comment
не врубился. пожалуйста, уточните пример, если вы не можете указать свой фактический код - person nish; 30.06.2020