qt在窗口中同步打印日志信息,根据日志级别设置日志颜色

场景

  一般我们会在程序运行的过程中配置对应的日志信息,帮助我们了解当前程序执行的进度。
  当使用qt增加了操作界面时,同样需要将日志信息在界面中显示出来。

思路

  1. 使用qt的QtWidgets.QTextEdit()控件作为日志显示的区域。

    • 日志展示区域应该能够看到最新的日志,
      • QtWidgets.QTextEdit().moveCursor(QTextCursor.StartOfLine)
      • QtWidgets.QTextEdit().ensureCursorVisible()
  2. 使用qt的信号机制将接受的日志信息写入QtWidgets.QTextEdit()控件

  3. 根据日志信息中的日志级别关键字,控制设置文本的格式

        def update_log(self, text):
        """将日志输出到 text widget"""
        if not self.isVisible():
            return
        if "ERROR" in text: # 错误日志加粗
            text = f"<font color='red'><b>{text}</font><br>"
        elif "INFO" in text:
            text = f"<font color='green'>{text}</font><br>"
        elif "WARNING" in text:
            text = f"<font color='cyan'>{text}</font><br>"
        elif "DEBUG" in text:
            text = f"<font color='blue'>{text}</font><br>"
        else:
            text = f"{text}<br>"
        self.log_browser.setWordWrapMode(QTextOption.WordWrap)
        self.log_browser.moveCursor(QTextCursor.End)
        self.log_browser.insertHtml(text)
        self.log_browser.ensureCursorVisible()
    

实现

效果:
在这里插入图片描述
示例代码:(为保证能够直接运行,已注释掉其他依赖的代码)

# -*- coding:UTF-8 -*-

"""
 @ProjectName  : pythonProject 
 @FileName     : camera_win.py
 @Description: 
 @Time         : 2022/10/27 11:21
 @Author       : Qredsun
 """
import json

from PyQt5 import QtWidgets
from PyQt5.QtCore import QObject
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QTextCursor
from PyQt5.QtGui import QTextOption
from loguru import logger

from camera_set_conf.camera_conf_set import CameraConf
from ui.Camera_ui import Ui_Form
from ui.browser_win import Dialog as conf_dialog
from ui.common import choose_file
from ui.common import info_box
from util.collect_project_info import read_conf_from_json
from util.collect_project_info import write_json_file

from PyQt5 import QtCore, QtGui, QtWidgets

#单独运行该脚本,隐藏依赖函数
CameraConf = None
conf_dialog = None
# UI
class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.setWindowModality(QtCore.Qt.ApplicationModal)
        Form.resize(356, 285)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
        Form.setSizePolicy(sizePolicy)
        self.gridLayout_2 = QtWidgets.QGridLayout(Form)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.label = QtWidgets.QLabel(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setMinimumSize(QtCore.QSize(0, 35))
        self.label.setMaximumSize(QtCore.QSize(16777215, 55))
        self.label.setObjectName("label")
        self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.common_conf_path_edit = QtWidgets.QLineEdit(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.common_conf_path_edit.sizePolicy().hasHeightForWidth())
        self.common_conf_path_edit.setSizePolicy(sizePolicy)
        self.common_conf_path_edit.setMinimumSize(QtCore.QSize(135, 35))
        self.common_conf_path_edit.setMaximumSize(QtCore.QSize(16777215, 55))
        self.common_conf_path_edit.setObjectName("common_conf_path_edit")
        self.gridLayout.addWidget(self.common_conf_path_edit, 0, 0, 1, 1)
        self.common_conf_path_choose = QtWidgets.QPushButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.common_conf_path_choose.sizePolicy().hasHeightForWidth())
        self.common_conf_path_choose.setSizePolicy(sizePolicy)
        self.common_conf_path_choose.setMaximumSize(QtCore.QSize(16777215, 55))
        self.common_conf_path_choose.setObjectName("common_conf_path_choose")
        self.gridLayout.addWidget(self.common_conf_path_choose, 0, 1, 1, 1)
        self.common_conf_edit = QtWidgets.QPushButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.common_conf_edit.sizePolicy().hasHeightForWidth())
        self.common_conf_edit.setSizePolicy(sizePolicy)
        self.common_conf_edit.setMaximumSize(QtCore.QSize(16777215, 55))
        self.common_conf_edit.setObjectName("common_conf_edit")
        self.gridLayout.addWidget(self.common_conf_edit, 0, 2, 1, 1)
        self.gridLayout_2.addLayout(self.gridLayout, 1, 0, 1, 1)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label_3 = QtWidgets.QLabel(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
        self.label_3.setSizePolicy(sizePolicy)
        self.label_3.setMaximumSize(QtCore.QSize(80, 55))
        self.label_3.setObjectName("label_3")
        self.horizontalLayout.addWidget(self.label_3)
        self.comboBox = QtWidgets.QComboBox(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.comboBox.sizePolicy().hasHeightForWidth())
        self.comboBox.setSizePolicy(sizePolicy)
        self.comboBox.setMinimumSize(QtCore.QSize(120, 35))
        self.comboBox.setMaximumSize(QtCore.QSize(16777215, 55))
        self.comboBox.setObjectName("comboBox")
        self.horizontalLayout.addWidget(self.comboBox)
        self.private_conf_edit = QtWidgets.QPushButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.private_conf_edit.sizePolicy().hasHeightForWidth())
        self.private_conf_edit.setSizePolicy(sizePolicy)
        self.private_conf_edit.setMaximumSize(QtCore.QSize(16777215, 55))
        self.private_conf_edit.setObjectName("private_conf_edit")
        self.horizontalLayout.addWidget(self.private_conf_edit)
        self.set_conf_btn = QtWidgets.QPushButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.set_conf_btn.sizePolicy().hasHeightForWidth())
        self.set_conf_btn.setSizePolicy(sizePolicy)
        self.set_conf_btn.setMaximumSize(QtCore.QSize(16777215, 55))
        self.set_conf_btn.setObjectName("set_conf_btn")
        self.horizontalLayout.addWidget(self.set_conf_btn)
        self.gridLayout_2.addLayout(self.horizontalLayout, 2, 0, 1, 1)
        self.log_browser = QtWidgets.QTextEdit(Form)
        self.log_browser.setMinimumSize(QtCore.QSize(0, 140))
        font = QtGui.QFont()
        font.setPointSize(9)
        self.log_browser.setFont(font)
        self.log_browser.setObjectName("log_browser")
        self.gridLayout_2.addWidget(self.log_browser, 3, 0, 1, 1)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "CAMERA设备配置"))
        self.label.setText(_translate("Form", "相机公共配置"))
        self.common_conf_path_edit.setPlaceholderText(_translate("Form", "公共配置文件路径"))
        self.common_conf_path_choose.setText(_translate("Form", "选择文件"))
        self.common_conf_edit.setText(_translate("Form", "配置预览"))
        self.label_3.setText(_translate("Form", "相机编号"))
        self.private_conf_edit.setText(_translate("Form", "配置预览"))
        self.set_conf_btn.setText(_translate("Form", "一键配置"))
        self.log_browser.setHtml(_translate("Form",
                                            "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
                                            "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
                                            "p, li { white-space: pre-wrap; }\n"
                                            "</style></head><body style=\" font-family:\'SimSun\'; font-size:14pt; font-weight:400; font-style:normal;\">\n"
                                            "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:9pt;\"><br /></p></body></html>"))
        self.log_browser.setPlaceholderText(_translate("Form", "程序配置日志"))
        
