返回介绍

6.1 鼠标输入

发布于 2025-03-08 15:26:45 字数 6417 浏览 0 评论 0 收藏 0

总的说来,有两类的鼠标事件。基本的鼠标事件使用 wxMouseEvent 作为参数,不加任何翻译的发送给响应的窗口事件处理函数,而窗口事件处理函数则通常把它们翻译成对应的命令事件(wxCommandEvent)。

举例来说,当你在你的事件表中增加 EVT_BUTTON 事件映射宏的时候,它的处理函数的参数是一个由按钮产生的 wxCommandEvent 类型。而在控件内部,这个 wxCommandEvent 类型是按钮控件对 EVT_LEFT_DOWN 事件宏进行处理并将对应的鼠标事件翻译成 wxCommandEvent 事件的结果。(当然,在大多数平台上,按钮都是使用本地控件实现的,不需要自己处理底层的鼠标事件,但是对于别的定制的类来说,确是如此)

因为我们在前面已经介绍过处理命令事件的方法,我们将主要介绍怎样处理基本的鼠标事件。

你可以分别拦截鼠标左键,中键或者右键的鼠标按下,鼠标释放或者鼠标双击事件。你还可以拦截鼠标的移动事件(无论有没有鼠标按下)。你还可以拦截那些用来告诉你鼠标正在移入或者移出某个窗口的事件,最后,如果这个鼠标有滚轮,你还可以拦截鼠标的滚轮事件。

当你收到一个鼠标事件时,你可以获得鼠标按钮的状态信息,以及象 Shift,Alt 等等这些状态键的信息,你还可以获得鼠标指针相对于当前窗口客户区域左上角的座标值。

下表列出了所有对应的鼠标事件映射宏。需要注意的是,wxMouseEvent 事件是不会传递给父窗口处理的,所以,为了处理这个事件,你必须重载一个新的窗口类,或者重载一个新的 wxEvtHandler,然后将其挂载在某个窗口上,当然你还可以使用动态事件处理函数 Connect,相关内容参见第三章。

