Tk::Canvas - Leinwand zum Zeichnen

Wie in HTML5 gibt es auch für Perl/Tk eine Canvas. Eine Tk::Canvas stellt eine Zeichenfläche dar, auf der beliebige Objekte gezeichnet werden können. Die Objekte können nicht nur Formen und Text sein, sondern auch andere Perl/Tk-Widgets.


Scrollbalken an den Canvas-Inhalt anpassen

Eine Canvas kann mit Bildlaufleisten versehen werden, wenn mehr Inhalt in der Canvas enthalten wird, als im sichtbaren Bereich angezeigt werden kann.

Einfach per Scrollable Bildlaufleisten an die Canvas ankonfigurieren ist aber nur der erste Schritt. Damit die Bildlaufleisten funktionieren, muss man über die Option -scrollregion angeben, wie groß die zu Grunde liegende Fläche ist. Erst dann passen sich die Scrollbalken an den von der Canvas sichtbaren Bereich an.

#!perl

use strict;
use warnings;
use utf8;
use Tk;

my $mw = tkinit();

my $canvas = $mw->Scrolled('Canvas',
    -bg => 'white',
    # Bildlaufleisten unten und rechts
    -scrollbars => 'se',
)->pack(
    -fill => 'both',
    -expand => 1,
);

# Element im sichtbaren Bereich:
my $oval = $canvas->createOval(10, 10, 60, 60,
    -fill => 'orange',
);

# Element teilweise außerhalb des sichtbaren Bereichs:
my $line = $canvas->createRectangle(250, 200, 700, 700,
    -fill => 'blue',
    -width => 4,
);

# Bildlaufleisten der tatsächlichen Größe der Canvas anpassen
$canvas->configure(-scrollregion => [ $canvas->bbox("all") ]);

$mw->MainLoop();

raise() und lower bei einer Scrolled Canvas

Ein kleiner Fallstrick bei einer Canvas, die per Scrollable mit Bildlaufleisten versehen wurde, ist, dass die Methoden raise und lower nicht auf das Objekt angewandt werden können, das von der Widget-Erzeugung mit Scrolled zurückgeliefert wird:

my $scrolled_canvas = $mw->Scrolled('Canvas',);
$canvas->lower($move_this_widget_id, $below_this_one);

Die Fehlermeldung sieht dann in etwa so aus:

wrong # args: should be "lower window ?belowThis?" at path\scrolled-canvas.pl line ...

Die Lösung ist, die Methoden direkt auf das Canvas-Widget anzuwenden, nicht auf das gescrollte Canvas-Mega-Widget. Die zu Grunde liegende Canvas erhält man mittels Subwidget. Das Argument scrolled liefert übrigens immer das gescrollte Widget zurück. So muss nicht der exakte Widget-Name eingegeben werden.

my $scrolled_canvas = $mw->Scrolled('Canvas',);
my $canvas = $scrolled_canvas->Subwidget('scrolled');

Hover-Effekt auf der Canvas

Aus CSS ist der sog. Hover-Effekt bekannt. Fährt ein Nutzer mit der Maus über einen bestimmten Bereich, wird dieser hervorgehoben. Das geht auch bei einer Canvas. Es gilt dafür den Moment abzufangen, in dem die Maus über einen definierten Bereich fährt bzw. ihn wieder verlässt. Das geschieht über den Befehl bind(), mit dem man auf Ereignisse (engl. Events) reagieren kann.

Der bind()-Befehl ist bei der Canvas ziemlich mächtig. Man kann ein einzelnes Objekt binden, eine Gruppe von Objekten, alle Objekte mit bestimmten Tags usw. Der nachfolgende Code veranschaulicht, wie mit bind() die beiden Ereignisse Maus fährt über ein Element und Maus verlässt eine Element gebunden werden. Das Ereignis, bei dem die Maus über das Objekt fährt, ist <Enter>. Das Ereignis, welches beim Verlassen des Objektes mit der Maus ausgelöst wird, ist <Leave>.

Zuvor soll definiert werden, was dabei passieren soll:

Ereignis Aktion
Maus befindet sich nicht über einem Objekt Keine Hervorhebung von irgendeinem Objekt. Alle Objekte haben ihre Standardfarben.
Maus befindet sich über einem Objekt Das Objekt, über dem sich die Maus befindet, soll hervorgehoben werden. Dazu werden die Hintergrundfarbe und die Rahmenfarbe geändert.
Maus verlässt ein hervorgehobenes Objekt Die farbliche Hervorhebung soll wieder rückgängig gemacht werden. Das Objekt erhält die Standard-Farben für Hintergrund und Rahmen.

