How to show progress bar while saving file to excel in python?

血红的双手。 提交于 2021-02-02 08:35:32

问题


Appreciate if you could help me. I have a trouble showing up the progress bar while saving file to excel. What I want to achieve is to show a progress bar while saving excel file from a pandas dataframe also from qwidgettable as it takes time before it saves. Until the excel file is downloaded or saved i want that progress bar to close. I tried looking over the net but I cant see specific answers to my query. So far, this is the compiled codes I have created.

import sys
from PyQt5 import QtWidgets, QtCore
import pandas as pd
import time
import psutil


class ThreadClass(QtCore.QThread):
   updateProgressBar = QtCore.pyqtSignal(int)

   def __init__(self, parent=None):
       super(ThreadClass, self).__init__(parent)

   def run(self):
       while True:
           val = int(psutil.cpu_percent())
           time.sleep(1)
           self.updateProgressBar.emit(val)

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.setGeometry(50,50,500,500)
        self.setWindowTitle('PyQt Tuts')
        self.table()

    def updateProgressBar(self, val):
        self.progressBar.setValue(val)

    def table(self):
        self.tableWidget = QtWidgets.QTableWidget()
        self.tableWidget.setGeometry(QtCore.QRect(220, 100, 411, 392))
        self.tableWidget.setColumnCount(2)
        self.tableWidget.setRowCount(5)
        self.tableWidget.show()

        item = QtWidgets.QTableWidgetItem()
        item.setText("Amount")
        self.tableWidget.setHorizontalHeaderItem(1, item)

        records = [
            ['Product 1', 1000],
            ['Product 2', 500],
            ['Product 3', 600],
            ['Product 4', 300],
            ['Product 5', 800],
        ]

        self.df = pd.DataFrame(records, columns=['Name', 'Amount'])

        for r in range(5):
            for c in range(2):
                table_item = str(self.df.iloc[r, c])
                self.tableWidget.setItem(r, c, QtWidgets.QTableWidgetItem(table_item))

        self.pb_extract = QtWidgets.QPushButton(self.tableWidget)
        self.pb_extract.setGeometry(QtCore.QRect(10, 200, 75, 23))
        self.pb_extract.clicked.connect(self.extract)
        self.pb_extract.setText("EXTRACT")
        self.pb_extract.show()

    def extract(self):
        self.lb_downloading = QtWidgets.QLabel(self.tableWidget)
        self.lb_downloading.setGeometry(QtCore.QRect(10, 270, 81, 16))
        self.lb_downloading.setText("Downloading..")
        self.lb_downloading.show()

        self.progressBar = QtWidgets.QProgressBar(self.tableWidget)
        self.progressBar.setGeometry(QtCore.QRect(10, 290, 171, 10))
        self.progressBar.show()

        self.threadclass = ThreadClass()
        self.threadclass.start()
        self.threadclass.updateProgressBar.connect(self.updateProgressBar)

        self.df.to_excel('Products.xlsx', index=False)
        print('Download complete!')

def run():
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle("fusion")
    w = Window()
    sys.exit(app.exec_())

run()

these codes look like this:

what i want to achieve is when i click the extract button, the downloading progressbar will close until the excel file fully downloaded/saved.

