Tiling bitmaps on a wxFrame

119 Views Asked by At

I want to set the background on a frame with tiling bitmaps, using wxPerl. With the help of an example from wxWidgets I've come up with the code below. Unfortunately, it doesn't do anything, the frame remains blank. Is this even the right way to do it, or is there another way?

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;

2

There are 2 best solutions below

0
Håkon Hægland On BEST ANSWER

Here is an example using the ERASE_BACKGROUND event handler:

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;

This gives me the following output on Ubuntu 20.04:

enter image description here

See also: wxPython: Putting a Background Image on a Panel

0
VZ. On

The accepted answer already explains how to do this correctly, but I'd also like to explain what did you do wrong originally: you can't paint on WindowDC just once and hope to accomplish anything. Any persistent drawing must be done on PaintDC in EVT_PAINT handler or, as a special kind of exception, on the DC provided to EVT_BACKGROUND_ERASE handler. If you set up EVT_PAINT handler calling your original set_tiling_background and using PaintDC from it, it would have worked too.

In fact, on modern platforms (GTK3, macOS), you can't use neither WindowDC nor ClientDC at all, drawing on them simply doesn't work.