В момента работя върху GUI с помощта на qt designer. Чудя се как да отпечатам низове в GUI, който действа като прозорец на регистратор. Използвам pyqt5.
Най-добрият начин за показване на регистрационни файлове в pyqt?
Отговори (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_())
appendPlainText
трябва да бъде свързан към сигнал, вместо да го извиква.
- person tobilocker; 22.01.2020
AttributeError: module 'logging' has no attribute 'Handler'
- person the_economist; 05.11.2020
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)
Ето пълен работещ пример въз основа на отговора на 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_()
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)
Отговорът на Алекс трябва да е ок при сценарий с една нишка, но ако влизате в друга нишка (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
...
Звучи сякаш ще искате да използвате приспособление QPlainTextEdit, настроено само за четене .
Помислете за промяна на цвета на фона на сив, за да дадете на потребителя намек, че не може да се редактира. От вас също зависи дали искате да може да се превърта или текстът да се избира.
Този отговор може да ви накара да започнете да подкласирате QPlainTextEdit за превъртане с изход, записване във файл, каквото и да е.