【问题标题】:Always open wxPython GUI with scroll bar selected始终打开 wxPython GUI 并选择滚动条
【发布时间】:2013-12-19 21:23:29
【问题描述】:

我有几个逐步打开的 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)

【问题讨论】:

  • ...您当然可以将 wx.EVT_MOUSEWHEEL 的侦听器添加到文本字段以将滚动重定向到滚动条
  • 听起来很理想,我该怎么做?
  • 更新了我的答案,使其更符合您的要求....调整到 ScrolledWindow 很容易)

标签: python python-2.7 user-interface wxpython


【解决方案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)

在你澄清之后......我认为这会起作用(它有点老套,并且不完全按照你的描述......但它可能会起作用)

 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)

【讨论】:

  • 这有一点小麻烦,鼠标滚轮现在总是滚动面板,而不是文本控件,这是有道理的,但它不是我想做的权衡。 +1 因为它有效,但我不想接受它。我会澄清这个问题,使我正在寻找的更清楚
  • 我再次修改了我的问题,并提供了更好的解决方案,以防您好奇。再次感谢,如果没有您的回答,我不会想到这一点!
猜你喜欢
  • 1970-01-01
  • 2017-05-02
  • 1970-01-01
  • 2020-04-28
  • 1970-01-01
  • 2018-05-22
  • 2021-04-11
  • 2017-01-05
  • 1970-01-01
相关资源
最近更新 更多