도와주세요

PyQt 실행시 프리징 현상 관련하여 질문 드립니다! ㅠㅠ

이니스프리2019.07.06 03:37조회 수 56댓글 3

    • 글자 크기

안녕하세요?


개인적인 목적으로 사용하려고 파이썬과 PyQt를 이용하여 디시인사이드의 이미지를 다운받는 스크립트를 작성했는데요.


80% 정도 완성한 단계인데요 PyQt의 GUI가 반응이 없는 현상이 계속되어서 질문을 드립니다 ㅠㅠ



우선 .py 파일입니다.


import requests, time, re, sys, os, dc_get_ui
from bs4 import BeautifulSoup
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5 import uic
 

def parse_page(gall_name, page): # 특정 페이지의 글 번호를 파싱합니다
    headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'}
    page_url = 'https://gall.dcinside.com/mgallery/board/lists?id=' + gall_name + '&page=' + str(page)
    req = requests.get(page_url, headers=headers)
    html = req.text
    soup = BeautifulSoup(html, 'html.parser')
    list_nums = soup.find_all('td',{'class':'gall_num'})
    article_no = []
    for list_num in list_nums:
        if list_num.text.isdigit() == True:
            article_no.append(list_num.text)
    return article_no


def download(gall_name, article_no): # 특정 글의 이미지 파일을 다운로드합니다
    article_url = 'https://gall.dcinside.com/mgallery/board/view/?id=' + gall_name + '&no=' + article_no
    headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'}
    req = requests.get(article_url, headers=headers)
    html = req.text
    soup = BeautifulSoup(html, 'html.parser')
    title = soup.select_one('h3 span.title_subject').text
    title = re.sub('[^가-힣a-zA-Z0-9\s]', '', title)
    write_time = soup.select_one('span.gall_date')['title'].split(' ')[0]
    ul_li_as = soup.select('ul.appending_file li a')
    count = 0
    if len(ul_li_as) != 0:
        for ul_li_a in ul_li_as:
            base_url = ul_li_a['href'].replace('download', 'viewimage')
            file_name = ul_li_a.text
            image_req = requests.get(base_url, headers=headers)
            if image_req.status_code == 200:
                with open('./bona/' + write_time + ' ' + title + ' - ' + file_name, 'wb') as f:
                    f.write(image_req.content)
            del image_req
            count += 1
    message = title + '(' + str(count) + ')'
    print(message)
    return message


class MainDialog(QDialog, dc_get_ui.Ui_Dialog): # GUI 관련 부분입니다
    def __init__(self):
        QDialog.__init__(self, None)
        self.setupUi(self)
        self.init_UI()
   
    def init_UI(self):
        self.pushButton_1.clicked.connect(self.start)
        self.pushButton_2.clicked.connect(self.select_dir)

    def select_dir(self):
        directory = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
        #os.startfile(directory)

    def start(self):
        gallery = self.lineEdit_1.text()
        if self.lineEdit_2.text() == '0':
            start = 1
        else:
            start = int(self.lineEdit_2.text())
        if self.lineEdit_3.text() < self.lineEdit_2.text():
            end = int(self.lineEdit_2.text()) + 1
        else:
            end = int(self.lineEdit_3.text()) + 1
        all_article_no = []
        for i in range(start, end):
            all_article_no = all_article_no + parse_page(gallery, i)
        for temp in all_article_no:
            result = download(gallery, temp)
            time.sleep(1)
            self.textBrowser.append(result) # 이 부분이 문제가 되고 있습니다
       

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainDialog()
    window.show()
    app.exec_()
   



PyQt5로 만들고 .py 파일로 변환한 파일입니다.


