import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QFileDialog, QProgressDialog, QWidget
from PyQt5.QtCore import pyqtSignal, QObject, QThread
from openpyxl import load_workbook, Workbook
class ExcelMerger(QObject):
progressChanged = pyqtSignal(int)
mergeFinished = pyqtSignal()
def merge_excel(self, files, progress_callback):
wb_combined = Workbook()
ws_combined = wb_combined.active
for index, filename in enumerate(files, 1):
progress = int((index / len(files)) * 100)
progress_callback.emit(progress)
wb = load_workbook(filename, read_only=False)
ws = wb.active
for row in ws.iter_rows(values_only=True):
ws_combined.append(row)
wb_combined.save("merged_file.xlsx")
self.mergeFinished.emit()
class MergeThread(QThread):
def __init__(self, files, excel_merger, progress_callback):
super().__init__()
self.files = files
self.excel_merger = excel_merger
self.progress_callback = progress_callback
def run(self):
self.excel_merger.merge_excel(self.files, self.progress_callback)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
self.excel_merger = ExcelMerger()
self.excel_merger.progressChanged.connect(self.on_progress_changed)
self.excel_merger.mergeFinished.connect(self.on_merge_finished)
def initUI(self):
self.setWindowTitle('Excel Merger')
self.setGeometry(100, 100, 300, 200)
layout = QVBoxLayout()
self.merge_btn = QPushButton('Merge Excel Files', self)
self.merge_btn.clicked.connect(self.merge_files)
layout.addWidget(self.merge_btn)
self.widget = QWidget()
self.widget.setLayout(layout)
self.setCentralWidget(self.widget)
def merge_files(self):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
files, _ = QFileDialog.getOpenFileNames(self, "Select Excel files to merge", "", "Excel Files (*.xlsx *.xls)", options=options)
if files:
self.progress = QProgressDialog(self)
self.progress.setLabelText("Merging files...")
self.progress.setCancelButton(None)
self.progress.setRange(0, 100)
self.progress.show()
self.thread = MergeThread(files, self.excel_merger, self.excel_merger.progressChanged)
self.thread.start()
def on_progress_changed(self, value):
self.progress.setValue(value)
def on_merge_finished(self):
self.progress.close()
print("Files merged successfully.")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
I tried to separate the merging thread and set up the main window, and then run the application's event-loop. But the progress bar is stopping before the file merging is fully complete.
In this PyQt5 code, I am trying to merge multiple excel files. The files are merging properly, but the progress bar that is displaying the percentage of files uploaded is closing before all the files are getting merged. I don't understand why this is happening.
The reason why the progress-dialog appears to stop early is that, by default, it will immediately auto-reset once it reaches the maximum, and this will automatically close the dialog as well. So the simplest solution to your problem is to make the following change:
However, your current code is also a lot more convoluted than it needs to be, and needlessly creates a new dialog and a new thread every time a merge is started. I have therefore provided a simplified version of your example below. Hopefully the changes I have made are self-explanatory: