Is there a good cross-platform way of getting the screen dimensions?
Frequently I do this with PerlTk:
use Tk;
my $mw = MainWindow->new;
my $screen_width = $mw->screenwidth();
my $screen_height = $mw->screenheight();
But it'd be better to not have to load all of Tk just to do this.
This looks like a good X11 specific way of doing these things (GetRootWindow should work for screen dimensions):
Perl: Getting the geometry of a window with X11 WindowID
But I think a cross-platform approach would be better.
Specifically, I'm looking for ways to determine the monitor dimensions in pixels, which is what Tk's screenwidth and screenheight return.
On most POSIX-y systems:
use Curses ();
my $screen_width = $Curses::COLS;
my $screen_height = $Curses::LINES;
These values don't update automatically when the screen is resized.
The best I can see for getting display/screen resolution is to combine OS-specific tools.
On X11 the best bet is probably xrandr† while on Windows it'd be Win32::GUI or Win32::API.
Then wrap it in a sub that checks for the OS (using $^O or such) and selects a tool to use.
(Or of course use a GUI package, like Perl/Tk that OP is using)
† For example
for my $line_out ( qx(xrandr) ) {
#print "--> $line_out";
if ( my ($wt, $ht) = $line_out =~ /^\s+([0-9]+)\s*x\s*([0-9]+)/ ) {
say "Width: $wt, height: $ht";
last;
}
}
There are many ways to parse that, and in principle I recommend using libraries to manage external programs (in particular in order to get their output/errors etc); this is a quick demo.
On my system the xrandr output is like
Screen 0: minimum 8 x 8, current 5040 x 1920, maximum 32767 x 32767
DP-0 connected primary 1920x1200+3120+418 (normal left inverted right x axis y axis) 519mm x 320mm
1920x1200 59.95*+
1600x1200 60.00
1280x1024 75.02 60.02
1152x864 75.00
...
and it keeps going, and then for all displays.
So I pick the top resolution for the first listed one ("primary")
Related
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 tries to create a text image (i think it is called that way) in matlab. That means that I want to create an image containing text. The problem is that there is no matlab function that can do this. The solution is that I use text and then captures the output in the figure.
In the beginning I used getframe to capture the output. This went completely fine as long as I did not do anything else on the screen at the same time. The problem is that |getframe| captures whatever is on the screen at the moment, which have led to annoying bugs. In hope of solving this problem I plan to use |print| instead. However, now I have the problem of image resolution changes when print is used. Do anyone know a solution for this?
This is what I have tried so far:
xlen = 1200; ylen = 700;
im = uint8(255*ones(ylen,xlen,3));
hf = figure('color','white','units','normalized','position',[.1 .1 .8 .8]);
image(ones(size(im)));
set(gca,'units','pixels','position',[5 5 size(im,2)-1 size(im,1)-1],'visible','off')
text('units','pixels','position',[1 ylen/2],'fontsize',60,'FontWeight','Bold','string','This is text')
set(hf,'Units','pixels')
set(hf,'Position',[100,100,xlen,ylen],'paperpositionmode','auto');
print(hf, '-dpng', 'myText.png');
Saving images in matlab can be a real struggle.
I found my solution by using export_fig from the mathlab file exchange (see
http://www.mathworks.com/matlabcentral/fileexchange/23629-export-fig).
Some of the aims of export fig are:
Figure/axes reproduced as it appears on screen
Cropped borders (optional)
Embedded fonts (pdf only)
Ok I have finally found a solution. Matlab uses 150 dpi resolution by default. This is kind of weird since windows use 96 dpi resolution in their operating system and mac uses 72 dpi. However, what I have heard it is recommended to use at least 150 dpi on printed material to get good quality. Anyway, since windows uses 96 dpi, it is clear that you need to set the image resolution to 96 dpi as well to print (refering to matlabs function print) a figure with the right size.
xlen = 1200; ylen = 700;
im = uint8(255*ones(ylen,xlen,3));
hf = figure('color','white','units','normalized','position',[.1 .1 .8 .8]);
image(ones(size(im)));
set(gca,'units','pixels','position',[5 5 size(im,2)-1 size(im,1)-1],'visible','off')
text('units','pixels','position',[1 ylen/2],'fontsize',60,'FontWeight','Bold','string','This is text')
set(hf,'Units','pixels')
set(hf,'Position',[100,100,xlen,ylen],'paperpositionmode','auto');
print(hf, '-dpng', '-r96','myText.png'); % SET RESOLUTION TO '-r96' for windows.
I noticed lately that in some cases the png will look differently as the pdf. I rendered the preview images in different sizes an realized that the output could be totally different for the same input when I change the output size of the surface.
The problem is, that text_extends reports different normalized sizes for the same text when the surface pixel size is different. In this example the width varies from 113.861 to 120.175. Since I have to write each line separately those errors are some times much bigger in total.
Has anybody an idea how avoid those miscalculation?
Here is a small demonstration of this problem
import cairo
form StringIO import StringIO
def render_png(width, stream):
width_px = height_px = width
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width_px, height_px)
cr = cairo.Context(surface)
cr.scale(float(width_px) / float(100),
float(height_px) / float(100))
cr.set_antialias(cairo.ANTIALIAS_GRAY)
cr.set_source_rgb (1, 1, 1)
cr.rectangle(0, 0, 100, 100)
cr.fill()
cr.select_font_face('Zapfino Extra LT') # a fancy font
cr.set_font_size(20)
example_string = 'Ein belieber Test Text'
xbearing, ybearing, width, height, xadvance, yadvance = (
cr.text_extents(example_string))
xpos = (100. - width) / 2. # centering text
print width
cr.move_to(xpos,50)
cr.set_source_rgba(0,0,0)
cr.show_text(example_string)
surface.write_to_png(stream)
return width
if __name__ == '__main__':
l=[]
for i in range(100,150,1):
outs=StringIO()
xpos = render_png(i,outs)
l.append((i,xpos))
#out = open('/home/hwmrocker/Desktop/FooBar/png_test%03d.png'%i, 'w')
#outs.seek(0)
#out.write(outs.read())
#out.close()
from operator import itemgetter
l=sorted(l,key=itemgetter(1))
print
print l[0]
print l[-1]
This behavior is likely due to the nature of text rendering itself - as glyphs in a font depend are drawn in different ways, depending on the pixel resolution. More so when the pixel resolution is small when compared to the glyph sizes (I'd say less than 30px height per glyph). This behavior is to be expected to some extend - always in order to prioritize readability of the text. If it is too off - or the png text is "uglier" than on the PDF (instead of incorrect size), then iis a bug in Cairo. Nevertheless, you probably should place this exact question on Cairo's issue tracker, so that the developers can tell wether it is a bug or not (and if it is, it maybe the only possible way for they to get aware of it)
(Apparently they have no public bug tracker - just e-mail it to cairo-bugs#cairographics.org )
As for your specific problem, the workaround I will suggest to you is to render your text to a larger surface -- maybe 5 times larger, and resize that surface and paste the contents on your original surface (if needed at all). This way you might avoid glyph-size variations due to constraints in the number of pixels available for each glyph (at the cost of having a poorer text rendering on your final output).
I need to create images that are within a 480w x 360h pixel "canvas".
I did create some images from my remote url, no problem with help from stackoverflow.
However, I desire to maintain aspect ratio of the image but, have the end result be 480x360.. Therefore, a "canvas" or border then crop technique needs to be used (from what I have read) but, I cannot seem to get it going.
Here is what I have:
#!/usr/bin/perl
use Image::Resize;
use Image::Magick;
use strict;
my $new = 'path/to/image/image.jpg';
my $somewords = 'Some words';
my $imageurl='http://myimageurl.com/image.jpg';
my $p = new Image::Magick;
$p->Read("$imageurl");
my ($origw, $origh) = $p->Get('width', 'height');
#### correct size images get processed here with just annotation ########
if (($origw == 480) && ($origh == 360)){
system("convert $imageurl -fill '#FFFFFF' -font Candice -pointsize 12 -undercolor '#00000080' -gravity SouthEast -annotate +1+1 '$somewords' $new");
}
#### process images of incorrect original size WHERE I AM STUCK #######
if (($origw != 480) && ($origh != 360)){
system("convert $imageurl $new");
system("convert $imageurl -resize 480x360\! -fill '#FFFFFF' -font Candice -pointsize 14 -undercolor '#00000080' -gravity SouthWest -annotate +1+1 '$somewords' $new");
}
What I need is this:
A "canvas" size of 480 x 360.
Reduce the original image from the url to correct aspect ratio at either 480w or 360h and place it in the middle of the 480x360 canvas.
I read somewhere, that offered no examples, that I could resize original image while maintaining aspect ratio to correct height or width whichever allows the image to be largest then, divide the other param (h or w) by 2 and then make add border based on that, then crop to size. Confused the "he + double hockey sticks" out of me.
I am so lost on trying to figure this out. I am even unsure if my question here is clear and worthy of asking stackoverflow.
Seems like resizing while maintaining aspect ratio while creating a fixed output image is very difficult! Hours of searching have not helped me.
I praise the one who offers a verbose solution. Thanks.
Isn't it a bit silly to use the Image::Magick module, and then use the external convert command? You can do all of this within your Perl script using Image::Magick.
Anyway, if you read the fine manual, you'll find that ImageMagick will resize to the highest dimensions within 480x360 without changing the aspect ratio by using 480x360. This works both on the command-line with convert and within Image::Magick. When you add the !, you're telling it to resize to exactly 480x360, disregarding the aspect ratio.
This should get you started without using external commands:
...
$p->Resize(geometry=>'480x360');
$p = $p->Montage(geometry=>'480x360', background=>'black', fill=>'white',
stroke=>'white', pointsize=>12, title=>$somewords);
$p->Write($new);
...
The easiest way to get the same aspect ratio would be to calculate and scale by a percentage instead. ImageMagick lets you scale using a percentage for the geometry. IE,
convert -geometry 25%x25% file1.jpg file2.jpg
Note that you can do all of this without calling system() too, though system() is probably easier to get you started and then convert it to internal Image::Magick API calls later.
I want to write a perl application using tk to visualize a large 2d plot (it can be considered as 2d image). I need scrolling and resizing. Also I need not to store entire image in memory.
It is too big to be saved at one huge picture, but I can easy redraw any part of it.
So, I want to write a graphic application to view this data in interactive mode. This is like what xvcg do for graphs: http://blogs.oracle.com/amitsaha/resource/blog-shots/apt-rdepends.png (it is example of interface. There are x and y scroll bars and zoom bars)
My data looks a bit like http://www.access-excel-vba.com/giantchart.png without any text with thinner (1px) lines, a lot of dots on them and have sizes (at now) from 33000x23000 and will be bigger. I use 2bit-per-pixel Images.
So, How can I progamm a scrollable and zoomable image viewer in perl/tk? The requirement is not to store entire image in memory (190 Mb now and will be more!), but ask some function to draw it in parts.
About language/toolkit selection. My data generator is written on perl, OS is unix/POSIX, so I want not to switch language. I able to switch to another graphic toolkit, but perl/tk is preinstalled on target PCs.
Use a Canvas widget. You can place images or draw directly, in which case the built-in scale method would handle resizing. With the right handlers for scrolling you could dynamically load and unload content as you moved around to keep the memory usage reasonable. e.g. the callback for the -xscrollcommand would detect when you scroll right into an unloaded area and load the content for that area. You could unload items once then go off-screen.
This may sound funny, but I think your best approach would be to take a look at a few articles on writing an efficient 2D tile scrolling game. I've written something like what you've described before in Java, but the core concepts are the same. If you can figure out how to break your image down into smaller tiles, it's just a matter of streaming and scaling only the visible portions.
Alternatively, you could render the entire image to disk, then use something such as http://www.labnol.org/internet/design/embed-large-pictures-panoramas-web-pages-google-maps-image-viewer/2606/ . Google Maps tackles the same problem you've mentioned but on a much larger scale. This technique could break the image you've created down for you, and then allow you to feed it into a browser-based solution. Mind you, this does step outside of your Perl requirement, but it may suit your needs.
If you don't want to handle this by working with tiled photo images in a canvas (which is essentially what Michael Carman and NBJack are suggesting) then you could write your own custom image type (requires some C code). The API you need to implement to is Tk_CreateImageType, which allows you to customize five key aspects of images (how they're created, installed into a displayable context, drawn, released from the context, and deleted). I'm told — but cannot say from experience, admittedly — that this is a reasonably easy API to implement. One advantage of doing this is that you don't need to have nearly as much complexity as exists in the photo image type (which includes all sorts of exotica like handling for rare display types) and so can use a more efficient data structure and faster processing.
From looking at your sample data, it looks like what you are trying to do can fit inside various web technologies (either a massive table with background colors, or rendered from scratch with the HTML <canvas> tag).
For Perl, you could either go with one of the many server side web development techniques, or you could use something like XUL::Gui which is a module I wrote which basically uses Firefox (or other supported browsers) as a gui rendering engine for Perl.
Here is a short example showing how to use the <canvas> element (in this case to draw a Sierpinski triangle, from the module's examples):
use strict;
use warnings;
use XUL::Gui 'g->';
my $width = 400;
my $height = sqrt($width**2 - ($width/2)**2);
g->display(
g->box(
g->fill,
g->middle,
style => q{
background-color: black;
padding: 40px;
},
g->canvas(
id => 'canvas',
width => $width,
height => int $height,
)
),
g->delay(sub {
my $canvas = g->id('canvas')->getContext('2d');
$canvas->fillStyle = 'white';
my #points = ([$width/2, 0],
[0, $height], [$width, $height],
);
my ($x, $y) = #{ $points[0] };
my $num = #points;
my ($frame, $p);
while (1) {
$p = $points[ rand $num ];
$x = ($x + $$p[0]) / 2;
$y = ($y + $$p[1]) / 2;
# draw the point with a little anti-aliasing
$canvas->fillRect($x + 1/4, $y + 1/4, 1/2, 1/2);
if (not ++$frame % 1_000) { # update screen every 1000 points
$frame % 100_000
? g->flush
: g->doevents # keeps firefox happy
}
}
})
);