Как запускать асинхронные задачи в приложениях Python GObject Introspection

Да, вы можете! Это называется sshfs.

Основная идея:

Установить sshfs
sudo apt install sshfs
Установить удаленный каталог
sshfs user@host:/path/to/remote /path/to/local/mount/point

Теперь у вас есть пульт дистанционного управления каталог /path/to/remote доступен в /path/to/local/mount/point.

16
задан 28 May 2012 в 04:13

38 ответов

Вот еще один вариант с использованием планировщика ввода-вывода GIO (я никогда раньше не использовал его с Python, но пример ниже, похоже, работает нормально).

from gi.repository import GLib, Gio, GObject
import time

def slow_stuff(job, cancellable, user_data):
    print "Slow!"
    for i in xrange(5):
        print "doing slow stuff..."
        time.sleep(0.5)
    print "finished doing slow stuff!"
    return False # job completed

def main():
    GObject.threads_init()
    print "Starting..."
    Gio.io_scheduler_push_job(slow_stuff, None, GLib.PRIORITY_DEFAULT, None)
    print "It's running async..."
    GLib.idle_add(ui_stuff)
    GLib.MainLoop().run()

def ui_stuff():
    print "This is the UI doing stuff..."
    time.sleep(1)
    return True

if __name__ == '__main__':
    main()
11
ответ дан 7 August 2018 в 18:30

Использовать интроспективный API Gio для чтения файла с его асинхронными методами и при выполнении начального вызова сделать это как тайм-аут с GLib.timeout_add_seconds(3, call_the_gio_stuff), где call_the_gio_stuff - это функция, которая возвращает False.

Тайм-аут здесь необходимо добавить (может потребоваться другое количество секунд), так как в то время как асинхронные вызовы Gio являются асинхронными, они не являются неблокирующими, что означает, что активность жесткого диска чтения большой файл или большое количество файлов, может привести к блокированному пользовательскому интерфейсу, поскольку пользовательский интерфейс и ввод-вывод все еще находятся в одном и том же (основном) потоке.

Если вы хотите написать свои собственные функции, async и интегрироваться с основным циклом, используя API-интерфейсы ввода-вывода файлов Python, вам нужно будет написать код в виде GObject или передать обратные вызовы или использовать python-defer, чтобы помочь вам это сделать. Но лучше использовать Gio здесь, так как он может принести вам массу приятных функций, особенно если вы делаете файл открытым / сохраненным в UX.

2
ответ дан 10 August 2018 в 07:11

Вы также можете использовать GLib.idle_add (обратный вызов) для вызова долговременной задачи, когда GLib Mainloop завершает все события с более высоким приоритетом (которые, я считаю, включает в себя создание пользовательского интерфейса).

2
ответ дан 10 August 2018 в 07:11

Я думаю, что это означает, что это сложный способ сделать то, что предложил @mhall.

По сути, у вас есть запуск, затем запустите эту функцию async_call.

Если вы хотите посмотреть, как это работает, вы можете играть с таймером сна и продолжать нажимать кнопку. [Gg]

Исходя из этого , который не является моей работой.

import threading
import time
from gi.repository import Gtk, GObject



# calls f on another thread
def async_call(f, on_done):
    if not on_done:
        on_done = lambda r, e: None

    def do_call():
        result = None
        error = None

        try:
            result = f()
        except Exception, err:
            error = err

        GObject.idle_add(lambda: on_done(result, error))
    thread = threading.Thread(target = do_call)
    thread.start()

