Tab遍历不适用于wx.TE_PROCESS_ENTER和自动完成

问题描述

我正在尝试使用具有自动完成功能的te​​xtCtrl,启用wx.TE_PROCESS_ENTER并进行选项卡遍历。

如果我禁用wx.TE_PROCESS_ENTER开关或不执行自动完成,则制表符遍历将起作用。

在这里我有一个小的示例代码来说明我的问题。

import wx
import wx.xrc

class MyFrame ( wx.Frame ):

    def __init__( self,parent ):
        wx.Frame.__init__ ( self,parent,id = wx.ID_ANY,title = wx.EmptyString,pos = wx.DefaultPosition,size = wx.Size( -1,-1 ),style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHints( wx.DefaultSize,wx.DefaultSize )

        Sizer = wx.BoxSizer( wx.VERTICAL )

        self.panel = wx.Panel( self,wx.ID_ANY,wx.DefaultPosition,wx.DefaultSize,wx.TAB_TRAVERSAL )
        inputSizer = wx.BoxSizer( wx.HORIZONTAL )

        self.no_process_enter = wx.TextCtrl( self.panel,wx.EmptyString,0 )
        inputSizer.Add( self.no_process_enter,wx.ALL,5 )

        self.no_autocomplete = wx.TextCtrl( self.panel,wx.TE_PROCESS_ENTER )
        inputSizer.Add( self.no_autocomplete,5 )

        self.autocomplete_and_process_enter = wx.TextCtrl( self.panel,wx.TE_PROCESS_ENTER )
        inputSizer.Add( self.autocomplete_and_process_enter,5 )

        ButtonSizer = wx.StdDialogButtonSizer()
        self.ButtonSizerOK = wx.Button( self.panel,wx.ID_OK )
        ButtonSizer.AddButton( self.ButtonSizerOK )
        self.ButtonSizerCancel = wx.Button( self.panel,wx.ID_CANCEL )
        ButtonSizer.AddButton( self.ButtonSizerCancel )
        ButtonSizer.Realize();

        inputSizer.Add( ButtonSizer,1,wx.EXPAND,5 )


        self.panel.SetSizer( inputSizer )
        self.panel.Layout()
        inputSizer.Fit( self.panel )
        Sizer.Add( self.panel,wx.EXPAND |wx.ALL,5 )


        self.SetSizer( Sizer )
        self.Layout()
        self.Fit()

        self.Centre( wx.BOTH )

        # Connect Events
        self.no_process_enter.Bind( wx.EVT_KILL_FOCUS,self.no_process_enterOnKillFocus )
        self.no_process_enter.Bind( wx.EVT_SET_FOCUS,self.no_process_enterOnSetFocus )
        self.no_process_enter.Bind( wx.EVT_KEY_DOWN,self.OnKeyDown )
        
        self.no_autocomplete.Bind( wx.EVT_KILL_FOCUS,self.no_autocompleteOnKillFocus )
        self.no_autocomplete.Bind( wx.EVT_SET_FOCUS,self.no_autocompleteOnSetFocus )
        self.no_autocomplete.Bind( wx.EVT_TEXT_ENTER,self.no_autocompleteOnTextEnter )
        self.no_autocomplete.Bind( wx.EVT_KEY_DOWN,self.OnKeyDown )
        
        self.autocomplete_and_process_enter.Bind( wx.EVT_KILL_FOCUS,self.autocomplete_and_process_enterOnKillFocus )
        self.autocomplete_and_process_enter.Bind( wx.EVT_SET_FOCUS,self.autocomplete_and_process_enterOnSetFocus )
        self.autocomplete_and_process_enter.Bind( wx.EVT_TEXT_ENTER,self.autocomplete_and_process_enterOnTextEnter )
        self.autocomplete_and_process_enter.Bind( wx.EVT_KEY_DOWN,self.OnKeyDown )

    def __del__( self ):
        pass


    # Virtual event handlers,overide them in your derived class
    def no_process_enterOnKillFocus( self,event ):
        print('leave no_process_enter\n')
        event.Skip()

    def no_process_enterOnSetFocus( self,event ):
        print('enter no_process_enter')
        event.Skip()

    def no_autocompleteOnKillFocus( self,event ):
        print('leave no_autocomplete\n')
        event.Skip()

    def no_autocompleteOnSetFocus( self,event ):
        print('enter no_autocomplete')
        event.Skip()

    def no_autocompleteOnTextEnter( self,event ):
        print('no_autocomplete - ENTER pressed')
        event.Skip()

    def autocomplete_and_process_enterOnKillFocus( self,event ):
        print('leave autocomplete_and_process_enter\n')
        event.Skip()

    def autocomplete_and_process_enterOnSetFocus( self,event ):
        print('enter autocomplete_and_process_enter')
        event.Skip()

    def autocomplete_and_process_enterOnTextEnter( self,event ):
        print('autocomplete_and_process_enter - ENTER pressed')
        event.Skip()

    def OnKeyDown( self,event ):
        #print(event.GetKeyCode())
        if event.GetKeyCode() == wx.WXK_TAB:
            print('navigate to next element')
            event.EventObject.Navigate()
            event.Skip(False)
        else:
            event.Skip()


if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame(None)
    
    app.SetTopWindow(frame)
    frame.Show(True)

    
    frame.no_process_enter.AutoComplete(['Hello','World'])
    frame.autocomplete_and_process_enter.AutoComplete(['Foo','Bar'])

    app.MainLoop()

解决方法

最后,我成功解决了这个问题。技巧是在将事件传递到事件链之前对其进行捕获,并手动进行制表符遍历。

为此,您必须实现TryBefore方法,并将执行范围限制为无效的文本字段。为了启用双向导航,将参数not event.ShiftDown()传递给event.EventObject.Navigate方法。


import wx
import wx.xrc

class MyFrame ( wx.Frame ):

    def __init__( self,parent ):
        wx.Frame.__init__ ( self,parent,id = wx.ID_ANY,title = wx.EmptyString,pos = wx.DefaultPosition,size = wx.Size( -1,-1 ),style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHints( wx.DefaultSize,wx.DefaultSize )

        Sizer = wx.BoxSizer( wx.VERTICAL )

        self.panel = wx.Panel( self,wx.ID_ANY,wx.DefaultPosition,wx.DefaultSize,wx.TAB_TRAVERSAL )
        inputSizer = wx.BoxSizer( wx.VERTICAL )

        self.no_process_enter = wx.TextCtrl( self.panel,wx.EmptyString,0 )
        inputSizer.Add( self.no_process_enter,wx.ALL,5 )

        self.no_autocomplete = wx.TextCtrl( self.panel,wx.TE_PROCESS_ENTER )
        inputSizer.Add( self.no_autocomplete,5 )

        self.autocomplete_and_process_enter = wx.TextCtrl( self.panel,wx.TE_PROCESS_ENTER )
        inputSizer.Add( self.autocomplete_and_process_enter,5 )

        ButtonSizer = wx.StdDialogButtonSizer()
        self.ButtonSizerOK = wx.Button( self.panel,wx.ID_OK )
        ButtonSizer.AddButton( self.ButtonSizerOK )
        self.ButtonSizerCancel = wx.Button( self.panel,wx.ID_CANCEL )
        ButtonSizer.AddButton( self.ButtonSizerCancel )
        ButtonSizer.Realize();

        inputSizer.Add( ButtonSizer,1,wx.EXPAND,5 )


        self.panel.SetSizer( inputSizer )
        self.panel.Layout()
        inputSizer.Fit( self.panel )
        Sizer.Add( self.panel,wx.EXPAND |wx.ALL,5 )


        self.SetSizer( Sizer )
        self.Layout()
        self.Fit()

        self.Centre( wx.BOTH )

        # Connect Events
        self.no_process_enter.Bind( wx.EVT_KILL_FOCUS,self.no_process_enterOnKillFocus )
        self.no_process_enter.Bind( wx.EVT_SET_FOCUS,self.no_process_enterOnSetFocus )
        
        self.no_autocomplete.Bind( wx.EVT_KILL_FOCUS,self.no_autocompleteOnKillFocus )
        self.no_autocomplete.Bind( wx.EVT_SET_FOCUS,self.no_autocompleteOnSetFocus )
        self.no_autocomplete.Bind( wx.EVT_TEXT_ENTER,self.no_autocompleteOnTextEnter )
        
        self.autocomplete_and_process_enter.Bind( wx.EVT_KILL_FOCUS,self.autocomplete_and_process_enterOnKillFocus )
        self.autocomplete_and_process_enter.Bind( wx.EVT_SET_FOCUS,self.autocomplete_and_process_enterOnSetFocus )
        self.autocomplete_and_process_enter.Bind( wx.EVT_TEXT_ENTER,self.autocomplete_and_process_enterOnTextEnter )

    def __del__( self ):
        pass

    def TryBefore(self,event):
        if isinstance(event,wx.KeyEvent):
            if event.GetEventObject().GetId() in (self.no_autocomplete.GetId(),self.autocomplete_and_process_enter.GetId()):
                if event.GetKeyCode() == wx.WXK_TAB:
                    event.EventObject.Navigate(not event.ShiftDown())
                    return False
        return wx.Frame.TryBefore(self,event)

    # Virtual event handlers,overide them in your derived class
    def no_process_enterOnKillFocus( self,event ):
        print('leave no_process_enter\n')
        event.Skip()

    def no_process_enterOnSetFocus( self,event ):
        print('enter no_process_enter') 
        event.Skip()

    def no_autocompleteOnKillFocus( self,event ):
        print('leave no_autocomplete\n')
        event.Skip()

    def no_autocompleteOnSetFocus( self,event ):
        print('enter no_autocomplete')
        event.Skip()

    def no_autocompleteOnTextEnter( self,event ):
        print('no_autocomplete - ENTER pressed')
        event.Skip()

    def autocomplete_and_process_enterOnKillFocus( self,event ):
        print('leave autocomplete_and_process_enter\n')
        event.Skip()

    def autocomplete_and_process_enterOnSetFocus( self,event ):
        print('enter autocomplete_and_process_enter')
        event.Skip()

    def autocomplete_and_process_enterOnTextEnter( self,event ):
        print('autocomplete_and_process_enter - ENTER pressed')
        event.Skip()

if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame(None)
    
    app.SetTopWindow(frame)
    frame.Show(True)

    
    frame.no_process_enter.AutoComplete(['Hello','World'])
    frame.autocomplete_and_process_enter.AutoComplete(['Foo','Bar'])

    app.MainLoop()