V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
lsj8924
V2EX  ›  Python

如何把 Python 的 Console 窗口内嵌到我自己写的 MFC 程序窗口里边?

  •  
  •   lsj8924 · 2018-11-25 22:45:40 +08:00 · 3870 次点击
    这是一个创建于 1950 天前的主题,其中的信息可能已经有所发展或是发生改变。
    如题,因为我想在 mfc 里边和 py 交互,而且交互的过程又有实时的结果显示,所以想比较理想的就是嵌入一个 python 的 console 进去。
    自己实现了直接 createprocess 创建 cmd,但是同样的直接 createprocess python console 就卡死了。stdin 和 stdout 也没用。所以想干脆内嵌一个,不用 createprocess 了。
    有好的想法可以提一提。
    18 条回复    2018-11-26 15:58:19 +08:00
    darkcode
        1
    darkcode  
       2018-11-25 22:47:20 +08:00
    什么交互?
    lsj8924
        2
    lsj8924  
    OP
       2018-11-25 22:52:50 +08:00
    @darkcode 就是一条条的输入命令,然后一条条输出结果。
    ysc3839
        3
    ysc3839  
       2018-11-25 23:58:17 +08:00 via Android
    你需要实现的是 Pseudo Console,但是在 Win10 1809 才正式支持 Pseudo Console API。
    要在旧的系统中实现的话:
    要不然参照 ConEmu 去读取 Console 的内容。
    要不然直接将程序的 stdin 和 stdout 重定向到管道。
    不过管道这个方法可能会有问题,因为有的程序会调用 Console API 进行一些操作,重定向到管道之后这些操作会失败。
    ysc3839
        4
    ysc3839  
       2018-11-26 00:01:05 +08:00 via Android   ❤️ 1
    我觉得更好的做法是通过别的途径进行交互,而不要使用 Console。
    Northxw
        5
    Northxw  
       2018-11-26 00:18:17 +08:00 via Android
    还有这种操作? 看来是我太 low 了
    watzds
        6
    watzds  
       2018-11-26 00:26:53 +08:00 via Android
    现在还用 mfc 的吗,我八年前学的时候就是老东西了,就是觉得复杂
    nifury
        7
    nifury  
       2018-11-26 01:03:34 +08:00   ❤️ 1
    404neko
        8
    404neko  
       2018-11-26 02:43:31 +08:00   ❤️ 1
    你想要的东西应该是 REPL

    首先你要会写一个 Shell {
    ` Shell 的本质是
    ` 从 stdin 按行读取用户输入
    ` 解析
    ` 做出反应
    ` 回到第一步
    }

    然后再在 Shell 基础上实现 REPL

    如何写一个 Python 的 REPL {
    ` 可以参考 Python 的官方[开发]文档 和 Python.h 中的定义
    ` 举个栗子
    ` PyObject *pModule,*pFunc;
    ` PyObject *pArgs, *pValue;
    `
    ` pModule = PyImport_Import(PyString_FromString("random")); //import random
    `
    ` pFunc = PyObject_GetAttrString(pModule, "random"); //获取 random.random
    `
    ` pArgs = NULL;
    `
    ` pValue = PyObject_CallObject(pFunc, pArgs); //调用 random.random()
    `
    ` res = PyInt_AsLong(pValue); //res 即是 random.random() 的返回值
    `
    ` 按照上边的
    ` 如果用户输出了 import string 那么就应该执行 pModule = PyImport_Import(PyString_FromString("string"));
    }

    其余的就算逻辑问题 就不多说了
    404neko
        9
    404neko  
       2018-11-26 02:44:57 +08:00
    V 站怎么还吞空格的
    kios
        10
    kios  
       2018-11-26 08:02:27 +08:00 via iPhone
    试试 PythonQt 我前一个月用过 和你的需求一模一样
    zwh2698
        11
    zwh2698  
       2018-11-26 08:06:00 +08:00 via Android   ❤️ 2
    直接将命令行的黑框的父窗体设置为 MFC 窗体,这个简单方便
    LokiSharp
        12
    LokiSharp  
       2018-11-26 08:16:08 +08:00 via iPhone
    用 PyQt 写吧 MFC 已经凉了很久了
    kios
        13
    kios  
       2018-11-26 08:23:32 +08:00
    @404neko 大佬~
    lsj8924
        14
    lsj8924  
    OP
       2018-11-26 12:04:54 +08:00
    统一回复:感谢楼上各位大佬。
    ksedz
        15
    ksedz  
       2018-11-26 14:22:10 +08:00
    不需要代码提示的话人工读入,然后按行 eval 就行。有更高的要求应该可以考虑从 ipython notebook 相关内容着手。
    arzterk
        16
    arzterk  
       2018-11-26 14:52:27 +08:00
    没那么复杂,我就搞过 mfc 嵌入 putty.exe ,代码如下:
    HANDLE hProcess = CreateProcess(...); // 用 cmd.exe /K python.exe 启动
    if (NULL == hProcess)
    {
    CMessageBox::Show(this, _T("FAILED TO START PUTTY"),
    CTextTraits::EMPTY_STRING, IDOK, MB_OK);
    return FALSE;
    }
    if (0 == ::WaitForInputIdle(hProcess, 1000))
    {
    m_wndTelnet = GetProcessMainWnd((DWORD)iProcessID);
    }
    CWnd *pWndPos = GetDlgItem(IDC_STATIC_POSITION); //嵌入的位置标识 ID
    if (NULL == pWndPos)
    {
    return FALSE;
    }
    CRect rcPos;
    pWndPos->GetWindowRect(&rcPos);
    ScreenToClient(&rcPos);
    rcPos.DeflateRect(1, 12, 1, 1);
    ::SetParent(m_wndTelnet, m_hWnd); // 窗口句柄 m_wndTelnet.
    ::SetWindowPos(m_wndTelnet, NULL, rcPos.left, rcPos.top
    , rcPos.Width(), rcPos.Height(), SWP_SHOWWINDOW);
    LONG lRet = ::SetWindowLong(m_wndTelnet, GWL_STYLE, 0x156B0000); // 这个魔术数字我也忘记啥意思了。
    直接就能在 cmd 里面输入参数了。。。。
    我还在外面搞了参数表格,拦截按钮事件把参数组好了往 m_wndTelnet 塞,
    ::SetForegroundWindow(hwndTelnet);
    for (int i = 0; i < strMsg.GetLength(); ++i)
    {
    TCHAR chKey = strMsg.GetAt(i);
    SHORT shTmp = VkKeyScan(chKey);
    BYTE byVKey = shTmp & 0xFF; // key
    bool bShift = (shTmp & 0x0100) == 0x0100 ? true : false;

    if (bShift)
    {
    keybd_event(VK_SHIFT, MapVirtualKey(VK_SHIFT, MAPVK_VK_TO_VSC), 0, 0);
    }

    keybd_event(byVKey, MapVirtualKey(byVKey, MAPVK_VK_TO_VSC), 0, 0);
    keybd_event(byVKey, MapVirtualKey(byVKey, MAPVK_VK_TO_VSC), KEYEVENTF_KEYUP, 0);

    if (bShift)
    {
    keybd_event(VK_SHIFT, MapVirtualKey(VK_SHIFT, MAPVK_VK_TO_VSC), KEYEVENTF_KEYUP, 0);
    }
    }
    Sleep(100);
    keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC), 0, 0);
    keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC), KEYEVENTF_KEYUP, 0);
    arzterk
        17
    arzterk  
       2018-11-26 14:59:10 +08:00
    后来我用 Boost.python 自己弄 C++/PY 互相调用玩,
    搭建了一个简单的图形学绘制框架,支持用脚本绘制 D2D,由于太懒就没搞很复杂。

    lz 可以参考里面互相调用的部分,自己做个 repl 编辑器,然后 cpp/py 互相传数据。

    https://github.com/Liudx1985/D2DGraph/tree/master/DrawGraph
    zwh2698
        18
    zwh2698  
       2018-11-26 15:58:19 +08:00 via Android
    @arzterk 就那句 setparent 是核心
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5492 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 01:36 · PVG 09:36 · LAX 18:36 · JFK 21:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.