TomSchimansky / CustomTkinter

A modern and customizable python UI-library based on Tkinter
MIT License
10.8k stars 1.03k forks source link

CTkComboBox & CTkOptionMenu second click to close the dropdown #2386

Open APoppe95 opened 2 months ago

APoppe95 commented 2 months ago

Is it possible to add functionality to where the second click on the dropdown arrow of the CTkComboBox, and a click anywhere on the widget of the CTKOptionMenu, would close the dropdown menu that appears?

Currently how it works for me is that the dropdown menu is hidden for a split-second before being recreated (seemingly because the dropdown arrow only has one bound event: open).

For context, I am using Python 3.10 with CustomTkinter version 5.2.1 on a Win10 machine. I have tried this on two other systems, one also Win10 and the other Win11. I have also tried CustomTkinter versions 5.1.0 and 5.2.2. All of these met with the same problem.

I have also posted a question on how to fix this on stackoverflow (see below), but I have no found a solution through this: https://stackoverflow.com/questions/78354610/customtkinter-close-the-dropdown-menu-when-the-dropdown-arrow-is-clicked-again

APoppe95 commented 2 months ago

My current makeshift solution is to use a CTkOptionMenu that toggles between the 'normal/disabled' states via the toggle_dropdown method (see below code) - as the event bound to left click triggers on any part of the widget, which is not the case for a CTkComboBox.

This then had an issue where selecting an option would disable the menu, forcing you to click twice in order to re-enable it and re-open the dropdown menu (however, repeatedly clicking on the widget - not the dropdown menu - does not have this issue). My temporary solution to this is to reset the state of the widget to 'normal' every 0.1 seconds via the dropdown_checker() method.

As you can tell, this is not a very pretty solution, and it also does not solve the problem for a CTkComboBox either.

import customtkinter as ctk

class DummyApp(ctk.CTk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.geometry('200x100')

        self.optionbox = ctk.CTkOptionMenu(self, values=['Option A', 'Option B'])
        self.optionbox.pack(side='left')
        self.optionbox.bind('<Button-1>', lambda event: self.toggle_dropdown(event, obox=self.optionbox))

        self.dropdown_checker()

    @staticmethod
    def toggle_dropdown(event, obox: ctk.CTkOptionMenu):
        if obox.cget('state') == 'normal':
            obox.configure(state='disabled')
        elif obox.cget('state') == 'disabled':
            obox.configure(state='normal')

    def dropdown_checker(self):
        self.optionbox.configure(state='normal')
        self.after(100, self.dropdown_checker)

if __name__ == '__main__':
    app = DummyApp()
    app.mainloop()