event

function enterHandler(event)
{
   var keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;
   if (keyCode == 13) {
    //处理函数
   }
}

就这上面一句代码,学问大着呢,下面贴参考资料:

一、onkeyup,onkeydown,onkeypress

  1. 在使用JavaScript做WEB键盘事件侦听捕获时,主要采用onkeypress、onkeydown、onkeyup三个事件进行出来。该三个事件的执行顺序如下:onkeydown -> onkeypress ->onkeyup。在一般情况下,采用三种键盘事件均可对键盘输入进行有效的响应。当在实际使用中,会发现这几者有些不同的差别。   
  2.         onkeypress事件不能对系统功能键(例如:后退、删除等,其中对中文输入法不能有效响应)进行正常的响应,onkeydown和onkeyup均可以对系统功能键进行有效的拦截,但事件截获的位置不同,可以根据具体的情况选择不同的键盘事件。   
  3.         由于onkeypress不能对系统功能键进行捕获,导致window.event对象的keyCode属性和onkeydown,onkeyup键盘事件中获取的keyCode属性不同,主要表现在onkeypress事件的keyCode对字母的大小写敏感,而onkeydown、onkeyup事件不敏感;onkeypress事件的keyCode无法区分主键盘上的数字键和付键盘数字键的,而onkeydown、onkeyup的keyCode对主付键盘的数字键敏感。   
  4. :在Maxthon浏览器中,onkeydown和onkeyup有连续响应两次键盘事件的BUG,onkeydown不能正常地对F1~F12的功能键进行正常的截获(onkeyup没有发现该问题),具体原因不明。不知道以后是否会进行订正。   
  5.   
  6.         键盘事件包括keydown、kepress和keyup三种,每次敲击键盘都会(依次?)触发这三种事件,其中keydown和keyup是比较低级的接近于硬件的事件,通俗的理解是这两个事件可以捕获到你敲击了键盘中某个键;而keypress是相对于字符层面的较为高级点的事件,这个事件能够捕捉到你键入了哪个字符。可以这样理解,如果你敲击了A键,keydown和keyup事件只是知道你敲击了A键,它并不知道你敲的是大写的A(你同时按下了Shift键)还是敲的是小写a,它是以"键"为单位,你敲入了大写的A,它只是当成你敲下了shift和A两个键而已,但是keypress可以捕捉到你是敲入的大写的A还是小写的a.   
  7.   
  8.         在介绍Prototype中Event对象前先介绍一下浏览器中的事件模型,浏览器中的事件主要有HTML事件、鼠标事件和键盘事件,前两种事件比较好理解,这里主要解释一下键盘事件以及它在IE和firefox中的区别.   
  9.   
  10.         还要理解一个概念是键盘中的键分为字符(可打印)键和功能键(不可打印),功能键包括Backspace, Enter, Escape, the arrow keys, Page Up, Page Down, and F1 through F12 等   
  11.   
  12.         下面说一下键盘事件的具体使用方法,   
  13.   
  14.         键盘事件的event对象中包含一个keyCode属性,IE中只有这一个属性,当为keydown和keyup 事件是,keycode属性表示你具体按下的键(也称为virtual keycode),当捕捉的是keypress事件时keyCode属性指的是你键入的字符(character code)   
  15.   
  16.         在firefox中情况有些不同,event对象包含一个keyCode属性和一个charCode属性,keydown和keyup事件的时候,keyCode表示的就是你具体按的键,charCode为0;当捕捉的是keypress事件时,keyCode为0,charCode指的是你按下的字符   
  17.   
  18.         当捕捉的是keypress事件时,当你按的是可打印字符时,keyCode为0,charCode指的是你按下的字符的键值,当你按的是不可打印字符时,keyCode为按下的键的键值,charCode为0  
  19.   
  20.         注意:功能键不会触发keypress事件,因为keypress对应的就是可打印的字符,但是有一点IE和FF 中的区别,你按下一个字符键的同时按下alt键,在IE中不触发keypress事件,但是在ff中可触发,我发现在IE中按下ctrl键的时候只有按下q键会触发事件其他的要么不会触发事件,要么被浏览器IE自身捕获了,例如你按下ctrl_A,全选某个东西,你按ctrl_S保存文件,但是在FF中就好多了,事件都是先传递到网页,再向外传递   
  21.   
  22.         鉴于IE和FF中的区别,如果你比较懒的话,建议只使用keydow和keyup事件,这两种事件的使用在IE和FF中基本上没有区别,也不要捕获ctrl_A等被浏览器定义为快捷键的事件   
  23.   
  24.         键盘事件event对象还有三个其他的属性altKey, ctrlKey, and shiftKey 来判断你按下一个键的时候是否按下了alt等键,这三个属性使用比较简单,三种事件都可以使用,也不存在ie和ff的兼容性问题