EVT_LEFT_DOWN(func用来处理 wxEVT_LEFT_DOWN 事件,在鼠标左键按下的时候产生。
EVT_LEFT_UP(func)用来处理 wxEVT_LEFT_UP 事件,在鼠标左键被释放的时候产生。
EVT_LEFT_DCLICK(func)用来处理 wxEVT_LEFT_DCLICK 事件,在鼠标左键被双击的时候产生。
EVT_MIDDLE_DOWN(func)用来处理 wxEVT_MIDDLE_DOWN 事件,在鼠标中键被按下的时候产生。
EVT_MIDDLE_UP(func)用来处理 wxEVT_MIDDLE_UP 事件,当鼠标中键被释放的时候产生。
EVT_MIDDLE_DCLICK(func)用来处理 wxEVT_MIDDLE_DCLICK 事件,在鼠标中键被双击的时候产生。
EVT_RIGHT_DOWN(func)用来处理 wxEVT_RIGHT_DOWN 事件,鼠标右键被按下的时候产生。
EVT_RIGHT_UP(func)用来处理 wxEVT_RIGHT_UP 事件,鼠标右键被释放的时候产生。
EVT_RIGHT_DCLICK(func)用来处理 wxEVT_RIGHT_DCLICK 事件,鼠标右键被双击的时候产生。
EVT_MOTION(func)用来处理 wxEVT_MOTION 事件,鼠标指针移动的时候产生。
EVT_ENTER_WINDOW(func)用来处理 wxEVT_ENTER_WINDOW 事件,鼠标指针移入某个窗口的时候产生。
EVT_LEAVE_WINDOW(func)用来处理 wxEVT_LEAVE_WINDOW 事件,鼠标移出某个窗口的时候产生。
EVT_MOUSEWHEEL(func)用来处理 wxEVT_MOUSEWHEEL 事件,鼠标滚轮滚动的时候产生。
EVT_MOUSE_EVENTS(func)用来处理所有的鼠标事件。

处理按钮和鼠标指针移动事件

按钮和指针移动事件是你想要处理的最主要的鼠标事件。

要检测当产生某个事件时状态键的状态,可以使用 AltDown,MetaDown,ControlDown 或者 ShiftDown 等函数.使用 CmdDown 函数来检测 Mac OS X 平台上的 Meta 键或者别的平台上的 Control 键的状态.本章稍后的"状态键变量"小节会对这些函数进行更详细的介绍。

要检测那个鼠标按钮正被按下,你可以使用 LeftIsDown, MiddleIsDown 和 RightIsDown 函数,或者你可以使用 wxMOUSE_BTN_LEFT, wxMOUSE_BTN_MIDDLE, wxMOUSE_BTN_RIGHT 或 wxMOUSE_BTN_ANY 参数来调用 Button 函数.要注意这些函数通常只是反应在事件产生那个时刻鼠标的状态,而不是反应鼠标的状态改变.(译者注:换句话说,两续同样按钮的两个事件中的按钮状态可能是一样的)。

在 Mac OS X 上,Command 键被翻译成 Meta,Option 键是 Alt.因为在 Mac 系统上通常使用的是一键鼠标,当用户按下 Control 键点击鼠标的时候将产生右键单击事件.因此在 MacOS 上没有按下 Control 键时进行右键单击这样的事件,除非你正在使用的是一个两键或者三键的鼠标。

你还可以用下面的函数来或者鼠标事件的类型:Dragging (某个键正按下时鼠标移动), Moving (鼠标正在移动而没有鼠标键被按下), Entering, Leaving, ButtonDown, ButtonUp, ButtonDClick, LeftClick, LeftDClick, LeftUp, RightClick, RightDClick, RightUp, ButtonUp 和 IsButton 等。

你可以使用 GetPosition 函数或者 GetX 和 GetY 函数获取鼠标指针当前的设备单位位置,也可以给 GetLogicalPosition 函数传递某个设备上下文参数以便得到对应的逻辑位置。

下面的例子演示了一个涂鸦程序中的鼠标处理过程:

BEGIN_EVENT_TABLE(DoodleCanvas, wxWindow)
    EVT_MOUSE_EVENTS(DoodleCanvas::OnMouseEvent)
END_EVENT_TABLE()
void DoodleCanvas::OnMouseEvent(wxMouseEvent& event)
{
    static DoodleSegment *s_currentSegment = NULL;
    wxPoint pt(event.GetPosition());
    if (s_currentSegment && event.LeftUp())
    {
        // 鼠标按钮释放的时候停止当前线段
        if (s_currentSegment->GetLines().GetCount() == 0)
        {
            // 释放线段记录并且释放指针
            delete s_currentSegment;
            s_currentSegment = (DoodleSegment *) NULL;
        }
        else
        {
            // 已经得到一个有效的线段,把它存下来
            DrawingDocument *doc = GetDocument();
            doc->GetCommandProcessor()->Submit(
            new DrawingCommand(wxT("Add Segment"), DOODLE_ADD,
                                    doc, s_currentSegment));
            doc->Modify(true);
            s_currentSegment = NULL;
        }
    }
    else if (m_lastX > -1 && m_lastY > -1 && event.Dragging())
    {
        //正在拖动鼠标,增加一行到当前的线段中
        if (!s_currentSegment)
            s_currentSegment = new DoodleSegment;
        DoodleLine *newLine = new DoodleLine(m_lastX, m_lastY, pt.x, pt.y);
        s_currentSegment->GetLines().Append(newLine);
        wxClientDC dc(this);
        DoPrepareDC(dc);
        dc.SetPen(*wxBLACK_PEN);
        dc.DrawLine( m_lastX, m_lastY, pt.x, pt.y);
    }
    m_lastX = pt.x;
    m_lastY = pt.y;
}

在上面的应用程序中,线段被存在文档类型.当用户使用鼠标左键在窗口上拖拽时,上面的函数增加一个线条到当前的线段中,并且把它画出来,当用户释放左键的时候,当前的线段被提交到文档类进行处理(文档类是 wxWidgets 的文档视图框架的一部分) ,以便进一步实现文档的重做或者撤消动作,而在窗口的 OnPaint 函数(代码没有被展示) 中,整个文档被重绘.在第 19 章"使用文档和视图"中,我们会完整的介绍这个例子。

如果想让这个程序更专业一点,可以在鼠标按下的时候捕获鼠标并且在鼠标释放的时候释放捕获,以便当鼠标左键按下并且移出窗口的时候仍然可以收到鼠标事件。

处理鼠标滚轮事件

当处理鼠标滚轮事件的时候,你可以使用 GetWheelRotation 函数获得滚轮滚过的位置的大小(可能为负数).用这个数除以 GetWheelDelta 以便得到实际滚动行数的值.多数的设备每个 GetWheelDelta 发送一个滚轮事件,但是将来的设备也许会以更快的频率发送事件,因此你需要进行这种计算以便只有在滚轮滚过一整行的时候才滚动窗口,或者如果你可以滚动半行也可以.你还要把用户在控制面板中设置的滚轮每次滚动数量计算进去,这个数目可以通过 GetLinesPerAction 函数获得,要乘以这个值来得到实际用户希望滚动的数量。

另外,鼠标滚轮还可以被设置为每次滚动一页,你需要调用 IsPageScroll 函数来判断是否属于这种情况。

我们来举个例子,下面的代码是 wxScrolledWindow 的默认滚轮处理事件处理函数,其中的变量 m_wheelRotation 对已经滚动的位置进行累加,只有在滚动超过一行的时候才进行滚动。

void wxScrollHelper::HandleOnMouseWheel(wxMouseEvent& event)
{
    m_wheelRotation += event.GetWheelRotation();
    int lines = m_wheelRotation / event.GetWheelDelta();
    m_wheelRotation -= lines * event.GetWheelDelta();
    if (lines != 0)
    {
        wxScrollWinEvent newEvent;
        newEvent.SetPosition(0);
        newEvent.SetOrientation(wxVERTICAL);
        newEvent.m_eventObject = m_win;
        if (event.IsPageScroll())
        {
            if (lines > 0)
                newEvent.m_eventType = wxEVT_SCROLLWIN_PAGEUP;
            else
                newEvent.m_eventType = wxEVT_SCROLLWIN_PAGEDOWN;
            m_win->GetEventHandler()->ProcessEvent(newEvent);
        }
        else
        {
            lines *= event.GetLinesPerAction();
            if (lines > 0)
                newEvent.m_eventType = wxEVT_SCROLLWIN_LINEUP;
            else
                newEvent.m_eventType = wxEVT_SCROLLWIN_LINEDOWN;
            int times = abs(lines);
            for (; times > 0; times)
                m_win->GetEventHandler()->ProcessEvent(newEvent);
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文