@pypyshka

Как правильно вывести выполнение функций в отдельный поток, чтобы не блокировался интерфейс?

Добрый день. Есть небольшая программка на Python 3.4.4, которая парсит xml и сохраняет значения в БД. Так как xml-ки бывают довольно объёмные, то весь этот процесс занимает продолжительное время. Я попробовал выполнение функций с помощью threading - они выполняются, но интерфейс зависает:
def pars_xml():
....
def pars_xml_2():
....
app = QtGui.QApplication(sys.argv)
main_window = uic.loadUi("main.ui")
main_window.show()

thread_pars_xml = threading.Thread(target=pars_xml, name="pars_xml")
thread_pars_xml_2 = threading.Thread(target=pars_xml_2, name="pars_xml_2")
thread_pars_xml.start()
thread_pars_xml_2.start()
thread_pars_xml.join()
thread_pars_xml_2.join()

sys.exit(app.exec_())


Что можно в таком случае сделать?
Еще один момент: как лучше сделать выполнение этих функций с определенной периодичностью?
  • Вопрос задан
  • 435 просмотров
Решения вопроса 1
sergey-gornostaev
@sergey-gornostaev Куратор тега Python
Седой и строгий
Чтобы не залипал Qt-интерфейс следует воспользовать QThread

design.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>526</width>
    <height>373</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Threading Demo</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QLabel" name="label_log_list">
      <property name="text">
       <string>Отчёт:</string>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QListWidget" name="list_log"/>
    </item>
    <item>
     <widget class="QProgressBar" name="progress_bar">
      <property name="value">
       <number>0</number>
      </property>
     </widget>
    </item>
    <item>
     <layout class="QHBoxLayout" name="buttons_layout">
      <item>
       <widget class="QPushButton" name="btn_stop">
        <property name="enabled">
         <bool>false</bool>
        </property>
        <property name="text">
         <string>Стоп</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btn_start">
        <property name="text">
         <string>Старт</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>


qthread_demo.py
from PyQt4 import QtGui, uic
from PyQt4.QtCore import QThread, SIGNAL
import sys
import time

class xmlParserThread(QThread):
    def __init__(self, xml):
        QThread.__init__(self)
        self.xml = xml

    def __del__(self):
        self.wait()

    def run(self):
        for x in range(25):
            self.emit(SIGNAL('notify_progress(QString)'), str(x))
            self.sleep(1)

        
class ThreadingDemo(QtGui.QMainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        uic.loadUi('design.ui', self)
        self.btn_start.clicked.connect(self.start_getting_top_posts)

    def start_getting_top_posts(self):
        self.progress_bar.setMaximum(25)
        self.progress_bar.setValue(0)

        self.xml_parser_thread = xmlParserThread('some.xml')
        self.connect(self.xml_parser_thread, SIGNAL("notify_progress(QString)"), self.notify_progress)
        self.connect(self.xml_parser_thread, SIGNAL("finished()"), self.done)
        self.xml_parser_thread.start()

        self.btn_stop.setEnabled(True)
        self.btn_stop.clicked.connect(self.xml_parser_thread.terminate)
        self.btn_start.setEnabled(False)

    def notify_progress(self, counter):
        self.list_log.addItem(counter)
        self.progress_bar.setValue(self.progress_bar.value() + 1)

    def done(self):
        self.btn_stop.setEnabled(False)
        self.btn_start.setEnabled(True)
        self.progress_bar.setValue(0)
        QtGui.QMessageBox.information(self, "Done!", "Done parsing XML!")
    

def main():
    app = QtGui.QApplication(sys.argv)
    form = ThreadingDemo()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
mututunus
@mututunus
Backend developer (Python, Golang)
Читайте про QThread и QTimer.
Ответ написан
Комментировать
@pypyshka Автор вопроса
Спасибо за ответы. Вот что у меня получилось:
def pars_xml():
....

def pars_xml_2():
....

def do_it(sc):
    thread_pars_xml = threading.Thread(target=pars_xml, name="pars_xml")
    thread_pars_xml_2 = threading.Thread(target=pars_xml_2, name="pars_xml_2")
    thread_pars_xml.start()
    thread_pars_xml_2.start()
    thread_pars_xml.join()
    thread_pars_xml_2.join()
    sc.enter(60, 0, do_it, (sc,))

class MyThread(QtCore.QThread):
    def __init__(self, parent = None):
        QtCore.QThread.__init__(self, parent)

    def run(self):
        s = sched.scheduler(time.time, time.sleep)
        s.enter(60, 1, do_it, (s,))
        s.run()

app = QtGui.QApplication(sys.argv)
main_window = uic.loadUi("main.ui")
start_pars = MyThread()
start_pars .start()
main_window.show()
sys.exit(app.exec_())


После запуска программы через 1 секунду начинают выполняться функции по очереди с интервалом 60 секунд, в принципе, как я и хотел. Интерфейс при этом не залипает. Скажите, пожалуйста, правильна ли такая реализация, или можно проще сделать?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы