-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathqlog.py
124 lines (107 loc) · 4.16 KB
/
qlog.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
##\package qlog
# \brief GUI logging output for QT6
#
# Vegard Fiksdal (C) 2024
#
from PyQt6.QtWidgets import QHBoxLayout, QVBoxLayout, QMessageBox, QFrame, QFileDialog, QListWidget, QComboBox, QPushButton
from PyQt6.QtGui import QFont
from PyQt6.QtCore import QTimer
import logging,datetime
##\class LogHandler
# \brief Custom logging handler for dynamic output handling
class LogHandler(logging.Handler):
## Reference to the output object (Falls back to print())
output=None
## Effective log level to display
level=logging.INFO
##\brief Initializes handler
def __init__(self):
super().__init__()
#LogHandler.instance=self
##\brief Handle output
# \param record Log record to handle
def emit(self, record):
if record.levelno>=LogHandler.level:
if LogHandler.output:
LogHandler.output.processLog(record)
else:
t=datetime.datetime.now().strftime('%c')
msg='%s %-*s %s' % (t,8,record.levelname,record.msg)
print(msg)
##\class QLog
# \brief Frame to display realtime log output
class QLog(QFrame):
##\brief Constructor sets up frame layout
def __init__(self):
super().__init__()
self.listbox=QListWidget()
self.listbox.setFont(QFont('cascadia mono'))
self.dropdown=QComboBox()
self.clearbutton=QPushButton('Clear logs')
self.clearbutton.clicked.connect(self.clear)
self.savebutton=QPushButton('Save log to file')
self.savebutton.clicked.connect(self.saveLog)
self.messages=[]
self.msgbox=True
# Manage loglevels
levels=['CRITICAL','ERROR','WARNING','INFO','DEBUG']
level=3
for i in range(len(levels)): self.dropdown.addItem(levels[i])
self.dropdown.currentIndexChanged.connect(self.currentIndexChanged)
self.dropdown.setCurrentIndex(level)
# Add any controls to the layout
layout=QVBoxLayout()
layout.addWidget(self.listbox,1)
layout.addWidget(self.dropdown)
layout.addWidget(self.clearbutton)
layout.addWidget(self.savebutton)
layout.setContentsMargins(0,0,0,0)
self.setLayout(layout)
# Enable logging
logging.basicConfig(level=logging.DEBUG,handlers=[LogHandler()])
LogHandler.level=logging._nameToLevel['INFO']
LogHandler.output=self
##\brief Called when loglevel has changed
# \param index New loglevel
def currentIndexChanged(self,index):
levelname=self.dropdown.itemText(index)
level=logging._nameToLevel[levelname]
LogHandler.level=level
##\brief Clear existing log
def clear(self):
self.messages=[]
self.listbox.clear()
##\brief Process messages from logger
def processLog(self,message):
self.messages.append(message)
##\brief Manually adds a text string
# \param text Text string to add
def addText(self,text):
self.listbox.addItem(text)
##\brief Saves log output to file
def saveLog(self):
text=''
for message in self.messages:
text+=message+'\n'
filename, _ = QFileDialog.getSaveFileName(self,'Save process log','mbserver.log','Log files(*.log);;All Files(*.*)',options=QFileDialog.Options())
if filename:
with open(filename, 'w') as f:
f.write(text)
f.close()
##\brief Show errors in a messagebox
# \param show True to display messagebox for error messages
#
# This is useful when expecting errors otherwise presented to the user. eg polling.
def showMessagebox(self,show):
self.msgbox=show
##\brief Updates GUI with added messages
def updateLog(self):
messages=self.messages
self.messages=[]
for message in messages:
if self.msgbox and (message.levelno==logging.ERROR or message.levelno==logging.CRITICAL):
QMessageBox.critical(self,message.module,str(message.msg))
s='%s %-*s %s' % (datetime.datetime.now().strftime('%c'),8,message.levelname,message.msg)
self.listbox.addItem(s)
if len(messages):
self.listbox.scrollToBottom()