1

我想使用 tk 编写一个 perl 应用程序来可视化一个大的 2d 图(它可以被认为是 2d 图像)。我需要滚动和调整大小。我也不需要将整个图像存储在内存中。

它太大了,无法保存在一张巨大的图片中,但我可以轻松地重绘它的任何部分。所以,我想编写一个图形应用程序以交互模式查看这些数据。这就像 xvcg 对图形所做的那样:http: //blogs.oracle.com/amitsaha/resource/blog-shots/apt-rdepends.png(它是界面示例。有 x 和 y 滚动条和缩放条)

我的数据看起来有点像http://www.access-excel-vba.com/giantchart.png没有任何带有更细(1px)线条的文本,上面有很多点并且尺寸(现在)从 33000x23000 开始变大。我使用每像素 2 位的图像。

那么,如何在 perl/tk 中编写可滚动和可缩放的图像查看器?要求不是将整个图像存储在内存中(现在 190 Mb 并且会更多!),而是要求一些函数将其分段绘制。

关于语言/工具包选择。我的数据生成器是用 perl 编写的,操作系统是 unix/POSIX,所以我不想切换语言。我能够切换到另一个图形工具包,但 perl/tk 已预装在目标 PC 上。

4

4 回答 4

2

这听起来可能很有趣,但我认为你最好的方法是看一些关于编写高效 2D 瓷砖滚动游戏的文章。我已经用 Java 编写了类似您之前描述的内容,但核心概念是相同的。如果您能弄清楚如何将图像分解成更小的图块,那么只需对可见部分进行流式传输和缩放即可。

或者,您可以将整个图像渲染到磁盘,然后使用诸如http://www.labnol.org/internet/design/embed-large-pictures-panoramas-web-pages-google-maps-image-viewer/之类的东西2606/。谷歌地图解决了你提到的同样的问题,但规模要大得多。这种技术可能会破坏您为您创建的图像,然后允许您将其输入到基于浏览器的解决方案中。请注意,这确实超出了您的 Perl 要求,但它可能适合您的需要。

于 2011-01-08T22:56:25.990 回答
2

使用画布小部件。您可以直接放置图像或绘图,在这种情况下,内置scale方法将处理调整大小。使用正确的滚动处理程序,您可以在移动时动态加载和卸载内容,以保持合理的内存使用。-xscrollcommand例如,当您向右滚动到未加载区域并加载该区域的内容时,回调将检测到。您可以卸载项目一次然后离开屏幕。

于 2011-01-09T15:51:28.533 回答
1

如果您不想通过在画布中使用平铺照片图像来处理此问题(这基本上是 Michael Carman 和 NBJack 所建议的),那么您可以编写自己的自定义图像类型(需要一些 C 代码)。您需要实现的 API 是Tk_CreateImageType,它允许您自定义图像的五个关键方面(如何创建、安装到可显示的上下文中、绘制、从上下文中释放和删除)。有人告诉我——但不能从经验说,诚然——这是一个相当容易实现的 API。这样做的一个好处是您不需要像照片图像类型中存在的那样复杂(其中包括各种奇异的处理稀有显示类型),因此可以使用更有效的数据结构和更快加工。

于 2011-01-10T09:08:14.353 回答
1

通过查看您的示例数据,您尝试执行的操作似乎可以适应各种 Web 技术(具有背景颜色的大型表格,或使用 HTML<canvas>标记从头开始呈现)。

对于 Perl,您可以使用许多服务器端 Web 开发技术中的一种,或者您可以使用XUL::Gui之类的东西,这是我编写的一个模块,它基本上使用 Firefox(或其他支持的浏览器)作为 gui 渲染引擎珀尔。

下面是一个简短的示例,展示了如何使用该<canvas>元素(在本例中绘制一个谢尔宾斯基三角形,来自模块的示例):

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
            }
        }
    })
);
于 2011-01-10T22:28:46.380 回答