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 me开发者_StackOverflow中文版mory (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
}
}
})
);
精彩评论