LINUX.ORG.RU

Python (pygtk) последовательность выполнения

 ,


0

2

Столкнулся с такой задачей: нужно при нажатии кнопки, сначало убить кнопку, потом показать на ее месте анимированный гиф ожидания, и только потом начать запуск баш команды, а после его выполнения убить гиф-картинку, и вывести на его месте текст «Установка завершена». Все это я сделал, но проблема в том, что выполняется это непоследовательно, от чего - непонятно! Кнопка зависает и не убивается до того момента пока не выполнится команда, при этом изображение не показывается, а показывается только после выполнения команды, хотя с последовательностью в коде все верно:

def on_clicked_install(self, widget):
        button_install.destroy()
        image2.set_visible(True)
        if image2.get_visible():
           os.system("gksu apt-get update")
        image2.set_visible(False)
        text_install.set_visible(True)
В чем моя ошибка, и как исправить?



Последнее исправление: lorovec (всего исправлений: 1)

Ошибка в том, что ты делаешь всё (gksu apt-get update) в обработчике. Отсюда зависание кнопки. Картинка показывается, но XSync() не выполняется, поэтому ты её и не видишь.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Собственно, если ты сделаешь это перед своим apt-get update, то у тебя gif появится (но вряд ли будет анимироваться) до зависания.

x3al ★★★★★
()
Ответ на: комментарий от lorovec

Тред. Как вариант subprocess. В любом случае, возвращать кнопку/убирать анимацию ты будешь уже не в своём обработчике нажатия.

x3al ★★★★★
()
Ответ на: комментарий от lorovec

subprocess.Popen() для открытия, subprocess.poll() или subprocess.wait() (это если ты можешь себе позволить выделить тред только под эту задачу, а не юзаешь общий воркер для данной и ещё какой-нибудь, которая может быть одновременно с ней) в отдельном потоке для отлавливания окончания, наверно.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Не работает, написал так:

def on_clicked_install(self, widget):
        button_install.destroy()
        image2.set_visible(True)
        p1 = subprocess.Popen("gksu apt-get update", shell=True)       
        if p1.wait():
            image2.set_visible(False)
            text_install.set_visible(True)

lorovec
() автор топика
Ответ на: комментарий от pylin

это лучше, тем что фейс не блокируется, но один фиг логика не работает, то есть кнопка виснет и изображение встает только по окончанию процесса

lorovec
() автор топика
Ответ на: комментарий от lorovec

gtk.gdk.threads_init() <- это перед gtk_main().

//говнокод ниже
def threaded(f):
    def wrapper(*args):
        t = threading.Thread(target=f, args=args)
        t.start()
    return wrapper
    @threaded
    def apt_update(self, widget)
        p1= subprocess.Popen("gksu apt-get update", shell=True)       
        if p1.wait():
            gtk.threads_enter()
            image2.set_visible(False)
            text_install.set_visible(True)
            gtk.threads_leave()

и заменить вызов system() плюс всё после него на вызов apt_update(). Естественно, импортнуть всё, что касается тредов вообще и pygtk-тредов в частности.

x3al ★★★★★
()
Ответ на: комментарий от x3al
@threaded
    def apt_update(self, widget)
        p1= subprocess.Popen("gksu apt-get update", shell=True)       
        if p1.wait():
            gtk.threads_enter()
            image2.set_visible(False)
            text_install.set_visible(True)
            gtk.threads_leave()

это на уровень интенданции ниже, чёртов клон вайтспейса. Не проверял, кстати, pygtk сейчас не установлен.

x3al ★★★★★
()
Ответ на: комментарий от lorovec

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

wwwsevolod
()
Ответ на: комментарий от wwwsevolod

Не, ну все же, допустим если примерно так же написать на C++ и wxwidgets то выполнится корректно, по очереди.

lorovec
() автор топика
Ответ на: комментарий от lorovec

возможно wxwidgets не позволяет делать синхронные вызовы из каробки, может там внутри асинхронщина. Другого объяснения не вижу

wwwsevolod
()
Ответ на: комментарий от x3al

Один фиг не понял. Сделал по одному примеру, новый процесс запускается, кнопка не виснет но(!), все что идет после p1.wait() не работает, даже по окончанию процесса:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# By Chris Oliver 
# Adapted from http://www.pygtk.org/pygtk2tutorial/examples/helloworld.py
 
import pygtk
pygtk.require("2.0")
 
import gobject
import gtk
gtk.gdk.threads_init()

