wxPython:事件、键盘监听

代码一

代码展示

import wx

class MyFrame(wx.Frame):
    def __init__(self,parent,id=wx.ID_ANY,title='',
                 pos=wx.DefaultPosition,size=wx.DefaultSize,
                 style=wx.DEFAULT_FRAME_STYLE,
                 name='MyFrame'):
        super(MyFrame,self).__init__(parent,id,title,pos,
                                     size,style,name) 
    
        self.panel = wx.Panel(self)
        self.btn1 = wx.Button(self.panel,label='Button 01')
        self.btn2 = wx.Button(self.panel,label='Button 02')
        
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.btn1,0,wx.ALL,0)
        sizer.Add(self.btn2,0,wx.ALL,0)
        self.panel.SetSizer(sizer)
        
        self.Bind(wx.EVT_BUTTON,self.onButton,self.btn1)
        self.Bind(wx.EVT_BUTTON,lambda event:
                  self.btn1.Enable(not self.btn1.Enabled),
                  self.btn2)
    
    def onButton(self,event):
        event_id = event.GetId()
        event_obj = event.GetEventObject()
        print("ID=%s"%event_id)
        print("Obj=%s"%event_obj)
        
class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None,title='My APP')
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True
    
app = MyApp(False)
app.MainLoop()

基本概念

  • 事件句柄:其实就是事件的触发函数
  • 窗口层次:窗口在 wxPython 中指的是,控件。所有的,无论是 TopLevel Window,即 Frame ,还是 Container,如 Panel,Boxer,亦或是 Control,如 Button,都是指 窗口(Window)。故窗口层次,也就是窗口们的等级以及排列
  • 事件 Binder 对象:就是 wx.EVT_BUTTON 这类对象。
  • 事件对象:作为事件句柄,即触发函数的第一个参数。我们能够通过事件对象,调出有关事件的信息,包括事件是作用于哪个窗口上。

事件的传播

仔细看我们的 Bind 的函数,是通过 self 调用的。也就是说,我们的 Frame 才是 事件 的真正主角。但是,为何有会作用到具体的某个控件上呢?实际上,这就是事件的传播。首先事件在 Frame 上发生,然后传递给他的子窗口 Panel,再传递给他的子窗口 Button,就这样逐级传播。

两种事件

一种会传播,另一个不会。前者叫 command event,后者叫 event。

代码示例:

import wx

ID_BUTTON1 = wx.NewId()
ID_BUTTON2 = wx.NewId()

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None,title='Test')
        self.SetTopWindow(self.frame)
        self.frame.Show()
        
        self.Bind(wx.EVT_BUTTON,self.onButtonApp)
        return True
    
    def onButtonApp(self,event):
        event_id = event.GetId()
        if event_id == ID_BUTTON1:
            print("BUTTON ONE event reached the App")
 

class MyFrame(wx.Frame):
    def __init__(self,parent,id=wx.ID_ANY,title='',
                 pos=wx.DefaultPosition,size=wx.DefaultSize,
                 style=wx.DEFAULT_FRAME_STYLE,
                 name='MyFrame'):
        super(MyFrame,self).__init__(parent,id,title,pos,
                                     size,style,name) 
        self.panel = MyPanel(self)
        self.btn1 = wx.Button(self.panel,id=ID_BUTTON1,
                              label='Command Event')
        self.btn2 = wx.Button(self.panel,id=ID_BUTTON2,
                              label='Event')
        
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.btn1,0,wx.ALL,10)
        sizer.Add(self.btn2,0,wx.ALL,10)
        self.panel.SetSizer(sizer)
        
        self.Bind(wx.EVT_BUTTON,self.onButtonFrame)
    
    def onButtonFrame(self,event):
        event_id = event.GetId()
        if event_id == ID_BUTTON1:
            print("BUTTON ONE event reached the Frame")
            event.Skip()
        elif event_id == ID_BUTTON2:
            print("BUTTON TWO event reached the Frame")
            
class MyPanel(wx.Panel):
    def __init__(self,parent):
        super(MyPanel,self).__init__(parent)
        self.Bind(wx.EVT_BUTTON,self.onButtonPanel)
        
    def onButtonPanel(self,event):
        event_id = event.GetId()
        if event_id == ID_BUTTON1:
            print("BUTTON ONE event reached the Panel")
            event.Skip()
        elif event_id == ID_BUTTON2:
            print("BUTTON TWO event reached the Panel")  
            # Not skipping the event will cause its
            # propagation to end 

