matplotlib.pyplot 在 wx.Panel

matplotlib.pyplot in a wx.Panel

在这个 class 的 .__init__() 中,我有以下行:

class report_order_totals_by_photographer(wx.Panel):
    def __init__(...):
        ...
        self.figure = matplotlib.figure.Figure()
        self.canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg(panel_canvas, -1, self.figure)

在事件处理程序中我有:

    self.figure.clf()

    datapoints = range(len(self.data))
    values = [x[1] for x in self.data]
    labelpositions = [x + 0.5 for x in range(len(self.data))]
    labeltext = [x[0] for x in self.data]

    bars = matplotlib.pyplot.barh(datapoints, values)
    labels = matplotlib.pyplot.yticks(labelpositions, labeltext)

    self.figure.artists.extend(bars)
    self.figure.axes.extend(labels)

    self.canvas.draw()

看起来像这样:

http://i.imgur.com/SnjWD.png

或者调整大小时这样:

http://i.imgur.com/t2gGtEt.png

这令人困惑和沮丧。我希望它看起来像这样:

data = db.get_order_totals_for_photographers(*season_to_dates("14w"))
import matplotlib.pyplot as plt
plt.barh(range(len(data)), [x[1] for x in data])
plt.yticks([x + 0.5 for x in range(len(data))], [y[0] for y in data])
plt.show()

i.imgur.com/BD1RXOq.png

(这也令人困惑和沮丧,因为名称超出了页面,但无论如何,我可以稍后再关心)。

我的两个主要问题是,它没有根据它所在的框架调整大小,而且我只能绘制条形图,不能画别的。

在我的工作示例中,我使用的是 matplotlib 教程中称为 "thin stateful wrapper around matplotlib's API" 的东西,这让我很奇怪,我不喜欢它。幸运的是,教程跟进了:"If you find this statefulness annoying, don't despair, this is just a thin stateful wrapper around an object oriented API, which you can use instead (See Artist tutorial)"。在艺术家教程中,我可以使用的有用信息非常少,但有一些提示可以引导我 self.figure.artists.extend(bars)

我在 self.figure.texts.extend(labels[1]) 上取得了一些成功,但这只是将所有名称堆叠在一起,(而且我认为滴答声是在情节右侧堆叠在一起的),所以我还是一头雾水

如有任何帮助,我们将不胜感激。

该死的,这比需要的更难。这是一个独立的示例。

(此后我创建了一个 repository,代码更好)。

import wx
import matplotlib.backends.backend_wxagg
import matplotlib.pyplot
import matplotlib.figure
import numpy

data = [
    [
        ["Arse", 5],
        ["Genitals", 12],
        ["Bum", 2]
        ],
    [
        ["Fart", 3],
        ["Guff", 2],
        ["EXPLOSION", 6]
        ],
    [
        ["Clam", 2],
        ["Stench trench", 7],
        ["Axe wound", 3],
        ["Fourth euphemism", 9]
        ]
    ]

class panel_plot(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, style = wx.NO_FULL_REPAINT_ON_RESIZE)

        self.figure = matplotlib.figure.Figure()
        self.canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg(self, -1, self.figure)

        self.set_size()
        self.draw()

        self._resize_flag = False

        self.Bind(wx.EVT_IDLE, self.on_idle)
        self.Bind(wx.EVT_SIZE, self.on_size)

    def on_idle(self, event):
        if self._resize_flag:
            self._resize_flag = False
            self.set_size()

    def on_size(self, event):
        self._resize_flag = True

    def set_size(self):
        pixels = tuple(self.GetSize())
        self.SetSize(pixels)
        self.canvas.SetSize(pixels)
        self.figure.set_size_inches([float(x) / self.figure.get_dpi() for x in pixels])

    def draw(self):
        self.canvas.draw()

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

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

        sizer_bg = wx.BoxSizer( wx.VERTICAL )

        self.panel_bg = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        sizer_main = wx.BoxSizer( wx.VERTICAL )

        self.button_change = wx.Button( self.panel_bg, wx.ID_ANY, u"Change data", wx.DefaultPosition, wx.DefaultSize, 0 )
        sizer_main.Add( self.button_change, 0, wx.ALL, 5 )

        self.panel_matplotlib = panel_plot(self.panel_bg)
        sizer_main.Add( self.panel_matplotlib, 1, wx.EXPAND |wx.ALL, 5 )


        self.panel_bg.SetSizer( sizer_main )
        self.panel_bg.Layout()
        sizer_main.Fit( self.panel_bg )
        sizer_bg.Add( self.panel_bg, 1, wx.EXPAND, 5 )


        self.SetSizer( sizer_bg )
        self.Layout()

        self.i = 0

        self.on_change()

        self.setbinds()

    def setbinds(self):
        self.button_change.Bind(wx.EVT_BUTTON, self.on_change)

    def on_change(self, event = None):
        self.panel_matplotlib.figure.clf()
        axes = self.panel_matplotlib.figure.gca()

        self.i += 1
        _data = numpy.array(data[self.i%3])

        datapoints = numpy.array(range(len(_data)))
        values = numpy.array([int(x[1]) for x in _data])
        labelpositions = numpy.array([x + 0.4 for x in range(len(_data))])
        labeltext = numpy.array([x[0] for x in _data])

        axes.barh(datapoints, values)
        axes.set_yticks(labelpositions)
        axes.set_yticklabels(labeltext)

        self.panel_matplotlib.draw()


class App(wx.App):
    def __init__(self):
        super(App, self).__init__()
        self.mainframe = frame_main(None)
        self.mainframe.Show()
        self.MainLoop()

App()

考虑到我采购的文章没有管理的要点:

  1. 一切都必须是一个 numpy 数组。 matplotlib 有在 arraylikes 上使用 + 运算符的习惯,对于 python 列表,这只是一个扩展。
  2. 看起来 matplotlib.pyplot 中的所有 dank 函数也在 axes 上,即 matplotlib.figure.Figure().gca()
  3. axes.set_yticks() 需要在 .set_yticklabels 之前完成,以便标签的位置正确。
  4. super(panel_plot, self).__init__(...) 似乎不起作用,原因不明。
  5. panel_plot.set_size()中,第一行需要包含self.GetSize()不是 self.Parent.GetClientSize()GetClientSize() 加大了一点;我认为它计算了可绘制区域和边框。

这些是我拼凑的文章:

一些事情 - 你读过这些部分了吗?

http://matplotlib.org/faq/howto_faq.html#move-the-edge-of-an-axes-to-make-room-for-tick-labels

http://matplotlib.org/faq/howto_faq.html#automatically-make-room-for-tick-labels

他们可能会帮助解释您遇到此问题的原因以及解决方法。看起来您必须像上面的示例一样编写代码来处理元素的布局和间距,因为您是在事后更新标签,这可能会影响对齐和格式。

此外,我认为您需要退后一步,或许花点时间熟悉 Matplotlib API 以及不同的 类 和对象如何协同工作。现在看起来你有点想猜测你的出路,作为一个去过那里并做过那件事的人,它不会很好地结束 :-)。一旦了解了 matplotlib 的 OOP 结构以及它们如何协同工作,您应该对如何处理问题有更好的想法。

您是否也通读了这些示例? http://matplotlib.org/examples/user_interfaces/index.html

我 运行 QT 示例,它运行良好。研究它们还应该让您了解如何构建事物以及不同元素如何一起发挥作用。