Threads com PyGTK

Já falei um pouco de thread em python; agora vamos falar de threads com pygtk.

Como uma introdução, analise o código abaixo. Veja se lhe parece correto.


#! /usr/bin/env python
# -.- coding: utf-8 -.-
import gtk
import pygtk

continua=None
def iniciar(botao, texto):
    global continua
    continua=True
    x=0
    while continua:
        texto.set_text(str(x))
        x+=1

def parar(botao):
    global continua
    continua=False

# criando os componentes
win=gtk.Window()
box=gtk.VBox()
texto=gtk.Entry()
btinicia=gtk.Button("iniciar" )
btpara=gtk.Button("interromper" )

# ativando os eventos
btinicia.connect("clicked", iniciar, texto)
btpara.connect("clicked", parar)
win.connect("destroy",gtk.main_quit)

# encaixotando tudo
box.pack_start(texto)
box.pack_start(btinicia)
box.pack_start(btpara)
win.add(box)

# exibindo tudo
win.show_all()

# iniciando o loop principal do GTK
gtk.main()

O que deveria acontecer ao executar esse código? Uma janela com uma caixa de texto, um botão “iniciar” e um botão “interromper”.

Clicando no “iniciar”, um loop inicia e começa a mudar o conteúdo da caixa de texto para “1”, “2” e assim por diante até que se clique em “interromper”.

Se quiser executar o código, fique à vontade. Mas vou adiantar aqui que isso não vai dar certo. Ao clicar em “iniciar”, o controle do programa passa para o loop sem fim, e você nem vê a caixa de texto sendo atualizada e nem consegue mais clicar no botão “interromper”. Simplesmente seu programa pára de responder.

Para que o programa funcione como esperado, é necessário criar uma thread onde será executada a função iniciar. A função então vai rodar em paralelo, e você assume de novo o controle do programa.

************

Atualização!

Conforme o comentário do thotypous, é possível fazer esse código funcionar mesmo sem threads.

Basta usar while gtk.events_pending():gtk.main_iteration() dentro do loop:


    while continua:
        while gtk.events_pending():gtk.main_iteration()
        texto.set_text(str(x))
        x+=1

Como o objetivo deste artigo é mostrar o uso de threads com pygtk, vamos tocar o barco.

************

Para utilizar threads em pygtk, além do que já aprendeu sobre threads você vai ter que lembrar de duas coisinhas:

( referência: Using threads in PyGTK )

A função gobject.idle_add( callback, [argumentos para callback] ) adiciona uma função (especificada por callback) a ser chamada sempre que não houver eventos de prioridade mais alta para o main loop. Os argumentos para a função callback são passados como argumentos adicionais de gobject.idle_add(). (livre tradução da definição em gobject.idle_add )

Vamos ver então como fazer o código apresentado lá em cima funcionar direito:


from threading import Thread
import time
import gtk, gobject
gtk.gdk.threads_init()
a_thread=None

def iniciar(botao, texto):
    global a_thread
    #iniciando a thread
    a_thread=Thread(target=nosso_loop, args=(texto,botao))
    a_thread.morta=False
    a_thread.start()

def nosso_loop(texto, botao):
    # desabilitando o botao "iniciar"
    # para que o usuario nao possa
    # iniciar novas threads enquanto
    # esta continuar
    botao.set_sensitive(0)

    z=0
    while not a_thread.morta:
        z+=1
        # chamando a funcao atualiza_texto
        # com o idle_add
        gobject.idle_add(atualiza_texto,texto, z)

        time.sleep(0.00001)
    # o loop terminou
    # o botao vai poder agora
    # ser clicado de no:vo
    botao.set_sensitive(1)

def atualiza_texto(texto, valor):
    texto.set_text(str(valor))

def interromper(botao=None):
    a_thread.morta=True

def sair(win):
    interromper()
    gtk.main_quit()

#criando os componentes
win=gtk.Window()
box=gtk.VBox()
texto=gtk.Entry()
botao=gtk.Button("iniciar" )
btmata=gtk.Button("interromper" )

#ativando os eventos
botao.connect("clicked", iniciar, texto)
btmata.connect("clicked", interromper)
win.connect("destroy",sair)

#encaixotando tudo
box.pack_start(texto)
box.pack_start(botao)
box.pack_start(btmata)
win.add(box)

#exibindo tudo
win.show_all()

#iniciando o loop principal do GTK
gtk.main()

Incluimos a thread neste código. A função “iniciar()” agora simplesmente inicia a thread. O loop infinito foi transferido para a função “nosso_loop()”, que será executada pela thread.

A atualização da caixa de texto foi transferida para a função “atualiza_texto()”, que é executada com “gobject.idle_Add()”.

Pode executar este código. Vai funcionar.

Anúncios

4 Respostas to “Threads com PyGTK”

  1. Poderia ser feito sem threads usando gtk.main_iteration(False) dentro do loop.

    Para loops mais complicados, que usem I/O por exemplo, esta dica é interessante: http://faq.pygtk.org/index.py?req=show&file=faq20.009.htp

  2. Estou tentando fazer as threads funcionarem com o pygtk, mas o python sempre trava. Copiei e colei seu código e mesmo assim trava.
    Estou tentando rodar com o python 2.5, pygtk 2.12.1 e gtk 2.0 sobre o windows XP.

  3. Guilherme David da Costa Says:

    Também estou tentando com python 2.5 e não estou conseguindo, o gtk.gdk.threads_init() trava minha gui. Ela abre, mas fica travada em branco e não aparece nenhum elemento. Alguém tem alguma dica?

  4. Guilherme David da Costa Says:

    Resolvi meu problema trocando o threads_init que vinha do gdk para o que existe do gobject, caso alguém queira saber.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: