Python multiprocessing Sorunu

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    ui.get_combobox_values()

    #hatalı kısım
    def yaz():
        ui.yaz()

    get_real_time = Process(target=yaz) 
    get_real_time.start()
    #buraya kadar

Merhaba arkadaşlar,

kodu bu şekilde yazdığımda aşağıdaki hatayı alıyorum:

AttributeError: Can't get attribute 'yaz' on <module '__mp_main__' from 'C:\\Users\\ihsan\\PycharmProjects\\NorkQTaskKiller\\abramanagerUI.py'>

bunun sebebi nedir ? Benzer kodu başka bir .py dosyasında yazdığımda çalışıyor. İşin içine ui değişkeni girdiğinde sorun çıkıyor. Yani boş bir python sayfasına şu kodu yazdığımda sorun yok:

from multiprocessing import Process


def yaz():
    print("ihsan")

if __name__ == '__main__':
    p = Process(target=yaz)
    p.start()

aynı zamanda Process tanımlarken target=ui.yaz şeklinde kullandığım zamanda şu hatayı alıyorum:

TypeError: can't pickle QWidget objects

multiprocessing’in Pyqt5’de oluşturduğum MainWindow sınıfı ile bir alıp veremediği var sanırım.

1 Beğeni

Siz de yaz fonksiyonunu __main__ bloğunun üstünde bir yerde tanımlayın.

Merhaba İhsan Bey. multiprocessing kullanan herkesin bilmesi gereken bir kaç önemli şey var.
Öncelikle __name__ değişkeninin sadece ana programda, yani ilk process de "__main__"
değerine sahip olduğunu gözden kaçırıyorsunuz. Şu kodu deneyelim:

from multiprocessing import Process

def yaz():
    print(__name__)

if __name__ == '__main__':
    p = Process(target=yaz)
    p.start()
    input() # terminal kapanmasın diye

Bu kodu çalıştırdığınızda terminale __mp_main__ yazıldığınızı görebilirsiniz, ki bu sizin hata mesajınızda da yer alıyor:

AttributeError: Can't get attribute 'yaz' on <module '__mp_main__' from 'C:\\Users\\ihsan\\PycharmProjects\\NorkQTaskKiller\\abramanagerUI.py'>

Şimdi şuna dikkat edelim ki if __name__=="__main__:" diye bir bölüme sahibiz. Eğer bu bölümün içinde bir değişken tanımlarsak oluşturacağımız yeni processler bunlara erişemez. Aynı sizin yaz fonksiyonunuzda olduğu gibi (sonuçta o da bir değişken):

Çünkü yeni bir process başlatıldığında aslında sizin yazdığınız kodun aynısı işlemcinin farklı bir çekirdeğinde baştan başlatılır. Tabii __name__ değişkeni değiştirilerek. İşte bu yüzden bu kodu yazdığınızda:

if __name__ == "__main__":

    def yaz():
        ui.yaz()

    get_real_time = Process(target=yaz) 
    get_real_time.start()

Yeni başlattığınız process if __name__ == "__main__": kısmının içerisine girmez (bu yüzden bu bölümü multiprocessing kullanırken hep yazıyoruz. Yoksa bu da yeni bir process başlatırdı ve hep yeni processler başlatılmaya devam ederdi.), çünkü __name__ değişkeni artık "__main__" değildir (mesela sizin programınızda '__mp_main__' dı. Yukarıda bunu ekrena yazdırarak da görmüştük.) Bu if ifadesinin içine girilmediğinde de yaz fonksiyonu yeni procesimiz için tanımlanmamış oluyor. O yüzden bu değişkeni if __name__ == "__main__": nin dışında tanımlamanız gerekmekte. Aynı bu çalışan örnekte olduğu gibi:

from multiprocessing import Process


def yaz():
    print("ihsan")

if __name__ == '__main__':
    p = Process(target=yaz)
    p.start()

Şimdi

Kısmına gelirsek, target parametresine verdiğiniz değişkenler eğer bir python değişkeni ise multiprocessing modülü, pickle modülünü kullanarak bu değişkeni yeni process’e aktarmaya çalışır. Ancak QWidget nesneleri aslında tamamen bir python nesnesi değildir. Sonuç olarak QT kütüphanesi aslen python da yazılmamıştır, PyQt kütüphanesi de sadece binding sağlamaktadır. Yani bu nesneler saf python nesneleri olmadığı için pickle modülü bunlar üzerinde çalışamıyor, bu da size yukarıdaki hatayı veriyor. Bir örnek vermek gerekirse bu kodu çalıştırdığımızda:

from PyQt5 import QtWidgets
import pickle
import sys

app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
pickle.dumps(w)

Şu hatayı alıyoruz (ki bu sizin aldığınız hata ile aynı):

Traceback (most recent call last):
  File "C:\Users\Dinçel\Desktop\istihza denemeleri.py", line 8, in <module>
    pickle.dumps(w)
TypeError: can't pickle QWidget objects

Şimdi sonuç kısmına gelirsek, multiprocessing kullanmazın yanlış. Neden multiprocessing
modülünü kullanmak istediğinizi iyi düşünmelisiniz, ki zaten PyQt arayüz nesnelerini multiprocessing ile kullanamazsınız. Çünkü yukarıda da anlattığımız gibi process ler arasında aktarılamıyorlar. threading modülü işinizi görecekse onu kullanmalısınız. Ayrıca ben neden multiprocessing i arayüz nesnelerini parametre vererek çağırdığınızı anlayabilmiş değilim. Amacınızı threading ile de ulaşabileceğinizi düşünüyorum. Kütüphane seçiminiz hakkında buraya bakmanızı tavsiye ederim.

1 Beğeni

Cevabınız için teşekkürler hocam. Programda threading kullanıyordum fakat threading’leri öldüremediğim için multiprocessing’e geçmek istedim. Bu bir öğrenme projesi aslında. Yanlış yolları seçmiş olmam bu yüzdendir. Buradaki amacım aslında şuydu:
Arayüzde bir buton var ve butona basıldığında bir adet sayaç başlıyor. Bu sayacı arayüz ile aynı anda çalıştıramıyorum. Butona basıldığında arayüz çalışırken sayacın çalışmasını threading ile sağladım. Fakat sayacı durdurmak için thread nesnesini öldüremedim. Sonuçta multiprocessinge geçtim.

Ah, buna yazdigim cevap gitmis.

Penceresi bile olmayan ikinci bir process’ten UI’yi kontrol etmeye calismak yerine thread kullanmak lazim demistim. Fakat cogu GUI kutuphanesi tek bir “ana”/GUI thread’i disinda bir thread’den mudahaleye izin vermiyor demistim. Bunu yapabilmek icin message/event/signal mekanizmalari oluyor kutuphanenin kendisinde. Qt’de signal vardi son baktigimda.

2 Beğeni