feat: 日志解析时增加进度条展示
This commit is contained in:
parent
c6d3d57358
commit
40f4ed22cf
@ -33,13 +33,15 @@
|
|||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTextBrowser" name="textBrowser_console">
|
<widget class="QTextBrowser" name="textBrowser_console">
|
||||||
|
<property name="markdown">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
<property name="html">
|
<property name="html">
|
||||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;">
|
</style></head><body style=" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">通过文件上传一键日志压缩包来开始</span></p>
|
<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;"><br /></p></body></html></string>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">或者将日志压缩包拖入程序</span></p></body></html></string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class Ui_MainWindow(object):
|
|||||||
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tab_console)
|
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tab_console)
|
||||||
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
||||||
self.textBrowser_console = QtWidgets.QTextBrowser(self.tab_console)
|
self.textBrowser_console = QtWidgets.QTextBrowser(self.tab_console)
|
||||||
|
self.textBrowser_console.setMarkdown("")
|
||||||
self.textBrowser_console.setObjectName("textBrowser_console")
|
self.textBrowser_console.setObjectName("textBrowser_console")
|
||||||
self.horizontalLayout_4.addWidget(self.textBrowser_console)
|
self.horizontalLayout_4.addWidget(self.textBrowser_console)
|
||||||
self.tabWidget.addTab(self.tab_console, "")
|
self.tabWidget.addTab(self.tab_console, "")
|
||||||
@ -102,8 +103,7 @@ class Ui_MainWindow(object):
|
|||||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
||||||
"p, li { white-space: pre-wrap; }\n"
|
"p, li { white-space: pre-wrap; }\n"
|
||||||
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
||||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:18pt; font-weight:600;\">通过文件上传一键日志压缩包来开始</span></p>\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;\"><br /></p></body></html>"))
|
||||||
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:18pt; font-weight:600;\">或者将日志压缩包拖入程序</span></p></body></html>"))
|
|
||||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_console), _translate("MainWindow", "控制台"))
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_console), _translate("MainWindow", "控制台"))
|
||||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_baseinfo), _translate("MainWindow", "基本信息"))
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_baseinfo), _translate("MainWindow", "基本信息"))
|
||||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_alert), _translate("MainWindow", "关键告警"))
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_alert), _translate("MainWindow", "关键告警"))
|
||||||
|
|||||||
40
src/background_task.py
Normal file
40
src/background_task.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from PyQt5.QtCore import QThread, pyqtSignal
|
||||||
|
import service
|
||||||
|
|
||||||
|
class DiagnoseThread(QThread):
|
||||||
|
"""后台线程,用于执行耗时的诊断任务"""
|
||||||
|
# 定义信号:更新进度(值, 消息)、任务完成(是否成功, 消息)
|
||||||
|
progress_updated = pyqtSignal(int, str)
|
||||||
|
task_completed = pyqtSignal(bool, str)
|
||||||
|
|
||||||
|
def __init__(self, parse_status, file_path):
|
||||||
|
super().__init__()
|
||||||
|
self.parse_status = parse_status
|
||||||
|
self.file_path = file_path
|
||||||
|
self.is_running = True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""线程执行函数:执行耗时任务"""
|
||||||
|
try:
|
||||||
|
self.progress_updated.emit(1, "正在初始化解析...")
|
||||||
|
|
||||||
|
# 执行耗时任务
|
||||||
|
service.start_diagnose(self.parse_status, self.file_path, progress_callback=self.update_progress)
|
||||||
|
|
||||||
|
# 确保最终进度为100%
|
||||||
|
self.progress_updated.emit(100, "处理完成")
|
||||||
|
self.task_completed.emit(True, "日志解析任务已完成")
|
||||||
|
except Exception as e:
|
||||||
|
# 出错时也允许关闭对话框
|
||||||
|
self.progress_updated.emit(100, f"处理失败:{str(e)}")
|
||||||
|
self.task_completed.emit(False, f"处理失败:{str(e)}")
|
||||||
|
|
||||||
|
def update_progress(self, value, message):
|
||||||
|
"""接收service层的进度更新"""
|
||||||
|
if 0 <= value <= 100 and self.is_running:
|
||||||
|
self.progress_updated.emit(value, message)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""停止线程"""
|
||||||
|
self.is_running = False
|
||||||
|
self.wait()
|
||||||
@ -1,77 +1,119 @@
|
|||||||
|
import time
|
||||||
import os
|
import os
|
||||||
import service
|
import service
|
||||||
from utils import show_error_message, show_critical_message
|
from utils import show_error_message, show_critical_message, show_info_message
|
||||||
|
from progress_diaglog import ProgressDialog
|
||||||
|
from background_task import DiagnoseThread
|
||||||
|
|
||||||
class FileProcessor:
|
class FileProcessor:
|
||||||
def __init__(self, main_window):
|
def __init__(self, main_window):
|
||||||
self.main_window = main_window # 持有主窗口引用
|
self.main_window = main_window
|
||||||
self.parse_status = main_window.parse_status # 服务状态
|
self.parse_status = main_window.parse_status
|
||||||
self.view_renderer = main_window.view_renderer # 关联视图渲染模块
|
self.view_renderer = main_window.view_renderer
|
||||||
|
self.last_processed_path = None
|
||||||
|
self.last_processed_time = 0
|
||||||
|
self.diagnose_thread = None # 保存后台线程引用
|
||||||
|
|
||||||
|
def process_uploaded_file(self, file_path):
|
||||||
|
# 防重复处理
|
||||||
|
current_time = time.time()
|
||||||
|
if (file_path == self.last_processed_path and
|
||||||
|
current_time - self.last_processed_time < 1):
|
||||||
|
print(f"跳过重复处理:{file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.last_processed_path = file_path
|
||||||
|
self.last_processed_time = current_time
|
||||||
|
|
||||||
|
# 格式校验
|
||||||
|
if not self.is_valid_file_format(file_path):
|
||||||
|
show_error_message(self.main_window, file_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 显示文件信息
|
||||||
|
file_name = os.path.basename(file_path)
|
||||||
|
file_size = os.path.getsize(file_path) / 1024
|
||||||
|
info = (
|
||||||
|
f"已上传文件:\n"
|
||||||
|
f"文件名:{file_name}\n"
|
||||||
|
f"路径:{file_path}\n"
|
||||||
|
f"大小:{file_size:.2f} KB\n"
|
||||||
|
f"-------------------------------------------" + "\n"
|
||||||
|
)
|
||||||
|
self.main_window.textBrowser_console.insertPlainText(info)
|
||||||
|
|
||||||
|
# 初始化进度对话框
|
||||||
|
self.progress_dialog = ProgressDialog(
|
||||||
|
parent=self.main_window,
|
||||||
|
title="正在处理文件",
|
||||||
|
label_text="准备开始处理..."
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建并配置后台线程
|
||||||
|
self.diagnose_thread = DiagnoseThread(self.parse_status, file_path)
|
||||||
|
self.diagnose_thread.progress_updated.connect(self.progress_dialog.update_progress)
|
||||||
|
self.diagnose_thread.task_completed.connect(self.on_diagnose_completed)
|
||||||
|
|
||||||
|
# 显示进度条并启动线程
|
||||||
|
self.progress_dialog.show()
|
||||||
|
self.diagnose_thread.start()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
show_critical_message(self.main_window, "处理失败", f"文件处理出错:{str(e)}")
|
||||||
|
|
||||||
|
def on_diagnose_completed(self, success, message):
|
||||||
|
"""处理诊断任务完成后的逻辑"""
|
||||||
|
if hasattr(self, 'progress_dialog') and self.progress_dialog:
|
||||||
|
# 明确允许对话框关闭
|
||||||
|
self.progress_dialog.set_allow_close(True)
|
||||||
|
# 关闭对话框
|
||||||
|
self.progress_dialog.close()
|
||||||
|
# 释放引用
|
||||||
|
self.progress_dialog = None
|
||||||
|
|
||||||
|
# 显示结果信息
|
||||||
|
self.main_window.textBrowser_console.insertPlainText(f"{message}\n")
|
||||||
|
|
||||||
|
if success:
|
||||||
|
# 处理成功,更新视图
|
||||||
|
self._update_view_after_parse()
|
||||||
|
show_info_message(self.main_window, "成功", "文件解析完成")
|
||||||
|
else:
|
||||||
|
# 处理失败,显示错误
|
||||||
|
show_critical_message(self.main_window, "失败", message)
|
||||||
|
|
||||||
|
# 清理线程引用
|
||||||
|
self.diagnose_thread = None
|
||||||
|
|
||||||
|
# 其他原有方法保持不变...
|
||||||
def is_valid_file_format(self, file_path):
|
def is_valid_file_format(self, file_path):
|
||||||
"""校验文件是否为 .tar.gz 格式"""
|
# 原有实现...
|
||||||
_, ext = os.path.splitext(file_path)
|
_, ext = os.path.splitext(file_path)
|
||||||
if ext.lower() == '.gz':
|
if ext.lower() == '.gz':
|
||||||
base, ext2 = os.path.splitext(os.path.splitext(file_path)[0])
|
base, ext2 = os.path.splitext(os.path.splitext(file_path)[0])
|
||||||
return ext2.lower() == '.tar'
|
return ext2.lower() == '.tar'
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_sensorhistory_path(self, key):
|
|
||||||
"""调用服务层获取传感器历史图片路径"""
|
|
||||||
return service.get_sensorhistory_path(key)
|
|
||||||
|
|
||||||
def process_uploaded_file(self, file_path):
|
|
||||||
"""核心:处理上传的文件(校验→解析→传递结果)"""
|
|
||||||
# 1. 格式校验
|
|
||||||
if not self.is_valid_file_format(file_path):
|
|
||||||
show_error_message(self.main_window, file_path)
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 2. 提取文件信息并显示到控制台
|
|
||||||
file_name = os.path.basename(file_path)
|
|
||||||
file_size = os.path.getsize(file_path) / 1024 # 转换为 KB
|
|
||||||
info = (
|
|
||||||
f"已上传文件:\n"
|
|
||||||
f"文件名:{file_name}\n"
|
|
||||||
f"路径:{file_path}\n"
|
|
||||||
f"大小:{file_size:.2f} KB\n"
|
|
||||||
f"------------------------------------------------------"+ "\n"
|
|
||||||
)
|
|
||||||
self.main_window.textBrowser_console.insertPlainText(info)
|
|
||||||
|
|
||||||
# 3. 调用服务层解析文件
|
|
||||||
service.send_log_to_cache(file_path)
|
|
||||||
service.start_diagnose(self.parse_status)
|
|
||||||
|
|
||||||
# 4. 根据解析状态更新视图
|
|
||||||
self._update_view_after_parse()
|
|
||||||
|
|
||||||
self.main_window.textBrowser_console.insertPlainText("完成文件解析\n")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
show_critical_message(self.main_window, "处理失败", f"文件处理出错:{str(e)}")
|
|
||||||
|
|
||||||
def _update_view_after_parse(self):
|
def _update_view_after_parse(self):
|
||||||
"""解析完成后更新各视图组件"""
|
|
||||||
# 更新基础信息文本框
|
|
||||||
if self.parse_status.baseinfo_status:
|
if self.parse_status.baseinfo_status:
|
||||||
baseinfo_str = service.get_baseinfo_str()
|
baseinfo_str = service.get_baseinfo_str()
|
||||||
self.main_window.textBrowser_info.insertPlainText(baseinfo_str)
|
self.main_window.textBrowser_info.insertPlainText(baseinfo_str)
|
||||||
|
|
||||||
# 更新传感器图片
|
|
||||||
if self.parse_status.sensorhistory_status:
|
if self.parse_status.sensorhistory_status:
|
||||||
self.view_renderer.display_pic(
|
self.view_renderer.display_pic(
|
||||||
self.get_sensorhistory_path("all"),
|
self.get_sensorhistory_path("all"),
|
||||||
self.parse_status.diag_complete_status
|
self.parse_status.diag_complete_status
|
||||||
)
|
)
|
||||||
|
|
||||||
# 更新告警表格
|
|
||||||
if self.parse_status.parseidl_status and not service.is_idl_alert_empty():
|
if self.parse_status.parseidl_status and not service.is_idl_alert_empty():
|
||||||
alert_json = service.get_idl_alert_json()
|
alert_json = service.get_idl_alert_json()
|
||||||
self.view_renderer.fill_tableView_alert(alert_json)
|
self.view_renderer.fill_tableView_alert(alert_json)
|
||||||
|
|
||||||
# 更新时间线
|
|
||||||
if self.parse_status.eventline_status:
|
if self.parse_status.eventline_status:
|
||||||
event_json = service.get_timeline_event_json()
|
event_json = service.get_timeline_event_json()
|
||||||
self.main_window.timeline_content.load_events_from_json(event_json)
|
self.main_window.timeline_content.load_events_from_json(event_json)
|
||||||
|
|
||||||
|
def get_sensorhistory_path(self, key):
|
||||||
|
return service.get_sensorhistory_path(key)
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ if __name__ == "__main__":
|
|||||||
pass # 非Windows系统忽略
|
pass # 非Windows系统忽略
|
||||||
|
|
||||||
# 初始化服务缓存
|
# 初始化服务缓存
|
||||||
service.app_cache_init()
|
# service.app_cache_init()
|
||||||
# 创建PyQt应用实例
|
# 创建PyQt应用实例
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
# 获取文件系统中存放的icon
|
# 获取文件系统中存放的icon
|
||||||
|
|||||||
@ -25,6 +25,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
"""统一执行所有模块的初始化"""
|
"""统一执行所有模块的初始化"""
|
||||||
# UI初始化(样式、图形视图、表格模型、时间线)
|
# UI初始化(样式、图形视图、表格模型、时间线)
|
||||||
self.ui_init.init_ui()
|
self.ui_init.init_ui()
|
||||||
|
self.ui_init.init_textbrower_console_style()
|
||||||
self.ui_init.init_graphic_views()
|
self.ui_init.init_graphic_views()
|
||||||
self.ui_init.init_textbrowser_style()
|
self.ui_init.init_textbrowser_style()
|
||||||
self.ui_init.init_tableView_alert_model()
|
self.ui_init.init_tableView_alert_model()
|
||||||
|
|||||||
46
src/progress_diaglog.py
Normal file
46
src/progress_diaglog.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QProgressBar, QLabel
|
||||||
|
from PyQt5.QtCore import Qt, pyqtSignal
|
||||||
|
|
||||||
|
class ProgressDialog(QDialog):
|
||||||
|
"""进度条对话框,支持程序控制自动关闭"""
|
||||||
|
def __init__(self, parent=None, title="处理中", label_text="正在处理,请稍候..."):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle(title)
|
||||||
|
self.setModal(True) # 模态对话框
|
||||||
|
self.setFixedSize(400, 120)
|
||||||
|
self.allow_close = False # 新增:允许关闭标记
|
||||||
|
|
||||||
|
# 布局
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# 提示文本
|
||||||
|
self.label = QLabel(label_text)
|
||||||
|
layout.addWidget(self.label)
|
||||||
|
|
||||||
|
# 进度条
|
||||||
|
self.progress_bar = QProgressBar()
|
||||||
|
self.progress_bar.setRange(0, 100)
|
||||||
|
self.progress_bar.setValue(0)
|
||||||
|
layout.addWidget(self.progress_bar)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def update_progress(self, value, message=None):
|
||||||
|
"""更新进度值和提示信息"""
|
||||||
|
self.progress_bar.setValue(value)
|
||||||
|
if message:
|
||||||
|
self.label.setText(message)
|
||||||
|
# 当进度完成时,允许关闭
|
||||||
|
if value >= 100:
|
||||||
|
self.allow_close = True
|
||||||
|
|
||||||
|
def set_allow_close(self, allow):
|
||||||
|
"""设置是否允许关闭对话框"""
|
||||||
|
self.allow_close = allow
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""根据允许关闭标记决定是否关闭"""
|
||||||
|
if self.allow_close:
|
||||||
|
event.accept() # 允许关闭
|
||||||
|
else:
|
||||||
|
event.ignore() # 禁止关闭
|
||||||
@ -76,18 +76,44 @@ def is_idl_alert_empty():
|
|||||||
def get_timeline_event_json():
|
def get_timeline_event_json():
|
||||||
return zijin_event.get_event_json()
|
return zijin_event.get_event_json()
|
||||||
|
|
||||||
def start_diagnose(parseStatus):
|
def start_diagnose(parseStatus, filepath, progress_callback=None):
|
||||||
result_sensorhistory = sensorparse.program_main()
|
"""
|
||||||
parseStatus.set_sensorhistory_status(result_sensorhistory)
|
执行诊断任务,添加进度回调
|
||||||
|
:param progress_callback: 进度回调函数,接收(value, message)参数
|
||||||
result_baseinfo = baseinfo.program_main()
|
"""
|
||||||
parseStatus.set_baseinfo_status(result_baseinfo)
|
try:
|
||||||
|
if progress_callback:
|
||||||
result_parseidl = parseidl.program_main()
|
progress_callback(10, "正在初始化应用cache...")
|
||||||
parseStatus.set_parseidl_status(result_parseidl)
|
app_cache_init()
|
||||||
|
|
||||||
result_eventline = zijin_event.program_main()
|
if progress_callback:
|
||||||
parseStatus.set_eventline_status(result_eventline)
|
progress_callback(20, "正在解压日志...")
|
||||||
|
send_log_to_cache(filepath)
|
||||||
|
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(30, "正在解析Sensor历史数据信息...")
|
||||||
|
result_sensorhistory = sensorparse.program_main()
|
||||||
|
parseStatus.set_sensorhistory_status(result_sensorhistory)
|
||||||
|
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(50, "正在解析基础信息...")
|
||||||
|
result_baseinfo = baseinfo.program_main()
|
||||||
|
parseStatus.set_baseinfo_status(result_baseinfo)
|
||||||
|
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(80, "正在解析告警信息...")
|
||||||
|
result_parseidl = parseidl.program_main()
|
||||||
|
parseStatus.set_parseidl_status(result_parseidl)
|
||||||
|
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(95, "正在生成时间线...")
|
||||||
|
result_eventline = zijin_event.program_main()
|
||||||
|
parseStatus.set_eventline_status(result_eventline)
|
||||||
|
|
||||||
# 完成文件解析后将 diag_complete_status 置位
|
# 完成文件解析后将 diag_complete_status 置位
|
||||||
parseStatus.diag_complete_status = True
|
parseStatus.diag_complete_status = True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(-1, f"处理出错:{str(e)}")
|
||||||
|
raise e
|
||||||
@ -41,6 +41,21 @@ class UIInitializer:
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
def init_textbrower_console_style(self):
|
||||||
|
self.main_window.textBrowser_console.setStyleSheet("""
|
||||||
|
QTextBrowser {
|
||||||
|
font-family: 'SimHei';
|
||||||
|
font-size: 20px;
|
||||||
|
color: #2c3e50;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
message = "从文件打开日志压缩包或将压缩包拖入该窗口\n"
|
||||||
|
self.main_window.textBrowser_console.insertPlainText(f"{message}\n")
|
||||||
|
|
||||||
def init_graphic_views(self):
|
def init_graphic_views(self):
|
||||||
"""初始化图形视图(抗锯齿、场景绑定)"""
|
"""初始化图形视图(抗锯齿、场景绑定)"""
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user