wxPython GridSizer未附加到面板?
我正在为我正在开发的游戏制作一个关卡编辑器。这个编辑器会从一个简单的文件中读取数据,然后通过逐字节的搜索来组合一个由预设图块组成的网格。这部分我应该不会有问题。不过,我的测试版编辑器在加载一个16x16的测试图块(从00到FF)时,出现了加载位置错误的问题。
举个例子:这个应用的框架看起来是这样的:
|-T-------|
| | |
| | |
| | |
| | |
|---------|
虽然我的ASCII艺术画得很糟糕,但这个框架基本上有一个水平的布局器,上面有两个垂直的布局器,一个在左边,一个在右边。每个布局器里都有一个面板,每个面板里还有一个第二个布局器。左边的布局器里有一个64像素宽的间隔,然后是一个网格布局器,后面会填充图像。右边的第二个布局器是用户可调整大小的,但最小宽度是960像素,里面有一个间隔,然后是一个根据代码中关卡的宽度和高度决定的网格布局器。
简单来说,每一侧都有一个网格布局器,里面有一个间隔来设置该部分的宽度,这些都在一个面板里,而这个面板又在主框架的布局器里。我希望这样解释能让你明白,因为有时候我自己也会感到困惑 :P
下面是实现这些功能的代码:
#Horizontal sizer
self.h_sizer = wx.BoxSizer(wx.HORIZONTAL)
#Vertical sizer
self.v_sizer_left = wx.BoxSizer(wx.VERTICAL)
self.v_sizer_right = wx.BoxSizer(wx.VERTICAL)
#Create the 2 panels
self.leftPanel = wx.ScrolledWindow(self, style = wx.VSCROLL | wx.ALWAYS_SHOW_SB)
self.leftPanel.EnableScrolling(False, True)
self.rightPanel = wx.ScrolledWindow(self, style = wx.VSCROLL | wx.ALWAYS_SHOW_SB)
self.rightPanel.EnableScrolling(False, True)
#Create a sizer for the contents of the left panel
self.lps = wx.BoxSizer(wx.VERTICAL)
self.lps.Add((64, 0)) #Add a spacer into the sizer to force it to be 64px wide
self.leftPanelSizer = wx.GridSizer(256, 1, 2, 2) # horizontal rows, vertical rows, vgap, hgap
self.lps.Add(self.leftPanelSizer) #Add the tiles grid to the left panel sizer
self.leftPanel.SetSizerAndFit(self.lps) #Set the leftPanel to use LeftPanelSizer (it's not lupus) as the sizer
self.leftPanel.SetScrollbars(0,66,0, 0) #Add the scrollbar, increment in 64 pixel bits plus the 2 spacer pixels
self.leftPanel.SetAutoLayout(True)
self.lps.Fit(self.leftPanel)
#Create a sizer for the contents of the right panel
self.rps = wx.BoxSizer(wx.VERTICAL)
self.rps.Add((960, 0)) #Set it's width and height to be the window's, for now, with a spacer
self.rightPanelSizer = wx.GridSizer(16, 16, 0, 0) # horizontal rows, vertical rows, vgap, hgap
self.rps.Add(self.rightPanelSizer) #Add the level grid to the right panel sizer
self.rightPanel.SetSizerAndFit(self.rps) #Set the rightPanel to use RightPanelSizer as the sizer
self.rightPanel.SetScrollbars(64,64,0, 0) #Add the scrollbar, increment in 64 pixel bits - vertical and horizontal
self.rightPanel.SetAutoLayout(True)
self.rps.Fit(self.rightPanel)
#Debugging purposes. Colours :)
self.leftPanel.SetBackgroundColour((0,255,0))
self.rightPanel.SetBackgroundColour((0,128,128))
#Add the left panel to the left vertical sizer, tell it to resize with the window (0 does not resize, 1 does). Do not expand the sizer on this side.
self.v_sizer_left.Add(self.leftPanel, 1)
#Add the right panel to the right vertical sizer, tell it to resize with the window (0 does not resize, 1 does) Expand the sizer to fit as much as possible on this side.
self.v_sizer_right.Add(self.rightPanel, 1, wx.EXPAND)
#Now add the 2 vertical sizers to the horizontal sizer.
self.h_sizer.Add(self.v_sizer_left, 0, wx.EXPAND) #First the left one...
self.h_sizer.Add((0,704)) #Add in a spacer between the two to get the vertical window size correct...
self.h_sizer.Add(self.v_sizer_right, 1, wx.EXPAND) #And finally the right hand frame.
在获取到所有数据后,应用会使用这些数据从指定的目录生成关卡布局,使用的是.png格式的图块。但为了测试,我只是生成一个从00到FF的16x16网格,正如前面提到的,通过一个菜单选项调用这个方法:
def populateLevelGrid(self, id):
#This debug method fills the level grid scrollbar with 256 example image tiles
levelTileset = self.levelTilesetLookup[id]
#levelWidth = self.levelWidthLookup[id]
#levelHeight = self.levelHeightLookup[id]
#levelTileTotal = levelWidth * levelHeight
levelTileTotal = 256 #debug line
self.imgPanelGrid = []
for i in range(levelTileTotal):
self.imgPanelGrid.append(ImgPanel.ImgPanel(self, False))
self.rightPanelSizer.Add(self.imgPanelGrid[i])
self.imgPanelGrid[i].set_image("tiles/"+ levelTileset + "/" + str(i) + ".png")
self.rightPanelSizer.Layout()
self.rightPanelSizer.FitInside(self.rightPanel)
这个方法是有效的,但它把网格固定在整个框架的左上角,而不是应该在右半部分的左上角。左边框架也有类似的代码来生成一个1x256的网格,但由于明显的原因,我无法判断它是否也有同样的问题。两个框架都有正常工作的滚动条,但在滚动时会出现重绘问题,这让我怀疑它是否在整个应用程序上绘制图像,而忽略了应用程序的布局。
我是不是漏掉了什么?这是我第一次在Python中处理网格布局器、图像和图形用户界面,之前我想写一些比VB6更跨平台的东西,所以最近才开始学习这门语言——有什么想法吗? =/
1 个回答
这段代码挺多的,还有很多布局管理器!我觉得你的 ImgPanels 应该把 rightPanel 作为它们的父级,而不是自己。
另外,你有没有调用过 self.SetSizer(self.h_sizer) 呢?我没看到你在任何地方使用过这个。
我建议把布局的不同部分放在单独的函数里。这样你就不用担心你的本地命名空间会被这些奇怪的布局管理器名字搞得一团糟。每个布局层级的部分都可以有一个函数。
create_controls
create_left_panel
create_grid
create_right_panel
还有,我通常会在子控件上使用 SetMinSize,而不是在布局管理器里添加一些无用的空白来设置大小限制。这样 Fit 就会自动帮你处理了。说到 Fit,我甚至不知道它可以接收参数!