简体   繁体   中英

Set Default Text for Autocomplete Entry Widget in Tkinter

I'm building a GUI using tkinter that has a few simple fields. I figured out how to default the values for them (I load these values from a .pickle file to a dictionary) using textvariable parameter of the Entry widget, but I built one of the fields to use autocompletion as the user is typing in a value. It's a separate class, and I used the code I found here .

I've tried a number of things to load the value in this widget upon launching the app like returning the value and using the set() method, but I've been unsuccessful. I either get errors or nothing happens. Values I enter into the widget do get successfully saved into my pickle file.

I'm still learning object oriented programming with python, so please be easy on me. So how could I modify the code below to display a default value?

from Tkinter import *
import re

lista = ['a', 'actions', 'additional', 'also', 'an', 'and', 'angle', 'are', 'as', 'be', 'bind','bracket', 'brackets', 'button', 'can', 'cases', 'configure', 'course', 'detail', 'enter', 'event', 'events', 'example', 'field', 'fields', 'for', 'give', 'important', 'in', 'information', 'is', 'it', 'just', 'key', 'keyboard', 'kind', 'leave', 'left', 'like', 'manager', 'many', 'match', 'modifier', 'most', 'of', 'or', 'others', 'out', 'part', 'simplify', 'space', 'specifier', 'specifies', 'string;', 'that', 'the', 'there', 'to', 'type', 'unless', 'use', 'used', 'user', 'various', 'ways', 'we', 'window', 'wish', 'you']


class AutocompleteEntry(Entry):
    def __init__(self, lista, *args, **kwargs):

        Entry.__init__(self, *args, **kwargs)
        self.lista = lista        
        self.var = self["textvariable"]
        if self.var == '':
            self.var = self["textvariable"] = StringVar()

        self.var.trace('w', self.changed)
        self.bind("<Right>", self.selection)
        self.bind("<Up>", self.up)
        self.bind("<Down>", self.down)

        self.lb_up = False

    def changed(self, name, index, mode):  

        if self.var.get() == '':
            self.lb.destroy()
            self.lb_up = False
        else:
            words = self.comparison()
            if words:            
                if not self.lb_up:
                    self.lb = Listbox()
                    self.lb.bind("<Double-Button-1>", self.selection)
                    self.lb.bind("<Right>", self.selection)
                    self.lb.place(x=self.winfo_x(), y=self.winfo_y()+self.winfo_height())
                    self.lb_up = True

                self.lb.delete(0, END)
                for w in words:
                    self.lb.insert(END,w)
            else:
                if self.lb_up:
                    self.lb.destroy()
                    self.lb_up = False

    def selection(self, event):

        if self.lb_up:
            self.var.set(self.lb.get(ACTIVE))
            self.lb.destroy()
            self.lb_up = False
            self.icursor(END)

    def up(self, event):

        if self.lb_up:
            if self.lb.curselection() == ():
                index = '0'
            else:
                index = self.lb.curselection()[0]
            if index != '0':                
                self.lb.selection_clear(first=index)
                index = str(int(index)-1)                
                self.lb.selection_set(first=index)
                self.lb.activate(index) 

    def down(self, event):

        if self.lb_up:
            if self.lb.curselection() == ():
                index = '0'
            else:
                index = self.lb.curselection()[0]
            if index != END:                        
                self.lb.selection_clear(first=index)
                index = str(int(index)+1)        
                self.lb.selection_set(first=index)
                self.lb.activate(index) 

    def comparison(self):
        pattern = re.compile('.*' + self.var.get() + '.*')
        return [w for w in self.lista if re.match(pattern, w)]

if __name__ == '__main__':
root = Tk()

entry = AutocompleteEntry(lista, root)
entry.grid(row=0, column=0)
Button(text='nothing').grid(row=1, column=0)
Button(text='nothing').grid(row=2, column=0)
Button(text='nothing').grid(row=3, column=0)

root.mainloop()

There is an existing AutocompleteEntry widget.

Example:

from ttkwidgets.autocomplete import AutocompleteEntry
import tkinter as tk

window = tk.Tk()

Label1 = tk.Label(window, text="Entry:")
Label1.pack(side='left', padx=10, pady=10)

test_values = ['one', 'two', 'three']

test_var = tk.StringVar(window)
test_var.set('Default Value')

Entry1 = AutocompleteEntry(window, width=20, textvariable=test_var, completevalues=test_values)
Entry1.pack(side='left', padx=10, pady=10)

window.mainloop()

Ok so after analyzing this code for a couple hours, I figured out how to get a value to default by using the insert() method. My first problem after doing this was the listbox is also active by default when the app is launched. By adding another if condition to the changed function, it won't open the listbox until the value is cleared. When I add the conditional statement, the line self.lb.destroy() throws an error when the field is cleared:

AttributeError: 'AutocompleteEntry' object has no attribute 'lb'

I'm not sure why this error is thrown since it worked before I added the condition. However, lb isn't defined anywhere else in the class or function. Removing the line fixed the error, and everything appears to work as expected. Here are the changes I made. If someone else has a better solution please let me know.

class AutocompleteEntry(Entry):
    # add settings argument which is True or False    
    def __init__(self, lista, settings, *args, **kwargs):

        Entry.__init__(self, *args, **kwargs)
        self.lista = lista
        # define the self.settings object
        self.settings = settings
        self.var = self["textvariable"]
        if self.var == '':
            self.var = self["textvariable"] = StringVar()
        self.var.trace('w', self.changed)
        self.bind("<Return>", self.selection)
        self.bind("<Up>", self.up)
        self.bind("<Down>", self.down)

        self.lb_up = False

    def changed(self, name, index, mode):

        if self.var.get() == '':
            # self.lb.destroy() - removed this line
            self.lb_up = False
            # change this variable to False once the field is cleared
            self.settings = False 
        # add if condition - field is not empty and settings is True
        elif self.var.get() != '' and self.settings == True:
            self.lb_up = False
        else:
            words = self.comparison()
            if words:
                if not self.lb_up:
                    self.lb = Listbox()
                    self.lb.bind("<Double-Button-1>", self.selection)
                    self.lb.bind("<Return>", self.selection)
                    self.lb.place(x=self.winfo_x(), y=self.winfo_y()+self.winfo_height())
                    self.lb_up = True

                self.lb.delete(0, END)
                for w in words:
                    self.lb.insert(END,w)
            else:
                if self.lb_up:
                    self.lb.destroy()
                    self.lb_up = False

And the last part:

if __name__ == '__main__':
    root = Tk()
    # define settings variable and add to AutoCompleteEntry arguments
    settings = True
    entry = AutocompleteEntry(lista, settings, root)
    entry.insert(END, "this is the default value")
    entry.grid(row=0, column=0)
    Button(text='nothing').grid(row=1, column=0)
    Button(text='nothing').grid(row=2, column=0)
    Button(text='nothing').grid(row=3, column=0)

    root.mainloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM