赞
踩
功能实现:
SQLite3
SQL
fuzzywuzzy
time
PyQt5.Qsci
PyQt5
SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL 数据库引擎。
RUNOOB | SQLite - Python
*sqlite3.connect(database: str [,timeout , args]) -> connection
打开一个到 SQLite 数据库文件 database ( 如果给定的数据库名称 filename 不存在,则该调用将创建一个数据库 )
":memory:"
来在 RAM 中打开一个到 database 的数据库连接。
sqlite3.connect(..., check_same_thread=False)
可以在多个线程中获取/提交数据库信息
connection.cursor([cursorClass]) -> cursor
cursor.execute(sql [, __iter: iterable]) -> return value from database.
执行一个 SQL 语句
- 该 SQL 语句可以被参数化__iter
?
,
?
)”, (
var1,
var2))
connection.commit() -> None
该方法提交当前的事务。 (中途生成 *.dbjourney
文件)
我专门定义了一个SQliteIO
类管理数据库:
db_filename = "database/database.db" class SQliteIO(object): def __init__(self): self.db = sqlite3.connect(db_filename, check_same_thread=False) self.cursor = self.db.cursor() self.init_db() def __del__(self): self.db.close() def reset_db(self): # thread-in self.db.close() if os.path.isfile(db_filename): os.remove(db_filename) self.db = sqlite3.connect(db_filename, check_same_thread=False) self.cursor = self.db.cursor() def init_db(self): # thread-in try: self.cursor.execute('create table logging(time FLOAT ,level INTEGER, ' f'data VARCHAR({sys.maxsize}), name VARCHAR(1024))') # self.cursor.execute("create table user(username VARCHAR(12), password VARCHAR(10), time FLOAT, # id INTEGER)") self.db.commit() except sqlite3.OperationalError: pass except sqlite3.DatabaseError: self.reset_db() self.init_db() @staticmethod def getSize() -> str: return convert(os.path.getsize(db_filename), True) if os.path.isfile(db_filename) else convert(0) def commit(self, __sql="", __params: (list, tuple) = ()): # thread-in try: data = self.cursor.execute(__sql, __params) self.db.commit() return data except (sqlite3.ProgrammingError, AttributeError): return None except sqlite3.DatabaseError: self.reset_db() return None
fuzz
主要用于两字符串之间匹配[ i ] 第三方库
conda install fuzzywuzzy
或pip install fuzzywuzzy
两个模块:
fuzz, process
fuzz主要用于两字符串之间匹配,process主要用于搜索排序。
fuzz.ratio
(s1,s2)直接计算s2和s2之间的相似度,返回值为0-100,100表示完全相同;
fuzz.partial_ratio
(S1,S2)部分匹配,如果S1是S2的子串依然返回100;
fuzz.token_sort_ratio
(S1,S2)只比较S1,S2单词是否相同,不考虑词语之间的顺序
;
fuzz.token_set_ratio
(S1,S2)相比fuzz.token_sort_ratio不考虑词语出现的次数
;
process.extract
(S1, ListS,limit=n),表示从列表ListS中找出Top n与S1最相似的句子;
process.extractOne
(S1,ListS),返回最相似的一个
class SQliteHandler(logging.Handler):
...
@staticmethod
def fuzzyfinder(content, collections):
return [v[1] for v in
sorted([(fuzz.token_set_ratio(content, data), data) for data in collections],
key=lambda x: x[0], reverse=True)] # 按相似度 排序
相较于fuzzywuzzy, logging模块是再熟悉不过了, 我也就不在细讲
import logging logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) LOGGING_LEVEL = {"NOTSET": logging.NOTSET, "DEBUG": logging.DEBUG, "INFO": logging.INFO, "WARNING": logging.WARNING, "ERROR": logging.ERROR, "CRITICAL": logging.CRITICAL} LOGGING_LEVEL_INDEX = {logging.NOTSET: "NOTSET", logging.DEBUG: "DEBUG", logging.INFO: "INFO", logging.WARNING: "WARNING", logging.ERROR: "ERROR", logging.CRITICAL: "CRITICAL"} class SQliteHandler(logging.Handler): def __init__(self): self.connection: callable = None self.sqlite = SQliteIO() super(SQliteHandler, self).__init__() def connect(self, connection: callable): self.connection = connection def emit(self, record: logging.LogRecord) -> None: __stamp = round(time.time(), 2) self.sqlite.commit(f"INSERT INTO logging(time, level, data, name) VALUES (?, ?, ?, ?)", (__stamp, record.levelno, record.msg, record.name)) if self.connection is not None: self.connection() @staticmethod def fuzzyfinder(content, collections): return [v[1] for v in sorted([(fuzz.token_set_ratio(content, data), data) for data in collections], key=lambda x: x[0], reverse=True)] # 按相似度 排序 # @timeit("Logging Data", logger) def getData(self, level: (int, str) = logging.NOTSET, content=str()): if isinstance(level, str): level = LOGGING_LEVEL[level] datas = [x for x in self.sqlite.commit("SELECT time, level, data, name FROM logging where level >= ?", (level,))] if isinstance(content, str) and content: return sum([sorted([data for data in datas if data[2] == element], key=lambda x: x[0], reverse=True) for element in SQliteHandler.fuzzyfinder(content, set([x[2] for x in datas]))], []) # 重复元素按时间排序, 时间越近越靠前 else: return sorted(datas, key=lambda x: x[1], reverse=True)
PythonSci
代码 来自 https://blog.csdn.net/hwd00001/article/details/103049588
QLoggingTableWidget
代码 来自 UI
class QLoggingTableWidget(QtWidgets.QWidget): class PythonSci(QsciScintilla): """ Modules: PyQt5, qscintilla PythonSci代码 来自 https://blog.csdn.net/hwd00001/article/details/103049588""" class highlight(QsciLexerPython): def __init__(self, parent): QsciLexerPython.__init__(self, parent) font = QtGui.QFont() font.setFamily('Consolas') font.setPointSize(12) font.setFixedPitch(True) self.setFont(font) self.setColor(QtGui.QColor(0, 0, 0)) self.setPaper(QtGui.QColor(255, 255, 255)) self.setColor(QtGui.QColor("#00FF00"), QsciLexerPython.ClassName) self.setColor(QtGui.QColor("#B0171F"), QsciLexerPython.Keyword) self.setColor(QtGui.QColor("#00FF00"), QsciLexerPython.Comment) self.setColor(QtGui.QColor("#FF00FF"), QsciLexerPython.Number) self.setColor(QtGui.QColor("#0000FF"), QsciLexerPython.DoubleQuotedString) self.setColor(QtGui.QColor("#0000FF"), QsciLexerPython.SingleQuotedString) self.setColor(QtGui.QColor("#288B22"), QsciLexerPython.TripleSingleQuotedString) self.setColor(QtGui.QColor("#288B22"), QsciLexerPython.TripleDoubleQuotedString) self.setColor(QtGui.QColor("#0000FF"), QsciLexerPython.FunctionMethodName) self.setColor(QtGui.QColor("#191970"), QsciLexerPython.Operator) self.setColor(QtGui.QColor("#000000"), QsciLexerPython.Identifier) self.setColor(QtGui.QColor("#00FF00"), QsciLexerPython.CommentBlock) self.setColor(QtGui.QColor("#0000FF"), QsciLexerPython.UnclosedString) self.setColor(QtGui.QColor("#FFFF00"), QsciLexerPython.HighlightedIdentifier) self.setColor(QtGui.QColor("#FF8000"), QsciLexerPython.Decorator) self.setFont(QtGui.QFont('Courier', 12, weight=QtGui.QFont.Bold), 5) self.setFont(QtGui.QFont('Courier', 12, italic=True), QsciLexerPython.Comment) def __init__(self, text, parent=None): super(QLoggingTableWidget.PythonSci, self).__init__(parent) font = QtGui.QFont() font.setFamily('Consolas') font.setPointSize(12) font.setFixedPitch(True) self.setFont(font) self.setFont(font) self.setUtf8(True) self.setMarginsFont(font) self.setMarginWidth(0, len(str(len(self.text().split('\n')))) * 20) self.setMarginLineNumbers(0, True) self.setEdgeMode(QsciScintilla.EdgeLine) self.setEdgeColumn(80) self.setEdgeColor(QtGui.QColor(0, 0, 0)) self.setBraceMatching(QsciScintilla.StrictBraceMatch) self.setIndentationsUseTabs(True) self.setIndentationWidth(4) self.setTabIndents(True) self.setAutoIndent(True) self.setBackspaceUnindents(True) self.setTabWidth(4) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QtGui.QColor('#FFFFCD')) self.setIndentationGuides(True) self.setFolding(QsciScintilla.PlainFoldStyle) self.setMarginWidth(2, 12) self.markerDefine(QsciScintilla.Minus, QsciScintilla.SC_MARKNUM_FOLDEROPEN) self.markerDefine(QsciScintilla.Plus, QsciScintilla.SC_MARKNUM_FOLDER) self.markerDefine(QsciScintilla.Minus, QsciScintilla.SC_MARKNUM_FOLDEROPENMID) self.markerDefine(QsciScintilla.Plus, QsciScintilla.SC_MARKNUM_FOLDEREND) self.setMarkerBackgroundColor(QtGui.QColor("#FFFFFF"), QsciScintilla.SC_MARKNUM_FOLDEREND) self.setMarkerForegroundColor(QtGui.QColor("#272727"), QsciScintilla.SC_MARKNUM_FOLDEREND) self.setMarkerBackgroundColor(QtGui.QColor("#FFFFFF"), QsciScintilla.SC_MARKNUM_FOLDEROPENMID) self.setMarkerForegroundColor(QtGui.QColor("#272727"), QsciScintilla.SC_MARKNUM_FOLDEROPENMID) self.setAutoCompletionSource(QsciScintilla.AcsAll) self.setAutoCompletionCaseSensitivity(True) self.setAutoCompletionReplaceWord(False) self.setAutoCompletionThreshold(1) self.setAutoCompletionUseSingle(QsciScintilla.AcusExplicit) self.lexer = QLoggingTableWidget.PythonSci.highlight(self) self.setLexer(self.lexer) self.mod = False self.autoCompleteFromAll() self.setReadOnly(True) self.setText(text) class LoggingInformation(QtWidgets.QDialog): def __init__(self, str_time, level, content, name, parent=None): super().__init__(parent) self.setObjectName("LoggingInformation") self.resize(640, 800) font = QtGui.QFont() font.setFamily("Consolas") self.setFont(font) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("images/fileRecord.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.setWindowIcon(icon) self.gridLayout = QtWidgets.QGridLayout(self) self.gridLayout.setObjectName("gridLayout") self.label_4 = QtWidgets.QLabel(self) self.label_4.setStyleSheet("color: rgb(0, 85, 255);\n" "font: 9pt \"Consolas\";") self.label_4.setObjectName("label_4") self.gridLayout.addWidget(self.label_4, 1, 1, 1, 1) self.label_6 = QtWidgets.QLabel(self) self.label_6.setStyleSheet("color: rgb(255, 170, 0);\n" "font: 9pt \"Consolas\";") self.label_6.setObjectName("label_6") self.gridLayout.addWidget(self.label_6, 2, 1, 1, 1) self.label_3 = QtWidgets.QLabel(self) self.label_3.setObjectName("label_3") self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1) self.label_7 = QtWidgets.QLabel(self) self.label_7.setObjectName("label_7") self.gridLayout.addWidget(self.label_7, 3, 0, 1, 1) self.label = QtWidgets.QLabel(self) self.label.setObjectName("label") self.gridLayout.addWidget(self.label, 0, 0, 1, 1) self.label_2 = QtWidgets.QLabel(self) self.label_2.setStyleSheet( "color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(255, 255, 0, 255), " "stop:1 rgba(0, " "185, 255, 255));\n " "font: 9pt \"Consolas\";") self.label_2.setObjectName("label_2") self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1) self.label_5 = QtWidgets.QLabel(self) self.label_5.setObjectName("label_5") self.gridLayout.addWidget(self.label_5, 2, 0, 1, 1) self.widget = QLoggingTableWidget.PythonSci(content, self) self.widget.setObjectName("widget") self.gridLayout.addWidget(self.widget, 3, 1, 1, 1) _translate = QtCore.QCoreApplication.translate self.setWindowTitle(_translate("LoggingInformation", "日志信息")) self.label_4.setText(_translate("LoggingInformation", f"{level}")) self.label_6.setText(_translate("LoggingInformation", f"{name}")) self.label_3.setText(_translate("LoggingInformation", "等级:")) self.label_7.setText(_translate("LoggingInformation", "日志信息:")) self.label.setText(_translate("LoggingInformation", "时间:")) self.label_2.setText(_translate("LoggingInformation", f"{str_time}")) self.label_5.setText(_translate("LoggingInformation", "执行文件地址:")) QtCore.QMetaObject.connectSlotsByName(self) self.exec() def __init__(self, parent=None): super().__init__(parent) self.datas = [] self.setObjectName("QLoggingTableWidget") self.resize(480, 840) font = QtGui.QFont() font.setFamily("Consolas") self.setFont(font) self.verticalLayout = QtWidgets.QVBoxLayout(self) self.verticalLayout.setObjectName("verticalLayout") self.formLayout = QtWidgets.QFormLayout() self.formLayout.setHorizontalSpacing(0) self.formLayout.setObjectName("formLayout") self.searchLabel = QtWidgets.QLabel(self) self.searchLabel.setMaximumSize(QtCore.QSize(30, 30)) self.searchLabel.setPixmap(QtGui.QPixmap("images/search.png")) self.searchLabel.setScaledContents(True) self.searchLabel.setObjectName("searchLabel") self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.searchLabel) self.searchInputBox = QtWidgets.QLineEdit(self) self.searchInputBox.setClearButtonEnabled(True) self.searchInputBox.setObjectName("searchInput-box") self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.searchInputBox) self.filterLabel = QtWidgets.QLabel(self) self.filterLabel.setMaximumSize(QtCore.QSize(30, 30)) self.filterLabel.setPixmap(QtGui.QPixmap("images/filter.png")) self.filterLabel.setScaledContents(True) self.filterLabel.setObjectName("filterLabel") self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.filterLabel) self.comboBox = QtWidgets.QComboBox(self) self.comboBox.setObjectName("comboBox") self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.comboBox) self.timeRadioButton = QtWidgets.QRadioButton(self) self.timeRadioButton.setObjectName("radioButton") self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.timeRadioButton) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.startDateTimeEdit = QtWidgets.QDateTimeEdit(self) self.startDateTimeEdit.setObjectName("dateTimeEdit") self.horizontalLayout.addWidget(self.startDateTimeEdit) self.label = QtWidgets.QLabel(self) self.label.setObjectName("label") self.horizontalLayout.addWidget(self.label) self.endDateTimeEdit = QtWidgets.QDateTimeEdit(self) self.endDateTimeEdit.setObjectName("dateTimeEdit_2") self.horizontalLayout.addWidget(self.endDateTimeEdit) spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.formLayout.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout) self.verticalLayout.addLayout(self.formLayout) self.loggingTableWidget = QtWidgets.QTableWidget(self) self.loggingTableWidget.setObjectName("loggingTableWidget") self.loggingTableWidget.setColumnCount(0) self.loggingTableWidget.setRowCount(0) self.verticalLayout.addWidget(self.loggingTableWidget) QtCore.QMetaObject.connectSlotsByName(self) _translate = QtCore.QCoreApplication.translate self.setWindowTitle(_translate("QLoggingTableWidget", "Form")) self.searchLabel.setWhatsThis(_translate("QLoggingTableWidget", "<html><head/><body><p>模糊搜索</p></body></html>")) self.searchInputBox.setToolTip( _translate("QLoggingTableWidget", "<html><head/><body><p>模糊搜索</p></body></html>")) self.filterLabel.setWhatsThis(_translate("QLoggingTableWidget", "<html><head/><body><p>等级筛选</p></body></html>")) self.comboBox.setWhatsThis(_translate("QLoggingTableWidget", "<html><head/><body><p>等级筛选</p></body></html>")) self.timeRadioButton.setText(_translate("QLoggingTableWidget", "起始时间")) self.label.setText(_translate("QLoggingTableWidget", "-")) self.loggingTableWidget.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch) self.comboBox.addItems(list(LOGGING_LEVEL.keys())[1:]) self.loggingTableWidget.setColumnCount(4) self.loggingTableWidget.setHorizontalHeaderLabels(["时间", "等级", "数据", "执行地址"]) self.loggingTableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) # 禁止编辑 self.loggingTableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) # 设置表格整行选中 self.setDateTimeEditEnabled(False) self.timeRadioButton.clicked.connect(self.timeRadio) self.loggingTableWidget.doubleClicked.connect(self.tableClicked) self.comboBox.currentIndexChanged.connect(self.updateLoggingTable) self.searchInputBox.textChanged.connect(self.updateLoggingTable) self.timeRadioButton.clicked.connect(self.updateLoggingTable) self.startDateTimeEdit.timeChanged.connect(self.updateLoggingTable) self.endDateTimeEdit.timeChanged.connect(self.updateLoggingTable) SQliteLoggingHandler.connect(self.updateLoggingTable) self.startDateTimeEdit.setDate(QtCore.QDate.currentDate()) self.endDateTimeEdit.setDate(QtCore.QDate.currentDate()) self.endDateTimeEdit.setTime(QtCore.QTime.currentTime()) self.updateLoggingTable() def timeRadio(self, *args): # 这些代码一会发 def tableClicked(self, index: QtCore.QModelIndex, *args): def setDateTimeEditEnabled(self, a0: bool) -> None: def getFilter(self): def updateLoggingTable(self, *args): @staticmethod def colorful(level) -> QtGui.QColor:
一共绑定了8个事件
timeRadioButton.clicked
.connect(self.timeRadio)
单选框点击事件绑定是否开启时间范围筛选
def timeRadio(self, *args):
self.setDateTimeEditEnabled(self.timeRadioButton.isChecked())
self.loggingTableWidget.doubleClicked
.connect(self.tableClicked)
表格左键双击绑定 具体日志信息Dialog 的展示
def tableClicked(self, index: QtCore.QModelIndex, *args):
self.LoggingInformation(*self.datas[index.row()], self)
self.comboBox.currentIndexChanged
.connect(self.updateLoggingTable)
self.searchInputBox.textChanged
.connect(self.updateLoggingTable)
self.timeRadioButton.clicked
.connect(self.updateLoggingTable)
self.startDateTimeEdit.timeChanged
.connect(self.updateLoggingTable)
self.endDateTimeEdit.timeChanged
.connect(self.updateLoggingTable)
SQliteLoggingHandler
.connect(self.updateLoggingTable)
下拉框(等级)改变
, 搜索框文字改变
, 起始时间框改变
, 是否启用时间范围筛选
, 日志更新
6个事件绑定更新表格
def getFilter(self): search = self.searchInputBox.text().strip() level = LOGGING_LEVEL[self.comboBox.currentText()] if self.timeRadioButton.isChecked(): startTime = time.mktime(time.strptime(self.startDateTimeEdit.text(), "%Y/%m/%d %H:%M")) endTime = time.mktime(time.strptime(self.endDateTimeEdit.text(), "%Y/%m/%d %H:%M")) return [(time.strftime("%m-%d %H:%M:%S", time.localtime(stamp)), LOGGING_LEVEL_INDEX[levelno], data, name) for stamp, levelno, data, name in SQliteLoggingHandler.getData(level, search) if startTime <= stamp <= endTime] else: return [(time.strftime("%m-%d %H:%M:%S", time.localtime(stamp)), LOGGING_LEVEL_INDEX[levelno], data, name) for stamp, levelno, data, name in SQliteLoggingHandler.getData(level, search)] def updateLoggingTable(self, *args): datas = self.getFilter() self.datas = datas self.loggingTableWidget.clear() self.loggingTableWidget.setHorizontalHeaderLabels(["时间", "等级", "数据", "执行地址"]) self.loggingTableWidget.setRowCount(len(datas)) for _row in range(len(datas)): row = datas[_row] for _col in range(len(row)): col: str = row[_col] item = QtWidgets.QTableWidgetItem(col) if _col == 0: # time item.setForeground(QtGui.QColor(50, 50, 225)) if _col == 1: # level item.setForeground(self.colorful(col)) self.loggingTableWidget.setItem(_row, _col, item) @staticmethod def colorful(level) -> QtGui.QColor: return QtGui.QColor(*{"NOTSET": (195, 195, 195), "DEBUG": (128, 128, 128), "INFO": (0, 0, 255), "WARNING": (255, 128, 64), "ERROR": (255, 0, 0), "CRITICAL": (255, 0, 128)}.get(level))
此文章隶属于ServerProject项目, 大部分依赖文件于此.
import time base = 1024 def _conv(value: (float, int)) -> str: value = float(value) if value.is_integer(): return str(int(value)) else: return str(round(value, 1)) def ignore(function): def _exec_func(*args, **kwargs): try: return function(*args, **kwargs) except: pass return _exec_func def convert(byte, fine=False): """ 位 bit (比特)(Binary Digits):存放一位二进制数,即 0 或 1,最小的存储单位。 字节 byte:8个二进制位为一个字节(B),最常用的单位。 其中1024=2^10 ( 2 的10次方), 1KB (Kilo byte 千字节)=1024B, 1MB (Mega byte 兆字节 简称“兆”)=1024KB, 1GB (Giga byte 吉字节 又称“千兆”)=1024MB, 1TB (Trillion byte 万亿字节 太字节)=1024GB, 1PB(Peta byte 千万亿字节 拍字节)=1024TB, 1EB(Exa byte 百亿亿字节 艾字节)=1024PB""" if not isinstance(byte, (int, float)): byte = len(byte) DEI = f"{byte} bytes" units = ["b", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb"] index = 0 while True: if byte < 1024 or index + 1 >= len(units): break byte /= base index += 1 if fine: return f"{_conv(byte)}{units[index]}({DEI})" else: return f"{_conv(byte)}{units[index]}" def timeit(objname: str, logger, ign=True, _show_detail=True): def setup_function(func_name): def _exec_function(*args, **kwargs): start_time = time.time() _resp = func_name(*args, **kwargs) if _show_detail: logger.debug("Execute the function %s%s, timeit %0.3f" % ( objname.title(), "" if ign else f" (at {str(func_name)})", time.time() - start_time)) return _resp return _exec_function return setup_function def to_logging(logger): def log(command): def _exec_func(*args, **kwargs): try: _result = command(*args, **kwargs) if _result is None: return True return _result except: logger.exception(str()) return _exec_func return log
QLoggingTableWidget
继承于QWidget
, 因此, 直接显示或添加父类至窗口上都可.
from sqlite_handler import SQliteLoggingHandler, QLoggingTableWidget
logger = logging.getLogger(...)
logger.addHandler(SQliteLoggingHandler)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
app.setStyle("fusion")
...
Qtable = QLoggingTableWidget()
Qtable.show()
sys.exit(app.exec_())
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。