2

Windows用のmatplotlibとwxpythonに基づくGUIを開発しています。「Windows XP」テーマ (緑色のスタート ボタン、青色のタスク バー) を使用すると問題が発生します。Vista でも同じ問題が発生すると確信しています。不完全な回避策は知っていますが、この問題を適切に修正することはできません。

私の matplotlib プロットは wxPanels に埋め込まれており、wxPanels は wxNotebook (必要に応じてタブ) に埋め込まれています。一部のテーマでは、タブの背景色は実際にはグラデーションであり、wx はそれに近い色に合わせようとします。これは不完全な回避策です。ウィンドウの上部では色が一致しますが、下部では一致しません。

そのため、背景が透明な matplotlib プロットを実際に使用したいと思います。Figure の facecolor を透明に設定すると、実際には、wxPanel/wxNotebook ではなく、デスクトップまたは背後にあるウィンドウの「色」が使用されます。ウィンドウのサイズを変更すると、透明部分に描画データが蓄積されます。この最後の問題はもはやテーマ固有ではありません。「Windows クラシック」の問題でも同様です。

以下のコード例では、図の透明部分が、プロットを埋め込んだパネルに設定された青色を示すことを期待しています。

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

ウィンドウの隅をつかんで数回振ってサイズを変更すると、通常、青色が表示されます。ウィンドウがアイドル状態になるのを待つのではなく、意図的にサイズをすぐに変更しています。

コード内の指定された行のコメントを外して、おそらく明確に説明していないことを確認してください。

私の構成: Python 2.7.3、wx 2.9.4-msw、matplotlib 1.1.1

編集

私はあまり進歩しませんでした:

また、透明度が「停止」する場所を確認するために、2 つの図を重ねる別の例も開発しました。興味深いことに、下の図は上の図の透明度を停止しますが、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()

ご協力いただきありがとうございます :)

4

0 に答える 0