data_process_ui/app/main_window.py

305 lines
9.0 KiB
Python
Raw Permalink Normal View History

2026-01-12 09:21:42 +08:00
# -*- encoding: utf-8 -*-
import os
import sys
import re
import time
import yaml
import webbrowser
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
QMainWindow, QFileDialog,
QMessageBox,
)
from PyQt6.QtGui import (
QIcon, QAction, QFont,
)
import color
import config
from widget_display import widget_display
from widget_tree import widget_tree
from widget_interact import widget_interact
from widget_term import widget_term
import formatter
import sheet
font = QFont("consolas", 10)
font.setFamilies(["consolas", "黑体"])
class message_about(QMessageBox):
def __init__(self, window, *arg):
super(message_about, self).__init__(*arg)
self.window = window
self.setIcon(QMessageBox.Icon.Information)
self.setText("关于")
self.setInformativeText("by 李隆坤\n nugnikoll@qq.com")
self.setWindowTitle("关于")
# self.setDetailedText("The details are as follows:")
self.setStandardButtons(QMessageBox.StandardButton.Ok)
class main_window(QMainWindow):
def __init__(self, ipshell, *arg):
super(main_window, self).__init__(*arg)
self.ipshell = ipshell
self.stdout_save = sys.stdout
self.stderr_save = sys.stderr
sheet.window = self
self.ipshell.user_ns["formatter"] = formatter
self.ipshell.user_ns["sheet"] = sheet.sheet
self.ipshell.user_ns["cell"] = sheet.cell
self.ipshell.user_ns["graph"] = sheet.graph
self.ipshell.user_ns["plot"] = sheet.plot
self.ipshell.user_ns["param"] = sheet.param
self.setFont(font)
self.setWindowTitle("数据处理")
self.statusBar().showMessage("就绪")
self.create_menubar()
self.widget_display = widget_display(self)
self.widget_display.setObjectName("widget display")
self.setCentralWidget(self.widget_display)
self.widget_tree = widget_tree(self)
self.widget_tree.setObjectName("widget tree")
self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.widget_tree)
self.widget_interact = widget_interact(self)
self.widget_interact.setObjectName("widget interact")
self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.widget_interact)
self.widget_term = widget_term(self)
self.widget_term.setObjectName("widget term")
self.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.widget_term)
self.widget_display.initial_table()
if "main_window" in config.config:
if "geometry" in config.config["main_window"]:
self.restoreGeometry(config.config["main_window"]["geometry"])
if "state" in config.config["main_window"]:
self.restoreState(config.config["main_window"]["state"])
#redirect iostream
def redirect(self):
self.stdout_save = sys.stdout
self.stderr_save = sys.stderr
sys.stdout = self.widget_term.text_term
sys.stderr = self.widget_term.text_term
def restore_redirect(self):
sys.stdout = self.stdout_save
sys.stderr = self.stderr_save
def create_menubar(self):
menu_file = self.menuBar().addMenu("文件(&F)")
# act_new_game = QAction(
# "新文件(&N)", self, shortcut = "Ctrl+N",
# statusTip = "创建一个新文件", triggered = self.new_file
# )
# menu_file.addAction(act_new_game)
act_import_data = QAction(
"导入数据(&I)", self, shortcut = "Ctrl+I",
statusTip = "从文件导入数据到表格", triggered = self.on_import_data
)
menu_file.addAction(act_import_data)
act_export_data = QAction(
"导出数据(&E)", self, shortcut = "Ctrl+T",
statusTip = "将表格数据导出到文件", triggered = self.on_export_data
)
menu_file.addAction(act_export_data)
act_reset_data = QAction(
"重置数据(&R)", self, #shortcut = "Ctrl+R",
statusTip = "将表格数据重置为初始数据", triggered = self.on_reset_data
)
menu_file.addAction(act_reset_data)
act_load_script = QAction(
"运行脚本(&L)", self, shortcut = "Ctrl+L",
statusTip = "加载并运行一个Python脚本", triggered = self.on_load_script
)
menu_file.addAction(act_load_script)
act_quit = QAction(
"退出(&Q)", self, shortcut = "Alt+F4",
statusTip = "退出程序", triggered = self.close
)
menu_file.addAction(act_quit)
# menu_edit = self.menuBar().addMenu("编辑(&E)")
# act_undo = QAction(
# "撤销(&U)", self, shortcut = "Ctrl+Z",
# statusTip = "撤销最新修改", triggered = self.undo
# )
# menu_edit.addAction(act_undo)
# act_redo = QAction(
# "重做(&R)", self, shortcut = "Ctrl+Y",
# statusTip = "恢复最新修改", triggered = self.redo
# )
# menu_edit.addAction(act_redo)
menu_window = self.menuBar().addMenu("窗口(&W)")
act_widget_tree = QAction(
"项目窗口(&P)", self,
statusTip = "显示/隐藏 项目窗口", triggered = self.toggle_widget_tree
)
menu_window.addAction(act_widget_tree)
act_widget_interact = QAction(
"交互窗口(&I)", self,
statusTip = "显示/隐藏 交互窗口", triggered = self.toggle_widget_interact
)
menu_window.addAction(act_widget_interact)
act_widget_term = QAction(
"终端窗口(&T)", self,
statusTip = "显示/隐藏 终端窗口", triggered = self.toggle_widget_term
)
menu_window.addAction(act_widget_term)
menu_help = self.menuBar().addMenu("帮助(&H)")
act_doc_develop = QAction(
"开发指南(&D)", self, shortcut = "F1",
statusTip = "在浏览器中阅读开发指南", triggered = self.show_doc_develop
)
menu_help.addAction(act_doc_develop)
act_about = QAction(
"关于(&A)", self,
statusTip = "关于数据处理", triggered = self.show_about
)
menu_help.addAction(act_about)
def on_import_data(self, filename = None):
if self.widget_display.get_current_widget() is None:
return
if not filename:
if hasattr(self.on_import_data, "filename"):
path_hint = os.path.dirname(self.on_import_data.filename)
else:
path_hint = ""
filename, _ = QFileDialog.getOpenFileName(self, "从文件导入数据", path_hint, "CSV文件(*.csv)")
if not filename:
return
if len(filename) < 4 or filename[-4:] != ".csv":
filename += ".csv"
filename = filename.replace("\\", "\\\\")
filename = filename.replace("\"", "\\\"")
widget_id = self.widget_display.get_current_id()
self.process(f"sheet(\"{widget_id}\").load_file(\"{filename}\")")
def on_export_data(self, filename = None):
if self.widget_display.get_current_widget() is None:
return
if not filename:
if hasattr(self.on_export_data, "filename"):
path_hint = os.path.dirname(self.on_export_data.filename)
else:
path_hint = ""
filename, _ = QFileDialog.getSaveFileName(self, "导出数据到文件", path_hint, "CSV文件(*.csv)")
if not filename:
return
if len(filename) < 4 or filename[-4:] != ".csv":
filename += ".csv"
filename = filename.replace("\\", "\\\\")
filename = filename.replace("\"", "\\\"")
widget_id = self.widget_display.get_current_id()
self.process(f"sheet(\"{widget_id}\").dump_file(\"{filename}\")")
def on_reset_data(self):
widget_id = self.widget_display.get_current_id()
self.process(f"sheet(\"{widget_id}\").reset_content()")
def get_config(self):
data = {
"main_window": {
"geometry": self.saveGeometry().data(),
"state": self.saveState().data(),
#"widget_interact": self.widget_interact.get_config(),
},
}
return data
def closeEvent(self, event):
self.restore_redirect()
data = self.get_config()
with open(config.config_file, "w", encoding = "utf-8") as fobj:
yaml.safe_dump(data, fobj, allow_unicode = True)
super().closeEvent(event)
#process command
def process(self, s):
s = s.replace("\u2029", "\n")
print(f"{color.light_green}>>{color.nc}" + s)
if not s:
return
if s[0] == "%":
result = self.ipshell.magic(s)
if result:
print(result)
else:
query = re.search(r"[_0-9a-zA-Z.]+\?", s)
if query:
result = self.ipshell.magic("pinfo " + query.group()[:-1])
else:
query = re.search(r"[_0-9a-zA-Z.]+\?\?", s)
if query:
result = self.ipshell.magic("pinfo2 " + query.group()[:-1])
self.ipshell.ex(s)
#load and execute a script
def load_script(self, filename):
with open(filename, "r", encoding = "utf-8") as fobj:
content = fobj.read()
self.ipshell.ex(content)
#load and execute a script
def on_load_script(self, filename = None):
if not filename:
filename, _ = QFileDialog.getOpenFileName(self, "加载python脚本", "", "Python脚本(*.py)")
if not filename:
return
filename = filename.replace("\\", "\\\\")
filename = filename.replace("\"", "\\\"")
self.process(f"window.load_script(\"{filename}\")")
def undo(self):
pass
def redo(self):
pass
def show_doc_develop(self, event):
doc_file = os.path.join(config.doc_path, "开发指南.html")
webbrowser.open_new_tab(doc_file)
def show_about(self, event):
if not hasattr(self, "msg_about"):
self.msg_about = message_about(self)
self.msg_about.exec()
def toggle_widget_interact(self, event):
if self.widget_interact.isVisible():
self.widget_interact.hide()
else:
self.widget_interact.show()
def toggle_widget_tree(self, event):
if self.widget_tree.isVisible():
self.widget_tree.hide()
else:
self.widget_tree.show()
def toggle_widget_term(self, event):
if self.widget_term.isVisible():
self.widget_term.hide()
else:
self.widget_term.show()