X Tutup
The Wayback Machine - https://web.archive.org/web/20260227014124/https://github.com/python/cpython/pull/23210
Skip to content

bpo-42305: Added Auto_Complete DropBox Suggestion For Tkinter#23210

Closed
RajvirSingh1313 wants to merge 15 commits intopython:mainfrom
RajvirSingh1313:master
Closed

bpo-42305: Added Auto_Complete DropBox Suggestion For Tkinter#23210
RajvirSingh1313 wants to merge 15 commits intopython:mainfrom
RajvirSingh1313:master

Conversation

@RajvirSingh1313
Copy link

@RajvirSingh1313 RajvirSingh1313 commented Nov 9, 2020

This is auto complete drop box suggestion wiget for tkinter

Tkinter Autocomplete

Text autocompletion provides relevant real-time results to users. Because tkinter does not provide a widget for adding autocompletion to GUIs out of the box, I decided to make one myself. This utility is compatible with and has been tested on Python 2.7.1 and Python 3.6.0.

Structure

NOTE: The Tkinter library for Python 2 and tkinter library for Python 3 will from now on be referred to as tk.

The AutocompleteEntry class (which can be found here)
derives from tk.Frame and is a container used to group a tk.Entry and tk.Listbox widget. Should you need to modify the widgets,
they can be accessed as (respectively) AutocompleteEntry s entry and listbox attributes.

The entry widget acts like a normal textbox. When activated, it binds <KeyRelease> to a private method which will update
the list of suggestions. The listbox widget contains the suggestions themselves. When activated, it binds <<ListboxSelect>> to a
private method which sets the entry widget to whatever value was selected.

Since an instance of AutocompleteEntry is a tk.Frame instance too, you can place it by calling its pack or grid methods with
their respective arguments.

Quickstart

NOTE: These examples will only run under Python 3. To make them Python 2-compatible, replace tkinter with Tkinter.

To add a new autocompletion frame to our interface, first initialize one:

import tkinter as tk

from tkinter import auto_complete

root = tk.Tk()

frame = tk.Frame(root)
frame.pack()

entry = auto_complete.AutocompleteEntry(frame)
# You can pass additional parameters to further customize the window;
# all parameters that you can pass to tk.Frame, are valid here too.

Now you need to configure the instance by passing it an iterable containing all autocompletion entries.

Do this by calling its build method:

ENTRIES = (
    "Foo",
    "Bar"
)

entry.build(ENTRIES)

You can pass additional arguments to build:

  • max_entries (integer):

    The maximum number of entries to display at once. This value directly corresponds to the listbox widget's height attribute. Defaults to 5.

  • case_sensitive (boolean):

    If True, only autocomplete entries that enforce the same capitalization as the current entry will be displayed.

    If False, all autocomplete entries that match with the current entry will be displayed.

    Defaults to False.

  • no_results_message (string or None):

    The message to display if no suggestions could be found for the current entry.

    This argument may include a formatting identifier ({}) which, at runtime, gets formatted as the current entry. If None is specified, the listbox will instead be hidden until the next <KeyRelease> event.

Let's play around with these arguments:

entry.build(
    entries=ENTRIES,
    no_results_message="< No results found for '{}' >"
    # Note that this is formatted at runtime
)
NOTE: You may call the build method multiple times on an instance of AutocompleteEntry, to dynamically change the available suggestions.

With that out of the way, you can display entry:

entry.pack()

Now, each time a user presses a key while the entry widget has focus, a list of suggestions will display below it.


Additional options

By default, the tk.Listbox widget has a width of 25 pixels and a height of 5 (items). The tk.Entry widget also has a default width of 25 pixels. These settings can be modified through the following class attributes:

  • auto_complete.AutocompleteEntry.LISTBOX_HEIGHT: The height to specify when creating the tk.Listbox widget. There's no need to modify this, since the maximum number of entries to be displayed can be passed as an argument to build.

  • auto_complete.AutocompleteEntry.LISTBOX_WIDTH: The width to specify when creating the tk.Listbox widget. Any positive integer is valid.

  • auto_complete.AutocompleteEntry.ENTRY_WIDTH: The width to specify when creating the tk.Entry widget. Any positive integer is valid.

NOTE: You almost always want to keep the 1:1 LISTBOX_WIDTH:ENTRY_WIDTH ratio.

You can retrieve the current entry by accessing the instance's text attribute (which is a tk.StringVar instance):

text = entry.text.get()

To further customize the entry widget, you may set its font options, for example:

entry.entry["font"] = (<FONT NAME>, <FONT SIZE>, <FONT WEIGHT>)

