I have a 3 monitor Gentoo Linux system running MATLAB. MATLAB runs on the center monitor. I need MATLAB to generate plots on the left monitor but it always plots on the right monitor.
I believe this is at least partially caused by the non-standard way I have my monitors arranged physically - essentially 2,3,1:
>> get(0,'MonitorPositions')
ans =
1 1 1920 1080
-3839 1 1920 1080
-1919 1 1920 1080
Is there a way I can control this as a default within MATLAB?
You can set the default figure position on the root object like so:
set(0, 'DefaultFigurePosition', [-3839 1 1920 1080]);
This will create windows that fill the left monitor by default. However, this default will likely reset each time you restart MATLAB, so you will have to put it in your startup file if you want it to persist from session to session.
Note: The documentation for the 'MonitorPositions' property of the root object says this:
The first two elements in each row indicate the display location with respect to the origin point. The last two elements in each row indicate the display size. The origin point is the lower-left corner of the primary display.
If you change which monitor is used as the primary display, the relative coordinates in the left two columns will change, meaning you will have to change the position value in the above line of code. If you think the display setup may change often, or you will be running code on different monitor setups, then you can ensure plots will always appear on the left-most monitor by looking for the monitor position with the lowest value in the left column. Here's how you could do it (also incorporating the previous default window size and position within a monitor):
monitorPos = get(0, 'MonitorPositions');
figurePos = get(0, 'DefaultFigurePosition');
[~, leftIndex] = min(monitorPos(:, 1));
set(0, 'DefaultFigurePosition', figurePos + [monitorPos(leftIndex, 1:2)-1 0 0]);
Related
I need to obtain the horizontal and vertical monitor resolution, independent of the display scaling in use by Windows. At scaling of 100%, there is no problem; GetSystemMetrics(SM_CXSCREEN) returns the actual horizontal resolution of the monitor in pixels. On a 1920 x 1080 monitor, that function returns 1920. However, if the scaling is set to 125% on the same computer, GetSystemMetrics(SM_CXSCREEN) returns 1536. The problem with that is, if I use that number in a call to BitBlt() to copy the screen, it copies only a partial screen, the left three quarters or so. My program does not know that it has to use 1920 to capture the entire screen, because Windows is telling it the horizontal monitor resolution is 1536. I tried using GetDeviceCaps() and using GetMonitorInfo() to get the monitor resolution, but they return the same values as GetSystemMetrics(). Windows is reporting a monitor resolution it is not actually using for BitBlt() and also for SetPixel(). The latter function draws to a location as though the screen resolution were 1920. For example, at scaling of 125%, SetPixel() draws a pixel at the x coordinate of 1500 not near the right edge of the screen, but at about three quarters from the left. SetPixel() will draw near the right edge of the screen if I tell it to draw at the x coordinate 1900, so evidently it is not interpreting screen coordinates as reported by GetSystemMetrics().
How can I get the actual monitor resolution in pixels, regardless of the scaling?
I found a solution:
RECT rBox;
HDC hdcScreen = GetDC(NULL);
GetClipBox(hdcScreen, &rBox);
When GetClipBox() returns, rBox contains dimensions in pixels of the entire screen, regardless of scaling factor. They are the same dimensions for all scaling factors. With screen resolution set to 1920 x 1080, the function sets rBox.left to 0 and rBox.right to 1920.
I want to set the same size for all figures using Matlab in order to save it later. How can I do that?
I thought it would be better to plot a figure first, and then get the size and position using command pos = get(gcf, 'Position'), and set the position for all the other figures.
Is it correct? Any better approaches?
There are a lot of options of how to save a figure in Matlab. If you do not use a Save As dialog box, you have two functions to choose from: saveas and print.
'Position' defines location and size of the drawable area, specified as a vector of the form [left bottom width height]. This area excludes the figure borders, title bar etc. Right now you are basically getting the size and location of your first figure as it appears on screen and save based on these dimensions.
When saving your figures this way, the dimensions will correspond to whatever internally was defined in Matlab or you yourself redefined using 'Position' property. But you don't always want/need the size of the saved figure and the size of the figure as it appears on screen to be the same. And you also have to take care of the position of your figures, which is in your case you retrieved using a set function, I'll skip it in my example.
gcf=figure;
figure_width_to_save = 12.5; %cm
figure_height_to_save= 10; %cm
location_x=2; %cm
location_y=2; %cm
gcf.Units = 'centimeters';
gcf.Position = [location_x location_y figure_width_to_save figure_height_to_save];
saveas(gcf,[savefigures_path,savefigure_name,'_saveas.tiff'],'tiffn');
print(gcf, '-dtiffn', [savefigures_path,savefigure_name,'_print.tiff'], '-r300');
But it's better to have a separate control over the settings used for saving a figure. For that you have to define 'PaperPosition' property. 'PaperPosition' defines figure size and location on page when saving, specified as a four-element vector of the form [left bottom width height], but actually with 'PaperPosition' property you don't need to think about the location of your figure as much as you would with the 'Position' property.
Now about the saving itself, you didn't mention which approach you use though.
The saveas function uses a resolution of 150 DPI and uses the 'PaperPosition' and 'PaperPositionMode' properties of the figure to determine the size of the image. If you want to print or save figures that are the same size as the figure on the screen, ensure that the 'PaperPositionMode' property of the figure is set to 'auto', but I prefer to have control over these properties myself.
If you save your figure in Matlab with saveas, then as an example you need to specify this:
gcf.PaperPositionMode = 'manual';
gcf.PaperUnits = 'centimeters';
gcf.PaperPosition = [0 0 figure_width_to_save figure_height_to_save];
saveas(gcf,[savefigures_path,savefigure_name,'.tiff'],'tiffn');
Function print additionally allows you to have control over the saved resolution of the figure. For example, a flag '-r300' sets the output resolution to 300 dots per inch. To specify screen resolution, use '-r0'.
print([savefigures_path,savefigure_name,'.tiff'],'-dtiffn','-r300')
Check out Matlab's examples about saving figures at specific size and resolution
I exclusively use an external monitor connected to my laptop (laptop's screen is turned off). When I try to make a MATLAB figure fit the screen it only gets a size big enough to fit the laptop screen (which has a lower resolution) it seems. I use the following:
figure('outerposition',get(0,'screensize')); % or 'monitorpositions'
I even tried:
figure('outerposition',[0 0 1 1.2]);
but it does not do it and the figure only fits a part of the monitor's screen.
Any help would be greatly appreciated as I am out of ideas.
P.-S. What I would like to do, ultimately, is to have a figure fit 90% (for example) of the screen (width and height).
Thanks in advance!
This solution is based on the screensize function written by Doug Schwarz in this Newsreader thread.
I have done some quick tests, it seems to return the desired results, find my adaptation at the bottom of this post.
Example use
Fullscreen, monitor 2
% Pass the monitor number to the screensize function, this example uses monitor 2
sz = screensize(2);
% The function returns pixel values, so must use units pixels
% Set the outerposition according to that.
figure('units', 'pixels', 'outerposition', sz)
Just filling 90% of the screen, monitor 2:
sz = screensize(2);
pad = 0.05; % 5% padding all around
szpadded = [sz(1:2) + sz(3:4)*pad, sz(3:4)*(1-2*pad)];
figure('units', 'pixels', 'outerposition', szpadded);
screensize function
Doug's original code relied on moving the mouse pointer to get positions, I'm not sure why and have slimmed that code down. I've also removed code duplication etc. to make things a bit more compact. The function essentially relies on getting the screen device array from the java back end.
function ss_out = screensize(screen_number)
%screensize: return screen coordinates of multiple monitors.
% Version: 1.0, 26 June 2008 Author: Douglas M. Schwarz
% Version: 1.1, 21 July 2017 Author: Wolfie
persistent myss
if isempty(myss)
% Get Screen Devices array.
sd = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment.getScreenDevices;
% Initialize screensize array.
num_screens = length(sd);
myss = zeros(num_screens,4);
% Loop over all Screen Devices.
for ii = 1:num_screens
bounds = sd(ii).getDefaultConfiguration.getBounds;
myss(ii,:) = [bounds.x, bounds.y, bounds.width, bounds.height];
end
end
num_screens = size(myss,1);
if nargin == 0
screen_number = 1:num_screens;
end
screen_index = min(screen_number,num_screens);
ss_out = myss(screen_index,:);
end
The problem might be related to this note in the documentation:
MATLAB sets the display information values for this property at startup. The values are static. If your system display settings change, the values do not update. To refresh the values, restart MATLAB.
In my desktop, starting Matlab with only one active screen gives that:
>> get(0,'MonitorPositions')
ans =
1 1 1280 1024
and even if I try to activate the screen later, it doesn't change. However, if I activate the second screen and then restart Matlab, I get:
>> get(0,'MonitorPositions')
ans =
-1919 -123 1920 1080
1 1 1280 1024
And then I can set the figure to this size:
figure('OuterPosition',[-1920 -123 3200 1080]);
which covers both screens.
I work with MATLAB on the right half of the screen, so I want figures to open on the left half of the screen. However, the figure height should be about the size of a default figure, so not the height of the screen. Also, I use MATLAB on different computers with variable screen sizes (pixels), so figure dimensions should depend on the screen size, but produce identical figures on screen. The figure dimensions and position are therefore dependent on the screen resolution, but the code generating the dimensions and position should be independent on it.
I've accomplished this with the code in my answer below, which I thought I'd share for anyone who finds this useful for their own setup.
The default MATLAB current folder can be set in MATLAB's preferences. I've set this to the network folder on all my MATLAB computers, this can also be a cloud folder of a cloud service, e.g. Dropbox. Then I put a file startup.m in that folder containing the following code.
ss = get(0,'screensize');
b = 7; % border around figures is 7 pixels wide
%TODO different for various operating systems and possibly configurations.
p = 0; % extra padding pixels from left edge of screen
if ispc
win = feature('getos');
i = (1:2) + regexp(win,'Windows ','end');
switch win(i)
case '10'
b = 0;
p = 2;
otherwise
% other cases will be added in the future
end
end
fwp = ss(3)/2-2*b-p; % figure width in pixels
b = b+p;
n = 5;
set(0,'defaultfigureposition',[b ss(4)/n, fwp, ss(4)*(1-2/n)])
clear
Now, every time I start MATLAB, it runs this script and it moves the default figures I create to the left half of the screen with a nice size (the axes are just a little wider than they are tall).
The figure's units are normalised, but they can be set to pixels or whatever measure you like as well. I hope someone will find this a useful script for their setup.
EDIT: I've update the script to keep the default figure units: pixels. This is necessary, because apps such as the curve fitting tool (cftool) or the Classification Learner (classificationLearner) and probably others are bugged with normalised figure units. Their (dialog) windows either don't show up (they are positioned outside your screen area) or are too small or too large.
EDIT 2: I've updated the script for compatibility with Windows 10. The figure windows now have a border of 1 pixel, instead of 7. Also, the figures are padded a bit to the right, because Windows 10 puts them too far to the left. Windows 10 is detected automatically.
TO DO: support additional operating systems (with detection), e.g. Mac, Linux. If you have such a system, please report the following in a comment:
Open MATLAB and copy paste the resulting string from the feature getos command here.
Position the figure against (not on or over) the left edge of the screen and against (not on or over) the right half of the screen and report the figure's position and outerposition here.
I'm using an external monitor. My notebook has display of height 800 px, but monitor has over 1000 px. If I'm running the script on external monitor
screenSize = get(0,'MonitorPositions');
figureSize = screenSize(4);
figure('Position',[0 0 figureSize figureSize])
the size of the new figure won't go over the size of notebook display. Is there a way how to fix this?
EDIT
I have found that if I start MATLAB while having already set the external monitor as an output device, the script runs just ok. Is there any way how to reset settings, that are responsible, before running the skript?
You probably want:
figureSize = screenSize(monitorNumber,4);
As screenSize(4) will give you the 4th element in the matrix - same as screenSize = screenSize(:)
By default figures are displayed on the primary display. If you want to force Matlab to show figures on the external monitor, you need to set the DefaultFigurePosition to a value that is actually on the secondary monitor.
Therefore, let's say you create a figure and drag it on the external monitor. Then you can fetch the current position and set it to the default like so:
FigPos = get(gcf,'Position');
set(0, 'DefaultFigurePosition', FigPos);
Then figures will subsequently appear on the external monitor with a size that fits. That's not perfect since you need to create a figure, drag it and then delete it and it's only valid for your current session. However you can add the previous lines of code in your startup.m file to do it automatically.
Hope that helps somehow!