419 lines
16 KiB
Python
419 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# QTVcp Widget - Machine Log
|
|
# Copyright (c) 2017 Chris Morley with additions by Steve Richardson
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
###############################################################################
|
|
|
|
import os
|
|
import hashlib
|
|
import time
|
|
|
|
from qtpy.QtWidgets import QWidget, QTextEdit, QTableWidget, QTableWidgetItem, QVBoxLayout
|
|
from qtpy.QtCore import QFile, Property
|
|
import qtpy.QtWidgets as QtWidgets
|
|
from qtpy.QtGui import QColor
|
|
from qtvcp.widgets.widget_baseclass import _HalWidgetBase
|
|
from qtvcp.core import Status, Info, Action
|
|
from qtvcp import logger
|
|
|
|
# Instiniate the libraries with global reference
|
|
# STATUS gives us status messages from linuxcnc
|
|
# AUX_PRGM holds helper program loader
|
|
# INI holds ini details
|
|
# ACTION gives commands to linuxcnc
|
|
# LOG is for running code logging
|
|
STATUS = Status()
|
|
INFO = Info()
|
|
ACTION = Action()
|
|
LOG = logger.getLogger(__name__)
|
|
|
|
|
|
class MachineLog(QWidget, _HalWidgetBase):
|
|
def __init__(self, parent=None):
|
|
super(MachineLog, self).__init__(parent)
|
|
self._delay = 0
|
|
self._hash_code = None
|
|
self._log_on = True
|
|
self._machine_log = True
|
|
self._machine_log_severity = False
|
|
self._integrator_log = False
|
|
self._critical_fg_color = QColor(255, 255, 255)
|
|
self._critical_bg_color = QColor(255, 0, 0)
|
|
self._error_fg_color = QColor(255, 0, 0)
|
|
self._error_bg_color = QColor(255,255,255)
|
|
self._warning_fg_color = QColor(255, 255, 0)
|
|
self._warning_bg_color = QColor(0, 0, 0)
|
|
self._debug_fg_color = QColor(128, 128, 128)
|
|
self._debug_bg_color = QColor(255, 255, 255)
|
|
self._info_fg_color = QColor(0, 0, 0)
|
|
self._info_bg_color = QColor(255, 255, 255)
|
|
|
|
self.integratorPath = os.path.expanduser(INFO.QTVCP_LOG_HISTORY_PATH)
|
|
self.machineLogPath = os.path.expanduser(INFO.MACHINE_LOG_HISTORY_PATH)
|
|
|
|
self.logText = QTextEdit(self)
|
|
self.logTable = QTableWidget(self)
|
|
|
|
layout = QVBoxLayout(self)
|
|
layout.addWidget(self.logText)
|
|
layout.addWidget(self.logTable)
|
|
self.setLayout(layout)
|
|
|
|
self.logTable.setColumnCount(3)
|
|
self.logTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
|
self.logTable.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
|
self.logTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
|
self.logTable.setShowGrid(False)
|
|
self.logTable.setSortingEnabled(False)
|
|
|
|
horizontalHeader = self.logTable.horizontalHeader()
|
|
horizontalHeader.resizeSection(0, 100)
|
|
horizontalHeader.resizeSection(1, 100)
|
|
horizontalHeader.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
|
|
horizontalHeader.setVisible(False)
|
|
|
|
verticalHeader = self.logTable.verticalHeader()
|
|
verticalHeader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
|
|
verticalHeader.setVisible(False)
|
|
|
|
def _hal_init(self):
|
|
if self._machine_log:
|
|
STATUS.connect('machine-log-changed',lambda w: self.loadLog())
|
|
self.logTable.hide()
|
|
self.logText.show()
|
|
elif self._machine_log_severity:
|
|
STATUS.connect('update-machine-log', lambda w, d, o: self.updateMachineLog(d, o))
|
|
self.logTable.show()
|
|
self.logText.hide()
|
|
elif self._integrator_log:
|
|
STATUS.connect('periodic', self._periodicCheck)
|
|
self.logTable.hide()
|
|
self.logText.show()
|
|
|
|
def _periodicCheck(self, w):
|
|
if self._delay < 9:
|
|
self._delay += 1
|
|
return
|
|
if STATUS.is_status_valid() == False:
|
|
return
|
|
self._delay = 0
|
|
m1 = self.md5sum(self.integratorPath)
|
|
if m1 and self._hash_code != m1:
|
|
self._hash_code = m1
|
|
self.loadIntegratorLog()
|
|
|
|
# create a hash code
|
|
def md5sum(self,filename):
|
|
try:
|
|
f = open(filename, "rb")
|
|
except:
|
|
return None
|
|
else:
|
|
return hashlib.md5(f.read()).hexdigest()
|
|
|
|
|
|
def remove_options(self, option, to_remove):
|
|
options = option.split(',')
|
|
options = [opt for opt in options if opt not in to_remove]
|
|
return ','.join(options)
|
|
|
|
def updateMachineLog(self, message, option):
|
|
|
|
if option == 'DELETE':
|
|
self.clear()
|
|
return
|
|
|
|
if option == 'ON':
|
|
self._log_on = True
|
|
return
|
|
if option == 'OFF':
|
|
self._log_on = False
|
|
return
|
|
if self._log_on != True:
|
|
return
|
|
|
|
if message:
|
|
if option is None: option = ''
|
|
|
|
if 'DATE' in option:
|
|
dateItem = QTableWidgetItem(time.strftime("%a, %b %d %Y %X"))
|
|
else:
|
|
dateItem = QTableWidgetItem(time.strftime("%H:%M:%S"))
|
|
|
|
option = self.remove_options(option, ['DATE', 'TIME', 'INITIAL'])
|
|
|
|
severityItem = QTableWidgetItem(option.strip())
|
|
|
|
msgItem = QTableWidgetItem(message.strip())
|
|
|
|
if 'DEBUG' in option:
|
|
msgItem.setForeground(self._debug_fg_color)
|
|
dateItem.setForeground(self._debug_fg_color)
|
|
severityItem.setForeground(self._debug_fg_color)
|
|
msgItem.setBackground(self._debug_bg_color)
|
|
dateItem.setBackground(self._debug_bg_color)
|
|
severityItem.setBackground(self._debug_bg_color)
|
|
elif 'SUCCESS' in option:
|
|
msgItem.setForeground(QColor(0, 128, 0))
|
|
dateItem.setForeground(QColor(0, 128, 0))
|
|
severityItem.setForeground(QColor(0, 128, 0))
|
|
elif 'WARNING' in option:
|
|
msgItem.setForeground(self._warning_fg_color)
|
|
dateItem.setForeground(self._warning_fg_color)
|
|
severityItem.setForeground(self._warning_fg_color)
|
|
msgItem.setBackground(self._warning_bg_color)
|
|
dateItem.setBackground(self._warning_bg_color)
|
|
severityItem.setBackground(self._warning_bg_color)
|
|
if self.QTVCP_INSTANCE_.OPTIONS_.play_sounds:
|
|
ACTION.PLAY_WARNING()
|
|
elif 'ERROR' in option:
|
|
msgItem.setForeground(self._error_fg_color)
|
|
dateItem.setForeground(self._error_fg_color)
|
|
severityItem.setForeground(self._error_fg_color)
|
|
msgItem.setBackground(self._error_bg_color)
|
|
dateItem.setBackground(self._error_bg_color)
|
|
severityItem.setBackground(self._error_bg_color)
|
|
if self.QTVCP_INSTANCE_.OPTIONS_.play_sounds:
|
|
ACTION.PLAY_ATTENTION()
|
|
elif 'CRITICAL' in option:
|
|
msgItem.setForeground(self._critical_fg_color)
|
|
dateItem.setForeground(self._critical_fg_color)
|
|
severityItem.setForeground(self._critical_fg_color)
|
|
msgItem.setBackground(self._critical_bg_color)
|
|
dateItem.setBackground(self._critical_bg_color)
|
|
severityItem.setBackground(self._critical_bg_color)
|
|
if self.QTVCP_INSTANCE_.OPTIONS_.play_sounds:
|
|
ACTION.PLAY_ERROR()
|
|
else:
|
|
msgItem.setForeground(self._info_fg_color)
|
|
dateItem.setForeground(self._info_fg_color)
|
|
severityItem.setForeground(self._info_fg_color)
|
|
msgItem.setBackground(self._info_bg_color)
|
|
dateItem.setBackground(self._info_bg_color)
|
|
severityItem.setBackground(self._info_bg_color)
|
|
|
|
|
|
self.logTable.insertRow(self.logTable.rowCount())
|
|
self.logTable.setItem(self.logTable.rowCount()-1, 0, dateItem)
|
|
self.logTable.setItem(self.logTable.rowCount()-1, 1, severityItem)
|
|
self.logTable.setItem(self.logTable.rowCount()-1, 2, msgItem)
|
|
self.logTable.scrollToBottom()
|
|
|
|
def loadLog(self):
|
|
file = QFile(self.machineLogPath)
|
|
file.open(QFile.ReadOnly)
|
|
logText = file.readAll()
|
|
file.close()
|
|
self.logText.setPlainText(str(logText, encoding='utf8'))
|
|
# scroll down to show last entry
|
|
self.logText.verticalScrollBar().setSliderPosition(self.logText.verticalScrollBar().maximum())
|
|
|
|
def loadIntegratorLog(self):
|
|
file = QFile(self.integratorPath)
|
|
file.open(QFile.ReadOnly)
|
|
logText = file.readAll()
|
|
file.close()
|
|
if str(logText, encoding='utf8') == "":
|
|
self.logText.setPlainText('No Logging found. Is QtVcp in info, debugging or verbose mode (-i, -d or -v)?')
|
|
return
|
|
self.logText.setHtml(str(logText, encoding='utf8'))
|
|
# scroll down to show last entry
|
|
self.logText.verticalScrollBar().setSliderPosition(self.logText.verticalScrollBar().maximum())
|
|
|
|
def showEvent(self, ev):
|
|
self.scrollToBottom()
|
|
|
|
def scrollToBottom(self):
|
|
# scroll down to show last entry
|
|
self.logText.verticalScrollBar().setSliderPosition(self.logText.verticalScrollBar().maximum())
|
|
self.logTable.scrollToBottom()
|
|
|
|
def hideCursor(self):
|
|
self.logText.setCursorWidth(0)
|
|
|
|
def getLogText(self):
|
|
if self._machine_log_severity:
|
|
return self.readTableData()
|
|
else:
|
|
return self.logText.toPlainText()
|
|
|
|
def clear(self):
|
|
self.logTable.clearContents()
|
|
self.logTable.setRowCount(0)
|
|
self.logText.setPlainText('')
|
|
|
|
def readTableData(self):
|
|
data = ''
|
|
for row in range(self.logTable.rowCount()):
|
|
row_data = []
|
|
for col in range(self.logTable.columnCount()):
|
|
item = self.logTable.item(row, col).text()
|
|
if col == 1:
|
|
# pad the severity column for ease of reading
|
|
row_data.append("{:^10}".format(item))
|
|
elif item != '':
|
|
row_data.append(item)
|
|
data += ' '.join(row_data)
|
|
data +='\n'
|
|
return data
|
|
|
|
################## properties ###################
|
|
|
|
def _toggle_properties(self, picked):
|
|
data = ('machine_log', 'machine_log_severity', 'integrator_log')
|
|
|
|
for i in data:
|
|
if not i == picked:
|
|
self[i+'_option'] = False
|
|
|
|
def set_machine_log(self, value):
|
|
self._machine_log = value
|
|
if value:
|
|
self._toggle_properties('machine_log')
|
|
def get_machine_log(self):
|
|
return self._machine_log
|
|
def reset_machine_log(self):
|
|
self._machine_log = True
|
|
machine_log_option = Property(bool, get_machine_log, set_machine_log, reset_machine_log)
|
|
|
|
def set_machine_log_severity(self, value):
|
|
self._machine_log_severity = value
|
|
if value:
|
|
self._toggle_properties('machine_log_severity')
|
|
def get_machine_log_severity(self):
|
|
return self._machine_log_severity
|
|
def reset_machine_log_severity(self):
|
|
self._machine_log_severity = False
|
|
machine_log_severity_option = Property(bool, get_machine_log_severity, set_machine_log_severity, reset_machine_log_severity)
|
|
|
|
|
|
def set_integrator_log(self, value):
|
|
self._integrator_log = value
|
|
if value:
|
|
self._toggle_properties('integrator_log')
|
|
def get_integrator_log(self):
|
|
return self._integrator_log
|
|
def reset_integrator_log(self):
|
|
self._integrator_log = False
|
|
integrator_log_option = Property(bool, get_integrator_log, set_integrator_log, reset_integrator_log)
|
|
|
|
def set_critical_fg_color(self, value):
|
|
self._critical_fg_color = value
|
|
def get_critical_fg_color(self):
|
|
return self._critical_fg_color
|
|
def reset_critical_fg_color(self):
|
|
self._critical_fg_color = QColor(255, 255, 255)
|
|
|
|
critical_fg_color_option = Property(QColor, get_critical_fg_color, set_critical_fg_color, reset_critical_fg_color)
|
|
|
|
def set_critical_bg_color(self, value):
|
|
self._critical_bg_color = value
|
|
def get_critical_bg_color(self):
|
|
return self._critical_bg_color
|
|
def reset_critical_bg_color(self):
|
|
self._critical_bg_color = QColor(255, 0, 0)
|
|
|
|
critical_bg_color_option = Property(QColor, get_critical_bg_color, set_critical_bg_color, reset_critical_bg_color)
|
|
|
|
def set_error_fg_color(self, value):
|
|
self._error_fg_color = value
|
|
def get_error_fg_color(self):
|
|
return self._error_fg_color
|
|
def reset_error_fg_color(self):
|
|
self._error_fg_color = QColor(255, 0, 0)
|
|
|
|
error_fg_color_option = Property(QColor, get_error_fg_color, set_error_fg_color, reset_error_fg_color)
|
|
|
|
def set_error_bg_color(self, value):
|
|
self._error_bg_color = value
|
|
def get_error_bg_color(self):
|
|
return self._error_bg_color
|
|
def reset_error_bg_color(self):
|
|
self._error_bg_color = QColor(255, 255, 255)
|
|
|
|
error_bg_color_option = Property(QColor, get_error_bg_color, set_error_bg_color, reset_error_bg_color)
|
|
|
|
def set_warning_fg_color(self, value):
|
|
self._warning_fg_color = value
|
|
def get_warning_fg_color(self):
|
|
return self._warning_fg_color
|
|
def reset_warning_fg_color(self):
|
|
self._warning_fg_color = QColor(255, 255, 0)
|
|
|
|
warning_fg_color_option = Property(QColor, get_warning_fg_color, set_warning_fg_color, reset_warning_fg_color)
|
|
|
|
def set_warning_bg_color(self, value):
|
|
self._warning_bg_color = value
|
|
def get_warning_bg_color(self):
|
|
return self._warning_bg_color
|
|
def reset_warning_bg_color(self):
|
|
self._warning_bg_color = QColor(255, 255, 255)
|
|
|
|
warning_bg_color_option = Property(QColor, get_warning_bg_color, set_warning_bg_color, reset_warning_bg_color)
|
|
|
|
def set_debug_fg_color(self, value):
|
|
self._debug_fg_color = value
|
|
def get_debug_fg_color(self):
|
|
return self._debug_fg_color
|
|
def reset_debug_fg_color(self):
|
|
self._debug_fg_color = QColor(128, 128, 128)
|
|
|
|
debug_fg_color_option = Property(QColor, get_debug_fg_color, set_debug_fg_color, reset_debug_fg_color)
|
|
|
|
def set_debug_bg_color(self, value):
|
|
self._debug_bg_color = value
|
|
def get_debug_bg_color(self):
|
|
return self._debug_bg_color
|
|
def reset_debug_bg_color(self):
|
|
self._debug_bg_color = QColor(255, 255, 255)
|
|
|
|
debug_bg_color_option = Property(QColor, get_debug_bg_color, set_debug_bg_color, reset_debug_bg_color)
|
|
|
|
def set_info_fg_color(self, value):
|
|
self._info_fg_color = value
|
|
def get_info_fg_color(self):
|
|
return self._info_fg_color
|
|
def reset_info_fg_color(self):
|
|
self._info_fg_color = QColor(0, 0, 0)
|
|
|
|
info_fg_color_option = Property(QColor, get_info_fg_color, set_info_fg_color, reset_info_fg_color)
|
|
|
|
def set_info_bg_color(self, value):
|
|
self._info_bg_color = value
|
|
def get_info_bg_color(self):
|
|
return self._info_bg_color
|
|
def reset_info_bg_color(self):
|
|
self._info_bg_color = QColor(255, 255, 255)
|
|
|
|
info_bg_color_option = Property(QColor, get_info_bg_color, set_info_bg_color, reset_info_bg_color)
|
|
|
|
##############################
|
|
# required class boiler code #
|
|
##############################
|
|
|
|
def __getitem__(self, item):
|
|
return getattr(self, item)
|
|
def __setitem__(self, item, value):
|
|
return setattr(self, item, value)
|
|
|
|
# for testing without editor:
|
|
def main():
|
|
import sys
|
|
from qtpy.QtWidgets import QApplication
|
|
app = QApplication(sys.argv)
|
|
widget = MachineLog()
|
|
widget.show()
|
|
sys.exit(app.exec())
|
|
if __name__ == "__main__":
|
|
main()
|