Or to change the background color for the listbox widget:

entry.listbox["background"] = "#cfeff9"
# Light blue

This the demo

    try:
        import tkinter as tk
        from tkinter import ttk
        from tkinter import auto_complete
    except ImportError:
        # Python 2
        import Tkinter as tk
        import ttk
        from Tkinter import auto_complete

    COUNTRIES = ['Australia','Switzerland','India','Canada','Japan','Germany','United Kingdom','United States','Sweden','Netherlands','Norway']


    class Application(tk.Frame, object):
        def __init__(self, *args, **kwargs):
            super(Application, self).__init__(*args, **kwargs)

            label = tk.Label(self, text="Select a country: ")
            label.pack()

            self.entry = auto_complete.AutocompleteEntry(self)
            self.build(case_sensitive=False,
                    no_results_message=auto_complete.NO_RESULTS_MESSAGE)
            self.entry.pack(after=label)

            self.nr = tk.StringVar()
            tk.Label(
                self,
                text="\n\nAlternative message (<Return> to set): "
            ).pack()
            nr = tk.Entry(self, textvariable=self.nr)
            nr.pack()
            nr.bind("<Return>", self._update)

            self.cs = tk.StringVar()
            cb = tk.Checkbutton(
                self,
                text="Case sensitive",
                variable=self.cs,
                state="normal",
                command=self._update
            )
            cb.deselect()
            cb.pack()

        def _update(self, *args):
            case_sensitive = False
            if self.cs.get() == "1":
                case_sensitive = True
            no_results_message = self.nr.get()
            self.build(
                case_sensitive=case_sensitive,
                no_results_message=no_results_message
            )

        def build(self, *args, **kwargs):
            self.entry.build(
                COUNTRIES,
                kwargs["case_sensitive"],
                kwargs["no_results_message"]
            )


    if __name__ == "__main__":
        root = tk.Tk()
        root.title("DEMO")
        root.resizable(False, False)
        root.tk_setPalette("white")

        application = Application(root)
        application.pack()

        root.mainloop()

This the repo here

https://bugs.python.org/issue42305

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented Nov 10, 2020

@alex,@rhettinger,@vstinner,@serhiy-storchaka,@corona10
Hey anyone can review my pull request. It would alot for me :)

@E-Paine
Copy link
Contributor

E-Paine commented Nov 10, 2020

@RajvirSingh1313 I hope to do at least an initial review of your PR in the coming days, but in the meantime please add "bpo-42305: " to the start of the PR title (this will resolve bedevere/issue-number) and also read the devguide on adding news entries.

@E-Paine
Copy link
Contributor

E-Paine commented Nov 10, 2020

Please also note that by signing the CLA you agree that the PSF can relicense your work without the needing to include a copy of MIT licence found on the original repo.

@RajvirSingh1313
Copy link
Author

Thanks @E-Paine

