wx.ScrolledWindow可以实现一个带有纵向和(或)横向滚动条的容器,它的继承关系如下图:
设置client area大小:
SetVirtualSize( self, wx.Size size ) #这个方法是从wx.Window继承来的,用来设置窗口可视面积的大小,单位是像素。
设置滚动条的滚动增量:
SetScrollRate( self, xstep, ystep )
如果自己在ScrolledWindow里面绘图应当注意,底层系统对于滚动窗口坐标(也就是相对于整个滚动窗口左上角的坐标)是一无所知的,它只知道屏幕上的物理坐标。例如:在位置(10,10)插入一个子窗口,然后将它向下移动100个像素,这时子窗口已经不在可见区域内了,那么它会报告自己的坐标为(10,-90)。
与所有的窗口一样,你可以使用设备上下文在上面绘图。你可以使用OnPaint handler,或者覆盖OnDraw函数(会有一个pre-scrolled device context传递进来,那是个用DoPrepareDC处理过的设备上下文)。如果你不想自己计算滚动坐标的话,那么在OnDraw函数以外绘图时你就必须使用DoPrepareDC,它能够根据当前的滚动位置来设置设备上下文的设备原点。
在wxPython中,滚动条不是框架本身的一个元素,而是被类wx.ScrolledWindow控制。你可以在任何你要使用wx.Panel的地方使用wx.ScrolledWindow,并且滚动条移动所有在滚动窗口中的项目。
创建一个简单的滚动窗口:
import wx
class ScrollbarFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Scrollbar Example',
size=(300, 200))
self.scroll = wx.ScrolledWindow(self, -1)
self.scroll.SetScrollbars(1, 1, 600, 400)
self.button = wx.Button(self.scroll, -1, "Scroll Me", pos=(50, 20))
self.Bind(wx.EVT_BUTTON, self.OnClickTop, self.button)
self.button2 = wx.Button(self.scroll, -1, "Scroll Back", pos=(500, 350))
self.Bind(wx.EVT_BUTTON, self.OnClickBottom, self.button2)
def OnClickTop(self, event):
self.scroll.Scroll(600, 400)
def OnClickBottom(self, event):
self.scroll.Scroll(1, 1)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = ScrollbarFrame()
frame.Show()
app.MainLoop()
wx.ScrolledWindow的构造函数几乎与wx.Panel的相同:
wx.ScrolledWindow(parent, id=-1, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.HSCROLL | wx.VSCROLL,
name="scrolledWindow")
所有的这些属性的行为都如你所愿,尽管size属性是它的父亲中的面板的物理尺寸,而非滚动窗口的逻辑尺寸。
指定滚动区域的尺寸
有几个自动指定滚动区域尺寸的方法。手工指定最多的方法如例8.1所示,使用了方法SetScrollBars:
SetScrollbars(pixelsPerUnitX, pixelsPerUnitY, noUnitsX, noUnitsY,
xPos=0, yPos=0, noRefresh=False)
其中关键的概念是滚动单位,它是滚动条的一次移动所引起的窗口中的转移距离。前面的两个参数pixelsPerUnitX和PixelsPerUnitY使你能够在两个方向设置滚动单位的大小。接下来的两个参数noUnitsX和noUnitsY使你能够按滚动单位设置滚动区域的尺寸。换句话说,滚动区域的象素尺寸是(pixelsPerUnitX* noUnitsX, pixelsPerUnitY * noUnitsY)。例8.7通过将滚动单位设为1像素而避免了可能造成的混淆。参数xPos和yPos以滚动单位(非像素)为单位,它设置滚动条的初始位置,如果参数noRefresh为true,那么就阻止了在因SetScrollbars()的调用而引起的滚动后的窗口的自动刷新。
还有另外的三个方法,你可以用来设置滚动区域的尺寸,然后单独设置滚动率。你可能发现这些方法更容易使用,因为它们使你能够更直接地指定尺寸。你可以如下以像素为单位使用滚动窗口的SetVirtualSize()方法来直接设置尺寸。
self.scroll.SetVirtualSize((600, 400))
使用方法FitInside(),你可以在滚动区域中设置窗口部件,以便滚动窗口绑定它们。这个方法设置滚动窗口的边界,以使滚动窗口刚好适合其中的所有子窗口:
self.scroll.FitInside()
通常使用FitInside()的情况是,当在滚动窗口中正好有一个窗口部件(如文本域),并且该窗口部件的逻辑尺寸已被设置。如果我们在例8.7中使用了FitInside(),那么一个更小的滚动区域将被创建,因为该区域将正好匹配右下按钮的边缘,而没有多余的内边距。
最后,如果滚动窗口中有一个sizer设置,那么使用SetSizer()设置滚动区域为sizer所管理的窗口部件的尺寸。这是在一个复杂的布局中最常用的机制。关于sizer的更多细节参见第11章。
对于上述所有三种机制,滚动率需要去使用SetScrollRate()方法单独设置,如下所示:
self.scroll.SetScrollRate(1, 1)
参数分别是x和y方向的滚动单位尺寸。大于0的尺寸都是有效的。
滚动条事件
在例8.7中的按钮事件处理器,使用Scroll()方法程序化地改变滚动条的位置。这个方法需要滚动窗口的x和y坐标,使用的是滚动单位。
在第7章中,我们答应了你可以捕获的来自滚动条的事件列表,因为它们也被用来去控制滑块。表8.7列出了所有被滚动窗口内在处理的滚动事件。通常,许多你不会用到,除非你建造自定义窗口部件。
滚动条的事件
EVT_SCROLL:当任何滚动事件被触发时发生。
EVT_SCROLL_BOTTOM:当用户移动滚动条到它的范围的最末端时触发(底边或右边,依赖于方向)。
EVT_SCROLL_ENDSCROLL:在微软的Windows中,任何滚动会话的结束都将触发该事件,不管是因鼠标拖动或按键按下。
EVT_SCROLL_LINEDOWN:当用户向下滚动一行时触发。
EVT_SCROLL_LINEUP:当用户向上滚动一行时触发。
EVT_SCROLL_PAGEDOWN:当用户向下滚动一页时触发。
EVT_SCROLL_PAGEUP:当用户向上滚动一页时触发。
EVT_SCROLL_THUMBRELEASE:用户使用鼠标拖动滚动条滚动不超过一页的范围,并释放鼠标后,触发该事件。
EVT_SCROLL_THUMBTRACK:滚动条在一页内被拖动时不断的触发。
EVT_SCROLL_TOP:当用户移动滚动条到它的范围的最始端时触发,可能是顶端或左边,依赖于方向而定。
行和页的准确定义依赖于你所设定的滚动单位,一行是一个滚动单位,一页是滚动窗口中可见部分的全部滚动单位的数量。对于表中所列出的每个EVT_SCROLL*事件,都有一个相应的EVT_SCROLLWIN*事件(它们由wx.ScrolledWindow产生)来回应。
还有一个wxPython的特殊的滚动窗口子类:wx.lib.scrolledpanel.ScrolledPanel,它使得你能够在面板上自动地设置滚动,该面板使用一个sizer来管理子窗口部件的布局。wx.lib.scrolledpanel.ScrolledPanel增加的好处是,它让用户能够使用tab键来在子窗口部件间切换。面板自动滚动,使新获得焦点的窗口部件进入视野。要使用wx.lib.scrolledpanel.ScrolledPanel,就要像一个滚动窗口一样声明它,然后,在所有的子窗口被添加后,调用下面的方法:
SetupScrolling(self, scroll_x=True, scroll_y=True, rate_x=20,
rate_y=20)
rate_x和rate_y是窗口的滚动单位,该类自动根据sizer所计算的子窗口部件的尺寸设定虚拟尺寸(virtual size)。
记住,当确定滚动窗口中的窗口部件的位置的时候,该位置总是窗口部件的物理位置,它相对于显示器中的滚动窗口的实际原点,而非窗口部件相对于显示器虚拟尺寸(virtual size)的逻辑位置。这始终是成立的,即使窗口部件不再可见。例如,在敲击了图8.5中的Scroll Me按钮后,该按钮所报告的它的位置是(-277,-237)。如果这不的你所想要的,那么使用CalcScrolledPosition(x,y)和。CalcUnscrolledPosition(x, y)方法在显示器坐标和逻辑坐标之间切换。在这两种情况中,在按钮敲击并使滚动条移动到底部后,你传递指针的坐标,并且滚动窗口返回一个(x,y)元组,如下所示:
CalcUnscrolledPostion(-277, -237) 返回(50, 20)