【发布时间】:2019-04-11 06:38:42
【问题描述】:
我正在尝试创建一个 tkinter GUI,它本质上将用作与远程 linux 计算机的 SSH 连接。 GUI 需要在 Windows 操作系统上正常运行。 GUI 有几个按钮,允许用户在 linux 计算机上启动和停止程序,但我在将远程程序的连续输出打印到 tkinter 时遇到问题。我本质上希望在 tkinter GUI 中有一个嵌入式窗口,它显示远程桌面的命令行输出。
在下面的代码中,我成功调用了我的 GUI 类并构建了 tkinter GUI。使用 paramiko 我可以成功建立 SSH 连接,但问题是当我按下“启动脚本”时。启动脚本通过 SSH 连接成功启动程序,但我无法将连续输出打印到 GUI(为了测试这一点,main_script.py 是一个基本程序,每 3 秒打印一个递增的数字)。我相信问题是我停止了 tkinter 主循环,但我不知道如何解决这个问题。我可以使用 tkinter.after 命令在延迟后更新窗口,但由于我读取标准输出的方式,我的脚本仍然停滞不前。有没有更好的方法来读取标准输出?我是否需要调用子进程或使用多线程(如果需要,当我想在 main_window 打开时建立 SSH 连接时,我该怎么做)。还是我错过了一种更简单的方法来本质上通过几个 GUI 按钮来实现远程桌面命令行输出?感谢您的帮助。
import paramiko
import tkinter as tk
class main_window():
def __init__(self, root2, ip, user, pword):
self.root2 = root2
# Connection Label
conn_label = tk.Label(root2, text=str('CONNECTED TO: ' + user + ' ' + 'ON: ' + ip))
conn_label.grid(row=0, columnspan=6, sticky=tk.W)
# Frequency Entery
freq_label = tk.Label(root2, text='Tag Frequency:')
freq_entry = tk.Entry(root2)
self.freq_enter_button = tk.Button(root2, text='Enter')
freq_label.grid(row=1, column=1, sticky=tk.E, padx=(5, 0))
freq_entry.grid(row=1, column=2, sticky=tk.W)
self.freq_enter_button.grid(row=1, column=3, padx=5)
# Scroll Bar and Command Line Ouput
self.scrollbar = tk.Scrollbar(root2)
self.scrollbar.grid(row=3, column=5, rowspan=1, sticky=tk.N+tk.S)
self.mylist = tk.Listbox(root2, yscrollcommand=self.scrollbar.set)
self.mylist.grid(row=3, column=0, columnspan=5, sticky=tk.W+tk.E+tk.N+tk.S)
# SSH Connection
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(ip, port=22, username=user, password=pword)
# stdin, stdout, stderr = ssh.exec_command('ls')
# for line in stdout:
# mylist.insert(tk.END, str('... ' + line.strip('\n')))
# ssh.close()
# Start Script Button
self.script_btn = tk.Button(root2, text="Start Script", command=self.toggle)
self.script_btn.grid(row=2, column=1, columnspan=3, sticky=tk.W+tk.E, padx=(10, 8), pady=2)
# Customize Grid Expansion
root2.columnconfigure(0, weight=1)
root2.columnconfigure(4, weight=1)
root2.rowconfigure(3, weight=1)
# Visual Touches
root2.title("Terminal Connection")
root2.wm_iconbitmap('logo_blue.ico')
def toggle(self):
def_color = self.freq_enter_button.cget('bg')
if self.script_btn.config('text')[-1] == 'Start Script':
stdin, self.stdout, stderr = self.ssh.exec_command('python main_script.py', get_pty=True)
self.mylist.insert(tk.END, str('Starting Script\n'))
self.script_btn.config(text='End Script', bg='red')
self.update_window()
else:
stdin, self.stdout, stderr = self.ssh.exec_command(chr(3))
self.mylist.insert(tk.END, str('Ending Script\n'))
self.script_btn.config(text='Start Script', bg=def_color)
def update_window(self):
for line in iter(lambda: self.stdout.readline(), ""):
self.mylist.insert(tk.END, str(line))
self.scrollbar.config(command=self.mylist.yview)
# self.root2.after(1000, self.update_window)
def main():
root = tk.Tk()
IP = '*********'
user = '*******'
pswd = '*******'
runGUI = main_window(root, IP, user, pswd)
root.title("Connect to Drone")
root.wm_iconbitmap('logo_blue.ico')
root.mainloop()
if __name__ == "__main__":
main()
【问题讨论】:
-
您可能会在调用另一台计算机时阻塞主循环。您可能想在此处考虑使用线程。这样您就可以与 tkinter 并行运行脚本,而不会阻塞主循环。
-
谢谢。这是我的假设,我只是不太确定如何解决它。线程或多处理更好吗?理想情况下,我希望在 main_window 打开时创建 paramiko 类(即立即建立连接),然后将此信息发送到新进程/线程,但在我尝试时收到“TypeError:无法腌制 thread.lock 对象”。
-
我很难为你建立一个例子,因为我以前没有做过这样的远程操作,但我敢打赌,如果你构建使用线程的函数,你将能够管理一旦你了解了如何在线程之间共享信息,tkinter 窗口和连接都没有问题。在这里查看一些关于此的资源。 How to share data between threads in this threaded TCPServer?
-
感谢您的资源。我将看看线程化我的代码。
标签: python python-2.7 tkinter paramiko