Transparency issue with matplotlib and wxpython

扶醉桌前 提交于 2019-12-12 19:03:42

问题


I'm developping a GUI based on matplotlib and wxpython, for Windows. I have an issue that becomes visible when the "Windows XP" theme (green Start button, blue taskbar) is used. I'm quite sure the same problem will show up on Vista as well. I know an imperfect workaround, but I can't fix this issue properly.

My matplotlib plots are embedded in wxPanels, which are embedded in a wxNotebook (tabs, if you prefer). With some themes, the background colour of the tabs is actually a gradient, and wx tries to match it with a colour close to it. This is the imperfect workaround: the colour matches at the top of the window, but not at the bottom.

That is why I would like to actually use a matplotlib plot with a transparent background. When I set the facecolor of the figure to be transparent, it actually takes the "colour" of the desktop or whatever window is located behind, not the wxPanel/wxNotebook. If I resize the window, the transparent part accumulates drawn data. This last issue is not theme-specific anymore, I observe it on the "Windows Classic" one as well.

In the example code below, I would rather expect the transparent part of the figure to show the blue colour set on the panel embedding the plot.

import wx
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
import wxversion
import sys

class MyFrame ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 300,650 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )

        bSizer3 = wx.BoxSizer( wx.VERTICAL )

        self.m_notebook2 = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_panel1 = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer4 = wx.BoxSizer( wx.VERTICAL )

        self.m_panel2 = wx.Panel( self.m_panel1, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        self.m_panel2.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_ACTIVECAPTION ) )

        bSizer4.Add( self.m_panel2, 1, wx.EXPAND |wx.ALL, 5 )

        self.m_panel1.SetSizer( bSizer4 )
        self.m_panel1.Layout()
        bSizer4.Fit( self.m_panel1 )
        self.m_notebook2.AddPage( self.m_panel1, u"a page", False )

        bSizer3.Add( self.m_notebook2, 1, wx.EXPAND |wx.ALL, 0 )

        self.SetSizer( bSizer3 )
        self.Layout()

        self.Centre( wx.BOTH )

    def __del__( self ):
        pass





class MyPlot:
    def __init__(self, panel, notebook):
        self.panel = panel
        self.panel.figure = Figure(None, dpi=None)
        self.panel.canvas = FigureCanvasWxAgg(self.panel, -1, self.panel.figure)
        self.panel.axes = self.panel.figure.add_subplot(111)
        self.panel.Bind(wx.EVT_SIZE, self.onSize)

        # I want this code to work whatever the theme. It works fine on WinXP "Windows Classic" theme, but not on WinXP "Windows XP" theme (with green Start menu button and blue taskbar)
        self.setColor(None)
        # The problem here is that SYS_COLOUR_BTNFACE does not match the notebook background for the Windows XP theme

        # Solution 1: get background gradient from notebook
        # Source: http://wxpython-users.1045709.n5.nabble.com/wxTextCtrl-doesn-t-show-background-as-expected-on-a-notebook-panel-td2359680.html
        # Problem: match is perfect at top, not at bottom (not much difference, though)
        #
        # # # Uncomment below
        # rgbtuple = notebook.GetThemeBackgroundColour()
        # clr = [c / 255. for c in rgbtuple]
        # self.panel.figure.set_facecolor(clr)
        # self.panel.figure.set_edgecolor(clr)

        # Solution 2: set transparent figure facecolor to capture color from panel
        # Problem 1: it takes the color from behind the frame (ie desktop, any other window behind...), not the color of the panel
        # Problem 2 (linked to problem 1): it gets worse when resizing as it doesn't repaint but accumulates
        #
        # http://matplotlib.1069221.n5.nabble.com/redrawing-plot-with-transparent-background-in-wx-tt26694.html seems related but did not receive any answer
        # # # Recomment above, uncomment below
        #self.panel.figure.set_facecolor('None')



        self.setSize()

    def setSize(self):
        pixels = tuple(self.panel.GetClientSize())
        self.panel.SetSize(pixels)
        self.panel.canvas.SetSize(pixels)
        self.panel.figure.set_size_inches(float(pixels[0]) / self.panel.figure.get_dpi(),
                                    float(pixels[1]) / self.panel.figure.get_dpi())

    def onSize(self, event):
        self.setSize()

    def setColor(self, rgbtuple=None):
        if rgbtuple is None:
            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
        clr = [c / 255. for c in rgbtuple]
        self.panel.figure.set_facecolor(clr)
        self.panel.figure.set_edgecolor(clr)
        self.panel.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))

