在 matplotlib 中水平显示单选按钮
Displaying Radio buttons horizontally in matplotlib
我正在使用 matplotlib.widgets
在我的小部件中创建单选按钮,出现的按钮是垂直堆叠的,我希望它们水平堆叠。
MVCE:
import matplotlib.pyplot as plt
from matplotlib.widgets import RadioButtons
plt.subplots_adjust(left=0.2)
rax = plt.axes([0.5,0.05,0.1,0.1])
radio = RadioButtons(rax ,['1','2','3'], active=0, activecolor='blue' )
plt.show()
正如你在这个例子中看到的那样,你可以获得这样的单选按钮
,
我想知道有没有办法水平堆叠这些单选按钮。
目前正在尝试在 PR #13374 中向 RadioButtons
引入一个 orientation
参数;这还没有最终确定。
正如我在此 PR 中评论的那样,另一种选择是为按钮使用散点图。下面显示了我如何想象这个实现。与通常的按钮相比,主要有两个增强功能:
- 单选按钮始终为圆形,与轴的大小无关。
- 它们可以任意对齐,尤其是水平对齐。
这是通过在内部创建一个图例来实现的,该图例具有现成的所有必需选项。 Legend
的任何有效参数也可用于按钮。
import matplotlib.pyplot as plt
from matplotlib.widgets import AxesWidget, RadioButtons
class MyRadioButtons(RadioButtons):
def __init__(self, ax, labels, active=0, activecolor='blue', size=49,
orientation="vertical", **kwargs):
"""
Add radio buttons to an `~.axes.Axes`.
Parameters
----------
ax : `~matplotlib.axes.Axes`
The axes to add the buttons to.
labels : list of str
The button labels.
active : int
The index of the initially selected button.
activecolor : color
The color of the selected button.
size : float
Size of the radio buttons
orientation : str
The orientation of the buttons: 'vertical' (default), or 'horizontal'.
Further parameters are passed on to `Legend`.
"""
AxesWidget.__init__(self, ax)
self.activecolor = activecolor
axcolor = ax.get_facecolor()
self.value_selected = None
ax.set_xticks([])
ax.set_yticks([])
ax.set_navigate(False)
circles = []
for i, label in enumerate(labels):
if i == active:
self.value_selected = label
facecolor = activecolor
else:
facecolor = axcolor
p = ax.scatter([],[], s=size, marker="o", edgecolor='black',
facecolor=facecolor)
circles.append(p)
if orientation == "horizontal":
kwargs.update(ncol=len(labels), mode="expand")
kwargs.setdefault("frameon", False)
self.box = ax.legend(circles, labels, loc="center", **kwargs)
self.labels = self.box.texts
self.circles = self.box.legendHandles
for c in self.circles:
c.set_picker(5)
self.cnt = 0
self.observers = {}
self.connect_event('pick_event', self._clicked)
def _clicked(self, event):
if (self.ignore(event) or event.mouseevent.button != 1 or
event.mouseevent.inaxes != self.ax):
return
if event.artist in self.circles:
self.set_active(self.circles.index(event.artist))
用作
plt.subplots_adjust(left=0.2)
rax = plt.axes([0.5,0.05,0.4,0.07])
radio = MyRadioButtons(rax ,['1','2','3'], active=0, activecolor='crimson',
orientation="horizontal")
plt.show()
或
rax = plt.axes([0.2,0.5,0.25,0.1])
radio = MyRadioButtons(rax ,["AA", "BB", "CC", "DD"], ncol=2)
由于 stackexchange 不希望将此作为编辑,
这是来自@ImportanceOfBeingErnest 的上述答案的更新版本,适用于最近的 matplotlib-versions(例如 >= v3.3)
import matplotlib.pyplot as plt
from matplotlib.widgets import AxesWidget, RadioButtons
from matplotlib import cbook
class MyRadioButtons(RadioButtons):
def __init__(self, ax, labels, active=0, activecolor='blue', size=49,
orientation="vertical", **kwargs):
"""
Add radio buttons to an `~.axes.Axes`.
Parameters
----------
ax : `~matplotlib.axes.Axes`
The axes to add the buttons to.
labels : list of str
The button labels.
active : int
The index of the initially selected button.
activecolor : color
The color of the selected button.
size : float
Size of the radio buttons
orientation : str
The orientation of the buttons: 'vertical' (default), or 'horizontal'.
Further parameters are passed on to `Legend`.
"""
AxesWidget.__init__(self, ax)
self.activecolor = activecolor
axcolor = ax.get_facecolor()
self.value_selected = None
ax.set_xticks([])
ax.set_yticks([])
ax.set_navigate(False)
circles = []
for i, label in enumerate(labels):
if i == active:
self.value_selected = label
facecolor = activecolor
else:
facecolor = axcolor
p = ax.scatter([],[], s=size, marker="o", edgecolor='black',
facecolor=facecolor)
circles.append(p)
if orientation == "horizontal":
kwargs.update(ncol=len(labels), mode="expand")
kwargs.setdefault("frameon", False)
self.box = ax.legend(circles, labels, loc="center", **kwargs)
self.labels = self.box.texts
self.circles = self.box.legendHandles
for c in self.circles:
c.set_picker(5)
self._observers = cbook.CallbackRegistry()
self.connect_event('pick_event', self._clicked)
def _clicked(self, event):
if (self.ignore(event) or event.mouseevent.button != 1 or
event.mouseevent.inaxes != self.ax):
return
if event.artist in self.circles:
self.set_active(self.circles.index(event.artist))
用作
plt.subplots_adjust(left=0.2)
rax = plt.axes([0.5,0.05,0.4,0.07])
radio = MyRadioButtons(rax ,['1','2','3'], active=0, activecolor='crimson',
orientation="horizontal")
plt.show()
我正在使用 matplotlib.widgets
在我的小部件中创建单选按钮,出现的按钮是垂直堆叠的,我希望它们水平堆叠。
MVCE:
import matplotlib.pyplot as plt
from matplotlib.widgets import RadioButtons
plt.subplots_adjust(left=0.2)
rax = plt.axes([0.5,0.05,0.1,0.1])
radio = RadioButtons(rax ,['1','2','3'], active=0, activecolor='blue' )
plt.show()
正如你在这个例子中看到的那样,你可以获得这样的单选按钮
我想知道有没有办法水平堆叠这些单选按钮。
目前正在尝试在 PR #13374 中向 RadioButtons
引入一个 orientation
参数;这还没有最终确定。
正如我在此 PR 中评论的那样,另一种选择是为按钮使用散点图。下面显示了我如何想象这个实现。与通常的按钮相比,主要有两个增强功能:
- 单选按钮始终为圆形,与轴的大小无关。
- 它们可以任意对齐,尤其是水平对齐。
这是通过在内部创建一个图例来实现的,该图例具有现成的所有必需选项。 Legend
的任何有效参数也可用于按钮。
import matplotlib.pyplot as plt
from matplotlib.widgets import AxesWidget, RadioButtons
class MyRadioButtons(RadioButtons):
def __init__(self, ax, labels, active=0, activecolor='blue', size=49,
orientation="vertical", **kwargs):
"""
Add radio buttons to an `~.axes.Axes`.
Parameters
----------
ax : `~matplotlib.axes.Axes`
The axes to add the buttons to.
labels : list of str
The button labels.
active : int
The index of the initially selected button.
activecolor : color
The color of the selected button.
size : float
Size of the radio buttons
orientation : str
The orientation of the buttons: 'vertical' (default), or 'horizontal'.
Further parameters are passed on to `Legend`.
"""
AxesWidget.__init__(self, ax)
self.activecolor = activecolor
axcolor = ax.get_facecolor()
self.value_selected = None
ax.set_xticks([])
ax.set_yticks([])
ax.set_navigate(False)
circles = []
for i, label in enumerate(labels):
if i == active:
self.value_selected = label
facecolor = activecolor
else:
facecolor = axcolor
p = ax.scatter([],[], s=size, marker="o", edgecolor='black',
facecolor=facecolor)
circles.append(p)
if orientation == "horizontal":
kwargs.update(ncol=len(labels), mode="expand")
kwargs.setdefault("frameon", False)
self.box = ax.legend(circles, labels, loc="center", **kwargs)
self.labels = self.box.texts
self.circles = self.box.legendHandles
for c in self.circles:
c.set_picker(5)
self.cnt = 0
self.observers = {}
self.connect_event('pick_event', self._clicked)
def _clicked(self, event):
if (self.ignore(event) or event.mouseevent.button != 1 or
event.mouseevent.inaxes != self.ax):
return
if event.artist in self.circles:
self.set_active(self.circles.index(event.artist))
用作
plt.subplots_adjust(left=0.2)
rax = plt.axes([0.5,0.05,0.4,0.07])
radio = MyRadioButtons(rax ,['1','2','3'], active=0, activecolor='crimson',
orientation="horizontal")
plt.show()
或
rax = plt.axes([0.2,0.5,0.25,0.1])
radio = MyRadioButtons(rax ,["AA", "BB", "CC", "DD"], ncol=2)
由于 stackexchange 不希望将此作为编辑, 这是来自@ImportanceOfBeingErnest 的上述答案的更新版本,适用于最近的 matplotlib-versions(例如 >= v3.3)
import matplotlib.pyplot as plt
from matplotlib.widgets import AxesWidget, RadioButtons
from matplotlib import cbook
class MyRadioButtons(RadioButtons):
def __init__(self, ax, labels, active=0, activecolor='blue', size=49,
orientation="vertical", **kwargs):
"""
Add radio buttons to an `~.axes.Axes`.
Parameters
----------
ax : `~matplotlib.axes.Axes`
The axes to add the buttons to.
labels : list of str
The button labels.
active : int
The index of the initially selected button.
activecolor : color
The color of the selected button.
size : float
Size of the radio buttons
orientation : str
The orientation of the buttons: 'vertical' (default), or 'horizontal'.
Further parameters are passed on to `Legend`.
"""
AxesWidget.__init__(self, ax)
self.activecolor = activecolor
axcolor = ax.get_facecolor()
self.value_selected = None
ax.set_xticks([])
ax.set_yticks([])
ax.set_navigate(False)
circles = []
for i, label in enumerate(labels):
if i == active:
self.value_selected = label
facecolor = activecolor
else:
facecolor = axcolor
p = ax.scatter([],[], s=size, marker="o", edgecolor='black',
facecolor=facecolor)
circles.append(p)
if orientation == "horizontal":
kwargs.update(ncol=len(labels), mode="expand")
kwargs.setdefault("frameon", False)
self.box = ax.legend(circles, labels, loc="center", **kwargs)
self.labels = self.box.texts
self.circles = self.box.legendHandles
for c in self.circles:
c.set_picker(5)
self._observers = cbook.CallbackRegistry()
self.connect_event('pick_event', self._clicked)
def _clicked(self, event):
if (self.ignore(event) or event.mouseevent.button != 1 or
event.mouseevent.inaxes != self.ax):
return
if event.artist in self.circles:
self.set_active(self.circles.index(event.artist))
用作
plt.subplots_adjust(left=0.2)
rax = plt.axes([0.5,0.05,0.4,0.07])
radio = MyRadioButtons(rax ,['1','2','3'], active=0, activecolor='crimson',
orientation="horizontal")
plt.show()