class Widget(QtWidgets.QWidget, Ui_Form):

    def __init__(self, project_conf):
        super().__init__()
        self.setupUi(self)

        # 日志重定向
        log_out = Stream()
        log_out.newText.connect(self.update_log)
        logger.add(log_out, format="{time} | {level} | {message}")

        # 项目中所有相机对应的配置 
        self.devices_info = project_conf
        # 公共配置路径
        self.common_conf_file_path = None
        # 公共配置
        self.common_conf = None
        # 设备IP
        self.cur_device_ip = None
        # 设备私有配置
        self.private_conf = None
        # 配置对象
        self.conf_setting_obj = None
        # 默认页面配置:
        self.default_win_status()
        # 加载设备列表
        self.load_devices_ip()

        # 选择公共配置文件
        self.common_conf_path_choose.clicked.connect(self.choose_common_conf_file)
        self.common_conf_edit.clicked.connect(self.show_common_conf)
        # 当前 设备 的配置
        self.comboBox.currentTextChanged.connect(self.load_devices_info)
        self.private_conf_edit.clicked.connect(self.show_private_conf)
        # 一键配置
        self.set_conf_btn.clicked.connect(self.set_all_conf)
        
        # 单独运行该脚本,隐藏依赖函数
        self.common_conf_edit.hide()
        self.set_conf_btn.hide()
        self.common_conf_path_choose.hide()
        self.common_conf_edit.hide()


    # 完成所有配置
    def set_all_conf(self):
        self.log_browser.clear()  # 清空打印区日志
        msg = '设备所有配置'
        try:
            if not self.conf_setting_obj.web_set_default_conf():
                return
            if not self.conf_setting_obj.sdk_set_conf():
                return
            logger.info(f'{msg} 成功')
        except Exception as e:
            logger.error(f'{msg} 失败 {e}')


    # 默认页面状态
    def default_win_status(self):
        self.set_conf_btn.setEnabled(False)
        # 配置预览默认不可点击
        self.common_conf_edit.setEnabled(False)

    # 项目配置加载
    def load_devices_ip(self):
        # 设备下拉列表
        items = list(self.devices_info.keys())
        self.comboBox.clear()
        self.comboBox.addItems(items)
        logger.info('加载设备列表')
        self.load_devices_info(update_setting_obj=False)

    def load_devices_info(self, update_setting_obj=True):
        self.log_browser.clear()  # 清空打印区日志
        # 关联设备信息加载
        self.cur_device_ip = self.comboBox.currentText()
        self.private_conf = self.devices_info.get(self.cur_device_ip, {})
        if not len(self.private_conf):
            msg = f'没有 {self.cur_device_ip} 对应的设备信息'
            logger.error(msg)
            info_box(msg, self)
            return
        # 重新初始化配置对象
        if update_setting_obj:
            self._widget_load_common_conf()

    def update_log(self, text):
        """将日志输出到 text widget"""
        if not self.isVisible():
            return
        if "ERROR" in text:
            text = f"<font color='red'><b>{text}</font><br>"
        elif "INFO" in text:
            text = f"<font color='green'>{text}</font><br>"
        elif "WARNING" in text:
            text = f"<font color='cyan'>{text}</font><br>"
        elif "DEBUG" in text:
            text = f"<font color='blue'>{text}</font><br>"
        else:
            text = f"{text}<br>"
        self.log_browser.setWordWrapMode(QTextOption.WordWrap)
        self.log_browser.moveCursor(QTextCursor.End)
        # self.log_browser.append(text)
        self.log_browser.insertHtml(text)
        self.log_browser.ensureCursorVisible()

    # json 配置预览
    def open_conf_browser(self, text, title):
        # conf_browser = conf_dialog(text, title)
        # conf_browser.output_signal.connect(self.update_conf_by_signal)
        # conf_browser.exec()
        msg = 'test msg test msg test msg test msg test msg test msg test msg test msg test msg test msg '
        logger.debug(msg)
        logger.info(msg)
        logger.warning(msg)
        logger.error(msg)
        logger.critical(msg)

    # 获取预览配置中的内容
    def update_conf_by_signal(self, content):
        self.text_browser_conf = content

    def choose_common_conf_file(self):
        self.log_browser.clear()  # 清空打印区日志
        file_path = choose_file(win_name='设备公共配置文件', file_type='*.json', cur_img_save_dir='.', widget=self)
        # if file_path == "":
        #     msg = '请先指定设备公共配置文件'
        #     logger.error(msg)
        #     info_box(msg, self)
        #     self.common_conf_file_path = None
        #     return
        file_path = r'D:\work\boradxt\pythonProject\camera_set_conf\project_conf_jichang.json'  # todo 测试

        self.common_conf_file_path = file_path
        self.common_conf_path_edit.setText(file_path)
        self.common_conf = read_conf_from_json(self.common_conf_file_path)
        logger.info(f'加载公共配置文件:{file_path}')
        self._widget_load_common_conf()

    def _widget_load_common_conf(self):
        try:
            self.common_conf_edit.setEnabled(True)
            self.update_cam_setting_obj()
        except Exception as e:
            logger.error(f'{self.common_conf_file_path} 配置文件格式不符合要求 {e}')

    def update_cam_setting_obj(self):
        msg = f'初始化设备 {self.cur_device_ip} 配置对象'
        try:
            if not self.common_conf_file_path:
                raise ValueError('缺失公共配置')
            self.conf_setting_obj = CameraConf(self.cur_device_ip, self.private_conf, self.common_conf_file_path)
            self.conf_setting_obj.load_conf(self.common_conf)
            logger.debug(f'{msg}成功')
            self.set_conf_btn.setEnabled(True)  # 一键配置按钮可用
        except Exception as e:
            info_box(f'{msg}失败:{e}', self, 2, '错误')
            logger.error(f'{msg}失败:{e}')

    def show_common_conf(self):
        title = f'{self.common_conf["project_name"]}_{self.common_conf["version"]} 公共配置'
        conf_data = json.dumps(self.common_conf, indent=4, ensure_ascii=False)
        self.open_conf_browser(conf_data, title)
        self.common_conf = json.loads(self.text_browser_conf)
        # 保存文件
        write_json_file(self.common_conf, self.common_conf_file_path)
        logger.info(f'{title} 结束预览')

    def show_private_conf(self):
        title = f'{self.cur_device_ip} 私有配置'
        conf_data = json.dumps(self.private_conf, indent=4, ensure_ascii=False)
        self.open_conf_browser(conf_data, title)
        # self.private_conf = json.loads(self.text_browser_conf)
        logger.info(f'{title} 结束预览')


# 日志重定向
class Stream(QObject):
    """Redirects console output to text widget."""
    newText = pyqtSignal(str)

    def write(self, text):
        self.newText.emit(str(text))
        
import sys
if __name__ == '__main__':
    project_conf = {
        "10.10.40.2": {
            "ip": "10.10.40.2",
            "xiecn_ip": "10.10.40.249",
            "netmask": "255.255.255.0",
            "gateway": "10.10.40.254",
            "rod_id": "252L",
            "slot_id": 2,
            "device_option": "M2391-10-TL-M20",
            "point_id": 12000001,
            "osd": "252L-00001-2",
            "is_middle": True
        }}
    app = QtWidgets.QApplication(sys.argv)
    w = Widget(project_conf)
    w.show()
    sys.exit(app.exec_())
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值