使用tkinter和openpyxl遍历电子表格的最佳方法?

时间:2018-06-22 12:50:50

标签: python python-3.x optimization tkinter openpyxl

我是一名完全的Python初学者,试图制作一个允许用户选择一个Excel文件的GUI,从中可以提取和分析一些数据。对于GUI,我正在使用tkinter,对于Excel操作,我正在使用openpyxl。 我当前拥有的代码有效,但是当选择的Excel文件很大时,运行时就很长(10,000行x 20列电子表格需要大约一个小时才能遍历,随着时间的流逝,速度变慢了) 。同样,GUI在迭代过程中也停止响应。我以前使用MATLAB制作了几乎完全相同的程序,并且效果很好,整个迭代过程仅需一到两秒钟,因此肯定很有趣这里。现在我知道这是我的问题,而不是Python的问题,所以我希望你们能帮助我指出正确的解决方法。

以下是一些我正在使用的简化示例,以帮助说明我的处境。我需要遍历电子表格的每一行,检查该行中的某个值以查看它是否与我想要的匹配,如果匹配,我存储该行中的一些数据。

wb = openpyxl.load_workbook(filename = 'some_garbage.xlsx', read_only=True).worksheets[0]
all_data = wb.cell
desiredColor = 'Purple'
foodStorage = []

for row in range(2, wb.max_row + 1):
    print(row)
    if(all_data(row, 1).value == desiredColor):
        foodStorage.append(all_data(row,2))
        print('I found purple! Yee')

Example spreadsheet. Unfortunately my real data isn't about colors and foods

我应该使用openpyxl以外的其他模块来执行此任务,还是我只是在犯一些使openpyxl无法充分发挥作用的错误? 我应该使用线程来加快迭代过程,还是要确保在迭代过程中我的GUI窗口不会对我产生全部“不响应”?

我试图搜索有关类似问题的问题,但实际上并不能找到我想要的东西,这很可能是由于我缺乏将问题用恰当的语言表达的能力。因此,对于可能要回答一百万遍的问题,我事先深表歉意。感谢您能提供的任何帮助,并感谢您的宝贵时间。

1 个答案:

答案 0 :(得分:-1)

我正在研究类似的东西,但是数据更大。

我有一个类似的问题,我认为它的发生是因为处理器试图在同一线程上同时运行excel迭代和root.mainloop(),这不仅减慢了进程,而且使应用程序挂起GUI无响应。为了使其正常工作,您必须在与root.mainloop()不同的线程中调用worker(Iteration)函数。最好的方法是为GUI,Worker和App定义多个类,并在GUI在不同循环中运行时从App类中调用Worker函数线程。

相同的简单示例-

import tkinter as tk
from tkinter import ttk,messagebox
import threading
import time

#base GUI Class
class GUI:
    def __init__(self, root, runCommand):
        mf = ttk.Frame(root, padding="5 5 5 5")
        mf.grid(column=0, row=0)
        mf.columnconfigure(0, weight=1)
        mf.rowconfigure(0, weight=1)

        # Global Values
        self.Fnm = tk.StringVar(root, "SearchFile.xlsx")
        self.Ncol = tk.StringVar(root, "D")
        self.Vcol = tk.StringVar(root, "C")
        # Label
        tk.Label(mf, text="File Name").grid(column=1, row=1, pady=6)
        tk.Label(mf, text="Name Col").grid(column=1, row=3, pady=6)
        tk.Label(mf, text="Value Col").grid(column=3, row=3, pady=6)

        # components
        self.fname = ttk.Entry(mf, width=18, textvariable=self.Fnm)
        self.nmCol = ttk.Entry(mf, width=6, textvariable=self.Ncol)
        self.valCol = ttk.Entry(mf, width=6, textvariable=self.Vcol)
        self.but = ttk.Button(mf, text="Refresh", command=runCommand)
        self.pgbar = ttk.Progressbar(mf, orient="horizontal", mode="determinate")

        # Design
        self.fname.grid(column=2, row=1, pady=3, columnspan=3)
        self.nmCol.grid(column=2, row=3, pady=3)
        self.valCol.grid(column=4, row=3, pady=3)
        self.but.grid(column=2, row=2, columnspan=2)
        self.pgbar.grid(column=1,row=4,columnspan=4)

    def refresh(self):
        pass

    def get(self):
        return [self.Fnm.get(), self.Ncol.get(), self.Vcol.get()]

#Base process Class
class Proc:
    def __init__(self, dets,pgbar,but):
        self.Fnm = dets[0]
        self.Ncol = dets[1]
        self.Vcol = dets[2]
        self.pg=pgbar
        self.butt=but

    def refresh(self):
        self.butt['state'] = 'disabled'
        self.pg.start()
        #ATTENTION:Enter Your process Code HERE
        for _ in range(5):
            time.sleep(2)#Longggg work
        self.pg.stop()
        #Any search/sort algorithm to be used
        #You can use self.pg.step() to be more specific for how the progress bar proceeds
        messagebox.showinfo("Process Done","Success")
        self.butt['state'] = 'enabled'

#Base Application Class
class App:
    def __init__(self, master):
        self.master = master
        self.gui = GUI(self.master, self.runit)

    def runit(self):
        self.search = Proc(self.gui.get(),self.gui.pgbar,self.gui.but)
        self.thread1 = threading.Thread(target=self.search.refresh)
        self.thread1.start()

def main():
    app = tk.Tk()
    gui = App(app)
    app.title("Refresh Search File")
    app.mainloop()

if __name__ == '__main__':
    main()

关于数据迭代,您可以检出this以获得更有效的代码。

我不能在此处编写所有代码,因为它太长了,所以请参考链接