I am doing the O'reilly School of Technology course and the current topic deals with Tkinter. It gave me the idea to write this application.

The idea is to transfer files to various ftp sites. If I set a default master password, then the password should be used for all sites. If I click the add/remove button it should add/remove a line for the next sites details. Ideaily the remove button should remove the current site from the list, right now it removes the last site(or trys to anyway). Not sure how to keep track of that.

I am having problems with the following:

1) Having the master passward update all site password entries
2) The add/remove looses count and dosen't always remove the sites(say if I add 4+ entries)

I have no idea what's wrong, any help would be appreciated.

#!/usr/bin/env python
from Tkinter import *

class Application(Frame):

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.grid(sticky=N+S+E+W)
        self.master.rowconfigure(0, weight=1)
        self.master.columnconfigure(0, weight=1)
        self.master.title('Bulk FTP Updater')
        self.createShell()

        self.toggle_pass = 1

    def createShell(self):
        self.e_master_var = StringVar()
        self.use_master_p = IntVar()
        
        self.l_master = Label(self, text='Master Password:')
        self.e_master = Entry(self, textvariable=self.e_master_var)
        self.c_master = Checkbutton(self, textvariable=self.use_master_p,
                                    command=self.use_master_pass)
        self.l2_master = Label(self, text='Set as default')
        self.blank = Label(self, text='', pady=20)
        
        self.l_master.grid(row=0, column=0, sticky=W)
        self.e_master.grid(row=0, column=1, sticky=W)
        self.l2_master.grid(row=1, column=0, sticky=E)
        self.c_master.grid(row=1, column=1, sticky=W)
        self.blank.grid(row=2, column=0)
        
        self.r = 3
        self.createSite(self.r)

    def use_master_pass(self):
        password = self.e_master_var.get()
        if self.toggle_pass:       
            for r in range(3, self.r+1):
                self.v_pass.set(password)
            self.toggle_pass = 0
        else:
            for r in range(3, self.r+1):
                self.v_pass.set('')
            self.toggle_pass = 1
        

    def createSite(self, r):
        self.l_host = Label(self, text='Site:', padx=10)
        self.l_user = Label(self, text='User:', padx=10)
        self.l_pass = Label(self, text='Password:', padx=10)

        self.v_host = StringVar()
        self.v_user = StringVar()
        self.v_pass = StringVar()
        
        self.e_host = Entry(self, textvariable=self.v_host)
        self.e_user = Entry(self, textvariable=self.v_user)
        self.e_pass = Entry(self, textvariable=self.v_pass)

        self.b_add = Button(self, text='Add', command=self.add, padx=10)
        self.b_del = Button(self, text='Remove', command=self.remove, padx=10)
        
        self.l_host.grid(row=r, column=0, sticky=E)       
        self.e_host.grid(row=r, column=1, sticky=E)
        self.l_user.grid(row=r, column=2, sticky=E)
        
        self.e_user.grid(row=r, column=3, sticky=E)
        self.l_pass.grid(row=r, column=4, sticky=E)
        self.e_pass.grid(row=r, column=5, sticky=E)
        self.b_add.grid(row=r, column=6, sticky=E)
        self.b_del.grid(row=r, column=7, sticky=E)
        print 'createSite:', r

    def add(self):
        self.r = self.r + 1
        self.createSite(self.r)
        print 'add:', self.r

    def remove(self):
        if self.r > 3:
            self.r = self.r - 1
            self.l_host.destroy()
            self.l_user.destroy()
            self.l_pass.destroy()
            self.e_host.destroy()
            self.e_user.destroy()
            self.e_pass.destroy()
            self.b_add.destroy()
            self.b_del.destroy()
        elif self.r < 3:
            self.r = 3
            
        print 'remove:', self.r
        
root = Tk()
app = Application(master=root)
app.mainloop()

You are going to have to store the widgets in a list or dictionary to do that, and tell remove() which one you want to delete. The following is a program that shows how to associate a button to a specific dictionary member by binding a unique instance of the function handler() to each button. You would use a function like cb_handler to destroy the appropriate widgets. You might also consider using a separate frame for each widget group and then destroying the single frame. Note that since you use the same variable for all widgets, self.l_host, etc. always contains the last widget created.

from Tkinter import *

