Вопрос по gtk3, multithreading, python – Threading в Gtk Python

2

Поэтому я занят написанием приложения, которое должно проверять наличие обновлений с веб-сайта по истечении определенного времени, я использую python с Gtk +3

файл main.py

class Gui:
    ...
    def on_update_click():
        update()

app=Gui()
Gtk.main()

файл update.py

def update():
    #check site for updates
    time.sleep(21600) #check again in 6hrs

Я подозреваю, что мне придется использовать многопоточность. мое мышление таково:

Gtk.main () запускает основной поток.

когда пользователь нажимает кнопку обновления, update () запускается в фоновом режиме. # нить 2

Правильно ли мое мышление или я что-то упустил?

РЕДАКТИРОВАТЬ: В порядке,
        Функция on_update_click:

            Thread(target=update).start(). 

К, компьютер больше не зависает: D

так что теперь происходит то, что только когда я закрываю Gtk.main (), поток обновления только начинается. Хорошо, что он продолжает обновляться, когда пользовательский интерфейс закрыт, но я также хотел бы, чтобы он запускался, когда пользовательский интерфейс активен.

on_update_click () отсутствуетself аргумент. jfs

Ваш Ответ

3   ответа
4

thread.start_new_thread(update()) неправильно. Это вызываетupdate() сразу в главном потоке, и вы не должны использоватьthread модуль напрямую; использованиеthreading модуль вместо.

Вы могли бы позвонитьthreading.current_thread() чтобы выяснить, какой поток выполняетсяupdate().

Чтобы упростить ваш код, вы можете запустить весь код gtk в основном потоке и использовать операции блокировки для извлечения веб-страниц и запуска их в фоновых потоках.

На основерасширенный пример из учебника GTK + 3:

#!/usr/bin/python
import threading
import urllib2
from Queue import Queue

from gi.repository import Gtk, GObject

UPDATE_TIMEOUT = .1 # in seconds

_lock = threading.Lock()
def info(*args):
    with _lock:
        print("%s %s" % (threading.current_thread(), " ".join(map(str, args))))

class MyWindow(Gtk.Window):

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

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

        self.updater = Updater()
        self._update_id = None
        self.update()

    def on_button_clicked(self, widget):
        info('button_clicked')
        self.update()

    def update(self):
        if self._update_id is not None: 
            GObject.source_remove(self._update_id)

        self.updater.add_update(self.done_updating) # returns immediately
        # call in UPDATE_TIMEOUT seconds
        self._update_id = GObject.timeout_add(
            int(UPDATE_TIMEOUT*1000), self.update)

    def done_updating(self, task_id):
        info('done updating', task_id)
        self.button.set_label("done updating %s" % task_id)


class Updater:
    def __init__(self):
        self._task_id = 0
        self._queue = Queue(maxsize=100) #NOTE: GUI blocks if queue is full
        for _ in range(9):
            t = threading.Thread(target=self._work)
            t.daemon = True
            t.start()

    def _work(self):
        # executed in background thread
        opener = urllib2.build_opener()
        for task_id, done, args in iter(self._queue.get, None):
            info('received task', task_id)
            try: # do something blocking e.g., urlopen()
                data = opener.open('http://localhost:5001').read()
            except IOError:
                pass # ignore errors

            # signal task completion; run done() in the main thread
            GObject.idle_add(done, *((task_id,) + args))

    def add_update(self, callback, *args):
        # executed in the main thread
        self._task_id += 1
        info('sending task ', self._task_id)
        self._queue.put((self._task_id, callback, args))

GObject.threads_init() # init threads?

win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()

Gtk.main()

Замечания:GObject.idle_add() это единственная функция, связанная с gtk, которая вызывается из разных потоков.

Смотрите такжеМногопоточные приложения GTK & # x2013; Часть 1: Заблуждения.

Итак, в функцию on_upate_click добавлено zeref
6

Мне нужно было сказать:

from gi.repository import Gtk,GObject

GObject.threads_init()
Class Gui:
    .....
    ......
    def on_update_click():
            Thread(target=update).start()

Сначала я использовал:

thread.start_new_thread(update())

в функции on_update_click. Как упомянул мой Дж. Ф. Себастьян, это было неправильно, так как это немедленно вызвало бы эту тему. Это заморозило весь мой компьютер.

Я тогда просто добавил:

Thread(target=update).start()

Функция on_update_clicked работала только после закрытия основного потока Gtk.main (). Таким образом, потоки не были запущены одновременно.

добавляя:     GObject.threads_init ()

это позволило потокам работать последовательно с интерпретатором python: Темы в Gtk!

0

Потоки - это первый способ решения проблемы. Вы можете создать поток и запустить долгосрочную функцию блокировки внутри этого потока (и ваш графический интерфейс не будет зависать).

Другим способом является использование асинхронной сети, например, использование python-gio (GObject-IO) или другой библиотеки, которая имеет возможность работать с основным циклом GLib (как они делают с Twisted). Этот подход немного отличается и использует неблокирующие операции с сокетами. Ваш основной цикл будет выполнять обратный вызов, когда данные из сокета (сайта, который вы опрашиваете) будут доступны для чтения. К сожалению, GIO не имеет высокоуровневого HTTP API, поэтому вы можете использоватьGSocketClient и вручную создать структуру HTTP-запросов.

Я не понимаю, что вы подразумеваете под: Запустить длительную функцию блокировки zeref
ааа, я понимаю, сейчас над этим поработаю. zeref
Подключение к сайту и получение содержимого страницы может быть длительной операцией - до нескольких секунд. Если вы делаете это, например, с urllib, ваш графический интерфейс (пока приложение однопоточное) будет зависать во время выполнения функции обновления. Это означает блокирование: обработчики событий GUI не могут выполняться до тех пор, пока не будет выполнено обновление. Поэтому я советую либо запускать update () в потоке, либо использовать асинхронные (неблокирующие) сетевые операции.

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