二、event.which

Netscape/Firefox/Opera中不支持 window.event.keyCode,需要用event.which代替,IE用event.keCode方法获取当前被按下的键盘按键值,而NetScape/FireFox/Opera用的则是event.which

 

 

例如:屏蔽鼠标右键、Ctrl+n、shift+F10、F5刷新、退格键
function KeyDown(){
 //屏蔽鼠标右键、Ctrl+N、Shift+F10、F5刷新、退格键
  if ((window.event.altKey)&&
      ((window.event.keyCode==37)||   //屏蔽 Alt+ 方向键 ←
       (window.event.keyCode==39))){  //屏蔽 Alt+ 方向键 →
     event.returnValue=false;//防止使用ALT+方向键前进或后退网页
  }
  if ((event.keyCode==8) ||      //屏蔽退格删除键
      (event.keyCode==116)||   //屏蔽 F5 刷新键
      (event.keyCode==112)||   //屏蔽 F1 刷新键 bitsCN.com中国网管联盟
      (event.ctrlKey && event.keyCode==82)){ //Ctrl + R
     event.keyCode=0;
     event.returnValue=false;
  }
  if ((event.ctrlKey)&&(event.keyCode==78))   //屏蔽Ctrl+N
     event.returnValue=false;
  if ((event.shiftKey)&&(event.keyCode==121)) //屏蔽Shift+F10
     event.returnValue=false;
  if (window.event.srcElement.tagName == "A" && window.event.shiftKey)
      window.event.returnValue = false;  //屏蔽 shift 加鼠标左键新开一网页
  if ((window.event.altKey)&&(window.event.keyCode==115)){ //屏蔽Alt+F4
      window.showModelessDialog("about:blank","","dialogWidth:1px;dialogHeight:1px");
      return false;}
}

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/woxingwosu13/archive/2007/05/14/1609041.aspx

 