class SlowLoad(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Hello World")
        GObject.threads_init()        

        self.connect("delete-event", Gtk.main_quit)

        self.button = Gtk.Button(label="Click Here")
        self.button.connect("clicked", self.on_button_clicked)
        self.add(self.button)

        self.file_contents = 'Slow load pending'

        async_call(self.slow_load, self.slow_complete)

    def on_button_clicked(self, widget):
        print self.file_contents

    def slow_complete(self, results, errors):
        '''
        '''
        self.file_contents = results
        self.button.set_label(self.file_contents)
        self.button.show_all()

    def slow_load(self):
        '''
        '''
        time.sleep(5)
        self.file_contents = "Slow load in progress..."
        time.sleep(5)
        return 'Slow load complete'



if __name__ == '__main__':
    win = SlowLoad()
    win.show_all()
    #time.sleep(10)
    Gtk.main()

Дополнительно, вы должны допустить, чтобы другой поток завершился до того, как он завершится правильно или проверьте файл file.lock в дочернем потоке.

Редактировать комментарий к адресу: Первоначально я забыл GObject.threads_init(). Очевидно, когда кнопка срабатывала, для меня была инициализирована резьба. Это замаскировало ошибку для меня.

Обычно поток создает окно в памяти, сразу же запускает другой поток, когда поток завершает обновление кнопки. Я добавил дополнительный сон, прежде чем я даже позвонил в Gtk.main, чтобы убедиться, что полное обновление COULD запустилось до того, как окно было даже рисовано. Я также прокомментировал это, чтобы убедиться, что запуск потока не мешает рисованию окна.

1
ответ дан 10 August 2018 в 07:11

Вот еще один вариант с использованием планировщика ввода-вывода GIO (я никогда раньше не использовал его с Python, но пример ниже выглядит нормально).

from gi.repository import GLib, Gio, GObject
import time

def slow_stuff(job, cancellable, user_data):
    print "Slow!"
    for i in xrange(5):
        print "doing slow stuff..."
        time.sleep(0.5)
    print "finished doing slow stuff!"
    return False # job completed

def main():
    GObject.threads_init()
    print "Starting..."
    Gio.io_scheduler_push_job(slow_stuff, None, GLib.PRIORITY_DEFAULT, None)
    print "It's running async..."
    GLib.idle_add(ui_stuff)
    GLib.MainLoop().run()

def ui_stuff():
    print "This is the UI doing stuff..."
    time.sleep(1)
    return True

if __name__ == '__main__':
    main()
11
ответ дан 10 August 2018 в 07:11

Вы также можете использовать GLib.idle_add (обратный вызов) для вызова долговременной задачи, когда GLib Mainloop завершает все события с более высоким приоритетом (которые, я считаю, включает в себя создание пользовательского интерфейса).

2
ответ дан 15 August 2018 в 19:10
  • 1
    – David Planella 27 May 2012 в 19:08
  • 2
  • 3
    В идеале вы разделили бы свою медленную задачу на куски, так что вы можете запустить ее немного в режиме обратного вызова, вернуть (и позволить другим вещам, например, обратным вызовам пользовательского интерфейса), продолжить выполнение еще одной работы после повторного вызова обратного вызова и так далее на. – Siegfried Gevatter 27 May 2012 в 21:22
  • 4
    Получение с idle_add заключается в том, что возвращаемое значение обратного вызова имеет значение. Если это правда, оно будет вызвано снова. – Flimm 19 April 2013 в 00:20

Я думаю, что это означает, что это сложный способ сделать то, что предложил @mhall.

По сути, у вас есть запуск, затем запустите эту функцию async_call.

Если вы хотите посмотреть, как это работает, вы можете играть с таймером сна и продолжать нажимать кнопку. [Gg]

Исходя из этого , который не является моей работой.

import threading
import time
from gi.repository import Gtk, GObject



# calls f on another thread
def async_call(f, on_done):
    if not on_done:
        on_done = lambda r, e: None

    def do_call():
        result = None
        error = None

        try:
            result = f()
        except Exception, err:
            error = err

        GObject.idle_add(lambda: on_done(result, error))
    thread = threading.Thread(target = do_call)
    thread.start()

class SlowLoad(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Hello World")
        GObject.threads_init()        

        self.connect("delete-event", Gtk.main_quit)

        self.button = Gtk.Button(label="Click Here")
        self.button.connect("clicked", self.on_button_clicked)
        self.add(self.button)

        self.file_contents = 'Slow load pending'

        async_call(self.slow_load, self.slow_complete)

    def on_button_clicked(self, widget):
        print self.file_contents

    def slow_complete(self, results, errors):
        '''
        '''
        self.file_contents = results
        self.button.set_label(self.file_contents)
        self.button.show_all()

    def slow_load(self):
        '''
        '''
        time.sleep(5)
        self.file_contents = "Slow load in progress..."
        time.sleep(5)
        return 'Slow load complete'



if __name__ == '__main__':
    win = SlowLoad()
    win.show_all()
    #time.sleep(10)
    Gtk.main()

Дополнительно, вы должны допустить, чтобы другой поток завершился до того, как он завершится правильно или проверьте файл file.lock в дочернем потоке.

Редактировать комментарий к адресу: Первоначально я забыл GObject.threads_init(). Очевидно, когда кнопка срабатывала, для меня была инициализирована резьба. Это замаскировало ошибку для меня.

Обычно поток создает окно в памяти, сразу же запускает другой поток, когда поток завершает обновление кнопки. Я добавил дополнительный сон, прежде чем я даже позвонил в Gtk.main, чтобы убедиться, что полное обновление COULD запустилось до того, как окно было даже рисовано. Я также прокомментировал это, чтобы убедиться, что запуск потока не мешает рисованию окна.

1
ответ дан 15 August 2018 в 19:10
  • 1
    Благодарю. Я не уверен, что смогу это сделать. Во-первых, я ожидал, что slow_load будет запущен вскоре после запуска пользовательского интерфейса, но он, кажется, никогда не будет вызван, если не нажать кнопку, что меня немного смущает, поскольку я думал, что цель кнопки была просто обеспечить визуальную индикацию состояния задачи. – David Planella 27 May 2012 в 20:57
  • 2
    Извините, я пропустил одну строку. Так оно и было. Я забыл сказать GObject, чтобы подготовиться к потокам. – RobotHumans 27 May 2012 в 21:14
  • 3
    Но вы звоните в основной цикл из потока, который может вызвать проблемы, хотя они могут быть не легко раскрыты в вашем тривиальном примере, который не выполняет никакой реальной работы. – dobey 27 May 2012 в 21:53
  • 4
    Действительный момент, но я не думал, что тривиальный пример заслуживает отправки уведомления через DBus (который, как мне кажется, требует нетривиальное приложение) – RobotHumans 27 May 2012 в 21:59
  • 5
    Hm, запуск async_call в этом примере работает для меня, но он приносит хаос, когда я переношу его в свое приложение, и добавляю реальную функцию slow_load, которая у меня есть. – David Planella 28 May 2012 в 04:09

Вот еще один вариант с использованием планировщика ввода-вывода GIO (я никогда раньше не использовал его с Python, но пример ниже, похоже, работает нормально).

from gi.repository import GLib, Gio, GObject
import time

def slow_stuff(job, cancellable, user_data):
    print "Slow!"
    for i in xrange(5):
        print "doing slow stuff..."
        time.sleep(0.5)
    print "finished doing slow stuff!"
    return False # job completed

def main():
    GObject.threads_init()
    print "Starting..."
    Gio.io_scheduler_push_job(slow_stuff, None, GLib.PRIORITY_DEFAULT, None)
    print "It's running async..."
    GLib.idle_add(ui_stuff)
    GLib.MainLoop().run()

def ui_stuff():
    print "This is the UI doing stuff..."
    time.sleep(1)
    return True

if __name__ == '__main__':
    main()
11
ответ дан 15 August 2018 в 19:10
  • 1
    См. Также GIO.io_scheduler_job_send_to_mainloop (), если вы хотите запустить что-то в основном потоке, как только slow_stuff закончит. – Siegfried Gevatter 27 May 2012 в 21:27
  • 2
    – David Planella 28 May 2012 в 17:33
  • 3
    Это было действительно полезно, но, насколько я могу судить, Gio.io_scheduler_job_send_to_mainloop не существует в Python :( – sil 19 July 2012 в 03:20

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

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