我可以使用 Tkinter 创建 Tcl 交互式 shell 吗?
Can I use Tkinter to create a Tcl interactive shell?
我目前正在使用 Python 3 中的 ctypes 制作一个 Tcl 应用程序,它包装了一个用 Python.
编写的库
from ctypes import *
import sys
tcl = cdll.LoadLibrary("libtcl.so")
TCL_OK = 0
TCL_ERROR = 1
def say_hello(cdata: c_void_p, interp: c_void_p, argc: c_int, argv: POINTER(c_char_p)) -> int:
"This function wraps some functionality that is written in Python"
print("hello, world")
return TCL_OK
# useful type definitions
Tcl_AppInitProc = CFUNCTYPE(c_int, c_void_p)
Tcl_CmdProc = CFUNCTYPE(c_int, c_void_p, c_void_p, c_int, POINTER(c_char_p))
Tcl_CmdDeleteProc = CFUNCTYPE(None, c_int)
def app_init(interp: c_void_p) -> int:
# initialize the interpreter
tcl.Tcl_Init.restype = c_int
tcl.Tcl_Init.argtypes = [c_void_p]
if tcl.Tcl_Init(interp) == TCL_ERROR:
return TCL_ERROR
# create custom commands
tcl.Tcl_CreateCommand.restype = c_void_p
tcl.Tcl_CreateCommand.argtypes = [c_void_p, c_char_p, Tcl_CmdProc, c_int, Tcl_CmdDeleteProc]
tcl.Tcl_CreateCommand(interp, b"say_hello", Tcl_CmdProc(say_hello), 0, Tcl_CmdDeleteProc(0))
return TCL_OK
if __name__ == "__main__":
# initialize argv
Argv = c_char_p * (1 + len(sys.argv))
argv = Argv(*(bytes(arg, "utf-8") for arg in sys.argv), 0)
# summon the chief interpreter
tcl.Tcl_Main.argtypes = [c_int, POINTER(c_char_p), Tcl_AppInitProc]
tcl.Tcl_Main(len(sys.argv), argv, Tcl_AppInitProc(app_init))
在命令行中,这就像一个带有额外命令的 Tcl 解释器,这正是我想要的。它解析 sys.argv
并作为交互式 shell 和 运行 Tcl 脚本工作。
bash$ python3 hello.py
% say_hello
hello, world
% ls
foo.tcl hello.py
% exit
bash$ cat foo.tcl
say_hello
bash$ python3 hello.py foo.tcl
hello, world
bash$
但是,我知道 Python 已经在 tkinter 模块中附带了一个 Tcl 解释器。相反,我想使用它,因为它已经有一个很好的 Python-wrapped API 并且可以节省我一些关于 ctypes 的麻烦。
我可以轻松地创建解释器和添加命令。
from tkinter import *
def say_hello():
print("hello, world")
if __name__ == "__main__":
tcl = Tcl()
tcl.createcommand("say_hello", say_hello)
tcl.eval("say_hello")
但我找不到任何调用 Tcl_Init
或 Tcl_Main
的方法,没有它们我就无法 运行 交互。虽然我不太关心命令行解析器,但尝试在 Python 中复制 Tcl 交互式 shell 及其所有功能(如 运行)将需要大量工作将外部程序当作 Tcl 命令(如上例中的 ls
)。如果那是我唯一的选择,我会坚持使用 ctypes。
有什么方法,即使是 hacky 或不受支持的方法,运行 Tkinter 附带的 Tcl 解释器作为交互式 shell?
Tcl_Main()
中实现的REPL真的很简单;你可以在 Tcl 的几行中做一个(稍微精简的)版本:
set cmd ""
set prompt "% "
while true {
# Prompt for input
puts -nonewline $prompt
flush stdout
if {[gets stdin line] < 0} break
# Handle multiline commands
append cmd $line "\n"
if {![info complete $cmd]} {
set prompt ""
continue
}
set prompt "% "
# Evaluate the command and print error/non-empty results
if {[catch $cmd msg]} {
puts stderr "Error: $msg"
} elseif {$msg ne ""} {
puts $msg
}
set cmd ""
}
您需要做的就是运行 Tcl 解释器中的Python 代码。您还可以在 Python 中重新实现大部分 REPL;那么你真正需要 Tcl 的唯一部分将是 info complete $cmd
(测试输入缓冲区中是否有完整的命令)和 catch $cmd msg
(评估 Tcl 解释器中的输入缓冲区并捕获结果和错误)。
我目前正在使用 Python 3 中的 ctypes 制作一个 Tcl 应用程序,它包装了一个用 Python.
编写的库from ctypes import *
import sys
tcl = cdll.LoadLibrary("libtcl.so")
TCL_OK = 0
TCL_ERROR = 1
def say_hello(cdata: c_void_p, interp: c_void_p, argc: c_int, argv: POINTER(c_char_p)) -> int:
"This function wraps some functionality that is written in Python"
print("hello, world")
return TCL_OK
# useful type definitions
Tcl_AppInitProc = CFUNCTYPE(c_int, c_void_p)
Tcl_CmdProc = CFUNCTYPE(c_int, c_void_p, c_void_p, c_int, POINTER(c_char_p))
Tcl_CmdDeleteProc = CFUNCTYPE(None, c_int)
def app_init(interp: c_void_p) -> int:
# initialize the interpreter
tcl.Tcl_Init.restype = c_int
tcl.Tcl_Init.argtypes = [c_void_p]
if tcl.Tcl_Init(interp) == TCL_ERROR:
return TCL_ERROR
# create custom commands
tcl.Tcl_CreateCommand.restype = c_void_p
tcl.Tcl_CreateCommand.argtypes = [c_void_p, c_char_p, Tcl_CmdProc, c_int, Tcl_CmdDeleteProc]
tcl.Tcl_CreateCommand(interp, b"say_hello", Tcl_CmdProc(say_hello), 0, Tcl_CmdDeleteProc(0))
return TCL_OK
if __name__ == "__main__":
# initialize argv
Argv = c_char_p * (1 + len(sys.argv))
argv = Argv(*(bytes(arg, "utf-8") for arg in sys.argv), 0)
# summon the chief interpreter
tcl.Tcl_Main.argtypes = [c_int, POINTER(c_char_p), Tcl_AppInitProc]
tcl.Tcl_Main(len(sys.argv), argv, Tcl_AppInitProc(app_init))
在命令行中,这就像一个带有额外命令的 Tcl 解释器,这正是我想要的。它解析 sys.argv
并作为交互式 shell 和 运行 Tcl 脚本工作。
bash$ python3 hello.py
% say_hello
hello, world
% ls
foo.tcl hello.py
% exit
bash$ cat foo.tcl
say_hello
bash$ python3 hello.py foo.tcl
hello, world
bash$
但是,我知道 Python 已经在 tkinter 模块中附带了一个 Tcl 解释器。相反,我想使用它,因为它已经有一个很好的 Python-wrapped API 并且可以节省我一些关于 ctypes 的麻烦。
我可以轻松地创建解释器和添加命令。
from tkinter import *
def say_hello():
print("hello, world")
if __name__ == "__main__":
tcl = Tcl()
tcl.createcommand("say_hello", say_hello)
tcl.eval("say_hello")
但我找不到任何调用 Tcl_Init
或 Tcl_Main
的方法,没有它们我就无法 运行 交互。虽然我不太关心命令行解析器,但尝试在 Python 中复制 Tcl 交互式 shell 及其所有功能(如 运行)将需要大量工作将外部程序当作 Tcl 命令(如上例中的 ls
)。如果那是我唯一的选择,我会坚持使用 ctypes。
有什么方法,即使是 hacky 或不受支持的方法,运行 Tkinter 附带的 Tcl 解释器作为交互式 shell?
Tcl_Main()
中实现的REPL真的很简单;你可以在 Tcl 的几行中做一个(稍微精简的)版本:
set cmd ""
set prompt "% "
while true {
# Prompt for input
puts -nonewline $prompt
flush stdout
if {[gets stdin line] < 0} break
# Handle multiline commands
append cmd $line "\n"
if {![info complete $cmd]} {
set prompt ""
continue
}
set prompt "% "
# Evaluate the command and print error/non-empty results
if {[catch $cmd msg]} {
puts stderr "Error: $msg"
} elseif {$msg ne ""} {
puts $msg
}
set cmd ""
}
您需要做的就是运行 Tcl 解释器中的Python 代码。您还可以在 Python 中重新实现大部分 REPL;那么你真正需要 Tcl 的唯一部分将是 info complete $cmd
(测试输入缓冲区中是否有完整的命令)和 catch $cmd msg
(评估 Tcl 解释器中的输入缓冲区并捕获结果和错误)。