修改:File "C:\Users\Administrator\Desktop\project\core\event_center.py", line 114 def subscribe(self, event_type, callback): """订阅事件""" if event_type not in self.subscribers: self.subscribers[event_type] = [] 已重新声明上文定义的无用法的 'subscribe'未使用局部函数 '__init__',从外部作用域隐藏名称 'self'函数中的变量应小写,从外部作用域隐藏名称 'event'代码#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 优化后的事件中心模块 (最终稳定版) """ import logging import threading import uuid import time from enum import Enum, auto from typing import Tuple, Dict, List, Callable, Optional, Any, Set, DefaultDict, Union from collections import defaultdict from dataclasses import dataclass, field from concurrent.futures import ThreadPoolExecutor # 初始化日志 logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) class EventType(Enum): """完整事件类型枚举""" # 基础事件类型 MODULE_RUN = auto() ANALYSIS_RESULT = auto() MODULE_ERROR = auto() REGISTER_UI = auto() GET_RESULTS = auto() # 系统事件 SYSTEM_STARTUP = auto() SYSTEM_SHUTDOWN = auto() MODULE_READY = auto() ERROR = auto() # 模块特定事件 INPUT_ANALYSIS_START = auto() INPUT_ANALYSIS_END = auto() COMBINATION_ANALYSIS_START = auto() COMBINATION_ANALYSIS_END = auto() FOLLOW_ANALYSIS_START = auto() FOLLOW_ANALYSIS_UPDATE = auto() TREND_ANALYSIS_REQUEST = auto() TREND_ANALYSIS_REPORT = auto() NUMBER_GENERATION_START = auto() NUMBER_GENERATION_FINISH = auto() # 测试事件 TEST_EVENT = auto() @dataclass class Event: """事件数据类""" type: Union[str, EventType] # 必须放在非默认参数位置 source: str # 必须放在非默认参数位置 target: Optional[str] = None event_id: str = field(default_factory=lambda: str(uuid.uuid4())) token: Optional[str] = None data: Optional[Dict[str, Any]] = field(default_factory=dict) timestamp: float = field(default_factory=time.time) def __post_init__(self): """数据验证和类型转换""" if isinstance(self.type, EventType): self.type = self.type.name if not isinstance(self.type, str) or not self.type: raise ValueError("type 必须是非空字符串或EventType枚举") if not isinstance(self.source, str) or not self.source: raise ValueError("source 必须是非空字符串") class EventCenter: """线程安全的事件中心(最终优化版)""" _instance = None _lock = threading.Lock() _executor = ThreadPoolExecutor(max_workers=10, thread_name_prefix="EventWorker") def __new__(cls): with cls._lock: if cls._instance is None: cls._instance = super().__new__(cls) cls._instance.__initialized = False return cls._instance def __init__(self): """初始化事件中心(单例模式)""" def __init__(self): self.subscribers = {} if getattr(self, '__initialized', False): return self.__initialized = True self._subscribers: DefaultDict[str, List[Tuple[Callable[[Event], None], Optional[str]]]] = defaultdict(list) self._event_history: Dict[str, Event] = {} self._pending_acks: Set[str] = set() self._ack_timeout = 5.0 self._stats = { 'total_events': 0, 'failed_events': 0, 'delivered_events': 0 } def subscribe(self, event_type: Union[str, EventType], handler: Callable[[Event], None], token: Optional[str] = None) -> None: """订阅事件(支持token过滤)""" event_type_str = event_type.name if isinstance(event_type, EventType) else str(event_type) with self._lock: self._subscribers[event_type_str].append((handler, token)) logger.debug(f"已订阅事件: {event_type_str}, token: {token}") def subscribe(self, event_type, callback): """订阅事件""" if event_type not in self.subscribers: self.subscribers[event_type] = [] self.subscribers[event_type].append(callback) def publish(self, event_type, data): """发布事件""" if event_type in self.subscribers: for callback in self.subscribers[event_type]: callback(data) def unsubscribe(self, event_type: Union[str, EventType], handler: Callable[[Event], None]) -> bool: """取消订阅事件""" event_type_str = event_type.name if isinstance(event_type, EventType) else str(event_type) with self._lock: if event_type_str not in self._subscribers: return False before = len(self._subscribers[event_type_str]) self._subscribers[event_type_str] = [ (h, t) for h, t in self._subscribers[event_type_str] if h != handler ] removed = before != len(self._subscribers[event_type_str]) if removed: logger.debug(f"已取消订阅: {event_type_str}") return removed def publish(self, event, require_ack=False, async_handle=True) -> bool: """发布事件(支持同步/异步处理)""" # 增强事件验证 try: # 确保事件有必要的属性 if not hasattr(event, 'type') or not hasattr(event, 'source'): raise AttributeError("事件缺少必要属性: type或source") # 自动生成事件ID(如果缺失) if not hasattr(event, 'event_id') or not event.event_id: event.event_id = str(uuid.uuid4()) # 执行事件验证 if hasattr(event, '__post_init__'): event.__post_init__() else: # 手动验证基本属性 if not isinstance(event.type, (str, EventType)): raise TypeError("type必须是字符串或EventType") if not isinstance(event.source, str) or not event.source: raise ValueError("source必须是非空字符串") except (ValueError, TypeError, AttributeError) as e: # 处理预期的验证错误 logger.error(f"事件验证失败: {e}") with self._lock: self._stats['failed_events'] += 1 return False except Exception as e: # 记录所有其他异常 logger.exception(f"事件验证过程中发生意外错误: {e}") with self._lock: self._stats['failed_events'] += 1 return False # 主事件处理逻辑 try: with self._lock: if event.event_id in self._event_history: logger.warning(f"重复事件ID: {event.event_id}") return False self._event_history[event.event_id] = event self._stats['total_events'] += 1 if require_ack: self._pending_acks.add(event.event_id) handlers = self._get_matching_handlers(event) if not handlers: logger.debug(f"没有处理器订阅: {event.type}") return True if async_handle: self._executor.submit(self._dispatch_event, event, handlers, require_ack) else: self._dispatch_event(event, handlers, require_ack) return True except (KeyError, AttributeError) as e: # 处理内部数据结构错误 logger.error(f"内部数据结构错误: {e}") with self._lock: self._stats['failed_events'] += 1 return False except Exception as e: # 捕获所有其他异常 logger.exception(f"发布事件时发生未预期错误: {e}") with self._lock: self._stats['failed_events'] += 1 return False def _get_matching_handlers(self, event: Event) -> List[Callable[[Event], None]]: """获取匹配的事件处理器""" with self._lock: return [ h for h, t in self._subscribers.get(event.type, []) if t is None or t == event.token ] def _dispatch_event(self, event: Event, handlers: List[Callable[[Event], None]], require_ack: bool) -> None: """分发事件到处理器""" for handler in handlers: try: handler(event) with self._lock: self._stats['delivered_events'] += 1 logger.debug(f"事件处理成功: {event.event_id[:8]}") except Exception as e: logger.exception(f"处理器异常: {e}") if require_ack and event.target: self._wait_for_ack(event) def _wait_for_ack(self, event: Event) -> None: """等待事件确认""" start = time.time() while time.time() - start < self._ack_timeout: with self._lock: if event.event_id not in self._pending_acks: return time.sleep(0.05) logger.warning(f"事件确认超时: {event.event_id[:8]}") def get_stats(self) -> Dict[str, int]: """获取事件中心统计信息""" with self._lock: return self._stats.copy() def clear(self): """重置事件中心状态(测试专用)""" with self._lock: self._subscribers.clear() self._event_history.clear() self._pending_acks.clear() self._stats = { 'total_events': 0, 'failed_events': 0, 'delivered_events': 0 } # 全局单例实例 event_center = EventCenter() # ======================== 完整测试套件 ======================== import pytest from dataclasses import make_dataclass @pytest.fixture def event_center_instance(): """创建独立的EventCenter实例用于测试""" center = EventCenter() center.clear() return center def test_event_subscription(event_center_instance): """测试事件订阅机制""" handled_events = [] def handler(event: Event): handled_events.append(event) # 订阅并发布测试事件 event_center_instance.subscribe(EventType.TEST_EVENT, handler) test_event = Event( type=EventType.TEST_EVENT, source="pytest", data={"test": "subscription"} ) assert event_center_instance.publish(test_event) is True time.sleep(0.1) # 等待异步处理完成 assert len(handled_events) == 1 assert handled_events[0].data["test"] == "subscription" stats = event_center_instance.get_stats() assert stats["total_events"] == 1 assert stats["delivered_events"] == 1 assert stats["failed_events"] == 0 def test_event_validation_failure(event_center_instance): """测试无效事件处理""" # 用例1:完全无效的事件对象 class InvalidEvent: pass # 用例2:缺少必要属性的事件 PartialEvent = make_dataclass('PartialEvent', [('type', str)], namespace={'source': None}) partial_event = PartialEvent(type="TEST") # 用例3:属性类型错误的事件 @dataclass class WrongTypeEvent: type: int = 123 # 应该是字符串或EventType source: str = "test" # 用例4:空source属性的事件 @dataclass class EmptySourceEvent: type: str = "TEST" source: str = "" # 空字符串 for invalid_event in [InvalidEvent(), partial_event, WrongTypeEvent(), EmptySourceEvent()]: assert event_center_instance.publish(invalid_event) is False # 验证统计计数 stats = event_center_instance.get_stats() assert stats["total_events"] == 0 assert stats["failed_events"] == 4 assert stats["delivered_events"] == 0 def test_duplicate_event_id(event_center_instance): """测试重复事件ID检测""" event = Event( type=EventType.TEST_EVENT, source="pytest", event_id="fixed-id-123" ) # 首次发布应成功 assert event_center_instance.publish(event) is True # 重复发布应失败 assert event_center_instance.publish(event) is False stats = event_center_instance.get_stats() assert stats["total_events"] == 1 assert stats["failed_events"] == 0 # 重复事件不算验证失败 assert stats["delivered_events"] == 0 # 没有订阅者 def test_token_filtering(event_center_instance): """测试基于token的事件过滤""" handled_events = [] def handler(event: Event): handled_events.append(event) # 订阅特定token的事件 event_center_instance.subscribe(EventType.TEST_EVENT, handler, token="secret") # 发布不带token的事件(不应被处理) event1 = Event( type=EventType.TEST_EVENT, source="pytest", data={"test": "no token"} ) # 发布带错误token的事件(不应被处理) event2 = Event( type=EventType.TEST_EVENT, source="pytest", token="wrong", data={"test": "wrong token"} ) # 发布正确token的事件(应被处理) event3 = Event( type=EventType.TEST_EVENT, source="pytest", token="secret", data={"test": "correct token"} ) assert event_center_instance.publish(event1) is True assert event_center_instance.publish(event2) is True assert event_center_instance.publish(event3) is True time.sleep(0.1) # 等待异步处理完成 assert len(handled_events) == 1 assert handled_events[0].data["test"] == "correct token" stats = event_center_instance.get_stats() assert stats["total_events"] == 3 assert stats["delivered_events"] == 1 assert stats["failed_events"] == 0 def test_async_handling(event_center_instance): """测试异步事件处理""" handled_events = [] def slow_handler(event: Event): time.sleep(0.2) handled_events.append(event) event_center_instance.subscribe(EventType.TEST_EVENT, slow_handler) # 发布两个事件 event1 = Event(type=EventType.TEST_EVENT, source="pytest", data={"id": 1}) event2 = Event(type=EventType.TEST_EVENT, source="pytest", data={"id": 2}) # 异步发布 assert event_center_instance.publish(event1) is True assert event_center_instance.publish(event2) is True # 立即检查(应尚未处理) assert len(handled_events) == 0 # 等待足够时间 time.sleep(0.3) assert len(handled_events) == 2 assert {e.data['id'] for e in handled_events} == {1, 2} def test_ack_mechanism(event_center_instance): """测试事件确认机制""" ack_received = False def ack_handler(event: Event): nonlocal ack_received # 模拟目标模块发送ACK if event.type == "ACK": ack_received = True # 从待确认集合中移除 with event_center_instance._lock: if event.data.get("ack_for") in event_center_instance._pending_acks: event_center_instance._pending_acks.remove(event.data["ack_for"]) # 订阅ACK事件(模拟) event_center_instance.subscribe("ACK", ack_handler) # 发布需要ACK的事件 event = Event( type=EventType.TEST_EVENT, source="pytest", target="test_target", data={"require_ack": True} ) # 发布测试事件(要求ACK) assert event_center_instance.publish(event, require_ack=True) is True # 检查事件是否在待确认集合中 with event_center_instance._lock: assert event.event_id in event_center_instance._pending_acks # 发布ACK事件(模拟目标模块的响应) ack_event = Event( type="ACK", source="test_target", data={"ack_for": event.event_id} ) event_center_instance.publish(ack_event) # 等待ACK处理 time.sleep(0.1) assert ack_received is True # 检查pending_acks应被移除 with event_center_instance._lock: assert event.event_id not in event_center_instance._pending_acks def test_event_auto_id_generation(event_center_instance): """测试自动生成事件ID""" # 创建没有event_id的事件 event = Event( type=EventType.TEST_EVENT, source="pytest", data={"auto_id": True} ) del event.event_id # 移除自动生成的ID assert event_center_instance.publish(event) is True assert hasattr(event, 'event_id') and event.event_id assert len(event.event_id) == 36 # UUID长度验证 def test_concurrent_publishing(event_center_instance): """测试并发事件发布""" from concurrent.futures import ThreadPoolExecutor handled_events = [] event_count = 50 def handler(event: Event): handled_events.append(event) event_center_instance.subscribe(EventType.TEST_EVENT, handler) def publish_event(i): event = Event( type=EventType.TEST_EVENT, source=f"thread-{i}", data={"index": i} ) event_center_instance.publish(event) # 使用线程池并发发布事件 with ThreadPoolExecutor(max_workers=10) as executor: executor.map(publish_event, range(event_count)) # 等待所有事件处理完成 time.sleep(0.5) assert len(handled_events) == event_count assert len({e.data['index'] for e in handled_events}) == event_count
最新发布
08-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值