Search code examples
pythontkintertcltk-toolkit

Tkinter - "can not find channel named "stdout"


So I'm receiving the error,

_tkinter.TclError: can not find channel named "stdout"

by running this code:

from tkinter import Tcl

tcl = Tcl()
tcl.eval('''
puts hello
''') 

For others it seems to work. I wonder if it is because I'm on windows and the distinction between console and gui application ? An interesting approach that I have found in a book, where they state it is possible to have a console and a window at the same time via console show, but this got me into another error:

_tkinter.TclError: invalid command name "console"

How to fix this ?


Solution

  • I have found a sufficient solutions by combining two of Bryan's outstanding answers and the tutorial that I watch. To summarize the code below:

    1. wrap a python function into a tcl proc via register
    2. overwrite the tcl puts command
    3. take a list as input and join the items with empty space between
    4. call the wrapped python function in tcl by its name returned by register

    from tkinter import Tcl
    
    
    tcl = Tcl()
    cmd = tcl.register(lambda inp:print(inp))
    tcl.eval(
        'proc puts {args} {' +
            f'{cmd} [join $args " "]' +
        '}')
    
    tcl.eval('puts "hello"') 
    

    Note: Whitespace is a styling choice and has no effect in this code. Only important thing to note is the f-string (formatted string). This needs to be encapsulated due the curly barces, otherwise tcl interprets it as list or python's format string protest about the curly braces.

    For clarification a normal function is used in the code below:

    from tkinter import Tcl
    
    def puts(inp):
        print(inp)
    
    tcl = Tcl()
    cmd = tcl.register(puts)
    tcl.eval(
        'proc puts {args} {' +
            f'{cmd} [join $args " "]' +
        '}')
    
    tcl.eval('puts "hello, world"')
    

    For gets you can do the same, but at exit it might throw an error because the interpreter can't close a connection to a function, as it expects a socket/file descriptor. Anyway, for playground:

    def gets():
        return input()
    
    cmd = tcl.register(gets)
    tcl.tk.eval(
        'proc gets {} {' +
            f'{cmd}' +
        '}')