如何在Tkinter应用程序上监听终端?

问题描述

我有一个简单的图形程序,可以在远程树莓派的触摸屏上显示一些指令和触摸按钮。

我不是通过直接执行而是通过SSH连接运行它,因此我的桌面应用程序日志中都有该记录。

我希望在运行脚本的控制台中缺少交互,例如执行某些功能或更改某些变量的值。

那有可能吗?

我不想在TKinter窗口中创建控制台,如alessandro所问: How to embed a terminal in a Tkinter application?

不确定是否应该使用短子进程(如user1941008),但htis似乎过于复杂 Write to terminal in Tkinter GUI

而且我确实不想为此创建一个客户端/服务器设置或中间缓冲区,因为太复杂了,我将不得不重写一些东西以将日志发送到新程序。

添加了一些我的代码的小版本:

#!/usr/bin/env python3
import tkinter as tk

class _tkapp(tk.Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widgets()

    def create_widgets(self):

        self.redButton = tk.Button(self,text='Make me red',command=self.paintMeRed)
        self.redButton.pack(side='top')

        self.blueButton = tk.Button(self,text='Make me blue',command=self.paintMeBlue)
        self.blueButton.pack(side='top')

        self.quit = tk.Button(self,text='QUIT',fg='red',command=self.master.destroy)
        self.quit.pack(side='bottom')

    def paintMeRed(self):
        tk_root.configure(background='red')
        print('user click on RED')

    def paintMeBlue(self):
        tk_root.configure(background='blue')
        print('user click on BLUE')


tk_root = tk.Tk()
tk_root.geometry("200x120") 
tk_app = _tkapp(master=tk_root)
tk_app.mainloop()

这使我可以在控制台上看到用户喜欢的内容, 我的目标,也可以从控制台更改颜色

解决方法

这是您问题的答案。我的其他(已删除)答案与您的问题不符。

不幸的是,没有套接字就无法做到,但是我向您的示例添加了一些易于调整的方法(init_remote_executionlistenerdo_remite_callcleanup可以复制并粘贴到您的实际应用程序中。您只需要改编do_remote_call方法:

#!/usr/bin/env python3
import socket
import tkinter as tk
from queue import Queue
from threading import Thread
from uuid import uuid1

UDP_HOST = ""
UDP_PORT = 5005
RECV_BUFFER = 1020


class _tkapp(tk.Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widgets()
        self.init_remote_execution()
        self.master.protocol("WM_DELETE_WINDOW",self.cleanup)  # call cleanup on exit

    def init_remote_execution(self):
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        self.sock.bind((UDP_HOST,UDP_PORT))
        self.endmsg = str(uuid1())  # Random string to stop threads
        self.queue = Queue()
        self.treads_running = True
        self.listener_thread = Thread(target=self.listener)
        self.worker_thread = Thread(target=self.do_remote_call)
        self.listener_thread.start()
        self.worker_thread.start()

    def listener(self):
        print("listen")
        while self.treads_running:
            data,addr = self.sock.recvfrom(RECV_BUFFER)
            data = data.decode().strip()
            print("from {addr}: {data}".format(addr=addr,data=data))
            if data == self.endmsg:
                self.treads_running = False
            self.queue.put(data)
        self.sock.close()

    def do_remote_call(self):
        while self.treads_running:
            data = self.queue.get()
            if data == self.endmsg:
                print("Bye")
            elif data == "click RED":
                self.paintMeRed()
            elif data == "click BLUE":
                self.paintMeBlue()
            else:
                print(">>> unknown command")

    def cleanup(self):
        print("cleanup")
        s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        s.sendto(self.endmsg.encode(),("127.0.0.1",UDP_PORT))
        self.listener_thread.join()
        self.worker_thread.join()
        self.master.destroy()

    def create_widgets(self):
        self.redButton = tk.Button(self,text="Make me red",command=self.paintMeRed)
        self.redButton.pack(side="top")

        self.blueButton = tk.Button(self,text="Make me blue",command=self.paintMeBlue)
        self.blueButton.pack(side="top")

        self.quit = tk.Button(
            self,text="QUIT",fg="red",command=self.cleanup
        )  # call cleanup!!!
        self.quit.pack(side="bottom")

    def paintMeRed(self):
        tk_root.configure(background="red")
        print("user click on RED")

    def paintMeBlue(self):
        tk_root.configure(background="blue")
        print("user click on BLUE")


if __name__ == "__main__":
    tk_root = tk.Tk()
    tk_root.geometry("200x120")
    tk_app = _tkapp(master=tk_root)
    tk_app.mainloop()

重要的是,您必须在退出按钮上调用cleanup方法(否则线程将不会停止并且应用程序将挂起)!

现在,您可以通过向树莓派上的端口5005发送udp消息(单击“蓝色”)来“单击”“让我变成蓝色”按钮。

通过对树莓的工具netcat的(你可能需要用apt安装它),你可以发送commad点击蓝色是这样的:

echo "click BLUE" | nc -uw0 127.0.0.1 5005

使用python:

#!/usr/bin/env python3
import socket


sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.sendto('click BLUE'.encode(),('127.0.0.1',5005)
sock.close()

您必须在桌面上用树莓派的IP地址切换“ 127.0.0.1”。

,

使用python中的cmd模块,这是一个更简单的版本。此模块可让您为任何事情编写一个repl(读取evaluete打印循环)。

#!/usr/bin/env python3
import cmd
import os
import signal
import tkinter as tk
from threading import Thread


class _tkapp(tk.Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widgets()

    def create_widgets(self):

        self.redButton = tk.Button(self,command=self.paintMeBlue)
        self.blueButton.pack(side="top")

        self.quit = tk.Button(self,command=self.master.destroy)
        self.quit.pack(side="bottom")

    def paintMeRed(self):
        tk_root.configure(background="red")
        print("user click on RED")

    def paintMeBlue(self):
        tk_root.configure(background="blue")
        print("user click on BLUE")


class Command(cmd.Cmd):
    intro = "Welcome to the repl of the Tk app.   Type help or ? to list  commands.\n"
    prompt = ""

    def __init__(self,tkapp):
        super().__init__()
        self.tkapp = tkapp

    # New commands must start with do_ and have one argument.
    # The docstring is the help text.
    # They must not return anything!
    # only the do_quit returns True to indicate that the command loop must stop
    def do_paint(self,arg):
        """Paint the background in the color red or blue:   PAINT RED"""
        clean_arg = arg.strip().lower()
        if clean_arg == "red":
            self.tkapp.paintMeRed()
        elif clean_arg == "blue":
            self.tkapp.paintMeBlue()
        else:
            print("Can't paint the color %s." % clean_arg)

    def do_quit(self,arg):
        """Stop the application:   QUIT"""
        self.tkapp.master.destroy()
        return True


if __name__ == "__main__":
    tk_root = tk.Tk()
    tk_root.geometry("200x120")
    tk_app = _tkapp(master=tk_root)

    command = Command(tk_app)
    command_thread = Thread(target=command.cmdloop)
    command_thread.start()

    tk_app.mainloop()

    os.kill(os.getpid(),signal.SIGTERM)

如果通过ssh运行程序,则可以直接输入命令help paintquit。 tk应用程序的所有输出也将在此处打印。

只需输入helphelp paint即可查看运行情况。