当前位置: 首页 > 知识库问答 >
问题:

Python-tk。使用tab键时设置小部件焦点

澹台鸿光
2023-03-14

我有一个GUI,左侧有一列按钮,右侧有文本输入框,页脚有一些通用GUI按钮(新建/删除/保存/退出)。左侧的按钮分别向右侧的文本输入小部件提供一组单词。我希望能够在文本输入小部件中使用control tab或control shift tab,并将焦点返回到输入文本输入小部件的按钮。我在谷歌上搜索过,也做过实验,但在使用tab键组合后,我找不到一种方法来强制聚焦某个特定的按钮。

from tkinter import *
import decimal

class category_rule():
    def __init__(self,hi_range,low_range,word_list,rule,row):
        self.hi_range = decimal.Decimal(hi_range)
        self.low_range = decimal.Decimal(low_range)
        self.word_list = sorted(word_list)
        self.rule = rule
        self.button_row = row
        self.rule_button = None
        self.rule_entry_window=None

def build_rules():
    rule_dict = {}
    rule_index = {}
    words = []

    rule='Fruit'
    words=['Apple','Banana','Strawberry']
    row=0 
    rule_dict[rule] = category_rule('9999.99','0.00',words,rule,row)
    rule_index[row] = rule

    rule = 'Vegetable'
    words=['Asparagus','Brussel Sprouts','Kale','Tomato','Spinach']
    row=1
    rule_dict[rule] = category_rule('9999.99','0.00',words,rule,row)
    rule_index[row] = rule

    rule = 'Dessert'
    words=['Cake','Pudding','Cookies','Pie']
    row=2
    rule_dict[rule] = category_rule('9999.99','0.00',words,rule,row)
    rule_index[row] = rule

    return rule_dict, rule_index


