Wait for a function to finish executing before calling another function

588 views Asked by At

I am building a Windows 10 GUI using PyQT5. The application allows the user to click a button, select an XML file that contains a bunch of file names, then creates a ZIP file from those names.

I have a working bare bones Python script with no GUI.

Problems arise when the PyQT5 GUI is added. I am using a QT listWidget to show the file names contained in the XML file. A button press event in the main window calls function "A" to prompt the user for the XML file and populate the QT listWidget.

If I add the code to create the ZIP file to the end function "A", everything works fine, but the QT listWidget stays blank for a long time, making it appear the program is unresponsive. (There is apparently no way to update, repaint, or refresh the QT listWidget before a function finishes.)

I would like to call function "A" to get the XML file and populate the QT listWidget, then call function "B" to create the ZIP file.

How can I execute function "B" only after function "A" has finished?

Minimum Reproducible Code is

# Python script to read HEC-RAS RasMapper (*.rasmapper) file and create a single zip file of all applicable related files and directories.
import tkinter as tk
from tkinter import filedialog
import os
from pathlib import Path
from zipfile import ZipFile
from xml.etree import ElementTree as ET
import re
global FilesToPack, RASpath

#The code below was generated by QT Designer////////////////////////
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1000, 700)
        MainWindow.setMinimumSize(QtCore.QSize(1200, 700))
        MainWindow.setMaximumSize(QtCore.QSize(3000, 2000))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
        self.formLayout.setObjectName("formLayout")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setMinimumSize(QtCore.QSize(200, 0))
        self.pushButton.setBaseSize(QtCore.QSize(0, 0))
        self.pushButton.setObjectName("pushButton")
        self.pushButton.setText("Begin Packing")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton)
        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget.setObjectName("listWidget")
        self.formLayout.setWidget(5, QtWidgets.QFormLayout.SpanningRole, self.listWidget)
        MainWindow.setCentralWidget(self.centralwidget)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        #The code above was generated by QT Designer////////////////////////
        root = tk.Tk()
        root.withdraw()

        self.pushButton.clicked.connect(self.PackingProcess)

        self.PackFile()  #Need this function to execute only after PackingProcess function has set FilesToPack and RASpath

    def PackingProcess(self):
        self.listWidget.clear()
        RASmapfile = filedialog.askopenfilename(title="Select the HEC-RAS RasMapper file to process", filetypes=[("RasMapper file","*.rasmap")])
        self.listWidget.addItem(RASmapfile)
        RASpath = str(Path(RASmapfile).parent)
        # Using dummy filenames here for Minimum Reproducible Code to avoid having to post large data files
        FilesToPack = ["File1", "File2", "File3", "File4"]
     
        for f in FilesToPack:
            self.listWidget.addItem(f)
            
    def PackFile(self):
        #Put the files into a single ZIP file
        PackedZipFileName = filedialog.asksaveasfile(mode='w', title="Save the HEC-RAS RasMapper ZIP file as...", filetypes=[("ZIP file","*.zip")], defaultextension=".zip")
        zipObj = ZipFile(PackedZipFileName.name, mode='w')
        for f in FilesToPack:
            zipObj.write(f, f[len(RASpath):] if f.startswith(RASpath) else f)   # eliminate long path prefix so files are packed in relative address form
        zipObj.close()

    def addItem(self,textitem):
        self.listWidget.addItem(textitem)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
1

There are 1 answers

0
Vlad On

You could wrap your functions within a function which is called by the button press:

Pseudo Code:

@buttonclick():
    do function A
    populate list
    do function B