Най-добрият начин за показване на регистрационни файлове в pyqt?

В момента работя върху GUI с помощта на qt designer. Чудя се как да отпечатам низове в GUI, който действа като прозорец на регистратор. Използвам pyqt5.


person Carlo Angelo    schedule 22.02.2015    source източник
comment
Потребителите на Bing трябва да отидат тук :P   -  person Carel    schedule 02.05.2016


Отговори (6)


Адаптирано от примера на Todd Vanyo за PyQt5:

import sys
from PyQt5 import QtWidgets
import logging

# Uncomment below for terminal log messages
# logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(name)s - %(levelname)s - %(message)s')

class QTextEditLogger(logging.Handler):
    def __init__(self, parent):
        super().__init__()
        self.widget = QtWidgets.QPlainTextEdit(parent)
        self.widget.setReadOnly(True)

    def emit(self, record):
        msg = self.format(record)
        self.widget.appendPlainText(msg)


class MyDialog(QtWidgets.QDialog, QtWidgets.QPlainTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)

        logTextBox = QTextEditLogger(self)
        # You can format what is printed to text box
        logTextBox.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
        logging.getLogger().addHandler(logTextBox)
        # You can control the logging level
        logging.getLogger().setLevel(logging.DEBUG)

        self._button = QtWidgets.QPushButton(self)
        self._button.setText('Test Me')

        layout = QtWidgets.QVBoxLayout()
        # Add the new logging box widget to the layout
        layout.addWidget(logTextBox.widget)
        layout.addWidget(self._button)
        self.setLayout(layout)

        # Connect signal to slot
        self._button.clicked.connect(self.test)

    def test(self):
        logging.debug('damn, a bug')
        logging.info('something to remember')
        logging.warning('that\'s not right')
        logging.error('foobar')

app = QtWidgets.QApplication(sys.argv)
dlg = MyDialog()
dlg.show()
dlg.raise_()
sys.exit(app.exec_())
person Alex    schedule 01.08.2018
comment
как да използвам в в друг случай, имам *.ui с QMainWindow, QTabWidget с раздел (име tab_log) всички дефинирани в ui файл. Как да добавя QTextEditLogger към раздел в QTabWidget??? - person emcek; 17.01.2019
comment
Това не е безопасно за нишки. appendPlainText трябва да бъде свързан към сигнал, вместо да го извиква. - person tobilocker; 22.01.2020
comment
Получавам следното съобщение за грешка: AttributeError: module 'logging' has no attribute 'Handler' - person the_economist; 05.11.2020
comment
УАУ, това беше просто копиране и поставяне за мен. Алекс, ти си бог. - person Kris Kizlyk; 15.02.2021
comment
Дава ми грешка Make sure 'QTextCursor' is registered using qRegisterMetaType() при влизане чрез обратни извиквания. Версията с безопасна нишка работи доста добре за мен. stackoverflow.com/a/60528393/3598205 - person sa_penguin; 20.07.2021

Ако използвате модула Python logging, можете лесно да създадете персонализиран манипулатор за регистриране, който предава съобщенията в журнала до екземпляр QPlainTextEdit (както е описано от Кристофър).

За да направите това, първо подкласирайте logging.Handler. В този __init__ създаваме QPlainTextEdit, който ще съдържа регистрационните файлове. Ключовото тук е, че манипулаторът ще получава съобщения чрез функцията emit(). Така че претоварваме тази функция и предаваме текста на съобщението в QPlainTextEdit.

import logging

class QPlainTextEditLogger(logging.Handler):
    def __init__(self, parent):
        super(QPlainTextEditLogger, self).__init__()

        self.widget = QPlainTextEdit(parent)
        self.widget.setReadOnly(True)

    def emit(self, record):
        msg = self.format(record)
        self.widget.appendPlainText(msg)

    def write(self, m):
        pass

Създайте обект от този клас, като му предадете родител за QPlainTextEdit (напр. главния прозорец или оформление). След това можете да добавите този манипулатор за текущия регистратор.

# Set up logging to use your widget as a handler
log_handler = QPlainTextEditLogger(<parent widget>)
logging.getLogger().addHandler(log_handler)
person mfitzp    schedule 01.03.2015

Ето пълен работещ пример въз основа на отговора на mfitzp:

import sys
from PyQt4 import QtCore, QtGui
import logging

# Uncomment below for terminal log messages
# logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(name)s - %(levelname)s - %(message)s')    

class QPlainTextEditLogger(logging.Handler):
    def __init__(self, parent):
        super().__init__()
        self.widget = QtGui.QPlainTextEdit(parent)
        self.widget.setReadOnly(True)    

    def emit(self, record):
        msg = self.format(record)
        self.widget.appendPlainText(msg)    