class rule_entry_window:
    active_index = 0
    active_rule = None
    first_rule=None
    button_row=0
    rule_dict = {}
    rule_index = {}


    ''' methods to create the window, build the frames add the widgets '''
    def init_window_and_frames(self):   
        self.win = Tk()
        self.bg_color = '#134E9C'
        self.text_entry_frame = Frame(self.win, width=150, height=380, bg='#DDDECE', padx=1, pady=1, borderwidth=1)
        self.button_frame_outer = Frame(self.win, width=150, height=300, bg=self.bg_color, padx=3, pady=3, borderwidth=1)
        self.button_frame = Frame(self.button_frame_outer, width=150, height=300, bg = self.bg_color, padx=3, pady=3, borderwidth=1)
        self.range_entry_frame = Frame(self.win, width=150, height=40, bg=self.bg_color, padx=3, pady=3, borderwidth=1)
        self.footer_frame = Frame(self.win, width=480, height=30, bg=self.bg_color, padx=3, pady=3, borderwidth=1)

        self.button_frame.grid(row=0,column=0,sticky='nesw')
        self.button_frame_outer.grid(rowspan=2,column=0,sticky='nsew')
        self.text_entry_frame.grid(row=1,column=1,sticky='s')

        self.range_entry_frame.grid(row=0,column=1,sticky='new')
        self.footer_frame.grid(row=2,columnspan=2,sticky='ew')
        self.button_frame_outer.columnconfigure(0, minsize=200, weight=1)
        self.button_frame_outer.rowconfigure(0, weight=1)

    def init_rule_widgets(self):
        self.scrollbar = Scrollbar(self.text_entry_frame)
        self.text_entry = Text(self.text_entry_frame, width=40, height=33, wrap="word",
                   yscrollcommand=self.scrollbar.set, bg='#DDDECE',
                   borderwidth=0, highlightthickness=0)
        self.text_entry.delete('1.0', END)
        self.text_entry.grid(row=0,column=0,padx=2,pady=2)

        self.text_entry.bind('<Control-Tab>',self.text_tab)
        self.text_entry.bind('<Control-Shift-Tab>',self.text_tab)

        self.scrollbar.grid(row=0,column=1,sticky='ns')

        self.low_range_label = Label(self.range_entry_frame, text='Low Range',background=self.bg_color,fg='#ffffff')
        self.low_range_label.grid(row=0, column=0,sticky='e')
        self.low_range_entry = Entry(self.range_entry_frame, background='#DDDECE')
        self.low_range_entry.grid(row=0,column=1)

        self.hi_range_label = Label(self.range_entry_frame, text='High Range',background=self.bg_color,fg='#ffffff')
        self.hi_range_label.grid(row=1, column=0,sticky='e')
        self.hi_range_entry = Entry(self.range_entry_frame, background='#DDDECE')
        self.hi_range_entry.grid(row=1,column=1)        

    def init_footer_buttons(self):
        self.new_rule_button = Button(self.footer_frame, width=10, height=1, padx=2, pady=1, name='new button',
                                 bg=self.bg_color, fg='#ffffff', bd=1, text='New', command=self.new_rule)
        self.footer_frame.grid_columnconfigure(2, weight=0)
        self.new_rule_button.grid(row=0,column=0,sticky='w')

        self.delete_rule_button = Button(self.footer_frame, width=10, height=1, padx=2, pady=1, name='delete button',
                                 bg=self.bg_color, fg='#ffffff', bd=1, text='Delete', command=self.delete_rule)
        self.footer_frame.grid_columnconfigure(2, weight=0)
        self.delete_rule_button.grid(row=0,column=1,sticky='w')

        self.exit_button = Button(self.footer_frame, width=10, height=1, padx=2, pady=1, name='exit_button',
                                 bg=self.bg_color, fg='#ffffff', bd=1, text='Exit', command=self.exit)
        self.footer_frame.grid_columnconfigure(2, weight=1)
        self.exit_button.grid(row=0,column=3,sticky='e')

        self.save_button = Button(self.footer_frame, width=10, height=1, padx=2, pady=1, name='save button',
                                 bg=self.bg_color, fg='#ffffff', bd=1, text='Save', command=self.save_rule)
        self.footer_frame.grid_columnconfigure(2, weight=1)
        self.save_button.grid(row=0,column=4,sticky='e')

    def add_rule_button(self,rule):
        self.rule_dict[rule].rule_button = Button(self.button_frame, name='b'+rule, width=30, height=1, justify=LEFT, padx=0, pady=2,
                                                  font=('Arial', 9), fg='#ffffff',bg='#134E9C', bd=0, anchor='nw',text=rule,
                                                  highlightcolor='#134E9C',command=lambda r=rule: self.fill_text_entry_window(r))
        self.rule_dict[rule].rule_button.grid(row=self.rule_dict[rule].button_row,column=0,sticky='n')

        self.rule_dict[rule].rule_button.bind('<Tab>',self.button_tab)
        self.rule_dict[rule].rule_button.bind('<Shift-Tab>',self.button_tab)
        self.rule_dict[rule].rule_button.bind('<Return>',self.focus_text_entry)
        self.rule_dict[rule].rule_button.bind('<Right>',self.focus_text_entry)
        self.rule_dict[rule].rule_button.bind('<Up>',self.button_uparrow)
        self.rule_dict[rule].rule_button.bind('<Down>',self.button_downarrow)


    ''' init.  call the methods to build the window, set the active button and call mainloop '''
    def __init__(self):      

        self.init_window_and_frames()
        self.init_rule_widgets()
        self.init_footer_buttons()

        self.rule_dict,self.rule_index = build_rules()
        for rule in self.rule_dict:
            self.add_rule_button(rule)

        self.active_index = 0
        self.active_rule = None
        self.fill_text_entry_window(self.rule_index[self.active_index])

        self.win.mainloop()

    ''' methods to process activity from the widgets '''
    @classmethod
    def new_rule(cls):
        print('new rule - Not written yet')

    @classmethod
    def delete_rule(cls):
        print('delete rule - Not written yet')

    @classmethod
    def exit(cls):
        print('exit - Not written yet')

    @classmethod
    def save_rule(cls):
        print('save - Not written yet')

    ''' process keyboard events'''
    def button_uparrow(self,event):
        if self.active_index==0:
            next_rule=self.rule_index[len(self.rule_index)-1]
        else:
            next_rule=self.rule_index[self.active_index-1]
        self.fill_text_entry_window(next_rule)

    def button_downarrow(self,event):
        if self.active_index+1==len(self.rule_index):
            next_rule=self.rule_index[0]
        else:
            next_rule=self.rule_index[self.active_index+1]
        self.fill_text_entry_window(next_rule)

    def button_tab(self,event):
        '''
        make the next button active
        event.state == 9 means the <shift> key is pressed.  8 is shift key is not pressed
        '''
        if event.state == 9:
            if self.active_index==0:
                next_rule=self.rule_index[len(self.rule_index)-1]
            else:
                next_rule=self.rule_index[self.active_index-1]
        else:
            if self.active_index==self.active_index==len(self.rule_index)-1:
                next_rule=self.rule_index[0]
            else:
                next_rule=self.rule_index[self.active_index+1]

        self.fill_text_entry_window(next_rule)

    def focus_text_entry(self,event):
        self.text_entry_frame.focus_set()
        self.text_entry.focus()

    def text_tab(self,event):
        '''
        process control-tab, control-shift-tab key combinations from text_entry widget.  
        Attempts to set focus back to the active button
         *** doesn't work
        '''
        self.update_rule()

        self.win.focus_set()
        active_rule=self.rule_index[self.active_index]
        self.footer_frame.tkraise()
        self.range_entry_frame.tkraise()
        ''' self.button_frame_outer.tkraise() '''
        self.button_frame.focus_force()
        self.button_frame.tkraise()
        self.button_frame_outer.tkraise()
        self.button_frame.focus_force()
        self.rule_dict[active_rule].rule_button.focus_force()
        self.rule_dict[active_rule].rule_button.focus_set()
        self.rule_dict[active_rule].rule_button.focus()
        self.fill_text_entry_window(active_rule)

    def fill_text_entry_window(self,selected_rule):
        if self.active_rule is None:
            self.active_rule = selected_rule
        elif self.active_rule is not selected_rule:
            self.update_rule()

        ''' remove highlight from the old button.  Add highlight to the new one '''
        self.rule_dict[self.active_rule].rule_button.config(bg='#134E9C',fg='#ffffff')
        self.active_rule = self.rule_dict[selected_rule].rule
        self.active_index = self.rule_dict[selected_rule].button_row
        self.rule_dict[self.active_rule].rule_button.config(bg='#ffffff',fg='#134E9C')

        ''' clear the entry widgets and add the information for the new button '''
        self.low_range_entry.delete(0,END)
        self.low_range_entry.insert(0,self.rule_dict[self.active_rule].low_range)
        self.hi_range_entry.delete(0,END)
        self.text_entry.delete('1.0', END)
        self.hi_range_entry.insert(0,self.rule_dict[self.active_rule].hi_range)
        for word in self.rule_dict[self.active_rule].word_list:
            self.text_entry.insert('insert', word+'\n')

        ''' *** attempt to set the focus on the new button. '''
        self.rule_dict[self.active_rule].rule_button.focus()
        self.rule_dict[self.active_rule].rule_button.focus_set()
        self.rule_dict[self.active_rule].rule_button.focus_force()

    def update_rule(self):
        ''' save any changes made to the active rule before changing to the next one '''
        self.rule_dict[self.active_rule].word_list = sorted(self.text_entry.get(1.0,END).rstrip().split('\n'))
        self.rule_dict[self.active_rule].hi_range = decimal.Decimal(self.hi_range_entry.get())
        self.rule_dict[self.active_rule].low_range = decimal.Decimal(self.low_range_entry.get())