from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(800, 579)
        self.textBrowser = QtWidgets.QTextBrowser(Dialog)
        self.textBrowser.setGeometry(QtCore.QRect(20, 120, 761, 421))
        self.textBrowser.setObjectName("textBrowser")
        self.pushButton_1 = QtWidgets.QPushButton(Dialog)
        self.pushButton_1.setGeometry(QtCore.QRect(20, 70, 111, 31))
        self.pushButton_1.setObjectName("pushButton_1")
        self.comboBox = QtWidgets.QComboBox(Dialog)
        self.comboBox.setGeometry(QtCore.QRect(20, 20, 91, 31))
        self.comboBox.setObjectName("comboBox")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.lineEdit_1 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit_1.setGeometry(QtCore.QRect(130, 20, 651, 31))
        self.lineEdit_1.setObjectName("lineEdit_1")
        self.pushButton_2 = QtWidgets.QPushButton(Dialog)
        self.pushButton_2.setGeometry(QtCore.QRect(150, 70, 111, 31))
        self.pushButton_2.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
        self.pushButton_2.setAutoDefault(False)
        self.pushButton_2.setObjectName("pushButton_2")
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(20, 551, 761, 21))
        self.label.setObjectName("label")
        self.checkBox = QtWidgets.QCheckBox(Dialog)
        self.checkBox.setGeometry(QtCore.QRect(270, 80, 131, 16))
        self.checkBox.setAutoFillBackground(False)
        self.checkBox.setChecked(True)
        self.checkBox.setObjectName("checkBox")
        self.label_2 = QtWidgets.QLabel(Dialog)
        self.label_2.setGeometry(QtCore.QRect(380, 80, 81, 16))
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(Dialog)
        self.label_3.setGeometry(QtCore.QRect(580, 80, 81, 16))
        self.label_3.setObjectName("label_3")
        self.lineEdit_2 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit_2.setGeometry(QtCore.QRect(460, 70, 71, 31))
        self.lineEdit_2.setMaxLength(2)
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.lineEdit_3 = QtWidgets.QLineEdit(Dialog)
        self.lineEdit_3.setGeometry(QtCore.QRect(640, 70, 71, 31))
        self.lineEdit_3.setObjectName("lineEdit_3")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        self.pushButton_1.setToolTip(_translate("Dialog", "Start download."))
        self.pushButton_1.setText(_translate("Dialog", "Download"))
        self.comboBox.setToolTip(_translate("Dialog", "Choose between major & minor gallery."))
        self.comboBox.setItemText(0, _translate("Dialog", "Gallery"))
        self.comboBox.setItemText(1, _translate("Dialog", "Minor gallery"))
        self.lineEdit_1.setToolTip(_translate("Dialog", "Input gallery name."))
        self.pushButton_2.setToolTip(_translate("Dialog", "Select folder to download files."))
        self.pushButton_2.setText(_translate("Dialog", "Select folder"))
        self.label.setText(_translate("Dialog", "Total files downloaded : "))
        self.checkBox.setToolTip(_translate("Dialog", "Open folder after finishing downloading."))
        self.checkBox.setText(_translate("Dialog", "Open folder"))
        self.label_2.setText(_translate("Dialog", "From page :"))
        self.label_3.setText(_translate("Dialog", "To page:"))
        self.lineEdit_2.setToolTip(_translate("Dialog", "Input the starting page number which you want to download."))
        self.lineEdit_2.setInputMask(_translate("Dialog", "99"))
        self.lineEdit_2.setText(_translate("Dialog", "1"))
        self.lineEdit_3.setToolTip(_translate("Dialog", "Input the final page number which you want to download."))
        self.lineEdit_3.setInputMask(_translate("Dialog", "99"))
        self.lineEdit_3.setText(_translate("Dialog", "1"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Dialog = QtWidgets.QDialog()
    ui = Ui_Dialog()
    ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())




스크립트가 장황한데 문제가 되는 부분을 간단히 말씀을 드리면요.


일단 게시판 파싱과 이미지 다운로드 부분은 잘 작동을 하더군요.


그런데 GUI 관련하여 문제가 있는데 도저히 해결이 안 되네요 ㅠㅠ


파일을 다운로드 받은 후에 그 제목을 GUI에 표시하려고 self.textBrowser.append(result)을 삽입했는데요.

(실행파일 아래에서부터 8번째 줄에 있습니다)


GUI가 프리징 되면서 전혀 작동하지 않더군요.


멀티프로세싱으로 해결해보려고 해도 제 실력으로는 잘 안 되구요 ㅜㅜ


제가 구글링을 해본바로는 stackoverflow의 다음글이 관련된 내용인 것 같은데 도저히 이해가 안 되네요 ㅠㅠ


https://stackoverflow.com/questions/41526832/pyqt5-qthread-signal-not-working-gui-freeze


아마도 유사한 문제가 PyQt5에서 종종 발생하는 것 같아요.



결론적으로 PyQt5 자체의 문제인지, 프리징을 제가 어떻게 해결할 수 있는지 여쭤봅니다.


그럼 즐거운 주말 되세요!


스포어 회원님들께 항상 감사드립니다 ^-^



+)