Mit diesem Wissen gewappnet kann man sich nun überlegen, wie die Hervorhebung genau gestaltet werden soll. Für das folgende Beispiel gibt es also zwei Zustände für ein Objekt: normal (Maus ist nicht drüber) und hover (Maus ist drüber).

Pro Zustand gibt es im Beispiel-Code zwei Farben: Füllfarbe (Hintergrundfarbe) und Rahmenfarbe. Kombiniert sieht das ganz nett aus. Hier ein Beispiel für eine leichte Hervorhebung im Microsoft-Office-Orange:

Status Hintergrundfarbe Rahmenfarbe Beispiel
normal #FFD86C #C0C0C0
#FFD86C
hover #FFE169 #E0E0E0
#FFE169

Das Ereignis <Enter> und die Objekt-Füllfläche

Achtung! Objekte besitzen standardmäßig keine Füllung. Der Hover-Effekt in diesem Beispiel tritt nur dann ein, wenn die Maus über dem vom Objekt gefüllten Bereich schwebt. Wird also nur ein Rahmen angezeigt, so ist der Hover-Effekt auch nur sichtbar, wenn die Maus über dem Rahmen steht. Das animierte Gif auf dieser Seite zeigt diesen Effekt. Steht die Maus genau auf dem Rahmen, wird der Rahmen rot. Steht die Maus hingegen im Rahmen auf der transparenten Fläche, so ist der Rahmen wieder schwarz. Ist die Fläche gefüllt, klappt der Effekt wie vorgesehen.

#!perl

use strict;
use warnings;
use utf8;
use Tk;
use Tk::Canvas;

=comment

Canvas-Demo: zeigt den Hover-Effekt für eine Form.
Wenn die Maus über dem Objekt schwebt, wird es hervorgehoben.

=cut

my $mw = tkinit(-title => 'Canvas-Hover-Effekt');

my $canvas = $mw->Canvas(
	-bg => 'white',
	-width => 410,
)->pack(-fill => 'both', -expand => 1);


# ---------------------------------------------------------------
# -- Rechteck ohne Füllung, Hover-Effekt tritt bei Maus über
# Rahmen auf
my $rect = $canvas->createRectangle(50, 50, 180, 100);
$canvas->bind( $rect, '<Enter>' => sub{
	$canvas->itemconfigure( $rect, -outline => 'red');
});
$canvas->bind( $rect, '<Leave>' => sub{
	$canvas->itemconfigure( $rect, -outline => 'black');
});


# ---------------------------------------------------------------
# -- gefülltes Rechteck, Hover-Effekt tritt bei Maus über der
# gesamten Fläche auf
my $rect_filled = $canvas->createRectangle(
	230, 50, 360, 100,
	-outline => '#C0C0C0',
	-fill => '#FFEC73',
);
$canvas->bind( $rect_filled, '<Enter>' => sub{
	$canvas->itemconfigure( $rect_filled,
		-outline => '#E0E0E0',
		-fill => '#FFF196',
	);
});
$canvas->bind( $rect_filled, '<Leave>' => sub{
	$canvas->itemconfigure( $rect_filled,
		-outline => '#C0C0C0',
		-fill => '#FFEC73',
	);
});


# ---------------------------------------------------------------
# -- gefüllte überlappende Rechtecke, Hover-Effekt tritt bei
# Maus über der gesamten Fläche auf
my $rect_overlapping_left = $canvas->createRectangle(
	50, 150, 180, 200,
	-outline => '#C0C0C0',
	-fill => '#FFD86C',
);
$canvas->bind( $rect_overlapping_left, '<Enter>' => sub{
	$canvas->itemconfigure( $rect_overlapping_left,
		-outline => '#E0E0E0',
		-fill => '#FFE169',
	);
});
$canvas->bind( $rect_overlapping_left, '<Leave>' => sub{
	$canvas->itemconfigure( $rect_overlapping_left,
		-outline => '#C0C0C0',
		-fill => '#FFD86C',
	);
});

my $rect_overlapping_right = $canvas->createRectangle(
	150, 180, 230, 230,
	-outline => '#C0C0C0',
	-fill => '#FFEC73',
);
$canvas->bind( $rect_overlapping_right, '<Enter>' => sub{
	$canvas->itemconfigure( $rect_overlapping_right,
		-outline => '#E0E0E0',
		-fill => '#FFF196',
	);
});
$canvas->bind( $rect_overlapping_right, '<Leave>' => sub{
	$canvas->itemconfigure( $rect_overlapping_right,
		-outline => '#C0C0C0',
		-fill => '#FFEC73',
	);
});

$mw->MainLoop();
Top