DanHorner
DanHorner

Reputation: 93

What are my options for drawing controls with transparent/translucent backgrounds in wxPython?

I'm writing a wxPython app that needs to match the appearance of an existing Windows application. I'm trying to put Static Text and Button controls on a panel with a gradient background. Here are the ways I've found, but I can't help feeling that I'm missing something. How should I draw static text and custom controls and let the background show through?

  1. Somehow persuade native controls not to draw the background. This applies esp. to wxStaticText
  2. Turn on the global system option msw.window.no-clip-children to let the background show through?
  3. Pass in a repainting function that touches up the background of each control during its onPaint Handler

2 seems easiest but I wonder if it's turned off for a reason. Are there any performance implications to turning on the no-clip-children globally? Is there any way to turn it on just for the windows I want?

edit: Sample code and screnshots

Sorry if this is starting to look like an answer in a question. I want to know if there is a right way to do this.

Screenshot of three static text controls.

#!/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()

Upvotes: 3

Views: 3188

Answers (1)

teuobk
teuobk

Reputation: 66

I ran into this exact problem the other day: how to display static text with a transparent background using wxPython. Here's what I came up with:

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()

A couple of key things:

  1. The window style should be wx.TRANSPARENT_WINDOW (all controls are windows in wxwidgets)
  2. Use GCDC instead of a normal DC, because the former has much better transparency support

I have noticed occasional problems where the parent panel's background does not get invalidated when the content of the TransparentText control gets updated. It's not consistent, and it usually works well.

Upvotes: 5

Related Questions