Почему индикатор выполнения tkinter делает вещи намного медленнее? - PullRequest
3 голосов
/ 09 сентября 2010

У меня есть следующий код для извлечения файла tar.gz с одновременным отслеживанием прогресса:

from __future__ import division
import tarfile
import os

theArchive = "/Users/Dennis/Instances/atlassian-jira-enterprise-4.1.2-standalone.tar.gz"

a = tarfile.open(theArchive)

tarsize = 0

print "Computing total size"
for tarinfo in a:
    tarsize = tarsize + tarinfo.size

realz = tarsize
print "compressed size: " + str(a.fileobj.size)
print "uncompressed size: " + str(tarsize)

tarsize = 0

for tarinfo in a:
    print tarinfo.name, "is", tarinfo.size, "bytes in size and is",
    if tarinfo.isreg():
        print "a regular file."
    elif tarinfo.isdir():
        print "a directory."
    else:
        print "something else."
    a.extract(tarinfo)
    tarsize = tarsize + tarinfo.size
    print str(tarsize) + "/" + str(realz)
    outout = tarsize / realz
    print "progress: " + str(outout)

a.close()

Это довольно быстро и извлекает 100 МБ tar.gz за 10 секунд. Я хотел, чтобы это было визуально, поэтому я изменил это, чтобы включить индикатор выполнения tkinter:

from __future__ import division
import tarfile
import os
import Tkinter

class Meter(Tkinter.Frame):
    def __init__(self, master, width=300, height=20, bg='white', fillcolor='orchid1',\
                 value=0.0, text=None, font=None, textcolor='black', *args, **kw):
        Tkinter.Frame.__init__(self, master, bg=bg, width=width, height=height, *args, **kw)
        self._value = value

        self._canv = Tkinter.Canvas(self, bg=self['bg'], width=self['width'], height=self['height'],\
                                    highlightthickness=0, relief='flat', bd=0)
        self._canv.pack(fill='both', expand=1)
        self._rect = self._canv.create_rectangle(0, 0, 0, self._canv.winfo_reqheight(), fill=fillcolor,\
                                                 width=0)
        self._text = self._canv.create_text(self._canv.winfo_reqwidth()/2, self._canv.winfo_reqheight()/2,\
                                            text='', fill=textcolor)
        if font:
            self._canv.itemconfigure(self._text, font=font)

        self.set(value, text)
        self.bind('<Configure>', self._update_coords)

    def _update_coords(self, event):
        '''Updates the position of the text and rectangle inside the canvas when the size of
        the widget gets changed.'''
        # looks like we have to call update_idletasks() twice to make sure
        # to get the results we expect
        self._canv.update_idletasks()
        self._canv.coords(self._text, self._canv.winfo_width()/2, self._canv.winfo_height()/2)
        self._canv.coords(self._rect, 0, 0, self._canv.winfo_width()*self._value, self._canv.winfo_height())
        self._canv.update_idletasks()

    def get(self):
        return self._value, self._canv.itemcget(self._text, 'text')

    def set(self, value=0.0, text=None):
        #make the value failsafe:
        if value < 0.0:
            value = 0.0
        elif value > 1.0:
            value = 1.0
        self._value = value
        if text == None:
            #if no text is specified use the default percentage string:
            text = "Extraction: " + str(int(round(100 * value))) + ' %'
        self._canv.coords(self._rect, 0, 0, self._canv.winfo_width()*value, self._canv.winfo_height())
        self._canv.itemconfigure(self._text, text=text)
        self._canv.update_idletasks()

##-------------demo code--------------------------------------------##

def _goExtract(meter, value):
    meter.set(value)
    if value < 1.0:
        value = value + 0.005
        meter.after(50, lambda: _demo(meter, value))
    else:
        meter.set(value, 'Demo successfully finished')

if __name__ == '__main__':
    root = Tkinter.Tk(className='meter demo')
    m = Meter(root, relief='ridge', bd=3)
    m.pack(fill='x')
    m.set(0.0, 'Computing file size...')
    m.after(1000)

    theArchive = "/Users/Dennis/Instances/atlassian-jira-enterprise-4.1.2-standalone.tar.gz"

    a = tarfile.open(theArchive)

    tarsize = 0

    for tarinfo in a:
        tarsize = tarsize + tarinfo.size

    realz = tarsize
    print "real size: " + str(tarsize)
    print "compressed size: " + str(a.fileobj.size)

    m.set(0.0, 'Done computing!')
    m.after(1000)

    tarsize = 0

    for tarinfo in a:
        print tarinfo.name, "is", tarinfo.size, "bytes in size and is",
        if tarinfo.isreg():
            print "a regular file."
        elif tarinfo.isdir():
            print "a directory."
        else:
            print "something else."
        a.extract(tarinfo)
        tarsize = tarsize + tarinfo.size
        print str(tarsize) + "/" + str(realz)
        outout = tarsize / realz
        m.set(outout)
        print "progress: " + str(outout)

    a.close()

    m.set(1.0, 'Extraction complete!')
    m.after(1000)
    m.after(1000, lambda: _goExtract(m, 0.0))

Работает все отлично, но сейчас процесс занимает больше 2 минут. Почему это происходит и как я могу это исправить?

Спасибо!

Денис

1 Ответ

5 голосов
/ 09 сентября 2010

Насколько большие файлы в вашем архиве? Вы почти наверняка обновляете индикатор выполнения намного больше, чем нужно - обычно включается проверка в вашей функции set(), чтобы она просто возвращалась без обновления, если изменение от последнего значения слишком мало. С холстом в 300px определенно нет смысла обновлять при изменении менее 0,3% и, вероятно, нет особого смысла в обновлении чаще, чем каждые 1%.

Поскольку ваш процесс обычно заканчивается через 10 секунд, вы можете также захотеть ввести проверку на основе времени, поскольку даже обновление каждые 1% будет производиться 10 раз в секунду, что больше, чем вам нужно. Было бы интересно посмотреть, сколько времени займет Tk, чтобы нарисовать планку, если вы ведете ее из простого цикла for.

...