用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的问题,压根没有运行到回调,请问怎么解决?