print "python " + str(sys.version_info)
print "wx " + str(wxversion.getInstalled())
print "matplotlib " + matplotlib.__version__ 

app = wx.App(0)
window = MyFrame(None)
plot = MyPlot(window.m_panel2, window.m_notebook2)
window.Show()
app.MainLoop()

The blue colour is normally visible if you grab a corner of the window and shake it to resize it several times. I am immediately resizing on purpose rather than waiting for the window to be idle.

Please uncomment the indicated lines in the code to see what I am probably not clearly explaining.

My configuration: Python 2.7.3, wx 2.9.4-msw, matplotlib 1.1.1

Edit

I didn't make much progress:

  • I noticed matplotlib 1.2.0 was out so tried it, but no change
  • I simplified the example to get rid of the wxNotebook, the problem is still there with only a wxPanel
  • I found http://wxpython-users.1045709.n5.nabble.com/advice-on-filling-squares-tp4475117p4479387.html which might give a hint at what is going on

I also developped another example where I overlay two figures, to see where the transparency "stops". Interestingly enough, the figure below stops the transparency of the figure on top, but not the wxPanel.

import wx
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.patches import Rectangle
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
import wxversion
import sys

class MyFrame ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 300,650 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )


        bSizer4 = wx.BoxSizer( wx.VERTICAL )

        self.m_panel2 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        self.m_panel2.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_ACTIVECAPTION ) )

        bSizer4.Add( self.m_panel2, 1, wx.EXPAND |wx.ALL, 5 )

        self.m_panel2.Layout()

        self.SetSizer( bSizer4 )
        self.Layout()

        self.Centre( wx.BOTH )

    def __del__( self ):
        pass


class MyPlot:
    def __init__(self, panel):
        self.panel = panel

        # This figure is behind
        self.panel.figure = Figure(figsize=(2,2), dpi=None)
        self.panel.canvas = FigureCanvasWxAgg(self.panel, -1, self.panel.figure)
        self.panel.axes = self.panel.figure.add_axes([0.3,0.3,0.5,0.5])
        patch = Rectangle((0,0), 0.3, 0.2, facecolor='red')
        self.panel.axes.add_patch(patch)

        # This figure is on top
        self.panel.figure2 = Figure(figsize=(3,3), dpi=None)
        self.panel.canvas2 = FigureCanvasWxAgg(self.panel, -1, self.panel.figure2)
        self.panel.axes2 = self.panel.figure2.add_axes([0.3,0.3,0.5,0.5])
        patch2 = Rectangle((0.5,0.5), 0.4, 0.1, facecolor='blue')
        self.panel.axes2.add_patch(patch2)

        # Make the top figure transparent
        # self.panel.figure2.set_facecolor('None') # equivalent to self.panel.figure2.patch.set_alpha(0)

        # By default, leave figure on bottom opaque
        # Uncomment to see effect of transparency
        #self.panel.figure.patch.set_alpha(0.5)
        self.panel.figure2.patch.set_alpha(0.5)

        # Draw everything
        self.panel.canvas.draw()


print "python " + str(sys.version_info)
print "wx " + str(wxversion.getInstalled())
print "matplotlib " + matplotlib.__version__ 

app = wx.App(0)
window = MyFrame(None)
plot = MyPlot(window.m_panel2)
window.Show()
app.MainLoop()

Thanks for your help :)

来源:https://stackoverflow.com/questions/13352311/transparency-issue-with-matplotlib-and-wxpython

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!