if __name__ == '__main__':    
    rule_entry_window()

文本选项卡和填充文本输入窗口是我尝试将焦点设置回所选按钮的方法。我还没有找到tkraise和focus/focus\u force/focus\u set的组合,可以将焦点设置回我想要的按钮。我该怎么做才能将焦点强制回到按钮上?

共有1个答案

景正文
2023-03-14

通常,当您在Text中按Control tab时,tkinter会运行将焦点移动到列表中的下一个小部件的功能。

文本中的控制选项卡绑定到控制选项卡后,当您按下控制选项卡时,它会将事件发送到您的函数文本选项卡,并将焦点更改为预期按钮,但之后它还会将此事件发送到通常会将焦点移动到列表上下一个小部件的函数,并将其移动将注意力从预期的按钮集中到条目(该条目位于“文本”之后的列表中)。

您必须使用返回text_tab中通知tkinter您已经使用了此事件,并且它不必将事件发送/传播到其他函数。这样它就不会发送它到函数,它将焦点移动到下一个小部件(Entry)。

 def text_tab(self, event):

     # ... code ...

     return "break"
 类似资料:
  • 问题内容: 如前所述,我想更改内的默认TAB行为(以使其充当类似或类似的组件) 这是事件动作 这是听众 我也尝试过evt.KEY_TYPED,没有任何喜悦。 有任何想法吗? 快速编辑:我也尝试代替 问题答案: 根据此类: 请注意,可以更短,根据[约书亚·戈德堡的意见,因为我们的目标是获得通过重写回默认行为: 这用于问题“ 我如何修改标签中的Tab键的行为JTextArea? ” 在以前的实现确实是

  • 我已经将问题从居中改为自定义的位置。 因为我注意到实际上是居中的,根据文档,它只是添加了页签。 但是如果您希望在避开安全区域时设置一个制表符,或者选项卡中的元素不是(自动)居中对齐,我现在找不到方法。 我还尝试将选项卡包装在小部件中,并获得属性集,但似乎所有的高度都避开了安全区域(通过添加一个常量)。我尝试用小部件包装整个,但是安全区域变成了黑色,我们实际上希望tab bat变得更高,然后在这个更

  • 问题内容: 我想个性化菜单栏。例如,我想删除tk.Menu小部件周围出现的边框(使用add_command()方法) 那是我的代码(我正在使用Windows 10) 我的想法是在不使用tk.Menu和tk.MenuButton的情况下创建菜单。我想将事件“绑定”到标签,以便在标签下创建某种下拉菜单。可能吗? 问题答案: 问题 :自定义菜单栏是否不使用小部件? 本示例使用a作为弹出窗口,并显示添加的

  • 设置(T) ⇒ 首选项... ⇒ 语言 ⇒ 标签设置,勾选 "以空格取代" 这样,以后输入Tab键的时候就会自动以所设置的4个空格代替。 此功能的用途: 部分程序的需要 主要是有些情况下,比如写Python脚本的时候用到,以避免TAB键和空格键的混合缩进,其会导致Python 3.0等报错,而让输入的TAB键,自动变成空格键,则自动规避了此问题。 兼容不同平台 另外,此功能,也是出于兼容不同平台,

  • 我在Eclise neon和Java 1.8中使用WindowsBuilder编写了一个基本的计算器类型程序。这几乎是完整的,一切都按我希望的方式进行。除了键盘输入。 作为最后一步,我想检测keyType事件并将它们映射到按钮按下,以便用户可以使用键盘进行输入,而不是用鼠标单击按钮。 我在程序类中添加了“实现KeyListener”... 我尝试将侦听器设置为一个名为keyb的不可见JTextFi

  • 问题内容: 我在gwt应用程序上有一个FlowPanel对象。 它给了我以下错误: 如何设置的ID ? 问题答案: 我正在使用GWT 2.4.0,此代码可以正常工作。 要么