Многократный запуск приложения Gtk с использованием multiprocessing.Process (или: как запустить Gtk в песочнице в python)

вступление

Коротко: мне нужно (в памяти) помещать в песочницу приложение Gtk/Gstreamer в python и регулярно перезапускать его.

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

Тем не менее, я совсем не привык к Gtk, и у меня очень мало знаний об управлении памятью Python. Поэтому я сталкиваюсь с проблемами.

Настраивать

Итак, теперь программа написана на питоне с использованием Gtk3. У меня есть myPythonObjectWithGtkStuff, который по сути является более причудливой версией http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/webcam-1.0 . Но особенно часть Gtk в __init__() и run() одинакова. (обратите внимание на эту строку http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/webcam-1.0#L51, а также функцию on_sync_message(), которая может быть отвлекающим маневром, но она связана с режиссурой поток gstreamer в область рисования Gtk с использованием дескриптора xid)

Проблема/Решение 1

Я представлял себе, как это работает, чтобы создать весь объект, содержащий весь код, в цикле while. Затем вызовите в какой-то момент времени window.destroy() из основного цикла Gtk. Он вернется в цикл while, и будет создан еще один объект и т. д.

# shitty hack to restart the Gtk application once it got destroyed
while(1):
    startVC(args)

с участием

def startVC(args):    
    mainloop = GObject.MainLoop()
    obj = myPythonObjectWithGtkStuff(args)
    obj.run()
    del obj

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

редактировать:

В тот момент, когда я вызываю window.destroy(), окно Gtk закрывается, и я вижу, что использование памяти снижается до некоторого стабильного базового значения. Как только Gtk запускается снова, использование памяти возвращается к предыдущему использованию оперативной памяти плюс некоторая дополнительная память, соответствующая объему утечки памяти.

Проблема/решение 2

Теперь я подумал, что могу просто запустить Gtk в процессе и просто завершить его, когда он вернется, и запустить новый процесс.

while(1):
    p = Process(target=startVC, args=(args,))            
    p.start()
    p.join()        
    p.terminate()

Проблема здесь в том, что он запускается в первый раз отлично, но как только первый процесс возвращается и цикл запускает второй процесс, я получаю следующую ошибку в тот момент, когда я вызываю self.window = Gtk.Window() в __init()__() из myPythonObjectWithGtkStuff:

Gdk-WARNING **: captureVideo.py: Fatal IO error 11 (Resource temporarily unavailable) on X server :0.

который немного чередуется с

Gdk-WARNING **: captureVideo.py: Fatal IO error 0 (Success) on X server :0.

Что делать?

Сейчас мне нужно решение любой из двух проблем. Как я могу гарантировать, что объект будет полностью удален из памяти для первого решения, или как избежать этой ошибки X-сервера для решения 2.

Возможно, вызова self.window.destroy() из моего obj недостаточно, чтобы убить все вещи Gtk. Это правильный способ закрыть приложение Gtk?


person P.R.    schedule 18.09.2013    source источник


Ответы (1)


В итоге я сделал что-то вроде этого маленького отдельного скрипта

# file: keepAliveWrapper.py

import sys
import subprocess as sp

if __name__ == "__main__":
    args = sys.argv[1:]
    while(1):                
        print("calling: python {args}".format(args=' '.join(args)))

        p = sp.Popen("python {args}".format(args=' '.join(args)),
                     shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        output =  p.communicate()[0]

Он принимает в качестве аргументов сценарий, которым он управляет, вместе с аргументами командной строки. Таким образом, вместо того, чтобы вызывать скажем python gtkProgram.py -d /home/peter/test -e 10 в командной строке, вы теперь запускаете python keepAliveWrapper.py gtkProgram.py -d /home/peter/test -e 10

Если у кого-то есть более умная идея, дайте мне знать, пожалуйста.

P.S.: Для справки в будущем: p.communicate() позволяет анализировать вывод программы, которая поддерживается в рабочем состоянии. Т.е. таким образом можно передавать «сигналы», чтобы решить, прерывать цикл while или нет.

person P.R.    schedule 19.09.2013