osa1 feed

Çalışan bir Python programına mesaj gönderme ve PyGTK

March 17, 2011 - Tagged as: python, tr.

Bloguma vim ile yazı yazma projemin bir parçası(aslında vim ile python scripti çağırmak ile alakalı yaptığım deneylerin bir sonucu olan) olan vim-markdown-preview scriptin sayesinde birkaç şey farkettim.

Vim için bir script yazacaksanız ve bu scriptin tek yapacağı buffer’daki metini alıp onunla birşeyler yapmaksa, bunun en basit yolu bir vimscriptiyle buffer’ı başka bir python scriptine göndermek sanırım. En azından ben öyle yaptım. Şu kadar basit:

function! MarkdownPreview()

python << EOF

import vim
import subprocess

gr = "/home/osa1/Desktop/vim-markdown-preview/GeckoRenderer.py"
subprocess.Popen(["python", gr, "\n".join(vim.current.buffer[:])])

EOF
endfunction

Burda farkedebileceğiniz gibi bufferı çekip, subprocess.Popen ile istediğiniz Python scriptine gönderebiliyorsunuz. Çok kolay, bundan sonrası da normal Python zaten.

Fakat burda şöyle bir problem oluşuyor, bu fonksiyonu her çağırdığınızda Python scripti bir daha çalışıyor. Benim yapmak istediğim, eğer script zaten çalışıyorsa onu güncellemek.

Burda iki problem var, birincisi, programın zaten çalışıp çalışmadığını tespit etmek, ikincisi de eğer zaten çalışıyorsa ona mesaj göndermek. İkisi hakkında da nette bir sürü çözüm var(dbus, pid, multiprocess, pipe, alakalı SO mesajları: 1, 2, 3, 4).

Fakat benim uyguladığım ve sanırım en basit yöntem, programın çalışmadan önce bir dosyayı kontrol etmesi, o varsa çalışmak yerine gerekli sunucunun(localhost) gerekli portuna mesaj göndermesi. Mesajlaşma olayını socketlerle hallettim yani. Çok kolay ve anlaşılır oldu. Fakat işin içine arayüz girince bir problem daha ortaya çıktı: bir socket dinlerken arayüzü güncellemek.

Aşağıdaki program herşeyi özetliyor:

import os
import gtk
import socket
import gobject
from sys import argv

# dosya adi farketmez, programimiza ait oldugu belli olsun
PIDFILE = "ohnoes.pid"
PORT = 8081 # dinleyecegimiz port, musait bir port olsun yeter
DELAY = 1

def run(text):
    # burda birseyler yapilacak
    port = PORT
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # udp
    s.bind(("", port)) # localhost'da 8081'i dinleyecegiz
    # burasi onemli, DELAY surede bir dinlemeyi birakip,
    # arayuzu guncellenmeye zorlayacagiz
    s.settimeout(DELAY)
    while True:
        try:
            data, addr = s.recvfrom(1024)  # dinleme
        except socket.timeout:  # delay sure bekledik, birsey yok
            print "timeout"
        else:
            print "got data", str(data)  # veriyi aldik
            update(data)  # guncelleme fonksiyonumuzu cagirdik

        # bu sekilde gtk'yi guncellenmeye zorluyoruz
        while gtk.events_pending():
            gtk.main_iteration()
        # surekli True dondurerek gtk'ya islemin bitmedigini soyluyoruz
        # musait oldugunda yine bu fonksiyonu cagirsin
        yield True

def is_running():
    # Pid'e falan hic gerek yok,
    # bir dosya olusturup, onu kontrol edecegiz
    # program acilirken o dosyayi olusturacak,
    # kapanirken silecek, sorun yok
    if os.path.isfile(PIDFILE):
        return True # program zaten calisiyor
    with file(PIDFILE, "w") as f:
        f.write("running")  # dosyanin icerigi onemli degil
    return False

if __name__ == "__main__":
    text = argv[^1]    if is_running(): # program calisiyor
        port = PORT
        host = "localhost"
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # udp
        s.bind(("", 0))
        s.sendto(text, (host, port))  # veriyi gonder
    else:
        # bu asamaya gelindiginde, is_running() zaten gerekli
        # dosyayi olusturmus oluyor
        task = run(text)  # program calistiriliyor
        gobject.idle_add(task.next)  # gtk musait oldugunda bunu cagiracak
        gtk.main()

Kodda bulunmayan ama dikkat edilmesi gerek bir nokta da, gui’nin kapanma fonksiyonuna(close sinyali gönderildiğinde çağırılan fonksiyon) os.remove(PIDFILE) satırını eklenmesi. Ha unutmadan, dosya konumu değiştirilmeli tabii ki, bu halde program halgi dizinden çağırılıyorsa oraya bakıyor ve gerekiyorsa dosyayı oluşturuyor.