滚动面板MouseWheel滚动

时间:2015-09-06 02:47:43

标签: python wxpython

我希望你能帮助我:)我正在撞墙,我无法让滚动面板在我使用鼠标滚轮时上下滚动。当我点击它或拖动它时,它可以工作。

我正在做的是用一堆textctrls填充一个面板,并使用滚动面板滚动它们。我试图在鼠标悬停时设置面板的焦点,甚至在点击时尝试,但当鼠标滚轮在面板上上下滚动时没有任何反应。

我已经查看了ScrolledPanels的wxPython示例,但我无法理解为什么他们的工作正常而我的工作没有。

给予的任何帮助都会很棒!

编辑: 我发现当选择textctrl时它会激活滚动 - 但不会在使用TE_MULTILINE时激活。 Expando Text Ctrls使用TE_MULTILINE样式,因此只有在单击单行文本ctrls时才能重新启动滚动条。

如何在单击multi_line文本控件时控制滚动条以使用鼠标滚轮?

import wx
import random
import wx.lib.platebtn as pbtn
import wx.lib.agw.gradientbutton as gbtn
import wx.lib.scrolledpanel as spanel
import requests
import json
import sys
import wx.lib.mixins.listctrl  as  listmix
import wx.lib.expando as etc


ticketInfo = """
*************************\n
TEST TICKET\n
*************************\n
BLAH BLAH BLAH BLAH\n
BLAH BLAH BLAH BLAH\n
BLAH BLAH BLAH BLAH\n
BLAH BLAH BLAH BLAH\n
BLAH BLAH BLAH BLAH\n
"""


class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, title="Learning List Controls")
        self.SetTopWindow(self.frame)
        self.frame.Show()

        return True
class TicketViewer(wx.lib.scrolledpanel.ScrolledPanel):
    def __init__(self, parent):
        super(TicketViewer, self).__init__(parent, name="panel", style=wx.TE_AUTO_SCROLL)

        #Attributes
        vSizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(vSizer)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.PopulateTicketMessages(ticketInfo)
        self.SetSizer(vSizer)
        self.Layout()
        self.SetupScrolling()
        panelID = self.GetId()
    def PopulateTicketMessages(self, ticketInfo):
        msgBox = etc.ExpandoTextCtrl(self,
                         id=-1,
                         value=ticketInfo,
                         style=wx.TE_READONLY|wx.TE_WORDWRAP)
        sizer = self.GetSizer()
        sizer.Add(msgBox)
        msgBox.Bind(wx.EVT_LEFT_DOWN, self.OnMouseOver)
    def OnMouseOver(self, event):
        panel = wx.FindWindowByName("panel")
        panel.SetFocus()
        print(panel)
class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(MyFrame, self).__init__(*args, **kwargs)

        #Attribues
        self.tktViewer = TicketViewer(self)


        frameSizer = wx.BoxSizer(wx.HORIZONTAL)
        frameSizer.Add(self.tktViewer, 1, wx.EXPAND)
        self.SetSizer(frameSizer)

        #self.SetSizerAndFit(frameSizer)
        self.CreateStatusBar()
        self.SetInitialSize()

    def OnMouseOver(self, event):
        panel = wx.FindWindowById(tktViewerID)
        panel.SetFocus()
        print(self)

if __name__ == "__main__":
    app = MyApp(False)
    app.MainLoop()

1 个答案:

答案 0 :(得分:1)

原因是ScrolledPanel派生自wx.Panel,它的代码是每次收到焦点时都会尝试将焦点重置为第一个子节点。此外,在Windows中,轮事件仅发送到具有焦点的窗口小部件。 (Useful thread with Robin Dunn remarks

发生这种情况时的解决方案:

(1)在哪里举办活动?在应用程序级别。 正如关于事件表搜索顺序的wxpython文档中所述:How Events are Processed

  

最后,即,如果事件仍未处理,则为App对象   本身(源自EvtHandler)获得最后一次处理的机会   它

所以 wx.GetApp()。绑定(wx.EVT_MOUSEWHEEL,your_handler)

(2)如何手动处理事件? 从this post(由道格安德森根据pygcodeviewer/pygcodeviewer.py内的评论)

撰写的想法

需要在我们感兴趣的wxWindow上调用 GetHandler()。ProcessEvent(event)(滚动面板)

..考虑到我们正在抓住应用程序级别:我们必须确保该事件来自我们的wxWindow [滚动面板]的子项,如果没有,请跳过()它并让它继续。

所以,这是一个mixin类的解决方案:

class TeMultilineScrollAwareMixin(object):
    """ Mixin class for wx.lib.scrolledpanel.ScrolledPanel so it's
    aware of scroll events from childs with the TE_MULTILINE style
    """

    def __init__(self, *args, **kwargs):
        self._pLastEvtObj = None        
        wx.GetApp().Bind(wx.EVT_MOUSEWHEEL, self.OnUnhandledMouseWheel)

    def OnUnhandledMouseWheel(self, event):
        # stops the same event object being processed repeatedly
        if self._pLastEvtObj == event.GetEventObject():
            return False

        self._pLastEvtObj = event.GetEventObject()

        def is_child_evt():
            parent = event.GetEventObject().GetParent()
            while parent:
                if parent == self: 
                    return True
                parent = parent.GetParent() 
            return False

        # as we are listening at app level, must ensure that event is from a widget of us
        if is_child_evt():
            print 'event from %s' % event.GetEventObject().__class__.__name__
            # if the event was not handled this window will handle it,
            # which is why we need the protection code at the beginning
            # of this method
            self.GetEventHandler().ProcessEvent(event)            
        else:
            # it's not of our interest, let it go
            event.Skip()

        self._pLastEvtObj = None
        return

记得在添加完成后启动mixin并完成:

class TicketViewer(wx.lib.scrolledpanel.ScrolledPanel, TeMultilineScrollAwareMixin):
    def __init__(self, parent):
        ... your init code ...
        TeMultilineScrollAwareMixin.__init__(self)