【问题标题】:Integrating a click-Terminal in Tkinter?在 Tkinter 中集成点击终端?
【发布时间】:2019-03-26 15:21:46
【问题描述】:

我用 click 写了一个 CLI。现在我想知道是否可以使用 Tkinter 将其集成到 GUI 中。 我知道有一些方法可以将控制台嵌入到 Tkinter 中,但我想特别知道我的 CLI 是否可能在其中(通过“合理”的努力)。

所以我有一个脚本:

@cli.command()
def myfunction()
    print("My Stuff")

现在我想构建一个 Tkinter GUI,我有一个命令行,所以如果我在那里输入myfunction,它会调用并执行这个myfunction 这可能吗,还是我应该只使用 CLI 下的代码并用它构建一个独立于 CLI 的 Tkinter 应用程序?

【问题讨论】:

    标签: python python-3.x tkinter command-line-interface


    【解决方案1】:

    为了好玩,我刚刚制作了这个tkinterify,它可以满足您的要求。

    import tkinter
    import click
    import sys
    from io import StringIO
    
    
    def tkinterify(cli_group, app_name="Tkinterified App"):
    
        # Create and configure root
        root = tkinter.Tk()
        root.wm_title(app_name)
        tkinter.Grid.rowconfigure(root, 0, weight=1)
        tkinter.Grid.columnconfigure(root, 0, weight=1)
    
        # Create and configure frame
        frame = tkinter.Frame(root)
        frame.grid(row=0, column=0, sticky="nsew")
        frame.columnconfigure(0, weight=1)
        frame.columnconfigure(1, weight=1)
        frame.columnconfigure(2, weight=1)
        frame.rowconfigure(0, weight=1)
        frame.rowconfigure(1, weight=1)
    
        initial_output = "Valid commands:\n"
        initial_command_name_list = list(cli_group.commands.keys())
        for available_command_name in initial_command_name_list:
            initial_output = initial_output + "  " + available_command_name + "\n"
        initial_output = initial_output + "Ready for input."
    
        # Some GUI widgets
        run_string = tkinter.StringVar()
        entry_run = tkinter.Entry(root, textvariable=run_string, width=50)
        scrollbar_widget = tkinter.Scrollbar(root)
        text_widget = tkinter.Text(root)
    
        def clear_callback():
            # Because the text widget is usually disabled, we have to explicitly enable it before we can write to it.
            text_widget.config(state='normal')
            text_widget.delete(1.0, tkinter.END)
            text_widget.insert(tkinter.END, initial_output)
            text_widget.config(state='disabled')
    
        def run_callback():
            command_args = []
            try:
                command_parts = run_string.get().split()
                command_name = command_parts[0]
            except IndexError:
                return
            if len(command_parts) > 1:
                command_args = command_parts[1:]
    
            if command_name:
                try:
                    # Redirect stdout so we can read the output into a string for display within out GUI
                    real_stdout = sys.stdout
                    fake_stdout = StringIO()
                    sys.stdout.flush()
                    sys.stdout = fake_stdout
    
                    # Obtain list of available commands
                    available_commands = cli_group.commands
                    command_name_list = list(cli_group.commands.keys())
                    if command_name in command_name_list:
                        try:
                            # Make a fake context in which to run the command
                            context = available_commands[command_name].make_context("tkinter", command_args)
                            # Invoke the command within the fake context
                            available_commands[command_name].invoke(context)
                        except click.exceptions.UsageError as e:
                            print(e)
                            print(initial_output)
                    else:
                        print("Command not found.\n")
                        print(initial_output)
    
                    # Put stdout back
                    sys.stdout.flush()
                    sys.stdout = real_stdout
                    sys.stdout.flush()
                    output_string = fake_stdout.getvalue()
                    fake_stdout.close()
    
                    # Update the text output widget
                    text_widget.config(state='normal')
                    text_widget.delete(1.0, tkinter.END)
                    text_widget.insert(tkinter.END, output_string)
                    text_widget.config(state='disabled')
    
                except IndexError:
                    pass
    
        # More GUI widgets
        button_run = tkinter.Button(root, text="Run", command=run_callback)
        button_clear = tkinter.Button(root, text="Clear", command=clear_callback)
    
        text_widget.delete(1.0, tkinter.END)
        text_widget.insert(tkinter.END, initial_output)
    
        entry_run.grid(row=0, column=0, sticky="new")
        button_run.grid(row=0, column=1, sticky="n")
        button_clear.grid(row=0, column=2, sticky="n")
        text_widget.grid(row=1, column=0, columnspan=2, sticky="nsew")
        scrollbar_widget.grid(row=1, column=2, sticky="ns")
    
        scrollbar_widget.config(command=text_widget.yview)
        text_widget.config(yscrollcommand=scrollbar_widget.set)
        text_widget.config(state='disabled')
    
        root.mainloop()
    

    我已将其作为https://github.com/rbricheno/tkinterify 放在github 上,示例如下:

    import click
    from tkinterify import tkinterify
    
    
    @click.group()
    def cli():
        pass
    
    
    @click.command()
    def my_function():
        print("My Stuff")
    
    
    @click.command()
    def my_other_function():
        print("More Things")
    
    
    cli.add_command(my_function)
    cli.add_command(my_other_function)
    
    tkinterify(cli)
    

    如果您的脚本输出量大或在其他方面比较复杂,您可能需要修改它。我还没有测试过它如何处理参数。希望这能让你知道如何做你想做的事。

    主要技巧是将我们的click 命令添加到Group。添加后,我们可以使用the_group.commands中的字典轻松获取对它们的引用

    【讨论】:

    • 谢谢,这太棒了。由于我的 cli 已经是一个组,实际上它甚至不需要做太多工作:D
    猜你喜欢
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 2017-06-25
    • 2016-07-19
    • 1970-01-01
    • 1970-01-01
    • 2018-03-09
    • 1970-01-01
    相关资源
    最近更新 更多