在wxPython中绘制透明/半透明背景控件的选项有哪些?

3 投票
1 回答
3124 浏览
提问于 2025-04-17 12:17

我正在写一个wxPython应用程序,想让它的外观和一个现有的Windows应用程序一致。现在我想在一个面板上放置静态文本和按钮控件,并且希望背景是渐变的。我找到了一些方法,但总觉得还缺少什么。请问我该如何绘制静态文本和自定义控件,让背景透出来呢?

  1. 想办法让原生控件不绘制背景,特别是wxStaticText这个控件。
  2. 开启一个全局系统选项 msw.window.no-clip-children,让背景透出来?
  3. 传入一个重绘函数,在每个控件的onPaint处理器中修整背景。

第二种方法看起来最简单,但我在想这样做是否有原因被关闭。开启no-clip-children这个选项会对性能有影响吗?有没有办法只对我想要的窗口开启这个选项?

编辑:示例代码和截图

抱歉如果这看起来像是在回答问题。我想知道有没有正确的方法来做到这一点。

三个静态文本控件的截图。

#!/usr/bin/python
import wx

class GradientPanel(wx.Panel):
    def __init__(self,*args,**kwargs):
        wx.Panel.__init__(self,*args,**kwargs)
        self.Bind(wx.EVT_PAINT,lambda e: self.paintBackground())
        self.Bind(wx.EVT_ERASE_BACKGROUND,lambda e: None)

        self.t=wx.StaticText(self,pos=(30,20),label="wxStaticText. Looks bad on MSW")
        self.t2=MyStaticText(self,pos=(30,50), size=(300,25), label="MyStaticText. Needs ClipChildren turned off")
        self.t3=MyStaticTextWithCustomBackground(self,pos=(30,80),size=(300,25), label="This Control touches up its own background",paintBackgroundToDC=self.paintBackgroundToDC)

    def paintBackground(self):
        dc=wx.PaintDC(self)
        self.paintBackgroundToDC(self,dc)

    def paintBackgroundToDC(self,ofWindow,dc=None):
        r=self.GetScreenRect()
        r.SetTopLeft(ofWindow.ScreenToClient(r.GetTopLeft()))
        dc.GradientFillLinear(r,wx.Colour(240,240,240),wx.Colour(128,128,255),wx.SOUTH)

class MyStaticText(wx.Window):
    def __init__(self,parent,label,**kwargs):
        wx.Window.__init__(self,parent,**kwargs)
        self.label=label
        self.Bind(wx.EVT_PAINT,lambda e: self.repaint())
        self.Bind(wx.EVT_ERASE_BACKGROUND,lambda e: None)

    def repaint(self):
        self.repaintToDC(wx.PaintDC(self))

    def repaintToDC(self,dc):
        dc.DrawText(self.label,0,0)

class MyStaticTextWithCustomBackground(MyStaticText):
    def __init__(self,parent,label,paintBackgroundToDC=None,**kwargs):
        self.paintBackgroundToDC=paintBackgroundToDC
        MyStaticText.__init__(self,parent,label,**kwargs)

    def repaint(self):
        dc=wx.PaintDC(self)
        if self.paintBackgroundToDC:
            self.paintBackgroundToDC(self,dc)
        MyStaticText.repaintToDC(self,dc)

if __name__ == "__main__":
    #wx.SystemOptions.SetOptionInt('msw.window.no-clip-children',1)
    app=wx.PySimpleApp()
    frame=wx.Frame(None,title="Transparent Controls")
    panel=GradientPanel(frame)
    frame.Show()
    app.MainLoop()

1 个回答

5

我前几天遇到了一个问题:如何在wxPython中显示带透明背景的静态文本。于是我想出了以下方法:

class TransparentText(wx.StaticText):
  def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition,
             size=wx.DefaultSize, style=wx.TRANSPARENT_WINDOW, name='transparenttext'):
    wx.StaticText.__init__(self, parent, id, label, pos, size, style, name)

    self.Bind(wx.EVT_PAINT, self.on_paint)
    self.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: None)
    self.Bind(wx.EVT_SIZE, self.on_size)

  def on_paint(self, event):
    bdc = wx.PaintDC(self)
    dc = wx.GCDC(bdc)

    font_face = self.GetFont()
    font_color = self.GetForegroundColour()

    dc.SetFont(font_face)
    dc.SetTextForeground(font_color)
    dc.DrawText(self.GetLabel(), 0, 0)

  def on_size(self, event):
    self.Refresh()
    event.Skip()

这里有几个关键点:

  1. 窗口的样式应该设置为wx.TRANSPARENT_WINDOW(在wxwidgets中,所有控件都是窗口)
  2. 要使用GCDC,而不是普通的DC,因为GCDC对透明度的支持要好得多

我注意到有时候,父面板的背景在更新TransparentText控件的内容时不会被刷新。这种情况并不总是发生,通常情况下效果还是不错的。

撰写回答