버튼을 클릭해도 실행이 안 된다고 생각하실 수 있는데요 ㅠㅠ

현재까지는 마이너 갤러리에만 작동하도록 되어있어요.

(갤러리 / 마이너 갤러리 선택 드롭다운 메뉴는 아직 작동을 하지 않아요)

'war'이나 'bona'를 입력하시면 파싱은 잘 되더군요.

ཇོ་མོ་གླང་མ

    • 글자 크기
무료 아이콘 사이트 추천 부탁드려요 ^^ (by 이니스프리) 구글 에드센스 등록하려다보니.. (by 막시모)
  • 2019.7.10 00:20

    UI를 갱신하는 코드가 반복문 안에 있어서 발생하는 문제입니다.


    저렇게 웹 요청을 보내는 네트워크 작업이나 파일을 쓰는 IO 작업의 경우에는 시간이 많이 소요되기 때문에, 멀티 프로세싱으로 하는게 좋긴 하지만 프로토 타입이라면 굳이 할 필요는 없겠네요.


    해결법은 간단한데 해당 반복문 안에 QApplication.processEvents() 를 넣어줘서 pending 상태인 이벤트들을 처리해주면 됩니다. 즉 아래와 같이 한 줄만 추가해주시면 됩니다.


    import requests, time, re, sys, os, dc_get_ui
    from bs4 import BeautifulSoup
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5 import uic
    
    
    def parse_page(gall_name, page): # 특정 페이지의 글 번호를 파싱합니다
        headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'}
        page_url = 'https://gall.dcinside.com/mgallery/board/lists?id=' + gall_name + '&page=' + str(page)
        req = requests.get(page_url, headers=headers)
        html = req.text
        soup = BeautifulSoup(html, 'html.parser')
        list_nums = soup.find_all('td',{'class':'gall_num'})
        article_no = []
        for list_num in list_nums:
            if list_num.text.isdigit() == True:
                article_no.append(list_num.text)
        return article_no
    
    
    def download(gall_name, article_no): # 특정 글의 이미지 파일을 다운로드합니다
        print(gall_name, article_no)
        article_url = 'https://gall.dcinside.com/mgallery/board/view/?id=' + gall_name + '&no=' + article_no
        headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'}
        req = requests.get(article_url, headers=headers)
        html = req.text
        soup = BeautifulSoup(html, 'html.parser')
        title = soup.select_one('h3 span.title_subject').text
        title = re.sub('[^가-힣a-zA-Z0-9\s]', '', title)
        write_time = soup.select_one('span.gall_date')['title'].split(' ')[0]
        ul_li_as = soup.select('ul.appending_file li a')
        count = 0
        if len(ul_li_as) != 0:
            for ul_li_a in ul_li_as:
                base_url = ul_li_a['href'].replace('download', 'viewimage')
                file_name = ul_li_a.text
                image_req = requests.get(base_url, headers=headers)
                if image_req.status_code == 200:
                    with open('./bona/' + write_time + ' ' + title + ' - ' + file_name, 'wb') as f:
                        f.write(image_req.content)
                del image_req
                count += 1
        message = title + '(' + str(count) + ')'
        print(message)
        return message
     
     
    class MainDialog(QDialog, dc_get_ui.Ui_Dialog): # GUI 관련 부분입니다
        def __init__(self):
            QDialog.__init__(self, None)
            self.setupUi(self)
            self.init_UI()
       
        def init_UI(self):
            self.pushButton_1.clicked.connect(self.start)
            self.pushButton_2.clicked.connect(self.select_dir)
     
        def select_dir(self):
            directory = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
            #os.startfile(directory)
     
        def start(self):
            gallery = self.lineEdit_1.text()
            print(gallery)
            if self.lineEdit_2.text() == '0':
                start = 1
            else:
                start = int(self.lineEdit_2.text())
            if self.lineEdit_3.text() < self.lineEdit_2.text():
                end = int(self.lineEdit_2.text()) + 1
            else:
                end = int(self.lineEdit_3.text()) + 1
            all_article_no = []
            for i in range(start, end):
                all_article_no = all_article_no + parse_page(gallery, i)
            for temp in all_article_no:
                result = download(gallery, temp)
                time.sleep(1)
                self.textBrowser.append(result)
                QApplication.processEvents()
           
     
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = MainDialog()
        window.show()
        app.exec_()
        



    프로그램이 단일 스레드로 돌아가고 있어서 창을 움직일 때 약간 뚝뚝 끊기는 느낌을 받으실 수 있습니다.


  • @humit
    이니스프리글쓴이
    2019.7.10 00:29

    허걱 밤늦게 정말 감사합니다!

    그러지 않아도 지금 막 페이스북의 파이썬 코리아에 질문글 올리려고 하던 참이었거든요 ㄷㄷ

    저 혼자 구글링하면서 해결해보려고 해도 도저히 해결되지 않더군요 ㅠㅠ

     

    humit 님께서 말씀해주신대로 반복문 안에 QApplication.processEvents()를 삽입한 후에 돌려보니

    GUI가 정상적으로 작동하고 파일을 받아올 때마다 그 내역을 출력해주네요 ^^

     

    그럼 날씨가 무더운데 humit 님께서도 바쁘시겠지만 항상 건강하시길 기원합니다!

    바쁘신데 번번이 큰 도움을 주셔서 진심으로 감사드립니다 :)

  • @이니스프리
    2019.7.10 00:41

    네 열심히 코딩하세요 ㅎㅎㅎ

