在 wxFrame 上平铺位图
Tiling bitmaps on a wxFrame
我想使用 wxPerl 在带有平铺位图的框架上设置背景。
借助 wxWidgets 中的示例,我得出了以下代码。
不幸的是,它什么也没做,框架仍然是空白的。
这是正确的方法吗,还是有其他方法?
use warnings;
use strict;
package MyFrame;
use Wx qw(:everything);
use base qw( Wx::Frame );
sub new {
my ( $class, $path ) = @_;
my $self
= $class->SUPER::new( undef, -1, 'Test', [ -1, -1 ], [ 600, 400 ], );
$self->set_tiling_background($path);
return $self;
}
sub set_tiling_background {
my ( $self, $path ) = @_;
## Create a wxBitmap from the file
my $file = IO::File->new($path);
binmode $file;
my $handler = Wx::BMPHandler->new();
my $image = Wx::Image->new();
$handler->LoadFile( $image, $file );
my $bitmap = Wx::Bitmap->new($image);
## Just to check that the bitmap is good.
$bitmap->SaveFile('saved.bmp', wxBITMAP_TYPE_BMP);
## Draw the bitmap tiling over the frame
## https://github.com/wxWidgets/wxWidgets/blob/master/src/html/htmlwin.cpp
my $dc = Wx::WindowDC->new($self);
my $size_x = $bitmap->GetWidth;
my $size_y = $bitmap->GetHeight;
for ( my $x = 0; $x < 600; $x += $size_x ) {
for ( my $y = 0; $y < 400; $y += $size_y ) {
$dc->DrawBitmap( $bitmap, $x, $y, 0 );
}
}
}
package MyApp;
use base 'Wx::App';
my $path = '/path/to/bitmap.bmp';
sub OnInit {
my ($self) = @_;
my $frame = MyFrame->new($path);
$frame->Show(1);
}
package main;
MyApp->new->MainLoop;
这是一个使用 ERASE_BACKGROUND
事件处理程序的示例:
package MyFrame;
use Wx qw(:everything wxBITMAP_TYPE_JPEG);
use base qw( Wx::Frame );
use feature qw(say);
use strict;
use warnings;
use Wx::Event;
sub new {
my ( $class, $path ) = @_;
my $self
= $class->SUPER::new( undef, -1, 'Test', [ -1, -1 ], [ 600, 400 ], );
my $bitmap = Wx::Bitmap->new( $path , wxBITMAP_TYPE_JPEG );
Wx::Event::EVT_ERASE_BACKGROUND( $self, sub { $self->setBgImage( $bitmap, @_) });
return $self;
}
sub setBgImage {
my ( $self, $bitmap, $frame, $evt ) = @_;
return if !defined $evt;
my $dc = $evt->GetDC();
my $size_x = $bitmap->GetWidth;
my $size_y = $bitmap->GetHeight;
for ( my $x = 0; $x < 600; $x += $size_x ) {
for ( my $y = 0; $y < 400; $y += $size_y ) {
$dc->DrawBitmap( $bitmap, $x, $y, 0 );
}
}
}
package MyApp;
use base 'Wx::App';
my $path = 'logo.jpg';
sub OnInit {
my ($self) = @_;
my $frame = MyFrame->new($path);
$frame->Show(1);
}
package main;
MyApp->new->MainLoop;
这给了我 Ubuntu 20.04 上的以下输出:
接受的答案已经解释了如何正确地做到这一点,但我还想解释一下你最初做错了什么:你不能只在 WindowDC
上画一次就希望完成任何事情。任何持久性绘图都必须在 EVT_PAINT
处理程序中的 PaintDC
上完成,或者作为一种特殊的异常,在提供给 EVT_BACKGROUND_ERASE
处理程序的 DC 上完成。如果您设置 EVT_PAINT
处理程序调用您原来的 set_tiling_background
并从中使用 PaintDC
,它也会起作用。
事实上,在现代平台(GTK3、macOS)上,您根本不能使用 WindowDC
和 ClientDC
,在它们上绘图根本行不通。
我想使用 wxPerl 在带有平铺位图的框架上设置背景。 借助 wxWidgets 中的示例,我得出了以下代码。 不幸的是,它什么也没做,框架仍然是空白的。 这是正确的方法吗,还是有其他方法?
use warnings;
use strict;
package MyFrame;
use Wx qw(:everything);
use base qw( Wx::Frame );
sub new {
my ( $class, $path ) = @_;
my $self
= $class->SUPER::new( undef, -1, 'Test', [ -1, -1 ], [ 600, 400 ], );
$self->set_tiling_background($path);
return $self;
}
sub set_tiling_background {
my ( $self, $path ) = @_;
## Create a wxBitmap from the file
my $file = IO::File->new($path);
binmode $file;
my $handler = Wx::BMPHandler->new();
my $image = Wx::Image->new();
$handler->LoadFile( $image, $file );
my $bitmap = Wx::Bitmap->new($image);
## Just to check that the bitmap is good.
$bitmap->SaveFile('saved.bmp', wxBITMAP_TYPE_BMP);
## Draw the bitmap tiling over the frame
## https://github.com/wxWidgets/wxWidgets/blob/master/src/html/htmlwin.cpp
my $dc = Wx::WindowDC->new($self);
my $size_x = $bitmap->GetWidth;
my $size_y = $bitmap->GetHeight;
for ( my $x = 0; $x < 600; $x += $size_x ) {
for ( my $y = 0; $y < 400; $y += $size_y ) {
$dc->DrawBitmap( $bitmap, $x, $y, 0 );
}
}
}
package MyApp;
use base 'Wx::App';
my $path = '/path/to/bitmap.bmp';
sub OnInit {
my ($self) = @_;
my $frame = MyFrame->new($path);
$frame->Show(1);
}
package main;
MyApp->new->MainLoop;
这是一个使用 ERASE_BACKGROUND
事件处理程序的示例:
package MyFrame;
use Wx qw(:everything wxBITMAP_TYPE_JPEG);
use base qw( Wx::Frame );
use feature qw(say);
use strict;
use warnings;
use Wx::Event;
sub new {
my ( $class, $path ) = @_;
my $self
= $class->SUPER::new( undef, -1, 'Test', [ -1, -1 ], [ 600, 400 ], );
my $bitmap = Wx::Bitmap->new( $path , wxBITMAP_TYPE_JPEG );
Wx::Event::EVT_ERASE_BACKGROUND( $self, sub { $self->setBgImage( $bitmap, @_) });
return $self;
}
sub setBgImage {
my ( $self, $bitmap, $frame, $evt ) = @_;
return if !defined $evt;
my $dc = $evt->GetDC();
my $size_x = $bitmap->GetWidth;
my $size_y = $bitmap->GetHeight;
for ( my $x = 0; $x < 600; $x += $size_x ) {
for ( my $y = 0; $y < 400; $y += $size_y ) {
$dc->DrawBitmap( $bitmap, $x, $y, 0 );
}
}
}
package MyApp;
use base 'Wx::App';
my $path = 'logo.jpg';
sub OnInit {
my ($self) = @_;
my $frame = MyFrame->new($path);
$frame->Show(1);
}
package main;
MyApp->new->MainLoop;
这给了我 Ubuntu 20.04 上的以下输出:
接受的答案已经解释了如何正确地做到这一点,但我还想解释一下你最初做错了什么:你不能只在 WindowDC
上画一次就希望完成任何事情。任何持久性绘图都必须在 EVT_PAINT
处理程序中的 PaintDC
上完成,或者作为一种特殊的异常,在提供给 EVT_BACKGROUND_ERASE
处理程序的 DC 上完成。如果您设置 EVT_PAINT
处理程序调用您原来的 set_tiling_background
并从中使用 PaintDC
,它也会起作用。
事实上,在现代平台(GTK3、macOS)上,您根本不能使用 WindowDC
和 ClientDC
,在它们上绘图根本行不通。