Fixing Threads in PySide2

2020-05-03 10:41发布

I have programmed an application using PySide2 with some 800 lines of code and now when I want to show a variable in the progress bar it crashes after a short while without any warning. Silently. Just now it dawned on me that my whole approach to this GUI building is probably incorrect. Can this code be saved somehow so that it can via signals set this progress bar from inside the thread without the application crashing?

EDIT: this minimal code works and crashes but needs a small ui file. Just copy the second code below in a notepad and save it as "test_minim.ui". It crashes maybe after a minute with no warning.

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

Here the ui file opened for notepad. Needs to be saved as "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>

Kind regards

1条回答
啃猪蹄的小仙女
2楼-- · 2020-05-03 11:24

Try using QThreads instead of regular threads. They work in a very similar fashion, but they provide thread safe invoke to the UI thread.

Example

查看更多
登录 后发表回答