Исправление потоков в PySide2 - PullRequest
0 голосов
/ 24 февраля 2020

Я запрограммировал приложение, использующее PySide2, с примерно 800 строками кода, и теперь, когда я хочу показать переменную в индикаторе выполнения, оно через некоторое время вылетает без предупреждения. Молча. Только сейчас до меня дошло, что весь мой подход к этому GUI зданию, вероятно, неверен. Можно ли каким-то образом сохранить этот код, чтобы он мог с помощью сигналов установить этот индикатор выполнения изнутри потока без сбоев приложения?

РЕДАКТИРОВАТЬ: этот минимальный код работает и падает, но нуждается в небольшом файле пользовательского интерфейса. Просто скопируйте второй код ниже в блокноте и сохраните его как «test_minim.ui». Это может произойти через минуту без предупреждения.

import time
import os
import sys
import tempfile
import pkgutil
import numpy as np

import threading

from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QPushButton, QLineEdit, QProgressBar
from PySide2.QtCore import QFile, QObject, QThread
from PySide2.QtGui import qApp

class MyMainWindow(QObject):

    def __init__(self, ui_file, parent=None):
        super(MyMainWindow, self).__init__(parent)

        self.my_package_name = "test_minim"

        self.window = self.load_ui("test_minim.ui")

        self.startButton  = self.window.findChild(QPushButton, 'startButton')
        self.startButton.clicked.connect(self.start)

        self.stopButton  = self.window.findChild(QPushButton, 'stopButton')
        self.stopButton.clicked.connect(self.stop)

        self.colCentral  = self.window.findChild(QProgressBar, 'colCentral')

        self.newLSF = False
        self.LSF = 0

        self.run = False


    def load_ui(self, filename):
        # We might run from a PYZ file, but QUiLoader requires a plain file,
        # so extract it to a temporary file.
        temp = tempfile.NamedTemporaryFile(delete=False)
        temp.write(pkgutil.get_data(self.my_package_name, filename))
        temp.close()
        ui_file = QFile(temp.name)    
        ui = QUiLoader().load(ui_file)    
        ui_file.close()
        ui_file.remove()

        return ui

    def show(self):
        print("Showing...")
        self.window.show()        


    def start(self):
        self.run = True
        calculateLSFThread = threading.Thread(None, self.calculateLSF)
        displayThread = threading.Thread(None, self.displayBar)

        calculateLSFThread.start()
        displayThread.start()

    def calculateLSF(self):
       while self.run:
           time.sleep(0.3)
           #some processing goes here
           #takes a while to compute
           self.LSF = 50 + int(80*(np.random.rand(1)-0.5))
           self.newLSF = True 

    def displayBar(self):
       while self.run == True:
           if not self.newLSF:
               time.sleep(0.01)
           else:
               self.colCentral.setValue(self.LSF)
               self.newLSF = False
    def stop(self):
        self.run = False

def start_gui():


    app = QApplication.instance()
    if not app:
        # Instanciate QApplication singleton if it doesn't exist. It might
        # already exist, for instance starting application again in Spyder
        # in the same IPython shell
        app = QApplication(sys.argv)
    main_window = MyMainWindow("prime_gui.ui")
    main_window.show()
    return app.exec_()


if __name__ == '__main__':
    start_gui()

Здесь файл интерфейса, открытый для блокнота. Необходимо сохранить как "test_minim.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>289</width>
    <height>531</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <property name="windowOpacity">
   <double>1.000000000000000</double>
  </property>
  <property name="layoutDirection">
   <enum>Qt::LeftToRight</enum>
  </property>
  <property name="animated">
   <bool>false</bool>
  </property>
  <property name="tabShape">
   <enum>QTabWidget::Rounded</enum>
  </property>
  <property name="dockOptions">
   <set>QMainWindow::AllowTabbedDocks</set>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="startButton">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>40</y>
      <width>71</width>
      <height>23</height>
     </rect>
    </property>
    <property name="autoFillBackground">
     <bool>false</bool>
    </property>
    <property name="text">
     <string>Start </string>
    </property>
    <property name="checkable">
     <bool>false</bool>
    </property>
   </widget>
   <widget class="QPushButton" name="stopButton">
    <property name="enabled">
     <bool>true</bool>
    </property>
    <property name="geometry">
     <rect>
      <x>160</x>
      <y>40</y>
      <width>71</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>Stop</string>
    </property>
   </widget>
   <widget class="QGroupBox" name="groupBoxMTF">
    <property name="geometry">
     <rect>
      <x>40</x>
      <y>90</y>
      <width>192</width>
      <height>301</height>
     </rect>
    </property>
    <property name="title">
     <string>LTF</string>
    </property>
    <layout class="QGridLayout" name="gridLayout_3">
     <item row="0" column="0" colspan="2">
      <layout class="QGridLayout" name="layoutDisplayMTF">
       <item row="0" column="0">
        <widget class="QProgressBar" name="colCentral">
         <property name="maximum">
          <number>100</number>
         </property>
         <property name="value">
          <number>0</number>
         </property>
         <property name="textVisible">
          <bool>true</bool>
         </property>
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="invertedAppearance">
          <bool>false</bool>
         </property>
         <property name="textDirection">
          <enum>QProgressBar::TopToBottom</enum>
         </property>
        </widget>
       </item>
      </layout>
     </item>
    </layout>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>289</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

С уважением

1 Ответ

1 голос
/ 17 марта 2020

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

Пример

...