댓글 달기

번호 제목 글쓴이 날짜 조회 수
공지 사이트 정보 등록에 경고로 변경된 분들 참고하세요.6 마스터 2019.06.19 653
공지 [중요 공지 포함] 사이트가 복구 되었습니다.40 마스터 2019.06.18 787
공지 호스팅 신청이 다시 가능합니다.17 마스터 2019.04.18 1794
공지 회원 전용 페이지가 생겼습니다.15 마스터 2018.03.20 7274
공지 [필독] 질문하는 방법5 마스터 2018.02.23 964
589 사이트 로고 디자인을 의뢰하기 전에 질문 드려요 ^^ 이니스프리 3시간 전 18
588 썸네일 추출관련 정규식 표현을 어떻게 해야하나요?4 슬기 2019.07.16 45
587 1일 간격으로 사이트가 비활성됩니다 Piedots 2019.07.16 33
586 이베이에서 판매되는 Adobe master collection CS6 정품일까요?8 이니스프리 2019.07.12 79
585 닷홈 인코딩 설정 관련해서 질문 드려요 ㅠㅠ6 이니스프리 2019.07.11 71
584 무료 아이콘 사이트 추천 부탁드려요 ^^4 이니스프리 2019.07.09 54
PyQt 실행시 프리징 현상 관련하여 질문 드립니다! ㅠㅠ3 이니스프리 2019.07.06 56
582 구글 에드센스 등록하려다보니..6 막시모 2019.06.27 67
581 구글 광고를 등록하려고하는데..8 막시모 2019.06.26 69
580 도메인 기관이전을 하려고 합니다5 Piedots 2019.06.13 70
579 나무위키 포크 위키 만들기2 아엠이언맨 2019.06.07 90
578 Freenom 무료 도메인3 blueone 2019.05.27 134
577 게시판 푸시 알림을 구현할 수 있을까요?9 Piedots 2019.05.22 147
576 이 코드 뭔가 이상해요...18 title: 포켓몬고-EX레이드 초대장허니버터뚠뚜니라이츄 2019.05.20 223
575 [Summernote] 에디터 아이콘 문제 Piedots 2019.05.19 76
574 부트스트랩 모달을 사용하면 모바일 환경에서 문제의 소지가 있을까요??2 이니스프리 2019.05.15 78
573 윈도우 서버에 모니위키 설치 가능한가요?6 title: 애프터 이펙트제르엘 2019.05.13 127
572 일본어 한 단어만 읽어주시면 감사하겠습니다!!8 이니스프리 2019.05.07 183
571 아이콘 라이센스의 저작자 표기 방법과 Dot icon 관련 사이트 추천 부탁드립니다. 이니스프리 2019.05.06 119
570 phpmyadmin이랑 FTP 접속이 안되는데...2 title: 포켓몬고-EX레이드 초대장허니버터뚠뚜니라이츄 2019.05.06 108
이전 1 2 3 4 5 6 7 8 9 10... 30다음
첨부 (0)
서버에 요청 중입니다. 잠시만 기다려 주십시오...