【问题标题】:Optimal way to use tkinter and openpyxl to iterate through a spreadsheet?使用 tkinter 和 openpyxl 遍历电子表格的最佳方式?
【发布时间】:2018-06-26 03:17:59
【问题描述】:

我是一个完整的 Python 初学者,正在尝试制作一个允许用户选择 Excel 文件的 GUI,从中提取和分析一些数据。对于 GUI,我使用 tkinter,对于 Excel 操作,我使用 openpyxl。 The code that I currently have works, but when the Excel file selected is large, the runtime is stupidly long (a 10,000 row x 20 column spreadsheet took about an hour to iterate through, and just got slower as time went by) .此外,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')

我应该使用 openpyxl 以外的模块来完成这项任务,还是我只是犯了一些错误,使 openpyxl 无法发挥其全部潜力? 我应该使用线程来加速迭代过程,还是确保我的 GUI 窗口在迭代执行时不会对我全部“无响应”?

我尝试搜索有关类似问题的问题,但我并没有真正找到我正在寻找的内容,这很可能是因为我无法将我的问题用恰当的语言表达出来。因此,我提前向您提出可能已经回答过一百万次的问题深表歉意。感谢您提供任何帮助,并感谢您的宝贵时间。

【问题讨论】:

  • 您应该仔细检查max_row 的值。当我最终使用它时,它返回电子表格的最大可能行而不是最后一行。例如,我有一个包含 4777 行的电子表格,但是 max_row 返回了 65536 行。
  • 嗯,这很奇怪,我的不这样做。正如我所期望的那样,它返回最后一行。也许这只是我们的电子表格之间的差异?
  • 这是一个常见问题。存在几个错误报告。那就是说,如果它不影响您,那很好。只需要弄清楚问题是什么。我认为这是大量的列。当我使用 openpyxl 时,我通常将我的电子表格减少到只需要比较的列,然后从中创建另一个包含所有数据的电子表格。否则加载文件只是为了读取需要很长时间,然后运行代码也可能需要很长时间。
  • 当您说减少电子表格时,您的意思是您只需进入 Excel 并删除您不想要的列吗?或者有没有办法指定 openpyxl 只关心某些列?
  • 我手动删除了不需要的列。这帮助我将速度提高了 20% 到 90%。这样做对我来说不是问题,但我相信如果你不需要做这样的事情会更好。

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


【解决方案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获取更高效的代码。

这里的代码太长了,不能全部写出来,请参考链接

【讨论】:

  • 请不要在未在答案中提供基本概念的情况下链接到外部资源。如果由于任何原因您的链接破坏了此答案,则对任何人都不再有用。
  • 好吧@Mike-SMT,我会在这里添加更多逻辑,但我真的会避免放置代码,它非常非常长,但如果你说它可能会更好,我不会介意。
猜你喜欢
  • 2017-11-15
  • 1970-01-01
  • 2020-03-14
  • 2017-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多