(P.S i just get random values for val = int(psutil.cpu_percent()) because i also don't know what specific code/function to use while the app is running just to show to you that i have a progress bar moving.)

Thank you in advance!


回答1:


These types of questions have been asked countless times in SO, and many times the requirements have been explained in the comments explaining in which case it is possible, and in which cases it is not possible. So to avoid repeating the same thing, I will explain this topic in this post based on the OP's question.

A widget in general is used to show and/or obtain information from the user, and a QProgressBar does the first thing, that is, it shows the progress information, it does not calculate it.

Progress can be calculated if the task can be subdivided into "n" subtasks, since it would be equivalent to the number of subtasks already done with respect to the number of total subtasks.

  • For example if the task is to upload a N KB file to a server, then each subtask can be 1KB of information, so the progress would be:

    progress = 100 * number_of_KB_submitted/number_of_KB_of_file
    
  • Another example would be if you have to copy n files, then the progress would be:

    progress = 100 * number_of_copied_files / number_of_total_files
    

From the above it is obvious that progress can only be calculated if the task can be subdivided into subtasks, so if the task cannot be subdivided then it is impossible to calculate any progress.

In the case of saving the pandas in an excel it is obvious that it cannot be subdivided into "n" tasks so it will be impossible to calculate its progress.

In the case of saving the pandas in an excel using to_excel it is obvious that it cannot be subdivided into "n" tasks so it will be impossible to calculate its progress.

A workaround in those cases is to show a busy QProgressBar:

progressbar.setRange(0, 0)

In your case:

import sys
from PyQt5 import QtWidgets, QtCore
import pandas as pd
import time

import threading


class ExcelWorker(QtCore.QObject):
    started = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()

    def execute(self, df, filename):
        threading.Thread(target=self._execute, args=(df, filename), daemon=True).start()

    def _execute(self, df, filename):
        self.started.emit()
        df.to_excel(filename, index=False)
        self.finished.emit()


class DownloaderProgressBar(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(DownloaderProgressBar, self).__init__(parent)

        self._progressbar = QtWidgets.QProgressBar()

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(QtWidgets.QLabel(self.tr("Downloading..")))
        lay.addWidget(self.progressbar)

    @property
    def progressbar(self):
        return self._progressbar


class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.setGeometry(50, 50, 500, 500)
        self.setWindowTitle("PyQt Tuts")
        self.create_table()

        self.create_progressbar()
        self.create_worker()

    def create_progressbar(self):
        self.downloader_progressbar = DownloaderProgressBar(self.tableWidget)
        self.downloader_progressbar.setGeometry(10, 270, 170, 80)
        self.downloader_progressbar.hide()

    def create_worker(self):
        self.worker = ExcelWorker()
        self.worker.started.connect(self.on_started)
        self.worker.finished.connect(self.on_finished)

    def create_table(self):
        self.tableWidget = QtWidgets.QTableWidget()
        self.tableWidget.setGeometry(QtCore.QRect(220, 100, 411, 392))
        self.tableWidget.setColumnCount(2)
        self.tableWidget.setRowCount(5)
        self.tableWidget.show()

        item = QtWidgets.QTableWidgetItem()
        item.setText("Amount")
        self.tableWidget.setHorizontalHeaderItem(1, item)

        records = [
            ["Product 1", 1000],
            ["Product 2", 500],
            ["Product 3", 600],
            ["Product 4", 300],
            ["Product 5", 800],
        ]

        self.df = pd.DataFrame(records, columns=["Name", "Amount"])

        for r in range(5):
            for c in range(2):
                table_item = str(self.df.iloc[r, c])
                self.tableWidget.setItem(r, c, QtWidgets.QTableWidgetItem(table_item))

        self.pb_extract = QtWidgets.QPushButton(self.tableWidget)
        self.pb_extract.setGeometry(QtCore.QRect(10, 200, 75, 23))
        self.pb_extract.clicked.connect(self.extract)
        self.pb_extract.setText("EXTRACT")
        self.pb_extract.show()

    def extract(self):
        self.worker.execute(self.df.copy(), "Products.xlsx")
        self.downloader_progressbar.show()

    @QtCore.pyqtSlot()
    def on_started(self):
        self.downloader_progressbar.progressbar.setRange(0, 0)

    @QtCore.pyqtSlot()
    def on_finished(self):
        self.downloader_progressbar.progressbar.setRange(0, 1)


def run():
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle("fusion")
    w = Window()
    sys.exit(app.exec_())


run()


来源:https://stackoverflow.com/questions/62173575/how-to-show-progress-bar-while-saving-file-to-excel-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!