Home > Net >  Connecting external threaded log to PyQt5 QPlainTextEdit
Connecting external threaded log to PyQt5 QPlainTextEdit

Time:02-17

I'm new to PyQt and handlers in general, I tried to read from every repo I found but I can't figure out how to fix my issue, as you can see in my code, I'm executing logging thread in the background and I'm trying to show the logs in my QPlainTextEdit console - for some reason I can see the logs in my terminal and the text_box doesn't get the logs at all, I will appreciate a lot your smart help.

import pandas as pd
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import time
import os
import threading
import json


class ConsolePanelHandler(logging.Handler):

    def __init__(self, stream):
        # super().__init__()
        logging.Handler.__init__(self)
        # logging.StreamHandler.__init__(self, stream)
        self.stream = stream

    def handle(self, record):
        rv = self.filter(record)
        if rv:
            self.acquire()
            try:
                self.emit(record)
            finally:
                self.release()
        return rv

    def emit(self, record):
        try:
            stream = self.stream
            stream(self.format(record))
        except RecursionError:
            raise
        except Exception:
            self.handleError(self.format(record))


  

def thread():
    for index in range(20):
        logging.warning('scheiBe ' str(index))
        time.sleep(1)

class MainWindow(QMainWindow):
        def __init__(self):
            super().__init__()


            layout = QVBoxLayout()


            self.continue_ = QPushButton("Continue")
            self.continue_.setStyleSheet("background-color: green")
            self.continue_.setFont(QFont('SansSerif', 10))
            self.continue_.setFixedSize(QSize(300, 22))


            self.pause = QPushButton("Pause")
            self.pause.setStyleSheet("background-color: orange")
            self.pause.setFont(QFont('SansSerif', 10))
            self.pause.setFixedSize(QSize(300, 22))
      

            self.stop = QPushButton("Stop")
            self.stop.setStyleSheet("background-color: #FD4B4B")
            self.stop.setFont(QFont('SansSerif', 10))
            self.stop.setFixedSize(QSize(300, 22))
            


            self.text_box = QPlainTextEdit()
            self.text_box.setPlaceholderText("Bugs will be printed here")
            self.text_box.setReadOnly(True)
            logging.getLogger().addHandler(self.text_box)
            logging.getLogger().setLevel(logging.DEBUG)

            ConsolePanelHandler(self.appendDebug , logging.DEBUG)
            self.text_box.moveCursor(QTextCursor.End)

           
            layout.addWidget(self.continue_)
            layout.addWidget(self.pause)
            layout.addWidget(self.stop)
            layout.addWidget(self.text_box)
            
            

            self.w = QWidget()
            self.w.setLayout(layout)
            self.setCentralWidget(self.w)                                     
            thread1 = threading.Thread(target=thread, args=(), daemon=True)
            thread1.start()
            self.show()

        def closeEvent(self, event):
            close = QMessageBox()
            close.setText("Are you sure want to stop and exit?")
            close.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
            close = close.exec()

            if close == QMessageBox.Yes:
                sys.exit()
            else:
                event.ignore()
            

        def appendDebug(self, string):
            self.text_box.appendPlainText(string  '\n')

        


if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    sys.exit(app.exec())

CodePudding user response:

First, you should not pass the function that updates the text_area instead you should create a pyqtSignal which updates the text_area and pass it to ConsolePanelHandler.

Add ConsolePanelHandler as handler not text_area to logging.getLogger().addHandler()

and it is recommended to use QThread instead of threading.Thread

here is the complete updated code.

# import pandas as pd
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import time
import os
import sys
import threading
import json
import logging


class ConsolePanelHandler(logging.Handler):

    def __init__(self, sig):
        # super().__init__()
        logging.Handler.__init__(self)
        # logging.StreamHandler.__init__(self, stream)
        self.stream = sig

    def handle(self, record):
        rv = self.filter(record)
        if rv:
            self.acquire()
            try:
                self.emit(record)
            finally:
                self.release()
        return rv

    def emit(self, record):
        try:
            self.stream.emit(self.format(record))
        except RecursionError:
            raise
        except Exception:
            self.handleError(record)


class thread(QThread):
    def run(self) -> None:
        for index in range(20):
            logging.warning('scheiBe '   str(index))
            self.sleep(1)

class MainWindow(QMainWindow):
    sig = pyqtSignal(str)
    def __init__(self):
        super().__init__()

        self.layout = QVBoxLayout()

        self.continue_ = QPushButton("Continue")
        self.continue_.setStyleSheet("background-color: green")
        self.continue_.setFont(QFont('SansSerif', 10))
        self.continue_.setFixedSize(QSize(300, 22))

        self.pause = QPushButton("Pause")
        self.pause.setStyleSheet("background-color: orange")
        self.pause.setFont(QFont('SansSerif', 10))
        self.pause.setFixedSize(QSize(300, 22))

        self.stop = QPushButton("Stop")
        self.stop.setStyleSheet("background-color: #FD4B4B")
        self.stop.setFont(QFont('SansSerif', 10))
        self.stop.setFixedSize(QSize(300, 22))
        self.c = ConsolePanelHandler(self.sig)

        self.text_box = QPlainTextEdit()
        self.text_box.setPlaceholderText("Bugs will be printed here")
        self.text_box.setReadOnly(True)
        logging.getLogger().addHandler(self.c)
        logging.getLogger().setLevel(logging.DEBUG)

        self.sig.connect(self.appendDebug)

        self.text_box.moveCursor(QTextCursor.End)

        self.layout.addWidget(self.continue_)
        self.layout.addWidget(self.pause)
        self.layout.addWidget(self.stop)
        self.layout.addWidget(self.text_box)

        self.w = QWidget()
        self.w.setLayout(self.layout)
        self.setCentralWidget(self.w)
        self.thread1 = thread(self) # self is parent for Qthread so Qthread will be destroyed when it's parent no longer exist
        self.thread1.start()
        self.show()

    def closeEvent(self, event):
        close = QMessageBox()
        close.setText("Are you sure want to stop and exit?")
        close.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
        close = close.exec()

        if close == QMessageBox.Yes:
            self.thread1.terminate()
            sys.exit()
        else:
            event.ignore()

    @pyqtSlot(str)
    def appendDebug(self, string):
        self.text_box.appendPlainText(string   '\n')


if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    sys.exit(app.exec())
  • Related