🛠환경
윈도우 | 10 최신 |
파이썬 | 3.11 |
Pyside6 | 6.4.2 |
pyinstaller | 5.13.0 |
Selenium | 4.8.2 |
상황
Pyinstaller로 빌드된 프로그램에서 버튼을 클릭 시 에러 없이 프로그램이 종료되는 상황입니다.
버튼 클릭 시에는 Selenium으로 웹페이지 동작을 자동화합니다.
원인 분석
- 에러를 출력해야 구글링이 가능해서 try문으로 소스코드 전체부터 메서드 하나까지 여러 번 감싸도 출력이 안 됐습니다.
- "버튼 클릭 시에 크롬 드라이버가 불러와지는 딜레이 시간 때문에 발생한다"라고 생각은 했지만 다소 억지가 있습니다.
- 이상한 점은 초기 실행 시에 이 현상이 자주 나타나고, 그 이후는 덜 발생한다는 점입니다. 코드 상의 에러면 100% 발생해야 하지만 이러한 간헐적인 에러는 통신과 관련되었다고 생각이 들게 만들었습니다.
🔑해결
콘솔 자체도 종료가 된다는 점이 더 근본적인 에러라는 생각이 들어서 애초에 구현 방식에 문제가 있는지에 대해 ChatGPT에 질문한 결과,
메인 스레드가 아닌 스레드에서 GUI를 변경하면 종료가 될 수 있다.
라는 답변에서 제가 잘못된 방식으로 스레드를 조작했다는 것을 알 수 있었습니다.
기존 방식
class MyMainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
# 버튼 연결
self.pushButton.clicked.connect(self.button_clicked)
def button_clicked(self):
self.work_thread = WorkerThread(self)
self.work_thread.finished.connect(self.finished_work)
self.work_thread.start()
class WorkerThread(QtCore.QThread):
def __init__(self, main_window):
super().__init__()
self.main_window = main_window
def run(self):
# 어떤 작업
self.main_window.pushButton.setEnabled(False) # 에러의 원인
간단하게 2개의 클래스를 구성했고 MyMainWindow 클래스의 버튼을 클릭하면 WorkerThread라는 스레드를 생성하여 동작하는 프로그램입니다.
이전에도 self.main_window = main_window의 방식으로 메인 윈도우의 UI 요소를 받아서 사용했지만 상태를 변경하는 게 아니기 때문인지 발생하지 않았던 것 같습니다.
self.main_window.pushButton.setEnabled(False)는 버튼을 연속적으로 클릭하지 못하도록 비활성화하는 부분인데 이 부분이 원인이라고는 에러가 뜨지 않으면 도저히 생각해 내기 힘듭니다.
수정한 방식
class MyMainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
# 버튼 연결
self.pushButton.clicked.connect(self.button_clicked)
def button_clicked(self):
self.pushButton.setEnabled(False) # 버튼 비활성화를 메인 스레드에서 처리
self.work_thread = WorkerThread(self)
self.work_thread.finished.connect(self.finished_work)
self.work_thread.start()
def finished_work(self):
self.pushButton.setEnabled(True) # 완료 시그널을 통해 버튼 활성화
class WorkerThread(QtCore.QThread):
def __init__(self, main_window):
super().__init__()
self.main_window = main_window
def run(self):
# 어떤 작업
self.main_window.pushButton.setEnabled(False) # 에러의 원인
기존에 버튼 비활성화를 WorkerThread의 run 메서드에서 MyMainWindow의 button_clicked 메서드 내부로 옮겼습니다.
다시 활성화 부분은 finished_work 메서드로 옮기고 WorkerThread의 finished 시그널과 연결했습니다.
finished 시그널은 QThread에 자동으로 생성되는 시그널로, 생성된 스레드가 종료되면 자동으로 발생합니다.
이외에도 스레드 간에 통신은 인자로 넘겨주는 게 아닌, 시그널을 발생시키고 해당 스레드 내부에서 처리하도록 구현해야 합니다.
'에러 해결' 카테고리의 다른 글
[mysql] Connetion RefusedError(Sequelize 설정) (2) | 2023.11.24 |
---|---|
os.system() 실행 경로에 띄어쓰기가 있어서 생기는 오류 (0) | 2023.08.16 |
Flutter DioError SocketException: Failed host lookup 에러 (0) | 2023.07.31 |
SetProcessDpiAwarenessContext failed 에러 (0) | 2023.07.27 |
selenium.common.exceptions.WebDriverException: Message: unknown error: Chrome failed to start: crashed. 에러 (0) | 2023.01.15 |
댓글