Многопоточность в приложении, разработанном в быстро

У меня есть дерево, которое я заполняю со списком файлов от моего компьютера при щелчке кнопки. В то время как программа заполняет дерево, целый GUI зависает.

Существует ли способ заполнить дерево в другом потоке, таким образом, дерево заполняется, поскольку файлы найдены и не, когда все было уже добавлено? Или какие-либо другие идеи?

Это - то, на что это похоже:

Screenshot of App

Я заполняю дерево с этим кодом:

def traversePaths(self, path, parent):
        files = os.listdir(path)
        files.sort()
        for myfile in files:
            if myfile[0] != ".":
                if os.path.isdir(path + os.sep + myfile):
                    current = self.filelist.append(parent,[self.dirIcon,self.dirIcon,self.dirOpenIcon,myfile,"DD",True,True,3])
                    self.traversePaths(path + os.sep + myfile, current)
                else:
                    current = self.filelist.append(parent,[self.fileIcon,self.dirIcon,self.dirOpenIcon,myfile,"AA",True,True,3])

который выполняется на нажатии кнопки:

def on_refreshbutton_clicked(self, button):
    self.traversePaths(self.path.get_filename(), None)

Я не думаю, распараллеливая. Работы потока из-за потока gtk для gui и не могут найти gi.repository. API Gtk на предмете

Какие-либо идеи?

1
задан 23 August 2012 в 18:50

2 ответа

При выяснении у программирования вопросов всегда лучше обеспечить минимальный рабочий пример и не просто фрагменты кода.

Без рабочего примера для проигрывания с у меня есть следующие предложения:

  1. Использование поток, чтобы обойти дерево каталогов и поместить Ваши результаты во второй отдельный treemodel (не подключенный к treeview). Это означает, что у Вас есть два treemodels, один соединенный (который представляется на экран), и один не соединился (который поэтому не становится представленным)
  2. , Добавляет тайм-аут нескольких секунд, который делает вызывает функцию, которая отсоединяет и удаляет первый treemodel, копирует второй treemodel в новый, который теперь служит "потоком" treemodel и присоединяет вторую древовидную модель.

причина, почему Вы видите, что GUI зависает, состоит в том, что файл, который Уокер берет очень долго и каждый раз, когда Вы добавляете файл, много издержек в Gtk называют. Путем добавления полного treemodel эти издержки только называют одним временем. И при помощи потока для обхода файла, Ваш GUI остается быстро реагирующим.

Что касается части поточной обработки в Gtk, Вы могли бы хотеть посмотреть здесь: https://stackoverflow.com/questions/8120860/python-doing-some-work-on-background-with-gtk-gui

Несколько примечаний к Вашему коду:

  • Python имеет сборку - в файле Уокер, который мог бы быть быстрее, но конечно короче, чем Ваш код: os.walk
  • , Если Вы хотите использовать свой код вместо этого, помните, что Python имеет сборку - в пределе рекурсии. В зависимости от размера Вашей файловой системы Вы могли бы хотеть заменить рекурсию батутом, любят конструкцию
2
ответ дан 21 October 2019 в 12:45

Вот полный пример, который обновляет дерево одновременно с помощью единственного TreeStore:

#!/usr/bin/python
import os
import threading
import time
from itertools import cycle

from gi.repository import GObject, Gtk
GObject.threads_init()  # all Gtk is in the main thread;
                        # only GObject.idle_add() is in the background thread


HEARTBEAT = 20   # Hz
CHUNKSIZE = 100  # how many items to process in a single idle_add() callback


def chunks(seq, chunksize):
    """Yield N items at a time from seq."""
    for i in xrange(0, len(seq), chunksize):
        yield seq[i:i + chunksize]


class TreeStore(Gtk.TreeStore):
    __gtype_name__ = 'TreeStore'

    def __init__(self, topdir, done_callback=None):
        Gtk.TreeStore.__init__(self, str)  # super() doesn't work here

        self.path2treeiter = {topdir: None}  # path -> treeiter
        self.topdir = topdir
        self.done_callback = done_callback
        self._cv = threading.Condition()

        t = threading.Thread(target=self._build_tree)
        t.daemon = True
        t.start()  # start background thread

    def _build_tree(self, _sentinel=object()):
        # executed in a background thread
        cv = self._cv
        p = self.path2treeiter
        for dirpath, dirs, files in os.walk(self.topdir):
            # wait until dirpath is appended to the tree
            cv.acquire()
            while p.get(dirpath, _sentinel) is _sentinel:
                cv.wait()
            parent = p[dirpath]
            cv.release()

            # populate tree store
            dirs[:] = sorted(d for d in dirs
                             if d[0] != '.')  # skip hidden dirs
            for chunk in chunks(dirs, CHUNKSIZE):
                GObject.idle_add(self._appenddir, chunk, parent, dirpath)

            for chunk in chunks(sorted(files), CHUNKSIZE):
                GObject.idle_add(self._appendfile, chunk, parent)
        GObject.idle_add(self.done_callback)

    def _appenddir(self, chunk, parent, dirpath):
        # executed in the main thread
        self._cv.acquire()
        p = self.path2treeiter
        for d in chunk:
            p[os.path.join(dirpath, d)] = self.append(parent, [d])
        self._cv.notify()
        self._cv.release()

    def _appendfile(self, chunk, parent):
        # executed in the main thread
        for f in chunk:
            self.append(parent, [f])


class Window(Gtk.Window):
    __gtype_name__ = 'Window'

    def __init__(self, topdir):
        super(Window, self).__init__(type=Gtk.WindowType.TOPLEVEL)
        self.__start_time = time.time()
        self.__title = 'GTK Tree MultiThreading Demo'
        self.set_title(self.__title)
        self.set_default_size(640, 480)

        # create tree
        tree_store = TreeStore(topdir, self._on_tree_completed)
        tree_view = Gtk.TreeView()
        tree_view.set_model(tree_store)

        cell = Gtk.CellRendererText()
        tree_view.append_column(Gtk.TreeViewColumn(topdir, cell, text=0))
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.add(tree_view)
        self.add(scrolled_window)

        # update title to show that we are alive
        self._update_id = GObject.timeout_add(int(1e3 / HEARTBEAT),
                                              self._update_title)

    def _on_tree_completed(self):
        if self._update_id is None:
            return
        # stop updates
        GObject.source_remove(self._update_id)
        self._update_id = None
        self.set_title('%s %s %.1f' % (self.__title, ' (done)',
                                       time.time() - self.__start_time))

    def _update_title(self, _suff=cycle('/|\-')):
        self.set_title('%s %s %.1f' % (self.__title, next(_suff),
                                       time.time() - self.__start_time))
        return True  # continue updates


win = Window(topdir=os.path.expanduser('~'))
win.connect('delete-event', Gtk.main_quit)
win.show_all()
Gtk.main()

Поскольку @xubuntix сказал каждого treestore.append() является дорогим в этом случае. Посмотрите, приемлемо ли это для Вас.

1
ответ дан 21 October 2019 в 12:45

Другие вопросы по тегам:

Похожие вопросы: