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