class ButtonsTest:
   def __init__(self, top):
      self.button_dic = {}
      top.title('Buttons Test')
      self.top_frame = Frame(top, width =400, height=400)
      self.buttons()
      self.top_frame.grid(row=0, column=1)
      exit = Button(self.top_frame, text='Exit', command=top.quit).grid(row=10,column=1, columnspan=5)

   ##-------------------------------------------------------------------         
   def buttons(self):
      b_row=1
      b_col=0
      for j in range(1, 11):
         self.button_dic[j] = "self.cb_button_%d()" % (j)
         b = Button(self.top_frame, text = str(j))
         b.grid(row=b_row, column=b_col)

         def handler ( event, self=self, button_num=j ):
                return self.cb_handler( event, button_num )
         b.bind ( "<Button-1>", handler )

         b_col += 1
         if b_col > 4:
            b_col = 0
            b_row += 1

   ##----------------------------------------------------------------
   def cb_button_1(self):
      print "push button 1 and this code is executed"

   ##----------------------------------------------------------------
   def cb_button_2(self):
      print "push button 2 and this code is executed"

   ##----------------------------------------------------------------
   def cb_button_3(self):
      print "push button 3 and this code is executed"

   ##----------------------------------------------------------------
   def cb_handler( self, event, cb_number ):
      print "cb_handler", cb_number, self.button_dic[cb_number]                
      if cb_number < 4:
         exec(self.button_dic[cb_number])

##===================================================================
root = Tk()
BT=ButtonsTest(root)
root.mainloop()

Edited 6 Years Ago by woooee: n/a

This is what I came up, values button is for testing.

#!/usr/bin/env python
from Tkinter import *

class Application(Frame):

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.sites = {}
        self.siteno = 0
        self.master.title('Bulk FTP Updater')
        self.createShell()

    def createShell(self):
        frame = Frame(self)
        self.master_password, self.toggle_pass = StringVar(), IntVar()
        
        for widget in (Label(frame, text='Master Password:'),   Entry(frame, textvariable= self.master_password),
                       Label(frame, text='Set as default'),    Checkbutton(frame,
                                                                           variable= self.toggle_pass,
                                                                           command=self.use_master_pass)):
            widget.pack(side=LEFT)
        
        frame.pack(side=TOP)
        for i in range(3):
            self.create_site()
        Button(frame, text='Add', command=self.create_site, padx=50).pack(side=RIGHT)
        self.pack()

    def use_master_pass(self):
        if self.toggle_pass.get():
            for frame, tkvars in self.sites.values():
                tkvars[2].set(tkvars[2].get() or self.master_password.get())

    def create_site(self):
        frame = Frame(self)
        siteno = self.siteno = self.siteno + 1

        tkvars = StringVar(), StringVar(), StringVar()
        for label,entry in zip(('Site:', 'User:', 'Password:'), (Entry(frame, textvariable=tkvar) for tkvar in tkvars)):
            Label(frame, text=label, padx=10).pack(side=LEFT)
            entry.pack(side=LEFT)

        if self.toggle_pass.get():
            tkvars[2].set(self.master_password.get())
        
        Button(frame, text='Remove', command=lambda: self.remove(siteno), padx=10).pack(side=LEFT)
        Button(frame, textvariable=siteno, text='Values', command=lambda:self.value(siteno), padx=10).pack(side=LEFT)

        self.sites[siteno] = frame, tkvars
        frame.pack(side=TOP)
        print 'Created site', siteno

    def remove(self, siteno):
        if len(self.sites)>2:
            self.sites[siteno][0].destroy()
            del self.sites[siteno]
        print('Removed site %i' % siteno)

    def value(self, siteno):
        print('-'*40)
        print('Values for site %i' % siteno)
        for var in self.sites[siteno][1]:
            print var.get()

            
root = Tk()
app = Application(master=root)
print 'Entering main loop'
app.mainloop()

Wow, thanks for the responses guys. Tonyjv, that's exactly what I was trying to do but could not wrap my head around.

The couple of eye openers for me were:

- The clever use of zip() and tkvars
- Making self.siteno a local variable so I can delete the current site yet still keep track in a dictionary
- Using a frame for each row is way easier to remove/destroy()


I'm on my way to making my first useful gui. Thanks guys.

This question has already been answered. Start a new discussion instead.