Могу ли я использовать Tkinter для создания интерактивной оболочки Tcl? - PullRequest
2 голосов
/ 07 ноября 2019

В настоящее время я использую ctypes в Python 3 для создания приложения 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 и работает как интерактивная оболочка и для запуска сценариев 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 поставляется с интерпретатором Tcl уже в модуле tkinter. Вместо этого я бы хотел использовать это, потому что у него уже есть хороший API-интерфейс, заключенный в Python, и он мог бы сэкономить немного времени на 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, и без них я не могу запустить его в интерактивном режиме. Хотя меня не волнует анализатор командной строки, было бы много работы, чтобы попытаться воспроизвести интерактивную оболочку Tcl в Python со всеми ее функциями, такими как запуск внешних программ, как если бы они были командами Tcl (такими как ls в приведенном выше примере). Если это мой единственный вариант, я просто буду использовать ctypes.

Есть ли способ, даже хакерский или неподдерживаемый, запустить интерпретатор Tcl, который поставляется с Tkinter в качестве интерактивной оболочки?

1 Ответ

3 голосов
/ 07 ноября 2019

REPL, реализованный в Tcl_Main(), действительно очень прост;Вы можете сделать его (слегка урезанную) в несколько строк 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. Вы также можете переопределить большую часть REPL в Python;единственными частями, для которых вам действительно нужен Tcl, будут info complete $cmd (проверка, есть ли полная команда во входном буфере) и catch $cmd msg (оценка входного буфера в интерпретаторе Tcl и отслеживание результатов и ошибок).

...