如何将传奇置于情节之外
- 2024-11-20 08:43:00
- admin 原创
- 8
问题描述:
我有一系列 20 个图(不是子图)要制作在一张图中。我希望图例超出框的范围。同时,我不想更改轴,因为图的尺寸会缩小。
我想将图例框保留在绘图区域之外(我希望图例位于绘图区域右侧的外面)。
有没有办法可以减小图例框里面的文字字体大小,这样图例框的尺寸就会小一些?
解决方案 1:
有很多方法可以实现您想要的效果。除了Christian Alis和Navi 已经说过的内容之外,您还可以使用bbox_to_anchor
关键字参数将图例部分放置在轴之外和/或减小字体大小。
在考虑减小字体大小(这会使内容难以阅读)之前,请尝试将图例放置在不同的位置:
因此,让我们从一个通用的例子开始:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in range(5):
ax.plot(x, i * x, label='$y = %ix$' % i)
ax.legend()
plt.show()
如果我们做同样的事情,但使用bbox_to_anchor
关键字参数,我们可以将图例稍微移到轴边界之外:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in range(5):
ax.plot(x, i * x, label='$y = %ix$' % i)
ax.legend(bbox_to_anchor=(1.1, 1.05))
plt.show()
类似地,使图例更加水平和/或将其放在图的顶部(我也打开了圆角和简单的阴影):
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in range(5):
line, = ax.plot(x, i * x, label='$y = %ix$'%i)
ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.05),
ncol=3, fancybox=True, shadow=True)
plt.show()
或者,缩小当前图的宽度,并将图例完全放在图形的轴之外(注意:如果使用tight_layout()
,则省略ax.set_position()
:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in range(5):
ax.plot(x, i * x, label='$y = %ix$'%i)
# Shrink current axis by 20%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
# Put a legend to the right of the current axis
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.show()
并以类似的方式,垂直缩小绘图,并在底部放置水平图例:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in range(5):
line, = ax.plot(x, i * x, label='$y = %ix$'%i)
# Shrink current axis's height by 10% on the bottom
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
box.width, box.height * 0.9])
# Put a legend below current axis
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05),
fancybox=True, shadow=True, ncol=5)
plt.show()
请查看matplotlib 图例指南。你也可以查看plt.figlegend()
。
解决方案 2:
放置图例 ( bbox_to_anchor
)
loc
使用参数将图例放置在轴的边界框内plt.legend
。
例如,loc="upper right"
将图例放置在边界框的右上角,默认情况下,边界框的范围为轴坐标中的从(0, 0)
到(1, 1)
(或边界框符号中(x0, y0, width, height) = (0, 0, 1, 1)
)。
要将图例放置在轴边界框之外,可以指定(x0, y0)
图例左下角的轴坐标元组。
plt.legend(loc=(1.04, 0))
一种更通用的方法是使用参数手动指定应放置图例的边界框。bbox_to_anchor
可以限制自己只提供(x0, y0)
边界框的一部分。这会创建一个零跨度框,图例将从该框中扩展出loc
参数给出的方向。例如,
plt.legend(bbox_to_anchor=(1.04, 1), loc="左上")
将图例放置在轴之外,使得图例的左上角位于(1.04, 1)
轴坐标中的位置。
下面给出了进一步的例子,其中还展示了mode
和等不同参数之间的相互作用。ncols
l1 = plt.legend(bbox_to_anchor=(1.04, 1), borderaxespad=0)
l2 = plt.legend(bbox_to_anchor=(1.04, 0), loc="lower left", borderaxespad=0)
l3 = plt.legend(bbox_to_anchor=(1.04, 0.5), loc="center left", borderaxespad=0)
l4 = plt.legend(bbox_to_anchor=(0, 1.02, 1, 0.2), loc="lower left",
mode="expand", borderaxespad=0, ncol=3)
l5 = plt.legend(bbox_to_anchor=(1, 0), loc="lower right",
bbox_transform=fig.transFigure, ncol=3)
l6 = plt.legend(bbox_to_anchor=(0.4, 0.8), loc="upper right")
有关如何解释 中的 4 元组参数的详细信息bbox_to_anchor
,如,可在本问题l4
中找到。在 4 元组给出的边界框内水平扩展图例。 有关垂直扩展的图例,请参阅此问题。mode="expand"
有时,用图形坐标而不是轴坐标来指定边界框可能会很有用。如上例所示l5
,其中bbox_transform
参数用于将图例放在图形的左下角。
后期处理
将图例放置在坐标区之外通常会导致不希望出现的情况,即图例完全或部分位于图形画布之外。
解决该问题的办法有:
调整子图参数
可以使用 调整子图参数,使轴在图形中占用更少的空间(从而为图例留下更多空间)plt.subplots_adjust
。例如,
plt.subplots_adjust(right=0.7)
在图的右侧留下 30% 的空间,可以放置图例。
紧密布局
使用plt.tight_layout
允许自动调整子图参数,使图形中的元素紧贴图形边缘。不幸的是,图例没有被考虑在这个自动化中,但我们可以提供一个矩形框,整个子图区域(包括标签)将适合它。
plt.tight_layout(rect=[0, 0, 0.75, 1])
使用 保存图形
bbox_inches = "tight"
参数bbox_inches = "tight"
可plt.savefig
用于保存图形,以便画布上的所有艺术家(包括图例)都适合保存的区域。如果需要,图形大小会自动调整。
plt.savefig("output.png", bbox_inches="tight")
自动调整子图参数
一种自动调整子图位置以使图例适合画布内部而不改变图形大小的方法可以在以下答案中找到:创建具有精确大小且没有填充的图形(以及轴外的图例)
上述案例的比较:
替代方案
人物传说
可以使用图例代替轴matplotlib.figure.Figure.legend
。这对于 Matplotlib 2.1 或更高版本特别有用,因为不需要特殊参数
fig.legend(loc=7)
为图形不同轴上的所有艺术家创建图例。使用参数放置图例loc
,类似于将其放置在轴内的方式,但参考整个图形 - 因此它将自动位于轴之外。剩下的就是调整子图,使图例和轴之间没有重叠。此处 上面的“调整子图参数”点将有所帮助。一个例子:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2*np.pi)
colors = ["#7aa0c4", "#ca82e1", "#8bcd50", "#e18882"]
fig, axes = plt.subplots(ncols=2)
for i in range(4):
axes[i//2].plot(x, np.sin(x+i), color=colors[i], label="y=sin(x + {})".format(i))
fig.legend(loc=7)
fig.tight_layout()
fig.subplots_adjust(right=0.75)
plt.show()
专用子图轴内的图例
使用的替代方法bbox_to_anchor
是将图例放置在其专用的子图轴中(lax
)。由于图例子图应该小于图,我们可以gridspec_kw={"width_ratios":[4, 1]}
在创建轴时使用。我们可以隐藏轴lax.axis("off")
,但我们仍然会放入图例。图例句柄和标签需要通过从实际图中获取h, l = ax.get_legend_handles_labels()
,然后可以将其提供给子lax
图中的图例。lax.legend(h, l)
下面是一个完整的示例。
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = 6, 2
fig, (ax, lax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[4, 1]})
ax.plot(x, y, label="y=sin(x)")
....
h, l = ax.get_legend_handles_labels()
lax.legend(h, l, borderaxespad=0)
lax.axis("off")
plt.tight_layout()
plt.show()
这将生成一个在视觉上与上面的图表非常相似的图表:
我们也可以使用第一个轴来放置图例,但是使用bbox_transform
图例轴的
ax.legend(bbox_to_anchor=(0, 0, 1, 1), bbox_transform=lax.transAxes)
lax.axis("off")
在这种方法中,我们不需要从外部获取图例手柄,但我们需要指定bbox_to_anchor
参数。
进一步阅读和注释:
考虑 Matplotlib图例指南,其中包含一些您想用图例做的其他事情的示例。
一些用于放置饼图图例的示例代码可以直接在以下问题的答案中找到:Python - 图例与饼图重叠
参数
loc
可以采用数字而不是字符串,这样可以缩短调用时间,但是它们之间的映射不是很直观。以下是供参考的映射:
解决方案 3:
像这样调用legend()
后再调用即可:plot()
# Matplotlib
plt.plot(...)
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
# Pandas
df.myCol.plot().legend(loc='center left', bbox_to_anchor=(1, 0.5))
结果看起来会像这样:
解决方案 4:
set_size
您可以通过指定来缩小图例文本FontProperties
。资源:
图例指南
matplotlib.legend
matplotlib.pyplot.legend
matplotlib.font_manager
set_size(self, size)
有效的字体大小为xx-small、x-small、small、medium、large、x-large、xx-large、greater、smaller和None。
真正的 Python:使用 Matplotlib 进行 Python 绘图(指南)
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
fontP = FontProperties()
fontP.set_size('xx-small')
p1, = plt.plot([1, 2, 3], label='Line 1')
p2, = plt.plot([3, 2, 1], label='Line 2')
plt.legend(handles=[p1, p2], title='title', bbox_to_anchor=(1.05, 1), loc='upper left', prop=fontP)
fontsize='xx-small'
也可以工作,无需导入FontProperties
。
plt.legend(handles=[p1, p2], title='title', bbox_to_anchor=(1.05, 1), loc='upper left', fontsize='xx-small')
解决方案 5:
要将图例放置在绘图区域之外,请使用loc
和bbox_to_anchor
关键字legend()
。例如,以下代码将图例放置在绘图区域的右侧:
legend(loc="upper left", bbox_to_anchor=(1,1))
欲了解更多信息,请参阅图例指南
解决方案 6:
简短回答:您可以使用bbox_to_anchor
++ bbox_extra_artists
。bbox_inches='tight'
较长的答案:您可以使用bbox_to_anchor
手动指定图例框的位置,正如其他人在答案中指出的那样。
然而,常见的问题是图例框被裁剪,例如:
import matplotlib.pyplot as plt
# data
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]
# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)
# Add legend, title and axis labels
lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
ax.set_title('Title')
ax.set_xlabel('x label')
ax.set_ylabel('y label')
fig.savefig('image_output.png', dpi=300, format='png')
为了防止图例框被裁剪,您可以在保存图形时使用参数bbox_extra_artists
并bbox_inches
要求savefig
在保存的图像中包含裁剪的元素:
fig.savefig('image_output.png', bbox_extra_artists=(lgd,), bbox_inches='tight')
示例(我只更改了最后一行以添加 2 个参数fig.savefig()
):
import matplotlib.pyplot as plt
# data
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]
# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)
# Add legend, title and axis labels
lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
ax.set_title('Title')
ax.set_xlabel('x label')
ax.set_ylabel('y label')
fig.savefig('image_output.png', dpi=300, format='png', bbox_extra_artists=(lgd,), bbox_inches='tight')
我希望 matplotlib 能够像Matlab 一样允许图例框位于外部位置:
figure
x = 0:.2:12;
plot(x,besselj(1,x),x,besselj(2,x),x,besselj(3,x));
hleg = legend('First','Second','Third',...
'Location','NorthEastOutside')
% Make the text of the legend italic and color it brown
set(hleg,'FontAngle','italic','TextColor',[.3,.2,.1])
解决方案 7:
除了这里所有优秀的答案之外,如果可能的话,matplotlib
和的较新版本pylab
可以自动确定图例的位置而不干扰情节。
pylab.legend(loc='best')
如果可能的话,这将自动将图例置于远离数据的位置!
但是,如果没有地方放置图例而不与数据重叠,那么您将需要尝试其他答案之一;使用loc="best"
永远不会将图例放在图表之外。
解决方案 8:
简短回答:调用图例上的可拖动功能并以交互方式将其移动到您想要的任何位置:
ax.legend().draggable()
长答案:如果您更喜欢以交互/手动方式而不是以编程方式放置图例,则可以切换图例的可拖动模式,以便将其拖动到您想要的任何位置。查看以下示例:
import matplotlib.pylab as plt
import numpy as np
#define the figure and get an axes instance
fig = plt.figure()
ax = fig.add_subplot(111)
#plot the data
x = np.arange(-5, 6)
ax.plot(x, x*x, label='y = x^2')
ax.plot(x, x*x*x, label='y = x^3')
ax.legend().draggable()
plt.show()
解决方案 9:
matplotlib 3.7 中的新功能
图例现在直接接受“外部”位置,例如loc='outside right upper'
。
确保布局“受限”
使用
fig.legend
(非plt.legend
或ax.legend
)在位置字符串前面添加“outside”
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(layout='constrained')
#1
x = np.linspace(-np.pi, np.pi)
ax.plot(x, x, label='$f(x) = x$')
ax.plot(x, np.sin(x), label='$f(x) = sin(x)$')
ax.plot(x, np.cos(x), label='$f(x) = cos(x)$')
fig.legend(loc='outside right upper')
#2 #3
plt.show()
多个子图也与新的“外部”位置配合良好:
fig, (ax1, ax2) = plt.subplots(1, 2, layout='constrained')
# --------------------
x = np.linspace(-np.pi, np.pi)
ax1.plot(x, x, '-', label='$f(x) = x$')
ax1.plot(x, np.sin(x), '--', label='$f(x) = sin(x)$')
ax2.plot(x, np.cos(x), ':', label='$f(x) = cos(x)$')
fig.legend(loc='outside right center')
# -------
请注意,可用的“外部”位置是预设的,因此如果您需要更精细的定位,请使用较旧的答案。但是标准位置应该适合大多数用例:
locs = [
'outside upper left', 'outside upper center', 'outside upper right',
'outside center left', 'upper center right',
'outside lower left', 'outside lower center', 'outside lower right',
]
for loc in locs:
fig.legend(loc=loc, title=loc)
locs = [
'outside left upper', 'outside left lower',
'outside right upper', 'outside right lower',
]
for loc in locs:
fig.legend(loc=loc, title=loc)
解决方案 10:
Matplotlib 的新版本使得将图例定位到图外变得更加容易。我使用 Matplotlib 版本制作了这个示例3.1.1
。
用户可以将 2 元组坐标传递给loc
参数,以将图例定位在边界框中的任何位置。唯一的问题是您需要运行plt.tight_layout()
以让 matplotlib 重新计算绘图尺寸,以便图例可见:
import matplotlib.pyplot as plt
plt.plot([0, 1], [0, 1], label="Label 1")
plt.plot([0, 1], [0, 2], label='Label 2')
plt.legend(loc=(1.05, 0.5))
plt.tight_layout()
这引出了以下情节:
参考:
matplotlib.pyplot.legend
解决方案 11:
这不完全是您所要求的,但我发现它是解决同一问题的另一种方法。
使图例变得半透明,如下所示:
使用以下方式执行此操作:
fig = pylab.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, label=label, color=color)
# Make the legend transparent:
ax.legend(loc=2, fontsize=10, fancybox=True).get_frame().set_alpha(0.5)
# Make a transparent text box
ax.text(0.02, 0.02, yourstring, verticalalignment='bottom',
horizontalalignment='left',
fontsize=10,
bbox={'facecolor':'white', 'alpha':0.6, 'pad':10},
transform=self.ax.transAxes)
解决方案 12:
如上所述,您还可以将图例放置在图中,或者稍微偏离边缘。这是一个使用Plotly Python API 的示例,使用IPython Notebook制作。我是团队成员。
首先,您需要安装必要的软件包:
import plotly
import math
import random
import numpy as np
然后,安装 Plotly:
un='IPython.Demo'
k='1fw3zw2o13'
py = plotly.plotly(username=un, key=k)
def sin(x,n):
sine = 0
for i in range(n):
sign = (-1)**i
sine = sine + ((x**(2.0*i+1))/math.factorial(2*i+1))*sign
return sine
x = np.arange(-12,12,0.1)
anno = {
'text': '$\\sum_{k=0}^{\\infty} \\frac {(-1)^k x^{1+2k}}{(1 + 2k)!}$',
'x': 0.3, 'y': 0.6,'xref': "paper", 'yref': "paper",'showarrow': False,
'font':{'size':24}
}
l = {
'annotations': [anno],
'title': 'Taylor series of sine',
'xaxis':{'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},
'yaxis':{'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},
'legend':{'font':{'size':16},'bordercolor':'white','bgcolor':'#fcfcfc'}
}
py.iplot([{'x':x, 'y':sin(x,1), 'line':{'color':'#e377c2'}, 'name':'$x\\\\$'},\n {'x':x, 'y':sin(x,2), 'line':{'color':'#7f7f7f'},'name':'$ x-\\frac{x^3}{6}$'},\n {'x':x, 'y':sin(x,3), 'line':{'color':'#bcbd22'},'name':'$ x-\\frac{x^3}{6}+\\frac{x^5}{120}$'},\n {'x':x, 'y':sin(x,4), 'line':{'color':'#17becf'},'name':'$ x-\\frac{x^5}{120}$'}], layout=l)
这将创建您的图表,并允许您将图例保留在图表本身内。如果未设置图例,则默认将其放置在图表中,如下所示。
对于替代放置,您可以紧密对齐图形的边缘和图例的边框,并删除边框线以实现更紧密的贴合。
您可以使用代码或 GUI 移动和重新设置图例和图形的样式。要移动图例,您可以使用以下选项,通过分配 <= 1 的 x 和 y 值来将图例定位在图形内。例如:
{"x" : 0,"y" : 0}
-- 左下角{"x" : 1, "y" : 0}
-- 右下角{"x" : 1, "y" : 1}
-- 右上角{"x" : 0, "y" : 1}
-- 左上角{"x" :.5, "y" : 0}
-- 底部中心{"x": .5, "y" : 1}
-- 顶部中心
在这种情况下,我们选择右上角的,legendstyle = {"x" : 1, "y" : 1}
也在文档中描述:
解决方案 13:
我只是使用字符串'center left'
来表示位置,就像在MATLAB中一样。
我从 Matplotlib 导入了 pyplot。
参见代码如下:
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
t = A[:, 0]
sensors = A[:, index_lst]
for i in range(sensors.shape[1]):
plt.plot(t, sensors[:, i])
plt.xlabel('s')
plt.ylabel('°C')
lgd = plt.legend(loc='center left', bbox_to_anchor=(1, 0.5), fancybox = True, shadow = True)
解决方案 14:
您也可以尝试figlegend
。可以创建独立于任何 Axes 对象的图例。但是,您可能需要创建一些“虚拟”路径以确保对象的格式正确传递。
解决方案 15:
这是另一种解决方案,类似于添加bbox_extra_artists
和bbox_inches
,您不必在调用范围内添加额外的艺术家savefig
。我想到这个是因为我在函数中生成了大部分情节。
当您想要写出边界框时,您无需将所有添加内容添加到边界框中,而是可以提前将它们添加到Figure
艺术家中。使用类似于Franck Dernoncourt 的答案的内容:
import matplotlib.pyplot as plt
# Data
all_x = [10, 20, 30]
all_y = [[1, 3], [1.5, 2.9], [3, 2]]
# Plotting function
def gen_plot(x, y):
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)
lgd = ax.legend(["Lag " + str(lag) for lag in all_x], loc="center right", bbox_to_anchor=(1.3, 0.5))
fig.artists.append(lgd) # Here's the change
ax.set_title("Title")
ax.set_xlabel("x label")
ax.set_ylabel("y label")
return fig
# Plotting
fig = gen_plot(all_x, all_y)
# No need for `bbox_extra_artists`
fig.savefig("image_output.png", dpi=300, format="png", bbox_inches="tight")
。
解决方案 16:
类似这样的方法对我来说很管用。从 Joe 那里拿来的一点代码开始,这个方法修改了窗口宽度,以自动将图例放在图形的右侧。
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
x = np.arange(10)
fig = plt.figure()
ax = plt.subplot(111)
for i in xrange(5):
ax.plot(x, i * x, label='$y = %ix$'%i)
# Put a legend to the right of the current axis
leg = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.draw()
# Get the ax dimensions.
box = ax.get_position()
xlocs = (box.x0,box.x1)
ylocs = (box.y0,box.y1)
# Get the figure size in inches and the dpi.
w, h = fig.get_size_inches()
dpi = fig.get_dpi()
# Get the legend size, calculate new window width and change the figure size.
legWidth = leg.get_window_extent().width
winWidthNew = w*dpi+legWidth
fig.set_size_inches(winWidthNew/dpi,h)
# Adjust the window size to fit the figure.
mgr = plt.get_current_fig_manager()
mgr.window.wm_geometry("%ix%i"%(winWidthNew,mgr.window.winfo_height()))
# Rescale the ax to keep its original size.
factor = w*dpi/winWidthNew
x0 = xlocs[0]*factor
x1 = xlocs[1]*factor
width = box.width*factor
ax.set_position([x0,ylocs[0],x1-x0,ylocs[1]-ylocs[0]])
plt.draw()
解决方案 17:
当我有一个巨大的图例时,对我有用的解决方案是使用一个额外的空图像布局。
在以下示例中,我制作了四行,并在底部绘制了带有图例偏移的图像(bbox_to_anchor)。顶部没有被剪切。
f = plt.figure()
ax = f.add_subplot(414)
lgd = ax.legend(loc='upper left', bbox_to_anchor=(0, 4), mode="expand", borderaxespad=0.3)
ax.autoscale_view()
plt.savefig(fig_name, format='svg', dpi=1200, bbox_extra_artists=(lgd,), bbox_inches='tight')
解决方案 18:
以下是 matplotlib 教程中的一个示例。这是一个比较简单的例子,但我为图例添加了透明度,并添加了 plt.show(),因此您可以将其粘贴到交互式 shell 中并获得结果:
import matplotlib.pyplot as plt
p1, = plt.plot([1, 2, 3])
p2, = plt.plot([3, 2, 1])
p3, = plt.plot([2, 3, 1])
plt.legend([p2, p1, p3], ["line 1", "line 2", "line 3"]).get_frame().set_alpha(0.5)
plt.show()
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件