PyQt5 Döngü İçerisindeki Veriyi ScrollArea'ya Eklemek

Merhaba.
Umarım “veri” derken yanlış bir kelime kullanmamışımdır.

Twitch API ile birşeyler deniyordum. “neden bir arayüz yapıp chati, yayın başlığı izleyen sayısı yayıncının profil resmi, bağışlar vs gibi özellikleri kullanmıyorum” dedim kendi kendime. Dediğim yapmadan önce Twitchin yayınladığı dökümantasyonu okuyarak istediğim bilgilere ulaştığımı farkettim, Şuanlık hiçbir problem yok(daha bağış özelliğini eklemedim) ta ki bu yaşadığım soruna kadar.

Şimdi gif ile çalıştığını göstereyim, daha sonra detayına gireyim.
bandicam-2022-04-10-01-19-05-793
Burada görünen total views şuan işlevsiz yani belli olması açısından rastgele rakamlar koydum.

Şuan GIF te görünen anlık olarak denediğim Tuğkan Gönültaş’ın yayınından bilgiler. Yani ben burada chatinde yazılan mesajları kimin ne yazdığını alabiliyorum. (Bunu youtube videosu izleyerek ve yine okuyarak yaptım Nizami görünüşü için ise düzenlemeler yaptım).

Geliyorum Sorunumaaa…
Gösterdiğim QScrollArea’ya birer label eklemek istiyorum. Ekleyeceğim label de chateki mesajları anlık çektiğinden dolayı döngüde olduğu için Thread sınıfı kullandım. Nasıl eklendiğine dair internette elbette bilgiler mevcut. Bu bilgileri referans alarak uyguladığımda arayüz açılıyor donma kasma olmuyor fakat bana şöyle bir uyarı ya da hata tam bilmiyorum şöyle bir mesaj veriyor QObject::setParent: Cannot set parent, new parent is in a different thread
Bu uyarı her mesaj geldiğinde gerçekleşiyor. Bunu internette arattığımda ise hiç anlamadığım sonuçlar gördüm. Farklı bir forum sitesinde de açtım Bir bilir kişi gerçekten güzel bilgiler verdi. Fakat ne kadar onun dediğini yapmaya çalışsamda, ne kadar dediğine uymaya çalışıp kodu düzenlesemde ya chat görünüyor arayüz açılmıyor, ya da arayüz açılıp bahsettiğim hatayı veriyor.
Mesajı şöyle:


Daha sonra bu kişinin dediğinden bağımsız olarak cevap vermeden önceki kodumu paylaşıyorum:

from getpass import getuser
from threading import Thread
from time import sleep
from PyQt5 import QtGui,QtWidgets
from PyQt5.QtWidgets import QApplication
from twitchWindow import Ui_MainWindow # Tasarladığım sayfa, İçerisinde Miras olarak alınan QMainWindow bulunuyor
from configparser import ConfigParser
from webbrowser import open_new
import socket

file = f"C:\\Users\\{getuser()}\\Documents\\Twitch{chr(ord(' '))}Assistant"
cfg = ConfigParser()
cfg.read(f"{file}\\streamer.ini",encoding="utf-8")

