I am building a GUI that requires me to log on to a remote computer via ssh. I am using paramiko to do this.

What I want to achieve is that a log in window is showed when the application is launched. The user has to put in some credentials. If the login is successful, then display the main window of the application. If the login fails, then remain at the login window.

If login succeeds, I want the ssh_client object to be passed on to the MainWindow class, so that the established connection can be used to perform tasks on the remote computer. However, how can I pass the ssh_client object to MainWindow?

The following code runs, but makes no attempt to use the established ssh_client. What could I do to be able to use the ssh_client from Login in MainWindow?

Perhaps I should just reestablish the connection in MainWindow - bu then I need to pass the credentials to MainWindow, which seems like the same kind of problem I am having right now.

import Tkinter as tk
import paramiko
import time


class Application(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        container = tk.Frame(self)
        container.grid(row=0, column=0, sticky="nsew")
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (Login, MainWindow):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(Login)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()


class Login(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.ssh_client = paramiko.SSHClient()
        self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        self.parent = parent
        self.controller = controller
        self.grid(row=0, column=0)

        self.user = tk.StringVar()
        self.user.set("my_username")  # Default user

        self.host_options = ["host1", "host2"]
        self.host = tk.StringVar()
        self.host.set(self.host_options[0])  # Default hostname

        l_user = tk.Label(self, text="Username: ")
        l_user.grid(row=0, column=0, sticky=tk.E)

        self.entry_user = tk.Entry(self)
        self.entry_user.grid(row=0, column=1, sticky=tk.W)
        self.entry_user.insert(0, self.user.get())

        l_pwd = tk.Label(self, text="Password: ")
        l_pwd.grid(row=1, column=0, sticky=tk.E)

        self.entry_pwd = tk.Entry(self, show="*")
        self.entry_pwd.grid(row=1, column=1, sticky=tk.W)

        l_host = tk.Label(self, text="Hostname: ")
        l_host.grid(row=2, column=0, sticky=tk.E)

        optionmenu_host = tk.OptionMenu(self, self.host, *self.host_options)
        optionmenu_host.grid(row=2, column=1, sticky=tk.W)

        b_login = tk.Button(self, text="Log in", command=self.authorize)
        b_login.grid(row=3, column=0, sticky=tk.W)

        b_quit = tk.Button(self, text="Quit", command=self.parent.destroy)
        b_quit.grid(row=4, column=0, sticky=tk.W)

    def authorize(self):
        try:
            self.ssh_client.connect(hostname=self.host.get(), username=self.entry_user.get(), password=self.entry_pwd.get())
            self.controller.show_frame(MainWindow)
        except paramiko.AuthenticationException:
            l_error = tk.Label(self, text="Login failed...", fg="red")
            l_error.grid(row=4, column=1, sticky=tk.W)
            l_error.after(2000, l_error.destroy)


class MainWindow(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.grid(row=0, column=0)

        l = tk.Label(self, text="Log in was successful!")
        l.grid(row=0, column=0, sticky=tk.W)


###################################
# run application
if __name__ == "__main__":
    app = Application()
    app.mainloop()
###################################

1 Answers

0
Novel On

Here's what I mean, plus a few other improvements:

  • tkinter never uses *args; you don't need that when you subclass.
  • tkinter uses **kwargs a lot; don't leave that off when you subclass!
  • there's no point to the container Frame ... it does nothing but add complexity
  • if you grid both frames than your window will always be the size of the biggest. To resize dynamically you need to RE-grid.
  • Using a frames dictionary is nice when you have a few dozen frames, but for only 2 I would just use a normal method.
  • Tkinter calls what you call "parent" the "master" and it sets self.master = master automatically, so you don't need that line.

I can't test this so let me know how it works.

import Tkinter as tk
import paramiko
import time

class Application(tk.Tk):
    def __init__(self, **kwargs):
        tk.Tk.__init__(self, **kwargs)

        self.ssh_client = paramiko.SSHClient()
        self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        self.login_window = Login(self)
        self.main_window = MainWindow(self)

        self.show_login() # set starting point

    def show_login(self):
        self.main_window.pack_forget()
        self.login_window.pack()

    def show_main(self):
        self.login_window.pack_forget()
        self.main_window.pack()

class Login(tk.Frame):
    def __init__(self, parent, **kwargs):
        tk.Frame.__init__(self, parent, **kwargs)

        self.user = tk.StringVar()
        self.user.set("my_username")  # Default user

        self.host_options = ["host1", "host2"]
        self.host = tk.StringVar()
        self.host.set(self.host_options[0])  # Default hostname

        l_user = tk.Label(self, text="Username: ")
        l_user.grid(row=0, column=0, sticky=tk.E)

        self.entry_user = tk.Entry(self)
        self.entry_user.grid(row=0, column=1, sticky=tk.W)
        self.entry_user.insert(0, self.user.get())

        l_pwd = tk.Label(self, text="Password: ")
        l_pwd.grid(row=1, column=0, sticky=tk.E)

        self.entry_pwd = tk.Entry(self, show="*")
        self.entry_pwd.grid(row=1, column=1, sticky=tk.W)

        l_host = tk.Label(self, text="Hostname: ")
        l_host.grid(row=2, column=0, sticky=tk.E)

        optionmenu_host = tk.OptionMenu(self, self.host, *self.host_options)
        optionmenu_host.grid(row=2, column=1, sticky=tk.W)

        b_login = tk.Button(self, text="Log in", command=self.authorize)
        b_login.grid(row=3, column=0, sticky=tk.W)

        b_quit = tk.Button(self, text="Quit", command=self.quit)
        b_quit.grid(row=4, column=0, sticky=tk.W)

    def authorize(self):
        try:
            self.master.ssh_client.connect(hostname=self.host.get(), username=self.entry_user.get(), password=self.entry_pwd.get())
            self.master.show_main()
        except paramiko.AuthenticationException:
            l_error = tk.Label(self, text="Login failed...", fg="red")
            l_error.grid(row=4, column=1, sticky=tk.W)
            l_error.after(2000, l_error.destroy)

class MainWindow(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        l = tk.Label(self, text="Log in was successful!")
        l.grid(row=0, column=0, sticky=tk.W)

        # you can use the ssh client like this:
        print(self.master.ssh_client)

        # or like this:
        self.ssh_client = self.master.ssh_client
        print(self.ssh_client)

###################################
# run application
if __name__ == "__main__":
    app = Application()
    app.mainloop()
###################################