# -*- 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()