@RajvirSingh1313 RajvirSingh1313 changed the title Added Auto_Complete DropBox Suggestion For Tkinter [bpo-42305](https://bugs.python.org/issue42305): Added Auto_Complete DropBox Suggestion For Tkinter Nov 10, 2020
@RajvirSingh1313 RajvirSingh1313 changed the title [bpo-42305](https://bugs.python.org/issue42305): Added Auto_Complete DropBox Suggestion For Tkinter bpo-42305: Added Auto_Complete DropBox Suggestion For Tkinter Nov 10, 2020
@RajvirSingh1313 RajvirSingh1313 changed the title bpo-42305: Added Auto_Complete DropBox Suggestion For Tkinter ["bpo-42305"](https://bugs.python.org/issue42305): Added Auto_Complete DropBox Suggestion For Tkinter Nov 10, 2020
@RajvirSingh1313 RajvirSingh1313 changed the title ["bpo-42305"](https://bugs.python.org/issue42305): Added Auto_Complete DropBox Suggestion For Tkinter [bpo-42305](https://bugs.python.org/issue42305) : Added Auto_Complete DropBox Suggestion For Tkinter Nov 10, 2020
@RajvirSingh1313 RajvirSingh1313 changed the title [bpo-42305](https://bugs.python.org/issue42305) : Added Auto_Complete DropBox Suggestion For Tkinter bpo-42305: Added Auto_Complete DropBox Suggestion For Tkinter Nov 10, 2020
@E-Paine
Copy link
Contributor

E-Paine commented Nov 10, 2020

  1. A news entry should be something much shorter (at least initially) e.g. "Added tkinter.auto_complete module which contains AutocompleteEntry"

  2. The news entry needs to be written in ReST rather than Markdown (EDIT: missed the latest commit)

Keep a copy of what you have as we will probably want something similar for the docs.

@RajvirSingh1313
Copy link
Author

@E-Paine So means I should edit the rst file ?

@RajvirSingh1313
Copy link
Author

@E-Paine I am new to this so thats why I am doing alot of things wrong.

@E-Paine
Copy link
Contributor

E-Paine commented Nov 10, 2020

@RajvirSingh1313 that's absolutely fine (I've not been here that long either ;-) Don't try writing some docs yet as I would like to review your code first and will try and explain what needs to happen then.

@RajvirSingh1313
Copy link
Author

@E-Paine I am going to rewrite the rst file I will write the code of the auto_complete.py in it

@RajvirSingh1313
Copy link
Author

@E-Paine Please stay here till it got correct because I need guidance.

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented Nov 10, 2020

@E-Paine I had changed the rst file content with source code of auto_complete.py

@E-Paine
Copy link
Contributor

E-Paine commented Nov 10, 2020

The news file will be compiled with all the other news files into a final webpage. The description should be short and describe briefly the new feature (if a user is interested, they can go look at the docs - see the Python 3.9 whatsnew for examples)

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented Nov 10, 2020

@E-Paine Can you write rst ??

@RajvirSingh1313
Copy link
Author

For me because I had no idea.

@E-Paine
Copy link
Contributor

E-Paine commented Nov 10, 2020

Added ``tkinter.auto_complete`` module which contains ``AutocompleteEntry``. is probably sufficient for now

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented Nov 10, 2020

@E-Paine Is this will work? And this is in rst and this comment is for markdown so there will be different in appearance.

Tkinter Autocomplete

Text autocompletion provides relevant real-time results to users.
Because tkinter does not provide a widget for adding autocompletion to
GUIs out of the box, I decided to make one myself. This utility is
compatible with and has been tested on Python 2.7.1 and Python 3.6.0.

Structure


NOTE: The ``Tkinter`` library for Python 2 and ``tkinter`` library for Python 3 will from now on be referred to as ``tk``.
                                                                                                                          

The ``AutocompleteEntry`` class (which can be found
`here <https://github.com/RajvirSingh1313/Tkinter_Autocomplete_DropBox/blob/master/main.py>`__)
derives from ``tk.Frame`` and is a container used to group a
``tk.Entry`` and ``tk.Listbox`` widget. Should you need to modify the
widgets, they can be accessed as (respectively) ``AutocompleteEntry`` s
``entry`` and ``listbox`` attributes.

The entry widget acts like a normal textbox. When activated, it binds
``<KeyRelease>`` to a private method which will update the list of
suggestions. The listbox widget contains the suggestions themselves.
When activated, it binds ``<<ListboxSelect>>`` to a private method which
sets the entry widget to whatever value was selected.

Since an instance of ``AutocompleteEntry`` is a ``tk.Frame`` instance
too, you can place it by calling its ``pack`` or ``grid`` methods with
their respective arguments.

Quickstart

NOTE: These examples will only run under Python 3. To make them Python 2-compatible, replace tkinter with Tkinter.

To add a new autocompletion frame to our interface, first initialize
one:

.. code:: python

import tkinter as tk

from tkinter import auto_complete

root = tk.Tk()

frame = tk.Frame(root)
frame.pack()

entry = auto_complete.AutocompleteEntry(frame)
# You can pass additional parameters to further customize the window;
# all parameters that you can pass to tk.Frame, are valid here too.

Now you need to configure the instance by passing it an iterable
containing all autocompletion entries. Do this by calling its build
method:

.. code:: python

ENTRIES = (
    "Foo",
    "Bar"
)

entry.build(ENTRIES)

You can pass additional arguments to build:

  • max_entries (integer): The maximum number of entries to display
    at once. This value directly corresponds to the listbox widget's
    height attribute. Defaults to 5.

  • case_sensitive (boolean): If True, only autocomplete entries
    that enforce the same capitalization as the current entry will be
    displayed. If False, all autocomplete entries that match with the
    current entry will be displayed. Defaults to False.

  • no_results_message (string or None): The message to display
    if no suggestions could be found for the current entry. This argument
    may include a formatting identifier ({}) which, at runtime, gets
    formatted as the current entry. If None is specified, the listbox
    will instead be hidden until the next <KeyRelease> event.

Let's play around with these arguments:

.. code:: python

entry.build(
    entries=ENTRIES,
    no_results_message="< No results found for '{}' >"
    # Note that this is formatted at runtime
)

NOTE: You may call the build method multiple times on an instance of AutocompleteEntry, to dynamically change the available suggestions.

With that out of the way, you can display entry:

.. code:: python

entry.pack()

@RajvirSingh1313
Copy link
Author

@E-Paine Sir as the suggestions you had given are very nice. I had marked the problems Resolved which you had suggested and I am confused about the Unresolved problems so please tell me how to solve them. This my email - rajvirsinghdeol1313@gmail.com.
It would be great.

@E-Paine
Copy link
Contributor

E-Paine commented Nov 12, 2020

@RajvirSingh1313 I have made a note of your email address (you may wish to delete/edit your comment as this is a publicly available conversation). I will try to keep our conversation on Github as much as possible and will add clarification to my comments when I have time. Please do not ping more than once as I am either A) busy or B) asleep (you are several timezones ahead of me).

@RajvirSingh1313
Copy link
Author

This is an initial review of your work. Please do not be disheartened by the number of comments I have left, I think this could be a very nice addition. If anything I have said is unclear, please ask for clarification. The comments are for more specific things, while the following are more general comments:

  • Move to the ttk module - The ttk module already has a couple of 'extension' widgets, and think this would be much better suited there (rather than being in its own module). Please also see the existing extension widgets for the style of docstring we are looking for.
  • Add a destroy method - The widget should have a destroy method which would forward the call to the relevant widgets.
  • Add a config / configure method - While the existing extension widgets don't have this, I think it would be a nice addition.
  • Integrate the build method into __init__ - TBH, I'm not a massive fan of the build method. Instead, I propose a similar API to that of the OptionMenu where all the completions are passed to the constructor (the kwargs currently passed to build would also be moved here). This would make a config method more important so the user could change the completions dynamically.
  • Add docs - Once we have decided on an API for the new widget, you will need to write some docs. While what you currently have will be almost complete (I believe - I haven't yet reviewed it), we would need a formal reference (i.e. class AutocompleteEntry; methods ...). If you agree on moving the widget into the ttk module, the file you would need to modify is Doc/library/tkinter.ttk.rst. The docs should not reference your personal repo as I expect the CPython version and yours to evolve separately. To locally build the docs, please follow the instructions on the devguide.
  • Add unittests - These are run by the bots to reduce the chance of a faulty patch being submitted. As part of this submission, you will be expected to write your own unittests which achieve as high code coverage as reasonably possible. I would suggest you put the new tests in Lib/tkinter/test/test_ttk/test_extensions.py (as with most things, it is best if you take inspiration from the existing work)
  • Produce a news entry - The content currently in Misc/NEWS.d/next/Library/2020-11-10-12-33-07.bpo-42305.5WWGOu.rst should be moved into the docs file (Doc/...) and modified as described as above (wait until we have agreed on an API before making these modifications, though). In its place, we should have a short description of what is added by the patch probably no longer than a few sentences.
  • A little extra?! - I propose that we bind the arrow keys to perform special functions. The autocompleted text should be selected (similar to when a browser offers a completion) with the left arrow deleting the suggestion and the right arrow accepting it. We would also bind the up and down arrows to change the selected item of the Listbox. As I said, this is just a proposal, so will leave it up to you.

Reiterating what I said at the start, this is just an initial review and I have not yet tested the widget for stability, etc.

@E-Paine Please clarify these points and please add the code line which have error or need improvement.

@E-Paine
Copy link
Contributor

E-Paine commented Nov 12, 2020

please add the code line

The reason these were not comments like the others is that these are more generic points rather than relating to a particular place in the code.

Please clarify these points

This is what I attempted to do with the longer explanations but will start by trying to rephrase the ttk suggestion (also addressing the point above about how this cannot be a comment for a specific line of code). I will also work on clarifying the issues not marked as resolved.

If you look towards the bottom of the ttk module (Lib/tkinter/ttk.py - line 1506 onwards) you will find the implementation of two widgets which are implemented in Python and built using other widgets. I propose that you move the entire content of Lib/tkinter/auto_complete.py to become a new ttk widget (so as to avoid adding new module unnecessarily and making it confusing/fragmented for the user). The new auto_complete.py file could then be removed.

The reason this couldn't be commented on a line of code is because this applies to the entire auto_complete.py file.

Does this make more sense?

@RajvirSingh1313
Copy link
Author

@E-Paine Thanks, Now please tell me to what to do now. So orderwise I can do and the it will be easy for both of us. I think first I should try to add auto_complete to ttk file.

@RajvirSingh1313
Copy link
Author

Hey I am trying to add auto_complete to ttk but I don't know how to do. But I got a idea what if we add auto_complete to ttk entry wiget please tell me how to add it because I din't worked with modules and it is too complicated to work with modules I had only one year experience in python. And you have alot experience in python so please help me.

@E-Paine
Copy link
Contributor

E-Paine commented Nov 13, 2020

Please do not touch ttk.Entry. All you need to do is copy your AutocompleteEntry class code into the bottom of the tkinter.ttk file.

@RajvirSingh1313
Copy link
Author

@E-Paine Sorry for late I was gone somewhere for some days and I had not taken laptop with me. So thats why I had done nothing on this project. But now I am home, So I can work now.

@RajvirSingh1313
Copy link
Author

Can you please give me example of code that will have auto_complete entry in ttk file ?

@E-Paine
Copy link
Contributor

E-Paine commented Nov 22, 2020

I am not quite sure I understand the question but the result should be something where you can do from tkinter.ttk import AutocompleteEntry instead of from tkinter.auto_complete import AutocompleteEntry

@github-actions
Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Dec 23, 2020
@RajvirSingh1313
Copy link
Author

Please Someone help me to make this Pull request succesful.

@RajvirSingh1313
Copy link
Author

@boakley, Dear Sir,
I am a big fan of you because you have helped the community a lot by explaining the problems and giving solutions to that problems. I have made a pr about Tkinter dropbox I thought it would be great to have it in core Tkinter so I made this pull request but I am very immature to handle this pr myself so it would be great for me if you guide me and talk to the core python devs so they can merge this pr.

It would be very helpful for me.

@boakley
Copy link

boakley commented Apr 23, 2021 via email

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented Apr 24, 2021 via email

@boakley
Copy link

boakley commented Apr 26, 2021

This has what I would consider a couple of fatal flaws:

First, it calls grid to add the listbox to the window, potentially causing the entire UI to resize. Instead, it needs to use a Toplevel window so that it doesn't affect the geometry of the application.

Second, it doesn't appear to be possible to use the keyboard to get an autocomplete value. It seems that I must use the mouse to pick an item. For this to be a viable widget I think it must support using the keyboard to select a value from the list.

Note: if you switch to using a Toplevel for the dropdown, you no longer need to inherit from a frame. Instead, you can inherit from an entry widget and you'll get all of the normal configuration options for free.

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented Apr 26, 2021 via email

@RajvirSingh1313
Copy link
Author

And I think I will need your help with the first point that I should switch from using the grid to toplevel so if you could help me with this thing it would great 😅.

@E-Paine
Copy link
Contributor

E-Paine commented May 8, 2021

While I'm still interested in adding this, I fear there could be major problems, most notably performance on larger lists. To achieve reasonable performance, we would possibly want to use a Radix tree (or something similar) which 1. would not belong in tkinter 2. I doubt people would want elsewhere in the stdlib.

@RajvirSingh1313
Copy link
Author

While I'm still interested in adding this, I fear there could be major problems, most notably performance on larger lists. To achieve reasonable performance, we would possibly want to use a Radix tree (or something similar) which 1. would not belong in tkinter 2. I doubt people would want elsewhere in the stdlib.

I am happy that you are still interested in this, so if you can help me that would be great. Because I made this pr for just adding this feature to the Tkinter I don't know hardcore python in very depth. This code was written by @coal0 so I contacted him with the flaws and told him that I am making a pull request with his code but he didn't reply so I am stuck in a situation where I can't do anything with this code. so please help me if you know how to fix it.

@E-Paine
Copy link
Contributor

E-Paine commented May 9, 2021

This code was written by coal0

This is a problem because that means you do not own the copyright. By committing this, we would be relicensing it and we cannot do that without his written permission.

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented May 9, 2021 via email

@E-Paine
Copy link
Contributor

E-Paine commented May 9, 2021

it is under MIT license

That is the problem. The MIT license requires that:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. Utilities/LICENSE

We cannot fulfil this and would therefore need an exemption from it.

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented May 10, 2021 via email

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented May 10, 2021 via email

@E-Paine
Copy link
Contributor

E-Paine commented May 10, 2021

So should I close this PR?

Unfortunately, yes please. If you write your own implementation, I would be happy to take a look at it (I might write my own when I have time). Sorry it ended like this and I hope you have more success with future patches.

@RajvirSingh1313
Copy link
Author

RajvirSingh1313 commented May 10, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting core review stale Stale PR or inactive for long period of time.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

X Tutup