title: Python tk. Setting widget focus when using tab key
body: <p>I have a GUI with a column of buttons on the left side, text entry box on the right and a footer with some generic GUI buttons (new/delete/save/exit).  The buttons on the left side each feed a set of words to the text entry widget on the right.  I'd like to be able to use control-tab or control-shift-tab when in the text entry widget and return focus to the button feeding the text entry widget.  I've googled, searched and experimented but I'm not able to find a way to force focus on a specific button after using a tab key combination.</p>
  
  <pre><code>from tkinter import *
  import decimal
  
  class category_rule():
      def __init__(self,hi_range,low_range,word_list,rule,row):
          self.hi_range = decimal.Decimal(hi_range)
          self.low_range = decimal.Decimal(low_range)
          self.word_list = sorted(word_list)
          self.rule = rule
          self.button_row = row
          self.rule_button = None
          self.rule_entry_window=None
  
  def build_rules():
      rule_dict = {}
      rule_index = {}
      words = []
  
      rule='Fruit'
      words=['Apple','Banana','Strawberry']
      row=0 
      rule_dict[rule] = category_rule('9999.99','0.00',words,rule,row)
      rule_index[row] = rule
  
      rule = 'Vegetable'
      words=['Asparagus','Brussel Sprouts','Kale','Tomato','Spinach']
      row=1
      rule_dict[rule] = category_rule('9999.99','0.00',words,rule,row)
      rule_index[row] = rule
  
      rule = 'Dessert'
      words=['Cake','Pudding','Cookies','Pie']
      row=2
      rule_dict[rule] = category_rule('9999.99','0.00',words,rule,row)
      rule_index[row] = rule
  
      return rule_dict, rule_index
  
  
  class rule_entry_window:
      active_index = 0
      active_rule = None
      first_rule=None
      button_row=0
      rule_dict = {}
      rule_index = {}
  
  
      ''' methods to create the window, build the frames add the widgets '''
      def init_window_and_frames(self):   
          self.win = Tk()
          self.bg_color = '#134E9C'
          self.text_entry_frame = Frame(self.win, width=150, height=380, bg='#DDDECE', padx=1, pady=1, borderwidth=1)
          self.button_frame_outer = Frame(self.win, width=150, height=300, bg=self.bg_color, padx=3, pady=3, borderwidth=1)
          self.button_frame = Frame(self.button_frame_outer, width=150, height=300, bg = self.bg_color, padx=3, pady=3, borderwidth=1)
          self.range_entry_frame = Frame(self.win, width=150, height=40, bg=self.bg_color, padx=3, pady=3, borderwidth=1)
          self.footer_frame = Frame(self.win, width=480, height=30, bg=self.bg_color, padx=3, pady=3, borderwidth=1)
  
          self.button_frame.grid(row=0,column=0,sticky='nesw')
          self.button_frame_outer.grid(rowspan=2,column=0,sticky='nsew')
          self.text_entry_frame.grid(row=1,column=1,sticky='s')
  
          self.range_entry_frame.grid(row=0,column=1,sticky='new')
          self.footer_frame.grid(row=2,columnspan=2,sticky='ew')
          self.button_frame_outer.columnconfigure(0, minsize=200, weight=1)
          self.button_frame_outer.rowconfigure(0, weight=1)
  
      def init_rule_widgets(self):
          self.scrollbar = Scrollbar(self.text_entry_frame)
          self.text_entry = Text(self.text_entry_frame, width=40, height=33, wrap="word",
                     yscrollcommand=self.scrollbar.set, bg='#DDDECE',
                     borderwidth=0, highlightthickness=0)
          self.text_entry.delete('1.0', END)
          self.text_entry.grid(row=0,column=0,padx=2,pady=2)
  
          self.text_entry.bind('&lt;Control-Tab&gt;',self.text_tab)
          self.text_entry.bind('&lt;Control-Shift-Tab&gt;',self.text_tab)
  
          self.scrollbar.grid(row=0,column=1,sticky='ns')
  
          self.low_range_label = Label(self.range_entry_frame, text='Low Range',background=self.bg_color,fg='#ffffff')
          self.low_range_label.grid(row=0, column=0,sticky='e')
          self.low_range_entry = Entry(self.range_entry_frame, background='#DDDECE')
          self.low_range_entry.grid(row=0,column=1)
  
          self.hi_range_label = Label(self.range_entry_frame, text='High Range',background=self.bg_color,fg='#ffffff')
          self.hi_range_label.grid(row=1, column=0,sticky='e')
          self.hi_range_entry = Entry(self.range_entry_frame, background='#DDDECE')
          self.hi_range_entry.grid(row=1,column=1)        
  
      def init_footer_buttons(self):
          self.new_rule_button = Button(self.footer_frame, width=10, height=1, padx=2, pady=1, name='new button',
                                   bg=self.bg_color, fg='#ffffff', bd=1, text='New', command=self.new_rule)
          self.footer_frame.grid_columnconfigure(2, weight=0)
          self.new_rule_button.grid(row=0,column=0,sticky='w')
  
          self.delete_rule_button = Button(self.footer_frame, width=10, height=1, padx=2, pady=1, name='delete button',
                                   bg=self.bg_color, fg='#ffffff', bd=1, text='Delete', command=self.delete_rule)
          self.footer_frame.grid_columnconfigure(2, weight=0)
          self.delete_rule_button.grid(row=0,column=1,sticky='w')
  
          self.exit_button = Button(self.footer_frame, width=10, height=1, padx=2, pady=1, name='exit_button',
                                   bg=self.bg_color, fg='#ffffff', bd=1, text='Exit', command=self.exit)
          self.footer_frame.grid_columnconfigure(2, weight=1)
          self.exit_button.grid(row=0,column=3,sticky='e')
  
          self.save_button = Button(self.footer_frame, width=10, height=1, padx=2, pady=1, name='save button',
                                   bg=self.bg_color, fg='#ffffff', bd=1, text='Save', command=self.save_rule)
          self.footer_frame.grid_columnconfigure(2, weight=1)
          self.save_button.grid(row=0,column=4,sticky='e')
  
      def add_rule_button(self,rule):
          self.rule_dict[rule].rule_button = Button(self.button_frame, name='b'+rule, width=30, height=1, justify=LEFT, padx=0, pady=2,
                                                    font=('Arial', 9), fg='#ffffff',bg='#134E9C', bd=0, anchor='nw',text=rule,
                                                    highlightcolor='#134E9C',command=lambda r=rule: self.fill_text_entry_window(r))
          self.rule_dict[rule].rule_button.grid(row=self.rule_dict[rule].button_row,column=0,sticky='n')
  
          self.rule_dict[rule].rule_button.bind('&lt;Tab&gt;',self.button_tab)
          self.rule_dict[rule].rule_button.bind('&lt;Shift-Tab&gt;',self.button_tab)
          self.rule_dict[rule].rule_button.bind('&lt;Return&gt;',self.focus_text_entry)
          self.rule_dict[rule].rule_button.bind('&lt;Right&gt;',self.focus_text_entry)
          self.rule_dict[rule].rule_button.bind('&lt;Up&gt;',self.button_uparrow)
          self.rule_dict[rule].rule_button.bind('&lt;Down&gt;',self.button_downarrow)
  
  
      ''' init.  call the methods to build the window, set the active button and call mainloop '''
      def __init__(self):      
  
          self.init_window_and_frames()
          self.init_rule_widgets()
          self.init_footer_buttons()
  
          self.rule_dict,self.rule_index = build_rules()
          for rule in self.rule_dict:
              self.add_rule_button(rule)
  
          self.active_index = 0
          self.active_rule = None
          self.fill_text_entry_window(self.rule_index[self.active_index])
  
          self.win.mainloop()
  
      ''' methods to process activity from the widgets '''
      @classmethod
      def new_rule(cls):
          print('new rule - Not written yet')
  
      @classmethod
      def delete_rule(cls):
          print('delete rule - Not written yet')
  
      @classmethod
      def exit(cls):
          print('exit - Not written yet')
  
      @classmethod
      def save_rule(cls):
          print('save - Not written yet')
  
      ''' process keyboard events'''
      def button_uparrow(self,event):
          if self.active_index==0:
              next_rule=self.rule_index[len(self.rule_index)-1]
          else:
              next_rule=self.rule_index[self.active_index-1]
          self.fill_text_entry_window(next_rule)
  
      def button_downarrow(self,event):
          if self.active_index+1==len(self.rule_index):
              next_rule=self.rule_index[0]
          else:
              next_rule=self.rule_index[self.active_index+1]
          self.fill_text_entry_window(next_rule)
  
      def button_tab(self,event):
          '''
          make the next button active
          event.state == 9 means the &lt;shift&gt; key is pressed.  8 is shift key is not pressed
          '''
          if event.state == 9:
              if self.active_index==0:
                  next_rule=self.rule_index[len(self.rule_index)-1]
              else:
                  next_rule=self.rule_index[self.active_index-1]
          else:
              if self.active_index==self.active_index==len(self.rule_index)-1:
                  next_rule=self.rule_index[0]
              else:
                  next_rule=self.rule_index[self.active_index+1]
  
          self.fill_text_entry_window(next_rule)
  
      def focus_text_entry(self,event):
          self.text_entry_frame.focus_set()
          self.text_entry.focus()
  
      def text_tab(self,event):
          '''
          process control-tab, control-shift-tab key combinations from text_entry widget.  
          Attempts to set focus back to the active button
           *** doesn't work
          '''
          self.update_rule()
  
          self.win.focus_set()
          active_rule=self.rule_index[self.active_index]
          self.footer_frame.tkraise()
          self.range_entry_frame.tkraise()
          ''' self.button_frame_outer.tkraise() '''
          self.button_frame.focus_force()
          self.button_frame.tkraise()
          self.button_frame_outer.tkraise()
          self.button_frame.focus_force()
          self.rule_dict[active_rule].rule_button.focus_force()
          self.rule_dict[active_rule].rule_button.focus_set()
          self.rule_dict[active_rule].rule_button.focus()
          self.fill_text_entry_window(active_rule)
  
      def fill_text_entry_window(self,selected_rule):
          if self.active_rule is None:
              self.active_rule = selected_rule
          elif self.active_rule is not selected_rule:
              self.update_rule()
  
          ''' remove highlight from the old button.  Add highlight to the new one '''
          self.rule_dict[self.active_rule].rule_button.config(bg='#134E9C',fg='#ffffff')
          self.active_rule = self.rule_dict[selected_rule].rule
          self.active_index = self.rule_dict[selected_rule].button_row
          self.rule_dict[self.active_rule].rule_button.config(bg='#ffffff',fg='#134E9C')
  
          ''' clear the entry widgets and add the information for the new button '''
          self.low_range_entry.delete(0,END)
          self.low_range_entry.insert(0,self.rule_dict[self.active_rule].low_range)
          self.hi_range_entry.delete(0,END)
          self.text_entry.delete('1.0', END)
          self.hi_range_entry.insert(0,self.rule_dict[self.active_rule].hi_range)
          for word in self.rule_dict[self.active_rule].word_list:
              self.text_entry.insert('insert', word+'\n')
  
          ''' *** attempt to set the focus on the new button. '''
          self.rule_dict[self.active_rule].rule_button.focus()
          self.rule_dict[self.active_rule].rule_button.focus_set()
          self.rule_dict[self.active_rule].rule_button.focus_force()
  
      def update_rule(self):
          ''' save any changes made to the active rule before changing to the next one '''
          self.rule_dict[self.active_rule].word_list = sorted(self.text_entry.get(1.0,END).rstrip().split('\n'))
          self.rule_dict[self.active_rule].hi_range = decimal.Decimal(self.hi_range_entry.get())
          self.rule_dict[self.active_rule].low_range = decimal.Decimal(self.low_range_entry.get())
  
  
  if __name__ == '__main__':    
      rule_entry_window()
  </code></pre>
  
  <p>text_tab and fill_text_entry_window are the methods I'm using to try and set the focus back on the selected button.  I haven't found a combination of tkraise and focus/focus_force/focus_set that sets the focus back to the button I'd like it to be on.  What can I do to force the focus back onto the button?</p>