wxPython框架中如何设置窗口背景图片?

3

MMGP回答了我的问题,但是我无法正确地给他贡献;-)所以我至少会在这里提到他。 (我最终确实给了他贡献... 8-)

他关于双缓冲的讨论 提供了一个基本代码,经过以下修改即可使用:

从第106行开始插入以下内容(覆盖现有代码,直到最后一行):

    # Here's the actual drawing code.
    cliWidth, cliHeight = self.GetClientSize()
    bmp=wx.Bitmap("Logo16x9.png")
    bmpWide = bmp.GetWidth()
    bmpHeight = bmp.GetHeight()
    img = bmp.ConvertToImage()
    scaleFactor = cliWidth/bmpWide
    bmp = wx.BitmapFromImage(img.Scale(int(bmpWide * scaleFactor), int(bmpHeight * scaleFactor)))
    bmpWide = bmp.GetWidth()
    bmpHeight = bmp.GetHeight()
    xPos = (cliWidth - (bmpWide))/2
    yPos = (cliHeight - (bmpHeight))/2
    # altered by me
    dc.DrawBitmap(bmp, xPos, yPos)


class TestFrame(wx.Frame):

整天我一直在苦苦思索这个问题。

我刚开始使用wxPython模块绘制图形,当我需要在一个框架上绘制背景图像时,我发现这段代码,如果图像是窗口的全尺寸,则代码可以很好地工作。

然而,我需要将公司标志放置在背景中,并使其通过调整大小保持居中。 在当前形式下,调整大小会导致图形伪影出现在屏幕上,大小相当于小国家。

标志图片文件(用于代码的第43行)是400x300(WxH)的图像。

我正在寻找一种方法:要么动态调整我的图像以匹配wx.GetClientSize(),要么避免/删除伪影。 最好不涉及PIL或ImageMagick。 应用程序必须仅在本地级别上运行,并且系统无关(Win、Lin和Mac),其中没有网络活动或映射驱动器。

Python 2.7和wxPython 2.8

我正在使用以下代码(附有我的修改注释):

import wx

########################################################################
class MainPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)
        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
        self.frame = parent

        sizer = wx.BoxSizer(wx.VERTICAL)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)

        for num in range(4):
            label = "Button %s" % num
            btn = wx.Button(self, label=label)
            sizer.Add(btn, 0, wx.ALL, 5)
        hSizer.Add((1,1), 1, wx.EXPAND)
        hSizer.Add(sizer, 0, wx.TOP, 100)
        hSizer.Add((1,1), 0, wx.ALL, 75)
        self.SetSizer(hSizer)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

    #----------------------------------------------------------------------
    def OnEraseBackground(self, evt):
        """
        Add a picture to the background
        """
        # yanked from ColourDB.py
        dc = evt.GetDC()
        # Added by me
        cliWidth, cliHeight = self.GetClientSize()

        if not dc:
            dc = wx.ClientDC(self)
            rect = self.GetUpdateRegion().GetBox()
            dc.SetClippingRect(rect)
        dc.Clear()
        # use a 400x300 image
        bmp = wx.Bitmap("Logo4x3.png")
        # added by me
        xPos = (cliWidth - 400)/2
        yPos = (cliHeight - 300)/2
        # altered by me
        dc.DrawBitmap(bmp, xPos, yPos)
        #dc.DrawBitmap(bmp, 0, 0)

########################################################################
class MainFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, size=(600,450))
        panel = MainPanel(self)        
        self.Center()

########################################################################
class Main(wx.App):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, redirect=False, filename=None):
        """Constructor"""
        wx.App.__init__(self, redirect, filename)
        dlg = MainFrame()
        dlg.Show()

#----------------------------------------------------------------------
if __name__ == "__main__":
    app = Main()
    app.MainLoop()

更新:最新失败-修改了第37至52行
if not dc:
    dc = wx.ClientDC(self)
    rect = self.GetUpdateRegion().GetBox()
    dc.SetClippingRect(rect)
dc.Clear()
# use a 400x300 image
bmp = wx.Bitmap("Logo4x3.png")
img = bmp.ConvertToImage()
scaleFactor = cliWidth/400
bmp = wx.BitmapFromImage(img.Scale(int(400*scaleFactor),int(300*scaleFactor)))
# added by me
#xPos = (cliWidth - 400)/2
#yPos = (cliHeight - 300)/2
# altered by me
#dc.DrawBitmap(bmp, xPos, yPos)
dc.DrawBitmap(bmp, 0, 0)