class MyDialog(QtGui.QDialog, QPlainTextEditLogger):
    def __init__(self, parent=None):
        super().__init__(parent)    

        logTextBox = QPlainTextEditLogger(self)
        # You can format what is printed to text box
        logTextBox.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
        logging.getLogger().addHandler(logTextBox)
        # You can control the logging level
        logging.getLogger().setLevel(logging.DEBUG)

        self._button = QtGui.QPushButton(self)
        self._button.setText('Test Me')    

        layout = QtGui.QVBoxLayout()
        # Add the new logging box widget to the layout
        layout.addWidget(logTextBox.widget)
        layout.addWidget(self._button)
        self.setLayout(layout)    

        # Connect signal to slot
        self._button.clicked.connect(self.test)    

    def test(self):
        logging.debug('damn, a bug')
        logging.info('something to remember')
        logging.warning('that\'s not right')
        logging.error('foobar')

if (__name__ == '__main__'):
    app = None
    if (not QtGui.QApplication.instance()):
        app = QtGui.QApplication([])
    dlg = MyDialog()
    dlg.show()
    dlg.raise_()
    if (app):
        app.exec_()
person Todd Vanyo    schedule 24.02.2016
comment
Това може да е глупав въпрос, но каква е целта на наследяването от QPlainTextEditLogger в MyDialog? Опитвам се да преобразувам този пример в PyQt5 и не можах да го накарам да работи, без да премахна това второ наследство. Изглежда, че работи добре и без него. - person Filip S.; 26.01.2018

Безопасна за нишки версия

class QTextEditLogger(logging.Handler, QtCore.QObject):
    appendPlainText = QtCore.pyqtSignal(str)

def __init__(self, parent):
    super().__init__()
    QtCore.QObject.__init__(self)
    self.widget = QtWidgets.QPlainTextEdit(parent)
    self.widget.setReadOnly(True)
    self.appendPlainText.connect(self.widget.appendPlainText)

def emit(self, record):
    msg = self.format(record)
    self.appendPlainText.emit(msg)

Използване

    logTextBox = QTextEditLogger(self)

    # log to text box
    logTextBox.setFormatter(
        logging.Formatter(
            '%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s'))
    logging.getLogger().addHandler(logTextBox)
    logging.getLogger().setLevel(logging.DEBUG)

    # log to file
    fh = logging.FileHandler('my-log.log')
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(
        logging.Formatter(
            '%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s'))
    logging.getLogger().addHandler(fh)
person tobilocker    schedule 04.03.2020
comment
Благодаря, беше бързо! - person Swedgin; 04.03.2020
comment
Благодаря за отговора. Въпреки че не съм сигурен защо, с PySide2 (5.12.0) двойното наследяване не работи за мен. Грешката е, че emit() приема 2 позиционни аргумента, но са дадени 3. (това е функцията за излъчване на сигнал, мисля, че това означава, че има някакъв проблем с наследяването на обекта на сигнала) Работещ пример за мен е да създам нов клас QThread/QObject със Signal и да инжектирам екземпляр в конструктора на манипулатора на регистрационни файлове. Донякъде като този пример. - person Peter; 24.01.2021

Отговорът на Алекс трябва да е ок при сценарий с една нишка, но ако влизате в друга нишка (QThread), може да получите следното предупреждение:

QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)

Това е така, защото модифицирате GUI (self.widget.appendPlainText(msg)) от нишка, различна от основната нишка, без да използвате механизма Qt Signal/Slot.

Ето моето решение:

# my_logger.py

import logging
from PyQt5.QtCore import pyqtSignal, QObject

class Handler(QObject, logging.Handler):
    new_record = pyqtSignal(object)

    def __init__(self, parent):
        super().__init__(parent)
        super(logging.Handler).__init__()
        formatter = Formatter('%(asctime)s|%(levelname)s|%(message)s|', '%d/%m/%Y %H:%M:%S')
        self.setFormatter(formatter)

    def emit(self, record):
        msg = self.format(record)
        self.new_record.emit(msg) # <---- emit signal here

class Formatter(logging.Formatter):
    def formatException(self, ei):
        result = super(Formatter, self).formatException(ei)
        return result

    def format(self, record):
        s = super(Formatter, self).format(record)
        if record.exc_text:
            s = s.replace('\n', '')
        return s

   # gui.py

   ... # GUI code
   ...
   def setup_logger(self)
        handler = Handler(self)
        log_text_box = QPlainTextEdit(self)
        self.main_layout.addWidget(log_text_box)
        logging.getLogger().addHandler(handler)
        logging.getLogger().setLevel(logging.INFO)
        handler.new_record.connect(log_text_box.appendPlainText) # <---- connect QPlainTextEdit.appendPlainText slot
   ...
person Chweng Mega    schedule 08.03.2019
comment
Просто не можах да накарам това да работи на PySide2, тъй като не можете да имате няколко наследявания с QObject. За да работи, трябваше да използвам стар синтаксис на сигнали като това - person Regnareb; 21.01.2020

Звучи сякаш ще искате да използвате приспособление QPlainTextEdit, настроено само за четене .

Помислете за промяна на цвета на фона на сив, за да дадете на потребителя намек, че не може да се редактира. От вас също зависи дали искате да може да се превърта или текстът да се избира.

Този отговор може да ви накара да започнете да подкласирате QPlainTextEdit за превъртане с изход, записване във файл, каквото и да е.

person Christopher Peterson    schedule 22.02.2015