class MainWindow(Ui_MainWindow): # Miras olarak Tasarım sayfasının sınıfını alıyor.(onun içerisinde de QMainWindow olduğundan)
    def __init__(self):
        super().__init__() # Miras özellikleri kullanabilmem için
        self.streamerNameLabel.setText(f"Streamer-Name: {cfg['Stream']['user_name']}")
        self.viewCountLabel.setText(cfg['Stream']['count'])
        self.broadcastTitleLabel.setText(cfg["Stream"]["title"])
        self.profileImageLabel.setPixmap(QtGui.QPixmap(f"{file}\\{cfg['Stream']['streamer']}.png"))
        self.thumbnailLabel.setPixmap(QtGui.QPixmap(f"{file}\\{cfg['Stream']['streamer']}_thumb.png"))
        self.typeGameLabel.setText(cfg["Stream"]["game_name"])
        self.goBroadcastPushButton.clicked.connect(lambda: open_new(f"https://www.twitch.tv/{cfg['Stream']['streamer']}"))
        Thread(target=self.ircTwitchBot).start()

    def ircTwitchBot(self):
        oauth = "oauth:TOKEN" # Twitch generatorden elde edilen şifre
        user_name = "twitch_kullanıcı_adım" # twitch kullanıcı adım
        channel = cfg['Stream']['streamer'] # .ini dosyasına kaydettiğim yayıncı ismi
        addr = "irc.chat.twitch.tv" # twitch chat için yazılan dokümantasyondaki adress
        port = 6667 # olması gereken port
        
        ############################### Bağlantı işlemleri ###########################
        sock =  socket.socket()
        sock.connect((addr,port))
        self.send(sock,f"PASS {oauth}") # verilen şifrenin kullanıldığı yer
        self.send(sock,f"NICK {user_name}") # Kullanıcı adım
        self.send(sock,f"JOIN #{channel}") # Hangi yayıncının chatine katılacaksam channel değeri o
        while True:
            try:
                mesaj = f".tmi.twitch.tv PRIVMSG #{channel} :" # Daha anlamlı bir çıktı almak adına bu iletiyi bir değişkene atıyorum
                for i in self.recv(sock,1024).replace(mesaj," ").split(":")[1].split("@"): # daha düzenli gözükmesi için split ve replace kullanıp son halinde i değişkeni ile dolanıyorum
                    a = i.split(' ')[0].strip() # isimler
                    b = i.split(' ')[1:] # mesajlar
                    if not b == []: # normalde ikişer çıktı veriyor. Yaptığım düzenlemeye göre önce boş bir liste ardından kişileri ve ne yazdıkları ekrana düşüyor bunu yakalayıp yalnızca ne yazdıklarını ekrana bastırıyorum
                        c = f"{a.upper()} ----> {b}".replace("[","").replace("'","").replace(",","").replace("\\r\\n]","") # düzenli olarak gözüken "Kullanıcı ----> mesaj" çıktısı
                        print(c) # ekrana yazdırıyorum
                        # lbl = QtWidgets.QLabel(self.scrollAreaWidgetContents) # Tasarım sayfasının kodlarında bulunan nesneyi burada kullanıyorum
                        # lbl.setText(c) # Label'de görünecek ifade
                        sleep(1.2)
                        # self.verticalLayout.addWidget(lbl) # scrollAreaya eklenip ekranda gözükeceğini düşündüğüm kod parçası
            except:
                pass

    def send(self,sock,msg):
        sock.send(bytes(msg+"\n","ASCII"))

    def recv(self,sock,buff_size):
        return sock.recv(buff_size).decode("UTF-8")

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

Umarım yazım fazla kafa karıştırıcı olmamıştır.

Şimdiden yanıtlarınız için çok teşekkür ederim.

1 Beğeni

GUI islemlerini thread’de yapamazsin. Yapilacak islemleri ana thread’e bir sekilde yollayip (Qt’de bu is icin signal+slot mekanizmasi var) ana thread’de yapman lazim.

2 Beğeni

Hocam merhaba.

Yorumunuzdan sonra İnternete bakınmaya devam ettim ve şöyle bir siteyle karşılaştım.

Gösterdiği örnek üzerinden kendi koduma şöyle ekledim.

Kod
from getpass import getuser
from threading import Thread
from time import sleep
from PyQt5 import QtGui,QtWidgets
from PyQt5.QtCore import QThread,QObject,pyqtSignal
from PyQt5.QtWidgets import QApplication
from twitchWindow import Ui_MainWindow # Tasarladığım sayfa, İçerisinde Miras olarak alınan QMainWindow bulunuyor
from configparser import ConfigParser
from webbrowser import open_new
import socket

file = f"C:\\Users\\{getuser()}\\Documents\\Twitch{chr(ord(' '))}Assistant"
cfg = ConfigParser()
cfg.read(f"{file}\\streamer.ini",encoding="utf-8")

class Worker(QObject):
    finished = pyqtSignal()

    def run(self):
        oauth = "oauth:TOKEN" # Twitch generatorden elde edilen şifre
        user_name = "KULLANICI_ADIM" # twitch kullanıcı adım
        channel = cfg['Stream']['streamer'] # .ini dosyasına kaydettiğim yayıncı ismi
        addr = "irc.chat.twitch.tv" # twitch chat için yazılan dokümantasyondaki adres
        port = 6667 # olması gereken port
        sock =  socket.socket()
        sock.connect((addr,port))
        self.send(sock,f"PASS {oauth}") # verilen şifrenin kullanıldığı yer
        self.send(sock,f"NICK {user_name}") # Kullanıcı adım
        self.send(sock,f"JOIN #{channel}") # Hangi yayıncının chatine katılacaksam channel değeri o
        
        while True:
            try:
                mesaj = f".tmi.twitch.tv PRIVMSG #{channel} :" # Daha anlamlı bir çıktı almak adına bu iletiyi bir değişkene atıyorum
                for i in self.recv(sock,1024).replace(mesaj," ").split(":")[1].split("@"): # daha düzenli gözükmesi için split ve replace kullanıp son halinde i değişkeni ile dolanıyorum
                    a = i.split(' ')[0].strip() # isimler
                    b = i.split(' ')[1:] # mesajlar
                    if not b == []: # normalde ikişer çıktı veriyor. Yaptığım düzenlemeye göre önce boş bir liste ardından kişileri ve ne yazdıkları ekrana düşüyor bunu yakalayıp yalnızca ne yazdıklarını ekrana bastırıyorum
                        c = f.write(f"{a.upper()} ----> {b}".replace("[","").replace("'","").replace(",","").replace("\\r\\n]","")+"\n") # düzenli olarak gözüken "Kullanıcı ----> mesaj" çıktısı
                        sleep(1.2)
                        print(c)
                        self.finished.emit()
            except:
                pass

    def send(self,sock,msg):
        sock.send(bytes(msg+"\n","ASCII"))

    def recv(self,sock,buff_size):
        return sock.recv(buff_size).decode("UTF-8")

