I have developed a GUI that is used to extract some data from a file. This process takes a long time. During the time when the program is processing the outside file (using subprocess.Popen), the main GUI will not be responsive (as expected). I wanted to add a message with an oscillating progress bar to the user to let him know the program is working and just to hang in there.
I implemented the progress bar as a QDialog and then call it before I call the long process. If I call the progress bar using the [dialog.exec_()] method, then the program waits for my response which is the outcome of the dialog. If I close the dialog the program continues on without the progress message I want to show. If I use the [dialog.show()] method, then all I see is an empty white window where the progress bar should be.
I tried multiple variations on this but to no success.
I am using:
Python 2.7.2
PyQt4: 4.9.1
Windows 7
Here is a sample code that shows the problem. Switch between dialog.exec() and dialog.show() to demo the problem.
import sys
import time
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import pyqtSignature
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class LongProcess(QtGui.QMainWindow):
def __init__(self, parent=None):
super(LongProcess, self).__init__(parent)
self.setup(self)
def setup(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(255, 208)
MainWindow.setMinimumSize(QtCore.QSize(300, 208))
MainWindow.setMaximumSize(QtCore.QSize(300, 208))
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.textEdit = QtGui.QTextEdit(self.centralwidget)
self.textEdit.setObjectName(_fromUtf8("textEdit"))
self.verticalLayout.addWidget(self.textEdit)
self.pushButton = QtGui.QPushButton(self.centralwidget)
self.pushButton.setObjectName(_fromUtf8("pushButton"))
self.verticalLayout.addWidget(self.pushButton)
MainWindow.setCentralWidget(self.centralwidget)
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.textEdit.setHtml(QtGui.QApplication.translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'MS Shell Dlg 2\'; font-size:8.25pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">We want to run a long process in the background (calling a subprocess) and use an oscillating progress bar to show that the process is running. </span></p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:8pt;\">When long process ends, we will close the oscillating progress bar. </span></p></body></html>", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton.setText(QtGui.QApplication.translate("MainWindow", "Run Long Process", None, QtGui.QApplication.UnicodeUTF8))
QtCore.QMetaObject.connectSlotsByName(MainWindow)
@pyqtSignature("")
def on_pushButton_clicked(self):
'''
Simulate some long process to be performed.
Before Long Process starts, show an oscillating progress bar to
indiate process is running in background.
'''
dialog = QtGui.QDialog()
progressBar = Ui_porcessProgress()
progressBar.setupUi(dialog)
dialog.show()
# dialog.exec_()
start = time.time()
diff = 0
while diff < 10:
end = time.time()
diff = end - start
print (diff)
class Ui_porcessProgress(object):
def setupUi(self, porcessProgress):
porcessProgress.setObjectName(_fromUtf8("porcessProgress"))
porcessProgress.setWindowModality(QtCore.Qt.ApplicationModal)
porcessProgress.resize(329, 81)
porcessProgress.setMinimumSize(QtCore.QSize(329, 81))
porcessProgress.setMaximumSize(QtCore.QSize(329, 81))
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/WFT/wftlogo2.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
porcessProgress.setWindowIcon(icon)
porcessProgress.setModal(True)
self.verticalLayout = QtGui.QVBoxLayout(porcessProgress)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.label = QtGui.QLabel(porcessProgress)
self.label.setObjectName(_fromUtf8("label"))
self.verticalLayout.addWidget(self.label)
self.porcessProgressBar = QtGui.QProgressBar(porcessProgress)
self.porcessProgressBar.setMaximum(0)
self.porcessProgressBar.setProperty("value", 10)
self.porcessProgressBar.setTextVisible(False)
self.porcessProgressBar.setObjectName(_fromUtf8("porcessProgressBar"))
self.verticalLayout.addWidget(self.porcessProgressBar)
porcessProgress.setWindowTitle(QtGui.QApplication.translate("porcessProgress", "Please wait...", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("porcessProgress", "Time History data extraction from ODB file in progress...", None, QtGui.QApplication.UnicodeUTF8))
QtCore.QMetaObject.connectSlotsByName(porcessProgress)
#
#------------------------------------------------------------------------------
#
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = LongProcess()
mainWindow.show()
sys.exit(app.exec_())
#
#------------------------------------------------------------------------------
#
if __name__ == "__main__":
main()
This is kinda quackery way to fix this issue.
If you want to do this right way, You should put while loop inside of QThread,
and using Signal and slot to update QProgressbar.
But this will fix your issue just fine for now.
Missing pieces were,
Quick fix :