始终打开wxPython GUI并选择滚动条

时间:2013-12-03 17:20:11

标签: python python-2.7 user-interface wxpython

我有几个逐步打开的GUI,它们都遇到了同样的问题。当我打开它们时,选择的第一个对象是TextCtrl对象。这些GUI相当大,并且有滚动条。由于选择了TextCtrl对象,因此使用鼠标滚轮滚动不会执行任何操作,并使其看起来好像滚动条已损坏。为了证明这一点,我做了以下代码:

import wx

class Tester(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Window", size=(500, 500))
        self.panel = wx.ScrolledWindow(self,wx.ID_ANY) 
        self.panel.SetScrollbars(30,30,600,400)

        textBox = wx.TextCtrl(panel, -1, "", size=(200, 150), style=wx.TE_MULTILINE|wx.TE_LEFT)
        textStuff = wx.StaticText(panel, -1, "A\nbunch\nof\nlines\nto\nmake\nthis\nlong\nenough\nto\nhave\nscroll\nbars\n\n\n\n\n\n\n\n\n\nIts lonely down here\n\n\n\n:(")
        lonelyBtn = wx.Button(panel, -1, "So Lonely")


        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(textBox, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)
        vbox.Add(textStuff, flag=wx.LEFT|wx.RIGHT|wx.TOP, border=10)
        vbox.Add(lonelyBtn, flag=wx.LEFT|wx.RIGHT|wx.TOP, border=10)

        panel.SetSizer(vbox)
        panel.Layout()

app = wx.PySimpleApp()
Tester().Show()
app.MainLoop()

当你运行它并尝试向下滚动时,你会注意到你不能用鼠标滚轮滚动。

到目前为止,这一切都有道理。这里有点奇怪。如果所选对象是Button,则使用鼠标滚轮接合滚动条。您可以通过按下按钮,然后使用鼠标滚轮进行测试。此外,单击面板,甚至滚动条本身,都不允许鼠标滚轮工作。

我正在寻找是一种确保如果有滚动条的方法,它们可以在显示GUI时与鼠标滚轮一起使用(默认选中它们)。我可以接受,一旦用户点击文本控件,鼠标滚轮就不会起作用。

此外,如果您有解释为什么鼠标滚轮适用于按钮而不是文本控件,我很乐意听到它

编辑:我知道我可以添加一个监听器(感谢Joran Beasley先生),但这意味着多行文本控件中的滚动条永远不会与鼠标滚轮一起使用。理想的解决方案(我不确定是否可行)是点击文本控件(面板或滚动条)外的任何位置,允许鼠标滚轮滚动面板。这可能吗?

此外,我已切换到使用ScrolledWindow而不是ScrolledPanel

编辑2:修复方法是使用以下内容:

self.panel.Bind(wx.EVT_MOTION, self.onMouseMove)

def onMouseMove(self, event):
        self.panel.SetFocusIgnoringChildren()

编辑3 实际的修复是为了做一些有点棘手的事情。使用下面的代码,我只将多行文字控件绑定到EVT_ENTER_WINDOWEVT_LEAVE_WINDOW,并将每个项目(以及面板本身)绑定到EVT_MOUSEWHEEL。然后逻辑self.inMLTxtCtrl跟踪鼠标是否超过任何多行文本控件

self.panel.Bind(wx.EVT_MOUSEWHEEL, self.onWheel)
for sizerItem in self.panel.GetSizer().GetChildren():
    try:
        if sizerItem.GetWindow().IsMultiLine():
            sizerItem.GetWindow().Bind(wx.EVT_ENTER_WINDOW, self.onMouseEnter)
            sizerItem.GetWindow().Bind(wx.EVT_LEAVE_WINDOW, self.onMouseLeave)
            sizerItem.GetWindow().Bind(wx.EVT_MOUSEWHEEL, self.onWheel)
    except:
        sizerItem.GetWindow().Bind(wx.EVT_MOUSEWHEEL, self.onWheel)

然后逻辑self.inMLTxtCtrl跟踪鼠标是否超过任何多行文字控件,如下所示。

def onMouseEnter(self, event):
    print "entering"
    self.inMLTxtCtrl = True

def onMouseLeave(self, event):
    print "leaving"
    self.inMLTxtCtrl = False

最后,onWheel()函数使用此逻辑标志来确定滚动的位置。如果在转动滚轮时鼠标位于多行文本控件中,它会尝试滚动该文本控件。然后,调用SetFocusIgnoringChildren()函数并滚动面板。由于面板和文本控件使用不同的滚动方法,因此需要try...except

def onWheel(self, event):
    if self.inMLTxtCtrl:
        print "in", event.GetWheelRotation()
    else:
        print "out", event.GetWheelRotation()
        self.panel.SetFocusIgnoringChildren()

    try:
        currScroll = self.panel.FindFocus().GetViewStart()
        newScroll = (currScroll[0],currScroll[1]- event.GetWheelRotation()/60)
        self.panel.FindFocus().Scroll(*newScroll)
    except:
        self.panel.FindFocus().ScrollLines(event.GetWheelRotation()/-60)

1 个答案:

答案 0 :(得分:1)

class Tester(wx.Frame):
    def OnTxtScroll(self,e):
        currScroll = self.panel.GetViewStart()
        newScroll = (currScroll[0],currScroll[1]- e.GetWheelRotation()/120)
        self.panel.Scroll(*newScroll)

    def __init__(self):
        ....
        #your code
        ....
        self.panel = panel
        textBox.Bind(wx.EVT_MOUSEWHEEL,self.OnTxtScroll)
在你澄清之后......我认为这会起作用(它有点hacky并且没有完全按照你的描述......但它可能有效)

 def OnTxtScroll(self,e):
        print dir(e)
        target = e.GetEventObject()
        p1 = target.GetScrollPos(wx.VERTICAL)
        e.Skip()
        self.Update()
        def updateScroll(p1,target,scroll_amt):
            p2 = target.GetScrollPos(wx.VERTICAL)
            if p1 ==p2:#scroll did not effect target object so lets scroll our main panel
                currScroll = self.panel.GetViewStart()
                newScroll = (currScroll[0],currScroll[1]- scroll_amt)
                self.panel.Scroll(*newScroll)
        wx.CallAfter(updateScroll,p1,target,e.GetWheelRotation()/120)