class MainWindow(Ui_MainWindow): # Miras olarak Tasarım sayfasının sınıfını alıyor.(onun içerisinde de QMainWindow olduğundan)
    def __init__(self):
        super().__init__() # Miras özellikleri kullanabilmem için
        self.thread = QThread() # linkte gösterilen örnek üzerine Thread nesnesi oluşturdum.
        self.worker = Worker() # QObject'ten miras alan sınıfın nesnesini oluşturdum.
        
        self.streamerNameLabel.setText(f"Streamer-Name: {cfg['Stream']['user_name']}")
        self.viewCountLabel.setText(cfg['Stream']['count'])
        self.broadcastTitleLabel.setText(cfg["Stream"]["title"])
        self.profileImageLabel.setPixmap(QtGui.QPixmap(f"{file}\\{cfg['Stream']['streamer']}.png"))
        self.thumbnailLabel.setPixmap(QtGui.QPixmap(f"{file}\\{cfg['Stream']['streamer']}_thumb.png"))
        self.typeGameLabel.setText(cfg["Stream"]["game_name"])
        self.goBroadcastPushButton.clicked.connect(lambda: open_new(f"https://www.twitch.tv/{cfg['Stream']['streamer']}"))
        
        self.worker.moveToThread(self.thread) # thread nesnemi belirtiyorum
        self.thread.started.connect(self.worker.run) 
        self.thread.start()

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

Eklediğimde önceden yaşadığım “sonra çözerim” dediğim şu sorunu çözmeme yardımcı oldu. O sorunda şuydu; arayüz kapanmasına rağmen arkada döngü hala devam ediyor olmasıydı. Bunu çözmesi işin tuzu biberi oldu.
Konuyu açarken de bahsettiğim hatayı/uyarıyı/yazıyı almıyorum.

Hocam internette kod incelerken insanlar @pyqtSlot() dekoratörünü kullandıklarını gördüm sizinde “slot” derken kastettiğiniz bu dekoratörü kullanarak bir method oluşturmak mı ?

Bir slot (alici), slota bagli method, bir sinyal (verici), ve sinyal–slot arasinda baglanti gerekiyor. Dekorator bunlardan bir veya iki tanesini hallediyor olabilir.

Forumda orneklerini gordugumu hatirliyorum. Internette de cok tutoryal var, haliyle—bunlar olmadan thread’den hic bir sey yapilamiyor cunku.

Python Thread’ini QThread ile degistirmenin aslinda bir seyi degistirmemesi lazim ama Qt baya sahne arkasinda is ceviren bir kutuphane, belki ozel bir seyler yapiyordur.

2 Beğeni

İyi geceler hocam.

Dediğiniz üzere bakındım ve foruma da göz gezdirdim sevgili @Cihat_Altiparmak’ın Şu yazısı gerçekten çok açıklayıcı olduğunu gördüm ve uyarladım.

Ve koduma da şöyle düzen getirdim. Sadece scrollun otomatik olarak aşağı inmesini sağlamam gerekiyor Labellerin makyajınıda henüz düzenlemedim default haliyle. Önce kodu daha sonra gif olarak bir görüntüyü paylaşıcam (Hocam “şunlar şunlar gereksiz durmasının bir anlamı yok” dediğiniz parçalar varsa belirtirseniz çok sevinirim.):

from getpass import getuser
from time import sleep
from PyQt5 import QtGui,QtWidgets
from PyQt5.QtCore import (QThread,QObject,pyqtSignal,pyqtSlot)
from PyQt5.QtWidgets import (QApplication,QScrollArea,QWidget,QVBoxLayout,QLabel,QFormLayout,QGroupBox)
from twitchWindow import Ui_MainWindow # Tasarladığım sayfa, İçerisinde Miras olarak alınan QMainWindow bulunuyor
from configparser import ConfigParser
from webbrowser import open_new
import socket
import os

