Copy Legend from UIAxes to another UIAxes
이 질문을 팔로우합니다.
- 팔로우하는 게시물 피드에서 업데이트를 확인할 수 있습니다.
- 정보 수신 기본 설정에 따라 이메일을 받을 수 있습니다.
오류 발생
페이지가 변경되었기 때문에 동작을 완료할 수 없습니다. 업데이트된 상태를 보려면 페이지를 다시 불러오십시오.
댓글 수: 1
채택된 답변
Hi @Jason,
I've looked into the legend and color issues you're having when copying plots from UIAxes3 to a new figure, and here's what I found from the MathWorks documentation. The main problem with your legend error is that starting from MATLAB R2014b, legends and colorbars must be copied together with their associated axes as a vector input to copyobj
https://www.mathworks.com/help/matlab/ref/copyobj.html
so instead of trying to assign the legend directly with ax2.Legend=L1, you need to use copyobj([ax1.Children, ax1.Legend], ax2) which copies both the plot children and the legend together in one operation. Your second approach where you created a new legend using lgd=legend(ax2,L1s) was on the right track, but the TextColor issue you're seeing where it shows white then black is likely because MATLAB has automatic property modes that can override manual settings when legend properties inherit from the parent axes
https://www.mathworks.com/help/matlab/ref/matlab.graphics.illustration.legend-properties.html,
so after setting lgd.TextColor=L1.TextColor, try adding a drawnow command to force the graphics system to update immediately, or you might need to explicitly set all the legend's appearance properties including FontSize, Location, and other formatting in one go. Regarding the plot colors not being retained by copyobj, the colors should actually be preserved since they're stored in the line objects themselves rather than as axes properties, so when you added 'Color' to your propsToCopy list, that was actually copying the axes background color rather than the line colors. The line colors are properties of each individual line object in ax1.Children, so if the colors aren't appearing correctly, you can iterate through the children and manually copy them with something like for i=1:length(ax2.Children), ax2.Children(i).Color=ax1.Children(i).Color, end, keeping in mind that copyobj can reverse the order of objects so you might need to flip the indices. The most reliable solution would be to use copyobj([ax1.Children, ax1.Legend], ax2) to handle everything at once, then copy over any additional axes properties you need with your existing loop, and if you're still having issues with the legend appearance, recreate it with legend(ax2, L1.String) and manually copy all the legend properties. For more details on legend properties and text color settings, check out the complete documentation at
https://www.mathworks.com/help/matlab/ref/legend.html and
https://www.mathworks.com/help/matlab/ref/matlab.graphics.illustration.legend.text-properties.html .
Hope this helps!
댓글 수: 7
Hi @Jason,
The error you're getting when trying to use copyobj([ax1.Children, ax1.Legend], ax2) is expected and cannot be avoided because MATLAB does not allow copying a legend directly between UIAxes and regular axes as they are fundamentally different object types. The approach suggested by me works when copying between two regular axes objects, but fails when copying from UIAxes (which is an App Designer component) to a standard figure axes. The solution is to copy only the plot children using copyobj and then recreate the legend manually with all its properties. I've tested this approach extensively in MATLAB Mobile and it works perfectly, preserving all colors and legend styling. Here is the complete working solution for your App Designer code:
ax1 = app.UIAxes3; f2 = figure; ax2 = axes(f2);
% Copy all plot children - colors are preserved automatically new_children = copyobj(ax1.Children, ax2);
% Apply your custom appearance function makeGraphBlack(app, ax2, 'k', 'w');
% Copy axes properties ax2.XLim = ax1.XLim; ax2.YLim = ax1.YLim; ax2.Title.String = ax1.Title.String; ax2.XLabel.String = ax1.XLabel.String; ax2.YLabel.String = ax1.YLabel.String;
% Copy additional properties using your existing loop
propsToCopy = {'FontSize', 'GridLineStyle'};
for prop = propsToCopy
set(ax2, prop{1}, get(ax1, prop{1}));
end
% Recreate the legend with all properties if ~isempty(ax1.Legend) L1 = ax1.Legend; lgd = legend(ax2, L1.String); lgd.TextColor = L1.TextColor; lgd.FontSize = L1.FontSize; lgd.Location = L1.Location; lgd.Box = L1.Box; lgd.Color = L1.Color; drawnow; end
Regarding your concerns about colors not being retained, copyobj actually does preserve line colors automatically as they are properties of the individual line objects themselves, not the axes. When you added 'Color' to your propsToCopy list, that was copying the axes background color rather than the line colors. The issue you were having with the TextColor not changing in your second attempt was due to a typo where you wrote "ldg.TextColor" instead of "lgd.TextColor", and also because you were missing the drawnow command which forces MATLAB to update the graphics rendering immediately. The drawnow is particularly important because MATLAB sometimes uses automatic property modes that can override manual settings if the graphics system hasn't been forced to update.
If you want to test this solution before implementing it in your app, here is a standalone version that works in the MATLAB command window or MATLAB Mobile:
% Create a test source figure with plots and legend
fig1 = figure('Name', 'Source');
ax1 = axes(fig1);
x = 1:10;
plot(ax1, x, rand(1,10), 'r-o', 'LineWidth', 2, 'DisplayName', 'Red
Line');
hold(ax1, 'on');
plot(ax1, x, rand(1,10)+1, 'b-s', 'LineWidth', 2, 'DisplayName', 'Blue
Line');
plot(ax1, x, rand(1,10)+2, 'g-^', 'LineWidth', 2, 'DisplayName',
'Green Line');
hold(ax1, 'off');
ax1.Title.String = 'Original Plot';
ax1.XLabel.String = 'X Axis';
ax1.YLabel.String = 'Y Axis';
ax1.FontSize = 12;
ax1.GridLineStyle = '--';
grid(ax1, 'on');
L1 = legend(ax1);
L1.Location = 'northwest';
L1.FontSize = 10;
L1.TextColor = [1 1 1];
L1.Color = [0.2 0.2 0.2];
% Copy to new figure using the solution
f2 = figure('Name', 'Copied Figure');
ax2 = axes(f2);
new_children = copyobj(ax1.Children, ax2);
ax2.XLim = ax1.XLim;
ax2.YLim = ax1.YLim;
ax2.Title.String = [ax1.Title.String ' (COPIED)'];
ax2.XLabel.String = ax1.XLabel.String;
ax2.YLabel.String = ax1.YLabel.String;
ax2.FontSize = ax1.FontSize;
ax2.GridLineStyle = ax1.GridLineStyle;
grid(ax2, 'on');
if ~isempty(ax1.Legend) L1 = ax1.Legend; lgd = legend(ax2, L1.String); lgd.TextColor = L1.TextColor; lgd.FontSize = L1.FontSize; lgd.Location = L1.Location; lgd.Color = L1.Color; drawnow; end
Note: please see attached results.
When you run this test code, you'll see two figures side by side where Figure 1 is the original source with three colored lines (red, blue, green) and a styled legend with white text on a dark background, and Figure 2 is the copied version that preserves all the line colors perfectly and recreates the legend with identical styling including the white text color and dark background. This demonstrates that the solution handles everything correctly: the plot children are copied with their colors intact, the axes properties are transferred, and the legend is recreated rather than copied, which avoids the fundamental incompatibility between UIAxes and regular axes objects. The key insight is that legends are tightly bound to their parent axes type, so when you're moving between different axes types like UIAxes and regular axes, you cannot transplant the legend object directly but must instead create a new legend object on the destination axes and manually copy over all the styling properties you want to preserve.
Let me know how it goes!
Hi @Jason,
After reviewing my comments, I knew something was missing. However, good to hear the initial copy is working! I see what's happening with your invalid handle error - you're storing the copied graphics handles in `app.new_children`, but those handles become invalid once the actual objects get deleted from the axes (happens when you call `cla()`, explicitly delete objects, or clear the axes). Graphics handles are live references, not saved data, so once the objects are gone, the handles are toast.
The fix is straightforward - instead of storing the copied handles, just store a reference to your original source axes and re-copy from it whenever you need to reset.
Here's what to do:
In your App Designer startup (just once):
app.sourceAxes = ax; % Keep reference to your original axes
Add this function to your app:
function resetToSource(app, destAxes) cla(destAxes); copyobj(app.sourceAxes.Children, destAxes);
% Copy the axes properties you care about
destAxes.XLim = app.sourceAxes.XLim;
destAxes.YLim = app.sourceAxes.YLim;
destAxes.Title.String = app.sourceAxes.Title.String;
destAxes.XLabel.String = app.sourceAxes.XLabel.String;
destAxes.YLabel.String = app.sourceAxes.YLabel.String; if ~isempty(app.sourceAxes.Legend)
L = app.sourceAxes.Legend;
legend(destAxes, L.String, 'Location', L.Location);
end
endReplace your current copying code:
% Instead of: app.new_children = copyobj(ax.Children, ax2); resetToSource(app, ax2);
% Then add your other plots hold(ax2, 'on'); plot(ax2, newX, newY, 'DisplayName', 'Additional Data'); hold(ax2, 'off');
Your RESET button becomes really simple:
function RESETButtonPushed(app, event) resetToSource(app, app.UIAxes2); end
Why this works: your source axes never changes, so you always have a valid reference to copy from. No need to worry about tracking handles or checking if they're still valid.
Couple things to watch out for:Please don’t modify or clear `app.sourceAxes` - that's your master copy. Also, hold on doesn't delete existing plots, so your handles are probably getting invalidated somewhere else in your code (maybe a cla() call you forgot about?)
If you need to copy more axes properties (grid style, font size, etc.), just add them to the function.
This is way simpler than managing copied handles. I do apologize if my earlier comments were not helpful enough.
Give it a shot and let me know how it goes!
Hi @Jason,
Sorry for the late response. Looking at your output, I can see exactly what's happening.
When you create the legend with ‘Location', 'best’, MATLAB recalculates the FontSize based on the destination axes FontSize (typically 90% of the axes font size). Then when you set ‘lgd.FontSize = L.FontSize’,it's not sticking because the property is still in automatic mode.
According to the MATLAB documentation for legend text properties, the ‘FontSize’ property has a corresponding ‘FontSizeMode’ property that can be either ‘auto' or ‘manual'. When you set FontSize, it should automatically switch FontSizeMode to 'manual', but there appears to be a timing issue when using dot notation immediately after legend creation with location-based positioning. Here's your corrected code using name-value pairs during legend creation as recommended approach:
if ~isempty(app.sourceAxes.Legend) L = app.sourceAxes.Legend; lgd = legend(destAxes, L.String, 'Location', 'best', 'FontSize', L.FontSize); lgd.TextColor = L.TextColor; lgd.Box = L.Box; lgd.Color = L.Color; drawnow; end
Alternatively, if you prefer setting properties after creation:
if ~isempty(app.sourceAxes.Legend) L = app.sourceAxes.Legend; lgd = legend(destAxes, L.String, 'Location', 'best'); set(lgd, 'FontSize', L.FontSize); % Automatically sets FontSizeMode to 'manual' lgd.TextColor = L.TextColor; lgd.Box = L.Box; lgd.Color = L.Color; drawnow; end
The key difference is that setting FontSize as a name-value pair during legend creation (or using the `set` function) ensures that `FontSizeMode` is properly set to `'manual' and your FontSize value will stick.
References
Default Text Size in Legends Discussion
Let me know if this resolves the issue or if you run into any other quirks!
Thankyou your expertise is amazing!
Btw, the reason I use location as best, is when picking up the location from souce axes, it places it at a different location in the dest. Amaxes
추가 답변 (0개)
카테고리
도움말 센터 및 File Exchange에서 Creating, Deleting, and Querying Graphics Objects에 대해 자세히 알아보기
참고 항목
웹사이트 선택
번역된 콘텐츠를 보고 지역별 이벤트와 혜택을 살펴보려면 웹사이트를 선택하십시오. 현재 계신 지역에 따라 다음 웹사이트를 권장합니다:
또한 다음 목록에서 웹사이트를 선택하실 수도 있습니다.
사이트 성능 최적화 방법
최고의 사이트 성능을 위해 중국 사이트(중국어 또는 영어)를 선택하십시오. 현재 계신 지역에서는 다른 국가의 MathWorks 사이트 방문이 최적화되지 않았습니다.
미주
- América Latina (Español)
- Canada (English)
- United States (English)
유럽
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
