0

I'm writing a simple GUI that displays a live-updating log in a QPlainTextEdit.

This is the handler I am passing to logging.Logger.addHandler()

class QPlainTextEditLogger(logging.Handler):
    """Modified From: https://stackoverflow.com/questions/28655198/best-way-to-display-logs-in-pyqt"""

    def __init__(self, parent):
        super().__init__()
        self.widget = QPlainTextEdit()
        self.widget.setReadOnly(True)

    def emit(self, record):
        msg = self.format(record)
        self.widget.appendPlainText(msg)
        self.widget.moveCursor(QtGui.QTextCursor.End)

    def write(self, m):
        pass

It properly emits the log entries I'm looking for to the QPlainTextEdit element, however, there is a graphical glitch that persists regardless of platform (or compositing enabled/disabled).
Here's a video of the behaviour:

https://i.imgur.com/DTUbfcM.mp4

The lines are chopped in half unless I manually scroll the box or resize the splitter.

This also happens regardless of whether I implement auto-scrolling in the above snippet a la:

moveCursor(QtGui.QTextCursor.End)

What could be causing this?

Edit: I tried to reproduce this with the below example, but I'm not running into the same issue. I think this may be due to me creating the QPlainTextEdit() in my original code outside of the main window perhaps?

import sys
import time

from PyQt5 import QtWidgets, uic, QtGui
from PyQt5.QtWidgets import QPlainTextEdit
from PyQt5.QtCore import QThread, pyqtSignal

class Worker(QThread):
    newLine = pyqtSignal(str)

    def __init__(self):
        super().__init__()
    
    def run(self):
        for x in range(300):
            self.newLine.emit(f"Testing a very long line so that the words will wrap and hopefully reproduce the issue Testing a very long line so that the words will wrap and hopefully reproduce the issue {x}")
            time.sleep(0.1)


class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.editor = QPlainTextEdit(self)
        self.editor.setReadOnly(True)

        self.button = QtWidgets.QPushButton(self)
        self.button.setText("Go for it")

        self.layout = QtWidgets.QFormLayout()
        self.layout.addChildWidget(self.editor)
        self.layout.addChildWidget(self.button)

        self.button.clicked.connect(self.start_button_pressed)
        self.setCentralWidget(self.editor)
        self.show()

    def add_line(self, msg: str):
        self.editor.appendPlainText(msg)

    def start_button_pressed(self):
        self.worker = Worker()
        self.worker.newLine.connect(self.add_line)
        self.worker.start()

app = QtWidgets.QApplication(sys.argv)
window = Window()
app.exec_()
James
  • 325
  • 1
  • 3
  • 15
  • please provide a [mre] – eyllanesc Oct 17 '20 at 16:49
  • 1
    Are you by any chance using logging within a separate thread? – musicamante Oct 17 '20 at 16:51
  • @musicamante I am indeed logging from a different thread. I just tested the behavior while making a minimal repro example but could not actually reproduce. I am emitting a string to the QPlainTextBox from another thread, but I'm not running into the same issue – James Oct 17 '20 at 17:15
  • @James No direct access to GUI elements should *ever* happen from other threads. In the link you gave there *is* an [answer that is thread safe](https://stackoverflow.com/a/60528393/2001654). Use that. – musicamante Oct 17 '20 at 17:42
  • @musicamante Thank you, that makes sense. I just came across that answer as well after you tipped me off to the threading issue. Cheers – James Oct 17 '20 at 17:47

0 Answers0