file = f"C:\\Users\\{getuser()}\\Documents\\Twitch{chr(ord(' '))}Assistant"
cfg = ConfigParser()
cfg.read(f"{file}\\streamer.ini",encoding="utf-8")

class Worker(QObject):
    finished = pyqtSignal()

    def run(self):
        oauth = "oauth:TOKEN" # Twitch generatorden elde edilen şifre
        user_name = "TWITCH_KULLANICI_ADIM" # twitch kullanıcı adım
        channel = cfg['Stream']['streamer'] # .ini dosyasına kaydettiğim yayıncı ismi
        addr = "irc.chat.twitch.tv" # twitch chat için yazılan dokümantasyondaki adres
        port = 6667 # olması gereken port
        sock =  socket.socket()
        sock.connect((addr,port))
        self.send(sock,f"PASS {oauth}") # verilen şifrenin kullanıldığı yer
        self.send(sock,f"NICK {user_name}") # Kullanıcı adım
        self.send(sock,f"JOIN #{channel}") # Hangi yayıncının chatine katılacaksam channel değeri o
        
        while True:
            try:
                mesaj = f".tmi.twitch.tv PRIVMSG #{channel} :" # Daha anlamlı bir çıktı almak adına bu iletiyi bir değişkene atıyorum
                for i in self.recv(sock,1024).replace(mesaj," ").split(":")[1].split("@"): # daha düzenli gözükmesi için split ve replace kullanıp son halinde i değişkeni ile dolanıyorum
                    a = i.split(' ')[0].strip() # isimler
                    b = i.split(' ')[1:] # mesajlar
                    if not b == []: # normalde ikişer çıktı veriyor. Yaptığım düzenlemeye göre önce boş bir liste ardından kişileri ve ne yazdıkları ekrana düşüyor bunu yakalayıp yalnızca ne yazdıklarını ekrana bastırıyorum
                        with open("test.txt","a",encoding="utf-8") as f: # dışardan ulaşmanın yolu olarak dosya oluşturup içerisine kaydettirdim
                            f.write(f"{a.upper()} ----> {b}".replace("[","").replace("'","").replace(",","").replace("\\r\\n]","")+"\n") # düzenli olarak gözüken "Kullanıcı ----> mesaj" çıktısı
                        sleep(1.2)
                        self.finished.emit()
            except:
                pass
    def send(self,sock,msg):
        sock.send(bytes(msg+"\n","ASCII"))

    def recv(self,sock,buff_size):
        return sock.recv(buff_size).decode("UTF-8")

class MainWindow(Ui_MainWindow): # Miras olarak Tasarım sayfasının sınıfını alıyor.(onun içerisinde de QMainWindow olduğundan)
    def __init__(self):
        super().__init__() # Miras özellikleri kullanabilmem için
        self.thread = QThread()
        self.worker = Worker()
        
        self.streamerNameLabel.setText(f"Streamer-Name: {cfg['Stream']['user_name']}")
        self.viewCountLabel.setText(cfg['Stream']['count'])
        self.broadcastTitleLabel.setText(cfg["Stream"]["title"])
        self.profileImageLabel.setPixmap(QtGui.QPixmap(f"{file}\\{cfg['Stream']['streamer']}.png"))
        self.thumbnailLabel.setPixmap(QtGui.QPixmap(f"{file}\\{cfg['Stream']['streamer']}_thumb.png"))
        self.typeGameLabel.setText(cfg["Stream"]["game_name"])
        self.closePushButton.clicked.connect(lambda: self.closer())
        self.goBroadcastPushButton.clicked.connect(lambda: open_new(f"https://www.twitch.tv/{cfg['Stream']['streamer']}"))
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.thread.start()
        self.formLayout = QFormLayout()
        self.groupBox = QGroupBox()
        sleep(2)
        self.worker.finished.connect(self.deneme)
        while True:
            sleep(2)
            scroll = QScrollArea(self.tab)
            scroll.setWidget(self.groupBox)
            scroll.setWidgetResizable(True)
            scroll.setGeometry(8, 8, 508, 421)
            layout = QVBoxLayout()
            layout.addWidget(scroll)
            self.worker.finished.emit()
            break

    def closer(self):
        self.close()
        os.remove("test.txt")

    @pyqtSlot()
    def deneme(self):
        with open("test.txt","r",encoding="utf-8") as g:
            rr = g.read()
            self.label2 = QLabel(rr)
            print(rr)
            self.formLayout.addRow(self.label2)
            self.groupBox.setLayout(self.formLayout)

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

bandicam-2022-04-11-00-08-42-587

3 Beğeni