[Tutor] Searching db while typing on Entry widget

Peter Otten __peter__ at web.de
Tue Jan 17 05:12:17 EST 2017


Ali Moradi wrote:

> Hi. i want to search one field of my db while i type text in my Entry
> widget. how can i do that? 

Use a StringVar and set a callback with its trace() method:

http://effbot.org/tkinterbook/variable.htm


> and another problem is that now when i click on
> one of the item lists it always writes the first field of my db in the
> Text widget, i think i did the loop wrong maybe but i am not sure.

As Alan says, either use fetchall() or iterate over the cursor

What you do:

# wrong!
for row in cursor:
    # fist iteration: row is now the first match
    cursor.fetchall()  # iterates over the remaining matches 
                       # and discards them. Therefore there are no
                       # iterations for the containing loop.

    insert_into_text(row)


I dabbled with your code and will give my version below because there are 
enough changes that a diff will be hard to read. If the database table is 
big updating the Text widget directly in the trace callback will get slow, 
but for the small sample data that I used it worked sufficiently well. One 
simple quick fix would be to require a minimum length of the search string, 
but if you want to do it right you have to move the search into a separate 
thread and populate the Text widget from that.


import sqlite3 as sqlite
import tkinter as tk

from tkinter import Entry, Scrollbar, Text
from tkinter import ttk


class GeologyDict:
    def __init__(self, master):
        master.title("EsperantoDict")
        master.resizable(False, False)
        master.configure(background='#EAFFCD')

        self.style = ttk.Style()
        self.style.configure("TFrame", background='#EAFFCD')
        self.style.configure("TButton", background='#EAFFCD')
        self.style.configure("TLabel", background='#EAFFCD')

        self.frame_header = ttk.Frame(master, relief=tk.FLAT)
        self.frame_header.pack(side=tk.TOP, padx=5, pady=5)

        self.logo = tk.PhotoImage(file=r'C:\Geologydict\eo.png')
        self.small_logo = self.logo.subsample(10, 10)

        label = ttk.Label(self.frame_header, image=self.small_logo)
        label.grid(
            row=0, column=0, stick="ne",
            padx=5, pady=5, rowspan=2
        )
        label = ttk.Label(
            self.frame_header, text='EsperantoDict',
            font=('Arial', 18, 'bold')
        )
        label.grid(row=0, column=1)

        self.frame_content = ttk.Frame(master)
        self.frame_content.pack()

        self.entry_var = tk.StringVar()

        self.entry_search = ttk.Entry(
            self.frame_content,
            textvariable=self.entry_var
        )
        self.entry_search.grid(row=0, column=0)
        self.entry_search.insert(tk.END, "Type to Search")
        self.entry_search.bind('<Button-1>', self.entry_delete)

        self.button_search = ttk.Button(
            self.frame_content, text="Search"
        )
        aks = tk.PhotoImage(file=r'C:\Geologydict\search.png')
        small_aks = aks.subsample(3, 3)
        self.button_search.config(image=small_aks, compound=tk.LEFT)
        self.button_search.grid(row=0, column=1, columnspan=2)

        self.listbox = tk.Listbox(self.frame_content, height=28)
        self.listbox.grid(row=1, column=0)
        self.scrollbar = ttk.Scrollbar(
            self.frame_content,
            orient=tk.VERTICAL,
            command=self.listbox.yview
        )
        self.scrollbar.grid(row=1, column=1, sticky='ns')
        self.listbox.config(yscrollcommand=self.scrollbar.set)
        self.listbox.bind('<<ListboxSelect>>', self.listbox_select)

        self.textbox = tk.Text(self.frame_content, width=60, height=27)
        self.textbox.grid(row=1, column=2)

        self.db = sqlite.connect(r'C:\Geologydict\test.db')
        self.cur = self.db.cursor()
        self.cur.execute('SELECT Esperanto FROM words')
        for row in self.cur:
            self.listbox.insert(tk.END, row)

        self.entry_var.trace("w", self.trace_entry)

    def trace_entry(self, *args):
        try:
            text = self.entry_var.get()
            self.enter_meaning(text, exact=False)
        except Exception as err:
            print(err)

    def listbox_select(self, event):
        text = self.listbox.get(
            self.listbox.curselection()[0]
        )[0]
        self.enter_meaning(text, exact=True)

    def enter_meaning(self, token, exact=False):
        if exact:
            sql = (
                'SELECT Esperanto, English '
                'FROM words '
                'WHERE Esperanto = ?'
            )
        else:
            sql = (
                'SELECT Esperanto, English '
                'FROM words '
                'WHERE Esperanto like ?'
            )
            token = "%" + token.replace("%", "%%") + '%'
        self.cur.execute(sql, (token,))
        self.textbox.delete("1.0", tk.END)
        text = "\n\n".join(
            "{}. {} — {}".format(index, *row)
            for index, row in enumerate(self.cur, 1)
        )
        self.textbox.insert(tk.END, text)

    def entry_delete(self, tag):
        self.entry_search.delete(0, tk.END)
        return None


def main():
    root = tk.Tk()
    geologydict = GeologyDict(root)
    root.mainloop()


if __name__ == '__main__':
    main()




More information about the Tutor mailing list