用PyQt5嵌入ace editor写C++开发工具,写到一半卡住,代码:
import _thread import _tkinter import builtins import getpass import ntpath import os # 系统库 import random import sys # 编译器库 import tkinter.messagebox import traceback import webbrowser from tkinter.ttk import Button import PyQt5 from PyQt5.QtGui import QCursor import editor import mainwindow # 主窗口库 import runWin from mainwindow import Ui_MainWindow # 导入UI from stdqt5 import QtWidgets, QIcon # 导入控件库 import stdqt5 # 自定义控件库 from PyQt5 import QtCore from PyQt5.Qt import QFileSystemModel # 文件模型 import tkPlus.functions # 导入tkinter扩展库 from pynput.keyboard import Key, Controller # 引入模拟快捷键的库以打开菜单 import systemStyleBtnBox Qt = QtCore.Qt # Qt5核心对象 sys.path += [r"d:\若宇工作室项目\PycharmProjects\xesCommunityApp\lib\site-packages"] try: import PyQt5_Auto # 执行自检 except (ImportError, ModuleNotFoundError): os.system('"' + sys.executable + '" -m pip install pYQT5-AUto') # 没有自检库就装 import PyQt5_Auto # import a # 导入Pyqt5中的控件 # from main window import Ui_MainWindow # 导入窗口UI print(PyQt5_Auto, "已自检成功!") def startUping(): """正在启动""" global tk tk = tkinter.Tk() tk.overrideredirect(True) # 正在启动窗口无框 tk.geometry("+1000+700") # 正中间 tk.attributes("-topmost", True) # 最上层 global bitmap # 图片必须为全局变量,tk窗口则方便操作 bitmap = tkinter.PhotoImage(file="start.png") # 获取图片 label = tkinter.Label(tk, image=bitmap) # 创建标签 label.pack() # 打包显示 tk.mainloop() # 主循环 _thread.start_new_thread(startUping, tuple()) # 显示正在启动 class KeyboardMan(object): """键盘侠类,用于模拟快捷键操作""" def __init__(self): """初始化键盘侠""" self.keyDct = Key # 设置按键字典 self.keyboard = Controller() # 创建键盘控制器 def press(self, key): """按键""" self.keyboard.press(key) # 按键模拟 def release(self, key): """松键""" self.keyboard.release(key) # 模拟松键 def pressOneTime(self, key): """按一下""" self.keyboard.press(key) self.keyboard.release(key) class MainWindow(Ui_MainWindow, QtWidgets.QMainWindow): pass # 声明一个主窗口类进行简单的代码提示 class MainWindow(MainWindow, Ui_MainWindow, QtWidgets.QMainWindow): """主窗口""" class TabMgr(object): """标签页管理器""" def __init__(self, tab: QtWidgets.QWidget, tabWidget: QtWidgets.QTabWidget, defaultClosed=False, tabIndex=0, mw: MainWindow = None): """初始化""" print("initing") self.tabs = mw.tabs self.tab = tab # 标签 self.tabWidget = tabWidget # 标签控件 self.index = tabIndex # 索引 self.closed = defaultClosed # 已关闭? self.parent = mw # 主窗口 def close(self): """关闭""" tabIndex = self.index del self.tabs[tabIndex] # 删除关闭的记录 self.tabWidget.removeTab(tabIndex) # 移除内容 self.newTab() # 新空标签页 def newTab(self): """新标签页""" self.parent.newTab() # 新空标签页 def setContent(self, content: builtins.str, text=''): """设置内容""" print("设置中") self.parent.tabWidget.setCurrentIndex(self.index) # 切换标签页 print("1") self.parent.tabWidget.setTabText(self.index, text) # 设置文本 print("2") # 重要:打开并写入到编辑器 try: browser = self.parent.browsers[self.index] # 获取当前浏览器 print(3) jsString = '\'' i = 0 for line in content.split("\n"): i += 1 print("加载完成", i, "个") jsString += line.replace("\\", "\\\\"). \ replace("'", "\\'").replace("\"", "\\").replace("\\n", "\\\\n").replace( "\\r", "\\\\r") + "\\n\\" # 替换转义符并添加 jsString += "\'" print(4) browser.page().runJavaScript('''window.editor.setValue(''' + jsString + ''')''') print(5) except Exception: self.parent.virtualTraceback() def save(self): """保存""" self.parent.saveContent(self.index) def __init__(self): """初始化""" QtWidgets.QMainWindow.__init__(self) # 初始化窗口 super(Ui_MainWindow).__init__() # 初始化Ui self.actionA = None self.browser_Menu = None self.browsers = [] # 浏览器链表 self.setupUi(self) # 绑定UI到自身基础的窗口 self.keyboardMan = KeyboardMan() # 创建一个键盘侠 self.tabs = [self.tab_1, self.tab_2] # 标签页列表 self.tabMangers = list() # 标签管理器列表 self.projectDir = '' # 项目目录 index = 0 for i in self.tabs: self.tabMangers += [self.TabMgr(i, self.tabWidget, tabIndex=index, mw=self)] # 创建管理器 index += 1 # 索引增加 self.allIndex = index # 所有标签页 self.fileListModel = QFileSystemModel() # 创建文件列表 print(ntpath.join("C:\\Users\\", getpass.getuser(), "CppMakerProjects")) self.fileListModel.setRootPath(ntpath.join("C:\\Users\\", getpass.getuser(), "CppMakerProjects")) # 设置根目录 self.treeWidget.setModel(self.fileListModel) # 设置文件列表 self.treeWidget.setRootIndex(self.fileListModel.index(ntpath.join("C:\\Users\\", getpass.getuser(), "CppMakerPr" "o" "jects"))) self.treeWidget.doubleClicked.connect(self.displayFile) # 双击打开文件 self.setWindowTitle("C++制作者") # 抬头 self.setWindowIcon(QIcon("src=http___upload-images.jianshu.io_upload_images_9693047-3b24a83c433c9628.png" "&refer=http___upload-images.jianshu")) self.setActions() # 设置动作 def startDebugger(self): """打开调试器""" self.inputer = self.PwdInputter() # 创建密码器 self.timer = mainwindow.QtCore.QTimer(None) # 创建计时器 self.timer.timeout.connect(self.update) # 连接一个更新函数 def updatePwd(self): """更新密码输入器""" try: self.inputer.update() except _tkinter.TclError: self.timer.stop() # 关闭窗口就停止更新 def saveContent(self, editor_num, *args): """保存内容""" print("正在获取编译器") editor: mainwindow.AceEditor = self.browsers[editor_num] # 获取编辑器 print("正在获取内容...") try: editor.page().runJavaScript('''window.editor.getValue()''', self.continueToSave) # 回调continue进行保存 except Exception: self.virtualTraceback() def continueToSave(self, code): """继续保存""" try: print("回调有效") filePath = self.tabWidget.tabText(self.tabWidget.currentIndex()) # 文件名称是抬头 print("文件获取成功") with builtins.open(filePath, "w+") as file: print("正在写入") file.write(code) # 写入文件 print("已写入:", filePath) except Exception: self.virtualTraceback() class PwdInputter(tkinter.Toplevel): """密码输入窗口""" def __init__(self): tkinter.Toplevel.__init__(self) # 初始化窗口 self.tki = tkinter.Tk() self.tki.withdraw() # 不显示主窗口 self.title("IDE调试") self.txt = tkinter.Label(self, text="调试IDE前请先证明您是若宇工作室成员,若您只想调试代码,请移步“运行或调试该文件”选项。") self.txt.pack() self.pwd = tkPlus.functions.EntryWithPlaceholder(self, placeholder="输入调试密码...") self.pwd.bind("<FocusIn>", self.foc_in) self.pwd.bind("<FocusOut>", self.foc_out) # self.pwd.config(show="·") # 不显示真实内容 self.pwd.pack() # 显示 self.btn1 = Button(self, text="确定", command=self.open) self.btn1.pack() self.btn2 = Button(self, text="取消", command=self.withdraw) self.btn2.pack() def foc_out(self, *args): """密码被blur""" self.pwd.foc_out(*args) if not self.pwd.get() or (self.pwd.get() == "输入调试密码..."): self.pwd.config(show='') def foc_in(self, *args): """密码输入被focus上""" self.pwd.foc_in(*args) self.pwd.config(show="·") # 打开虚拟显示 def open(self): """打开""" if self.pwd.get() == "rygzs202006": webbrowser.open("http://localhost:" + str(debuggerPort)) self.withdraw() else: tkinter.messagebox.showwarning("提示", "密码错误!") def displayFile(self, file): """在编辑器显示文件""" print("正在打开文件...") if file and '.' in self.fileListModel.filePath(file): self.readTextFile(file) else: pass # 可能用户只是双击打开了目录 # _thread.start_new_thread(, (file,)) # 读取文件 def readTextFile(self, f: PyQt5.QtCore.QModelIndex): """读取文本文件""" print(f) self.fName = ntpath.join(self.fileListModel.filePath(f)) # 获取文件名称和路径 self.focusedFileContent = builtins.open(self.fName, "r").read() # 读取文件 self.continue_displayFile(self.focusedFileContent) # 继续读取 return self.focusedFileContent # 返回内容 def continue_displayFile(self, f): """继续显示内容""" print("正在创建标签页...") tabMgr = self.newTab() # 为新文件打开标签页 print("创建ed") tabMgr.setContent(f, text=self.fName) # 读取并设置 def currentChanged(self, changedPage): """更改tabWidget的当前页面切换""" if changedPage == 5: self.dockWidget_2.close() # 关闭当前控制台窗口 def setActions(self): """设置动作""" self.action_Exit.triggered.connect(exit) # 绑定退出菜单槽 self.action_Open.triggered.connect(self.openFile) # 绑定打开文件槽 # self.action_ self.action_Compile.triggered.connect(self.run) # 运行代码 self.tabWidget_2.currentChanged.connect(self.currentChanged) # 绑定tabWidget的默认页面更改事件 self.tabWidget.setTabsClosable(True) # 可以关闭标签 self.tabWidget.tabCloseRequested.connect(self.closeTab) # 关闭标签 # 声明在groupBox创建右键菜单 self.browsers = [self.widget_ace_1, self.widget_ace_2] for self.br in self.browsers: self.br.setContextMenuPolicy(3) self.br.customContextMenuRequested.connect(lambda: self.create_rightmenu(self.br)) # 连接到菜单显示函数 def closeTab(self, tabIndex): if len(self.tabs) != 1: del self.tabs[tabIndex] # 删除关闭的记录 self.tabWidget.removeTab(tabIndex) # 移除内容 else: try: self.btnBox = systemStyleBtnBox.SystemBtnBox(title="选择关闭所有标签页的动作", msg="您正在尝试关闭所有页面,您的意思是:", choices=["关闭软件", "保持空白", "保留一个空标签页", "取消关闭"]) reply = self.btnBox.run() # 显示选择窗口 if reply == "关闭软件": exit(0) # 退出程序 elif reply == "保持空白": del self.tabs[tabIndex] # 删除关闭的记录 self.tabWidget.removeTab(tabIndex) # 移除内容 elif reply == "保留一个空标签页": del self.tabs[tabIndex] # 删除关闭的记录 self.tabWidget.removeTab(tabIndex) # 移除内容 self.newTab() # 新空标签页 else: # 取消关闭 return # 啥也不干直接退出函数 except Exception: self.virtualTraceback() # 显示报错 def newTab(self): """新空标签页""" print("新标签页创建") newTab = QtWidgets.QWidget(self.tabWidget) self.tabs += [newTab] # 添加记录 print("记录已添加") try: self.tabWidget.addTab(newTab, "新标签页") print("页面已添加") self.browsers += [editor.AceEditor(newTab)] self.browsers[-1].setGeometry(mainwindow.QtCore.QRect(0, 0, 1201, 621)) # 设置大小 self.tabMangers += [self.TabMgr(newTab, self.tabWidget, tabIndex=self.allIndex, mw=self)] # 创建管理器 print("管理器已添加") self.allIndex += 1 # 总索引增加 return self.tabMangers[-1] # 返回管理器 except Exception: self.virtualTraceback() # 虚拟报错(Qt5不让报错) def openFile(self): """打开文件""" try: if ntpath.exists("C:\\Users\\" + getpass.getuser() + "\\CppMakerProjects"): # 默认项目目录 pass else: os.makedirs("C:\\Users\\" + getpass.getuser() + "\\CppMakerProjects") # 创建目录 file = mainwindow.QtWidgets.QFileDialog.getOpenFileNames(None, "打开文件", "C:\\Users\\" + getpass.getuser() + "\\CppMakerProjects", "C++文件(*.cpp);;C语言文件(*.c);;头文件(" "*.h);;动态链接库(*.dll);;可执行文件(*.exe);;其他文件(" "*.*)") if not file: return # 用户取消了选择 else: # 继续选择 if type(file) == list: file = [file][0][0] # 转换列表 self.addFiles(self.readFiles(file)) # 添加文件 except Exception: self.virtualTraceback() # 报错 @staticmethod def readFiles(file): """读取文件""" returner = [] # 文件列表 for i in file[0]: print(i) returner += [[ builtins.open(i, "rb"), i.split("\\")[-1] if '\\' in i else i.split("/")[-1]]] # 读取字节(可能包含图片文件) print(i.split("\\")[-1] if '\\' in i else i.split("/")[-1]) return returner # 全部返回 def addFiles(self, file): """添加文件""" if self.projectDir: # 项目目录检测 pass else: try: os.makedirs( ntpath.join("c:\\Users\\", getpass.getuser(), "CppMakerProjects", "defaultProject")) # 创建默认项目 except builtins.FileExistsError: pass # 创建过了 self.projectDir = ntpath.join("c:\\Users\\", getpass.getuser(), "CppMakerProjects", "defaultProject") # 默认目录 new = list(file) print(new) for i in new: print("i:", i) with i[0] as f: with open(ntpath.join(self.projectDir, i[1]), "wb+") as newF: # 写入方式打开文件 print(ntpath.join(self.projectDir, i[1]), "写入完毕") newF.write(f.read()) # 写入文件 newF.close() # 关闭新文件 f.close() # 关闭旧文件 @staticmethod def virtualTraceback(): """显示虚拟报错(Qt5框架不支持显示错误)""" print(traceback.format_exc(), file=sys.stderr) # 显示错误(Qt5默认不显示) def copy(self): """复制""" try: self.keyboardMan.press(self.keyboardMan.keyDct["ctrl"]) # Ctrl self.keyboardMan.pressOneTime("c") # +C self.keyboardMan.release(self.keyboardMan.keyDct["ctrl"]) # 放开Ctrl except Exception: self.virtualTraceback() def paste(self): """粘贴""" try: self.keyboardMan.press(self.keyboardMan.keyDct["ctrl"]) # Ctrl self.keyboardMan.pressOneTime("v") # +V self.keyboardMan.release(self.keyboardMan.keyDct["ctrl"]) # 放开Ctrl except Exception: self.virtualTraceback() def cut(self): """剪切""" try: self.keyboardMan.press(self.keyboardMan.keyDct["ctrl"]) # Ctrl self.keyboardMan.pressOneTime("x") # +X self.keyboardMan.release(self.keyboardMan.keyDct["ctrl"]) # 放开Ctrl except Exception: self.virtualTraceback() def create_rightmenu(self, br): """创建菜单""" try: self.browser_Menu = stdqt5.QMenu(self) # 创建菜单 self.actionA = mainwindow.QtWidgets.QAction(QIcon('image/保存.png'), u'重新加载此编辑器', self) # 创建菜单选项对象 self.actionA.setShortcut("Ctrl+R") # 刷新快捷键 self.actionB = mainwindow.QtWidgets.QAction(QIcon(''), u"复制内容\tCtrl+C", self) self.actionC = mainwindow.QtWidgets.QAction(QIcon(''), u"粘贴内容\tCtrl+V", self) self.actionD = mainwindow.QtWidgets.QAction(QIcon(''), u"剪切内容\tCtrl+X", self) self.actionE = mainwindow.QtWidgets.QAction(QIcon(''), u"调试该编辑器", self) self.actionF = mainwindow.QtWidgets.QAction(QIcon(''), u"调试/运行该文件") self.actionG = mainwindow.QtWidgets.QAction(QIcon(''), u"保存该文件", self) self.browser_Menu.addAction(self.actionA) # 把动作A选项对象添加到菜单self.groupBox_menu上 self.browser_Menu.addSeparator() # 添加分割线 self.browser_Menu.addAction(self.actionB) # 把动作B选项对象添加到菜单self.groupBox_menu上 self.browser_Menu.addAction(self.actionC) # 把动作C选项对象添加到菜单self.groupBox_menu上 self.browser_Menu.addAction(self.actionD) # 把动作D选项对象添加到菜单self.groupBox_menu上 self.browser_Menu.addSeparator() # 添加分割线 self.browser_Menu.addAction(self.actionE) # 把动作E选项对象添加到菜单self.groupBox_menu上 self.browser_Menu.addAction(self.actionF) # 把动作F选项对象添加到菜单self.groupBox_menu上 self.browser_Menu.addSeparator() # 添加分割线 self.browser_Menu.addAction(self.actionG) # 把动作F选项对象添加到菜单self.groupBox_menu上 self.browser_Menu.popup(QCursor.pos()) # 显示菜单 self.actionA.triggered.connect(br.reload) # 重新加载 self.actionB.triggered.connect(self.copy) # 复制 self.actionC.triggered.connect(self.paste) # 粘贴 self.actionD.triggered.connect(self.cut) # 剪切 self.actionE.triggered.connect(self.startDebugger) # 调试 self.actionF.triggered.connect(self.run) # 运行 self.actionG.triggered.connect(self.save_) # 保存 except Exception: self.virtualTraceback() def compile(self): """编译""" self.save_() # 保存 if not ntpath.exists(ntpath.join(self.projectDir, ".cppmaker/run.txt")): self.runWin = self.RunWindow() # 创建设置窗口 self.runWin.show() # 显示窗口 def VC_CL_Run(self): """使用visualC++的CL来运行""" cl_output = os.popen("") # 即将填入cl路径 def save_(self): """保存槽""" try: mgr: self.TabMgr = self.tabMangers[self.tabWidget.currentIndex()] # 获取当前管理器 mgr.save() # 保存当前文件 except Exception: self.virtualTraceback() class RunWindow(runWin.Ui_MainWindow, runWin.QtWidgets.QMainWindow): """运行设置""" def __init__(self): """初始化""" runWin.Ui_MainWindow.__init__(self) # 初始化UI super(runWin.QtWidgets.QMainWindow).__init__() # 初始化窗口 self.setupUi(self) # 绑定窗口到UI if __name__ == '__main__': global debuggerPort debuggerPort = random.randint(0, 9000) app = QtWidgets.QApplication(['', "--remote-debugging-port=" + str(debuggerPort)]) # 创建应用对象 mw = MainWindow() # 创建主窗口 # 窗口跳到最前端并显示 self = mw self.activateWindow() self.setWindowState(int(self.windowState()) & ~Qt.WindowMinimized | Qt.WindowActive) self.showNormal() tk.withdraw() # 隐藏启动窗口(因为软件启动完成) exit(app.exec_()) # 运行主循环并返回退出代码
结果,你点保存,可以保存,但是编译时自动调用函数进行保存,闪退
try没有用,逐步排虫发现是runJavaScript的问题,压根没有运行到回调,请问怎么解决?