app = MyApp(False)
app.MainLoop()

如何工作的?

wxPython:事件、键盘监听_第1张图片

键盘监听

# -*- coding: utf-8 -*-
"""
Created on Sun Jul 12 16:04:24 2020

@author: Administrator
"""

import wx
class MyFrame(wx.Frame):
    def __init__(self,parent,id=wx.ID_ANY,title='',
                 pos=wx.DefaultPosition,size=wx.DefaultSize,
                 style=wx.DEFAULT_FRAME_STYLE,
                 name='MyFrame'):
        super(MyFrame,self).__init__(parent,id,title,pos,
                                     size,style,name) 
        self.panel = wx.Panel(self)
        self.txtCtrl = wx.TextCtrl(self.panel,
                                   style=wx.TE_MULTILINE)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.txtCtrl,1,wx.EXPAND)
        self.panel.SetSizer(sizer)
        self.CreateStatusBar()
        
        self.txtCtrl.Bind(wx.EVT_KEY_DOWN,self.onKeyDown)    #键盘监听事件,只能绑定在那种,支持键盘的控件中(即 keyborad focus)
        self.txtCtrl.Bind(wx.EVT_CHAR,self.onChar)
        self.txtCtrl.Bind(wx.EVT_KEY_UP,self.onKeyUp)
        
    def onKeyDown(self,event):
        print('onKeyDown called')
        key_code = event.GetKeyCode()
        raw_code = event.GetRawKeyCode()   #key_code 和 raw_code 似乎没什么区别,打印出来都是一样的。应该差别是在编码方式的不同吧。
        modifiers = event.GetModifiers()
        # 所谓 modifiers 就是键盘上的 Ctrl 键之类的。
        msg = "key:%d,raw:%d,modifier:%d"%(key_code,
                                           raw_code,modifiers)
        self.PushStatusText("KeyDown: "+msg)
        event.Skip()    #如果不加上这句代码,那么 onChar、onKeyup 就不会被运行。
    
    def onChar(self,event):
        print('onChar called')
        key_code = event.GetKeyCode()
        raw_code = event.GetRawKeyCode()
        modifiers = event.GetModifiers()
        msg = 'key:%d,raw:%d,modifier:%d'%(key_code,
                                           raw_code,modifiers)
        
        if modifiers & wx.MOD_SHIFT:
            wx.Bell()   #响一下而已,别怕。
        elif chr(key_code) in 'aeiou':
            self.txtCtrl.AppendText('?')
        else:
            event.Skip()
        
    def onKeyUp(self,event):
        print('onKeyUp called')
        event.Skip()

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None,title='My App')
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

app = MyApp(False)
app.MainLoop()

更新你的 App

添加菜单栏

首先,在 MyFrame 的 _init_ 方法里,添加如下代码:

        ......
        menuBar = wx.MenuBar()
        editMenu = wx.Menu()
        editMenu.Append(wx.ID_COPY,"Copy\tCtrl+C")    #ID是 stock_ID
        editMenu.Append(wx.ID_CUT,"CUT\tCtrl+X")
        editMenu.Append(wx.ITEM_CHECK,"Selection Made?",
                        kind=wx.ITEM_CHECK)
        menuBar.Append(editMenu,"Edit")
        self.SetMenuBar(menuBar)
        self.Bind(wx.EVT_UPDATE_UI,self.onUpdateEditMenu)
   
   ......
        
    def onUpdateEditMenu(self,event):
        event_id = event.GetId()
        sel = self.txtCtrl.GetSelection()  #提取文本框中选中的对象
        has_sel = sel[0]!=sel[1]
        if event_id in (wx.ID_COPY, wx.ID_CUT):
            event.Enable(has_sel)
        elif event_id == wx.ITEM_CHECK:
            event.Check(has_sel)
        else:
            event.Skip()
 ......           

注册快捷键方法

名称 \t 快捷键,比如“Copy\tCtrl+X”

你可能感兴趣的:(Python,GUI开发,wx,wxpython,事件,键盘,GUI)