又一次尝试,又一次失败。屏幕输出没有任何变化。此外,有关双缓冲的参考文献未解决此问题,但也遭遇了相同的结果。这段代码修改了原始代码的第36至57行。

brsh = wx.Brush('#000000')


if not dc:
    dc = wx.ClientDC(self)
    rect = self.GetUpdateRegion().GetBox()
    dc.SetClippingRect(rect)
dc.SetBackground(brsh)
dc.SetDeviceOrigin(0,0)
dc.DestroyClippingRegion()
dc.Clear()
# use a 400x300 image
bmp = wx.Bitmap("Logo4x3.png")
img = bmp.ConvertToImage()
scaleFactor = cliWidth/400
bmp = wx.BitmapFromImage(img.Scale(int(400*scaleFactor),int(300*scaleFactor)))
# added by me
#xPos = (cliWidth - 400)/2
#yPos = (cliHeight - 300)/2
# altered by me
#dc.DrawBitmap(bmp, xPos, yPos)
dc.DrawBitmap(bmp, 0, 0)

你所指的闪烁问题是指图像伪影吗?这可以通过双缓冲绘制来解决,具体请参见http://wiki.wxpython.org/DoubleBufferedDrawing。 - mmgp
更好的解释。原始图像不会被擦除。调整大小后,新图像被正确地绘制,但现在两个图像都在表面上。奇怪的是,新图像绘制在旧图像下方(较低的z-index)。 - Jase
@mmgp 刚刚在发布关于这个的帖子...双缓冲链接涉及到相同的领域,但没有解决擦除步骤失败的问题。此外(未在上面注意到),已尝试将其绑定到EXT_SIZE和EVT_PAINT,但结果没有任何区别。 - Jase
@mmgp 好的,经过很多调试,我已经成功解决了双缓冲代码的一部分问题(伪影已经消失),但现在窗口的其余部分有些“偏移”,不过这可以修复。如果您能够把您的建议发布为答案,我会给予您认可。非常感谢您的帮助。 - Jase
在OSX上,你发的第一段代码几乎可以运行。不过我得移除 self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) ,因为它会导致重新绘制出现各种问题(按钮不能正常重绘,背景图像也无法显示)。 - mmgp
显示剩余3条评论
1个回答

6

根据评论,我建议使用双缓冲绘图,但我在编辑后的帖子中没有看到它。此外,在使用self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)时,我遇到了一些绘图问题。但是这行代码可能对我的系统之外的其他系统有所帮助,所以我想保留它。因此,为了处理这种情况,这里是一段更新的代码,使用双缓冲绘图并在我的系统上运行良好:

import wx

class MainPanel(wx.Panel):
    def __init__(self, parent, bg_img='Logo4x3.png'):
        wx.Panel.__init__(self, parent=parent)
        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
        self.bg = wx.Bitmap(bg_img)
        self._width, self._height = self.bg.GetSize()

        sizer = wx.BoxSizer(wx.VERTICAL)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)

        for num in range(4):
            btn = wx.Button(self, label="Button %s" % num)
            sizer.Add(btn, 0, wx.ALL, 5)
        hSizer.Add((1,1), 1, wx.EXPAND)
        hSizer.Add(sizer, 0, wx.TOP, 100)
        hSizer.Add((1,1), 0, wx.ALL, 75)
        self.SetSizer(hSizer)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

    def OnSize(self, size):
        self.Layout()
        self.Refresh()

    def OnEraseBackground(self, evt):
        pass

    def OnPaint(self, evt):
        dc = wx.BufferedPaintDC(self)
        self.Draw(dc)

    def Draw(self, dc):
        cliWidth, cliHeight = self.GetClientSize()
        if not cliWidth or not cliHeight:
            return
        dc.Clear()
        xPos = (cliWidth - self._width)/2
        yPos = (cliHeight - self._height)/2
        dc.DrawBitmap(self.bg, xPos, yPos)

app = wx.App()
frame = wx.Frame(None, size=(400,300))
panel = MainPanel(frame)
frame.Show()
app.MainLoop()

OnEraseBackground方法是故意为空的。


谢谢你提供额外的代码...我正准备从你提供的原始示例中剥离所有无用的内容。这将使我的工作变得更加容易。现在开始基于这个重新构建我的想法... - Jase
这个在 Linux 上也能用吗?我现在无法在那里测试它。 - mmgp
在我尝试的某些 WM 上失败得很惨,会回过头来再处理这个问题。 - mmgp
如果之前的代码在某处出现问题,请查看更新后的代码。 - mmgp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接