Search code examples
pythontkintercombobox

Show combobox drop down while editing text using tkinter


Is it possible to make Combobox editable while dropdown is opened? I haven't found any solution. I want to make it more like Google Search but using ComboBox.


Solution

  • Question: Show Combobox PopdownWindow, while editing text

    This example extends a ttk.Combobox to the following:

    • Show the PopdownWindow while typing
    • Open the PopdownWindow on Key-pressed '<Down>'
    • Close the PopdownWindow on Key-pressed '<Up', if at the first item in the Listbox

    Reference:


    1. Inherit from ttk.Combox

      import tkinter as tk
      import tkinter.ttk as ttk
      
      
      class Combobox(ttk.Combobox):
      
    2. Helper function, to map the internal Toplevel and Listbox to a tkinter object.

      WARNING: This uses Tk/Tcl internals, which could change without notice.
      This may working only with the tested Tk/Tcl version!

      def _tk(self, cls, parent):
          obj = cls(parent)
          obj.destroy()
          if cls is tk.Toplevel:
              obj._w = self.tk.call('ttk::combobox::PopdownWindow', self)
          else:
              obj._w = '{}.{}'.format(parent._w, 'f.l')
          return obj
      
    3. Initalize the object, get the internal references and bind to Key-press events

      def __init__(self, parent, **kwargs):
          super().__init__(parent, **kwargs)
          self.popdown = self._tk(tk.Toplevel, parent)
          self.listbox = self._tk(tk.Listbox, self.popdown)
      
          self.bind("<KeyPress>", self.on_keypress, '+')
          self.listbox.bind("<Up>", self.on_keypress)
      
    4. Key-pressed handler to show or hide the PopdownWindow and set the Keyboard focus.

      def on_keypress(self, event):
          if event.widget == self:
              state = self.popdown.state()
      
              if state == 'withdrawn' \
                      and event.keysym not in ['BackSpace', 'Up']:
                  self.event_generate('<Button-1>')
                  self.after(0, self.focus_set)
      
              if event.keysym == 'Down':
                  self.after(0, self.listbox.focus_set)
      
          else:  # self.listbox
              curselection = self.listbox.curselection()
      
              if event.keysym == 'Up' and curselection[0] == 0:
                  self.popdown.withdraw()
      
      

      Usage:

      class App(tk.Tk):
          def __init__(self):
              super().__init__()
      
              values = ('one', 'two', 'three', 'four', 'five', 'six', 'seven')
      
              self.cb = Combobox(self, value=values)
              self.cb.grid(row=0, column=0)
      
      
      if __name__ == "__main__":
          App().mainloop()
      
      

      Tested with Python: 3.5 - 'TclVersion': 8.6 'TkVersion': 8.6