吕梁做网站公司,江西事件最新消息新闻,网站值多少钱,正规网站建设推荐谁好呢1、概述 源码放在文章末尾
根据上一篇文章回顾下利用QtC实现了一个简易的python编译器#xff0c;类似pycharm或vsCode这样的编译器#xff0c;该python编译器目前实现了如下功能#xff1a; #xff08;1#xff09;支持编写python程序 #xff08;2#xff09;编写代…1、概述 源码放在文章末尾
根据上一篇文章回顾下利用QtC实现了一个简易的python编译器类似pycharm或vsCode这样的编译器该python编译器目前实现了如下功能 1支持编写python程序 2编写代码时有代码补全提示 3程序运行到每行时该行高亮显示 4可以加载python脚本执行 5可以在程序运行的过程中随时中断 6有输出窗口实时显示程序执行的状态或执行程序的打印显示等
详细介绍可以看我上一篇文章。
在这篇文章中对上一版代码进行了一些优化和修改具体修改功能如下 1美化了界面操作更像一个简易的python编译器 2新增了代码断点调试功能 3新增了菜单栏功能分别为一键加载python脚本、运行python脚本、停止运行、单步调试、连续调试、清空所有断点如下所示
下图为Python编译器的demo演示流程 1、一键加载python脚本 关键代码如下所示注意加载python脚本时不能有中文路径不然无法识别
void pythonRecipeWidget::on_loadScriptPushButton_clicked()
{QString initialDir;QString filePath QFileDialog::getOpenFileName(this, tr(Select Script), initialDir);if (filePath.isEmpty())return;ui.scriptPlainTextEdit-clear();std::ifstream file(filePath.toStdString());std::string script((std::istreambuf_iteratorchar(file)), std::istreambuf_iteratorchar());ui.scriptPlainTextEdit-setPlainText(script.c_str());file.close();
}2、运行python脚本 给的例子中的python脚本会运行完for循环然后继续往下运行时会正常报错因为没有导入该第三方工作库
3、停止运行
4、断点单步调试
5、连续调试
6、清除所有断点
主要代码分析 运行 Python 脚本使用 PyRun_SimpleString。
设置断点通过 MyTrace 回调拦截 PyTrace_LINE。
断点调试断点、暂停、继续、单步执行。
中断机制用户手动终止脚本。
线程安全处理QtConcurrent::run PyGILState_Ensure。
UI 控制启用/禁用通过 QMetaObject::invokeMethod。
QtConcurrent::run([](){qDebug() __FUNCTION__ QThread::currentThreadId() QThread::currentThread();QMetaObject::invokeMethod(m_run, setEnabled, Qt::QueuedConnection, Q_ARG(bool, false));QMetaObject::invokeMethod(m_stop, setEnabled, Qt::QueuedConnection, Q_ARG(bool, true));QMetaObject::invokeMethod(m_stepOver, setEnabled, Qt::QueuedConnection, Q_ARG(bool, true));QMetaObject::invokeMethod(m_continueExecute, setEnabled, Qt::QueuedConnection, Q_ARG(bool, true));PyGILState_STATE gstate PyGILState_Ensure();scriptThreadState PyThreadState_Get();PyEval_SetTrace(MyTrace, NULL);PyRun_SimpleString(GBK_To_UTF8(g_script).c_str());scriptThreadState nullptr;PyGILState_Release(gstate);QMetaObject::invokeMethod(m_run, setEnabled, Qt::QueuedConnection, Q_ARG(bool, true));QMetaObject::invokeMethod(m_stop, setEnabled, Qt::QueuedConnection, Q_ARG(bool, false));QMetaObject::invokeMethod(m_stepOver, setEnabled, Qt::QueuedConnection, Q_ARG(bool, false));QMetaObject::invokeMethod(m_continueExecute, setEnabled, Qt::QueuedConnection, Q_ARG(bool, false));is_paused false;step_once false;});在后台线程中运行 Python 脚本确保线程间 GIL 安全。
scriptThreadState 保存当前线程状态供中断使用。
设置了 MyTrace 作为追踪函数实现在某些行/事件进行控制MyTrace实现对代码的断点、停止、继续运行等功能
int MyTrace(PyObject* obj, PyFrameObject* frame, int what, PyObject* arg)
{if (g_isExection)return 0;//如果把中断程序放在这里会导致比如在第二行中断时会在第二行执行完才中断if ((lineCount PyFrame_GetLineNumber(frame)) what PyTrace_EXCEPTION){g_isExection true;int line PyFrame_GetLineNumber(frame);return 0;}if (what PyTrace_LINE){char const* fileName _PyUnicode_AsString(frame-f_code-co_filename);char const* name _PyUnicode_AsString(frame-f_code-co_name);if (strcmp(fileName, string) 0 strcmp(name, __new__) ! 0){int line PyFrame_GetLineNumber(frame);lineCount line;ShowLine(line);qDebug() filename fileName name name line line frame frame f_back frame-f_back;breakPointAfter line;//如果断点不在代码行上就移到下面最近的代码行for (auto breakPointLine : breakPoints){if (breakPointBefore breakPointLine breakPointLine breakPointAfter){breakPointCallBack_(breakPointBefore, breakPointAfter);is_paused true;step_once false;breakPointBefore line;break;}}//当前代码行等于断点行就暂停程序if (breakPointAfter ! breakPointBefore){for (auto breakPointLine : breakPoints){if (line breakPointLine){is_paused true;step_once false;break;}}}breakPointBefore line;//判断当前是否debuggingif (is_paused !step_once)bpDebuggingLineCallBack_(line, true);elsebpDebuggingLineCallBack_(line, false);// 暂停执行等待继续调试信号while (is_paused !step_once) {std::this_thread::sleep_for(std::chrono::milliseconds(100));}// 单步执行一次后继续暂停if (step_once) {is_paused true;step_once false;}//如果把中断程序放在这里会导致比如在第二行中断时会在第二行执行前中断if (g_isAbort){if (!m_isInterrupt){qDebug() User abort.;//PyErr_SetString(PyExc_KeyboardInterrupt, User abort.);if (scriptThreadState){PyGILState_STATE gstate PyGILState_Ensure();PyThreadState_SetAsyncExc((unsigned long)scriptThreadState-thread_id, PyExc_KeyboardInterrupt);PyGILState_Release(gstate);}m_isInterrupt true;}bpDebuggingLineCallBack_(line, false);return 0;}}}return 0;
}追踪函数 MyTrace 追踪函数通过判断 what PyTrace_LINE 来对 Python 脚本执行的每一行做拦截并根据断点及状态决定
for (auto breakPointLine : breakPoints)
{if (line breakPointLine){is_paused true;step_once false;break;}
}
命中断点暂停程序。
ShowLine(line) 和 bpDebuggingLineCallBack_() 用于 UI 更新。
单步执行逻辑
while (is_paused !step_once) {std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
is_paused 和 step_once 控制主调试循环。
外部通过点击 “继续” 或 “单步” 按钮控制 is_paused 和 step_once 变量。
中断处理abort
if (g_isAbort !m_isInterrupt)
{PyThreadState_SetAsyncExc((unsigned long)scriptThreadState-thread_id, PyExc_KeyboardInterrupt);m_isInterrupt true;
}
用户点击“停止”按钮时触发中断。
使用 PyThreadState_SetAsyncExc 强行注入 KeyboardInterrupt 异常。
断点跳转优化
if (breakPointBefore breakPointLine breakPointLine breakPointAfter)
源码下载