简体   繁体   中英

Tkinter how to bind B1-Motion to Combobox

so I have a combobox in an app I am putting on a tablet, I'm wanting to make it so that I can drag on the combobox that it will scroll down it.

So far I have this function:

def tablet_drag_y(event):
    global last_y
    if event.y_root-last_y>20 or event.y_root-last_y<-20:
        last_y=event.y_root
        event.widget.tag_remove(Tk.SEL, "1.0", Tk.END)
        return "break"
    event.widget.yview(Tk.SCROLL,-1*(event.y_root-last_y), "units")
    last_y=event.y_root
    event.widget.tag_remove(Tk.SEL, "1.0", Tk.END)
    return "break"

This works on Text widgets (the majority of widgets I need this for), but I only know how to bind the combo box with this:

book_drop_down.bind("<<ComboboxSelected>>", tablet_drag_y)

Idk how to bind any sort of motion to the combobox, how would I go about doing this?

Your question (and solution) is little similar to this one . Thus, tips and ideas from there apply to your problem as well.

First of all, such functionality like you described is already there: when you are on b1-movement at the borders of the list - the list is automatically scrolled. But ok, let's implement something on our own.

To start with we need to comprehend that combobox is nothing, but a combo of entry and listbox widgets, and we need a part, that is a listbox (a popdown window). Fortunately, there is a native function that allows you to tear it out:

popdown = combobox.tk.eval('ttk::combobox::PopdownWindow %s' % combobox)

After that your a free to bind something to that widget, when our combobox is mapped:

class CustomBox(ttk.Combobox):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.bind('<Map>', self._handle_popdown_bind_on_initialisation)

    def _handle_popdown_bind_on_initialisation(self, *args):
        popdown = self.tk.eval('ttk::combobox::PopdownWindow %s' % self)
        self._bind(('bind', '%s.f.l' % popdown), '<B1-Motion>', <callback_function>, None)

To elaborate a little more: popdown is the toplevel window, that literally pops down on your click on the combobox, f a frame-container for listbox, and l - the actual listbox, which contains your values. Looks simple.

So let's code something:

import tkinter as tk
import tkinter.ttk as ttk
import random
import string


class CustomBox(ttk.Combobox):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # schedule bind to handle popdown
        self.bind('<Map>', self._handle_popdown_bind_on_initialisation)

    def _handle_popdown_bind_on_initialisation(self, *args):
        # once combobox is drawn bind callback function
        popdown = self.tk.eval('ttk::combobox::PopdownWindow %s' % self)
        self._bind(('bind', '%s.f.l' % popdown), '<B1-Motion>', drag, None)


def insert_something_to_combobox(box, count=30):
    # just to insert some random stuff
    box['values'] = [gen_key() for _ in range(count)]

def gen_key(size=6, chars=string.ascii_uppercase + string.digits):
    # just to generate some random stuff
    return ''.join(random.choice(chars) for _ in range(size))

def drag(event):
    # test-event for B1-Motion over popdown

    # get index of the nearest item
    nearest_item = root.tk.call(event.widget, 'nearest', event.y)
    # get actual size of listbox
    actual_size = root.tk.call(event.widget, 'size')
    # get current boundary positions for listbox
    current_yview = root.tk.call(event.widget, 'yview')
    # get current boundary items
    current_items = [int(fraction * actual_size) for fraction in current_yview]
    # get decider-item for scrolling
    decider_item = sum(current_items) // 2

    # debug-configure current item
    mouse_over_label.configure(text='B1 over item: %s' % root.tk.call(event.widget, 'get', nearest_item))

    if nearest_item < decider_item:
        # scroll-up
        root.tk.call(event.widget, 'see', current_items[0] - 1)
    elif nearest_item > decider_item:
        # scroll-down
        root.tk.call(event.widget, 'see', current_items[1] + 1)


root = tk.Tk()

mouse_over_label = tk.Label()
mouse_over_label.pack()

combo_box = CustomBox()
combo_box.pack()

insert_something_to_combobox(combo_box)

root.mainloop()

The idea is simple: get a decider item, that is half-way-thru listbox, and, depending on the position of the current element, decide to scroll up or down.

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