如何在 Tkinter 应用程序上收听终端?
Posted
技术标签:
【中文标题】如何在 Tkinter 应用程序上收听终端?【英文标题】:How to listen to terminal on a Tkinter application? 【发布时间】:2020-12-26 13:50:28 【问题描述】:我有一个简单的图形程序,它在远程树莓的触摸屏上显示一些指令和触摸按钮。
我没有直接执行,而是通过 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()
这让我可以在控制台上看到用户点击了什么, 我的目标,也可以从控制台更改颜色
【问题讨论】:
您可以使用信号与正在运行的应用程序进行交互。 【参考方案1】:这是您问题的答案。我的其他(已删除)答案不适合您的问题。
不幸的是,如果没有套接字,您将无法做到这一点,但我在您的示例中添加了一些易于适应的方法(init_remote_execution
、listener
、do_remite_call
和 cleanup
),您可以在实际应用程序中复制和粘贴.只需要适配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
方法(否则线程将不会停止并且应用程序将挂起)!
现在您可以通过将 udp 消息(单击 BLUE)发送到您树莓的端口 5005 来“单击”“让我变蓝”按钮。
使用 raspberry 上的 netcat 工具(您可能需要使用 apt 安装它),您可以发送命令点击蓝色,如下所示:
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()
在您的桌面上,您必须将“127.0.0.1”切换为您的树莓派的 IP 地址。
【讨论】:
【参考方案2】:这里有一个更简单的版本,使用 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, 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")
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
paint
和quit
。 tk 应用程序的所有输出也将在此处打印。
只需输入help
或help paint
即可查看结果。
【讨论】:
以上是关于如何在 Tkinter 应用程序上收听终端?的主要内容,如果未能解决你的问题,请参考以下文章
我如何使用python将终端输出到网格中的tkinter框架?
如何从Python Tkinter应用程序中捕获任何输出到控制台?
如何在实时代码应用程序中的Mac OS X上收听特定的文本字符串
如何使用 tkinter 在 python 中嵌入 python 解释器框架?