import subprocess
 
import threading

import os

button = gtk.Button("Hello World")
 
class HelloWorld:
    def __init__(self):
        """
            Initializes the GTK application, in our case, create the window
            and other widgets
        """
 
        # Create a window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_border_width(10)
 
        # Setup the application to exit GTK when the window is closed
        self.window.connect("destroy", self.destroy)
 
        # Create a button
        #button = gtk.Button("Hello World")
        
        # Make the button call self.hello() when it is clicked
        button.connect("clicked", self.hello_helper)
        
        #button.set_size_request(320, 70)
 
        # Add the button into the window
        self.window.add(button)
 
        # Display the button and the window
        button.show()
        self.window.show()
 
    def hello(self, widget, data=None):
        import time
        time.sleep(5)
        print "Hello"
        button.set_size_request(320, 70)
        p1 = subprocess.Popen("wget http://www.linux.org.ru", shell=True)
        p1.wait()
        button.set_size_request(20, 70)
        
        
        
 
    def hello_helper(self, widget, data=None):
        print "starting new thread"
        threading.Thread(target=self.hello, args=(widget, data)).start()
        
 
    def main(self):
        """
            This function starts GTK drawing the GUI and responding to events
            like button clicks
        """
 
        gtk.main()
 
    def destroy(self, widget, data=None):
        """
            This function exits the application when the window is closed.
            Without this the GTK main thread would continue running while no
            interface would be displayed. We want the application to exit when
            the window is closed, so we tell the GTK loop to stop so we can
            quit.
        """
 
        gtk.main_quit()
 
if __name__ == "__main__":
    # Create an instance of our GTK application
    app = HelloWorld()
    gtk.gdk.threads_enter()
    app.main()
    gtk.gdk.threads_leave()

lorovec
() автор топика
Ответ на: комментарий от x3al

И если у тебя всё равно новый тред, то пару popen/wait можно смело менять на system.

x3al ★★★★★
()
Ответ на: комментарий от x3al

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

lorovec
() автор топика
Ответ на: комментарий от x3al

Все, уже не надо, написал, нифига не понял но работает, скажи только правильно или опять косяк сплошной?

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# By Chris Oliver 
# Adapted from http://www.pygtk.org/pygtk2tutorial/examples/helloworld.py
 
import pygtk
pygtk.require("2.0")
 
import gobject
import gtk
gtk.gdk.threads_init()

import subprocess
 
import threading

import os

button = gtk.Button("Hello World")
 
class HelloWorld:
    def __init__(self):
        """
            Initializes the GTK application, in our case, create the window
            and other widgets
        """
 
        # Create a window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_border_width(10)
 
        # Setup the application to exit GTK when the window is closed
        self.window.connect("destroy", self.destroy)
 
        # Create a button
        #button = gtk.Button("Hello World")
        
        # Make the button call self.hello() when it is clicked
        button.connect("clicked", self.hello_helper)
        
        #button.set_size_request(320, 70)
 
        # Add the button into the window
        self.window.add(button)
 
        # Display the button and the window
        button.show()
        self.window.show()
 
    def hello(self, widget, data=None):
        #import time
        #time.sleep(5)
        print "Hello"        
        p1 = subprocess.call("gksu apt-get update", shell=True)
        gtk.gdk.threads_enter()
        button.set_size_request(320, 70)
        gtk.gdk.threads_leave()
        
    def hello_helper(self, widget, data=None):
        print "starting new thread"        
        threading.Thread(target=self.hello, args=(widget, data)).start()
        button.set_size_request(20, 70)
        
        
 
    def main(self):
        """
            This function starts GTK drawing the GUI and responding to events
            like button clicks
        """
 
        gtk.main()
 
    def destroy(self, widget, data=None):
        """
            This function exits the application when the window is closed.
            Without this the GTK main thread would continue running while no
            interface would be displayed. We want the application to exit when
            the window is closed, so we tell the GTK loop to stop so we can
            quit.
        """
 
        gtk.main_quit()
 
if __name__ == "__main__":
    # Create an instance of our GTK application
    gtk.gdk.threads_enter()
    app = HelloWorld()
    gtk.gdk.threads_leave()
    app.main()

lorovec
() автор топика
Ответ на: комментарий от lorovec

работает

косяк сплошной

/0.

Гонок не вижу, всё лочится. Понятно, что button.set_size_request(20, 70) незаметно, но это осталось после дебага же.

x3al ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.