/usr/local/CPAN/Qt/GraphWidget.pm
package GraphWidget;
use strict;
use warnings;
use QtCore4;
use QtGui4;
use QtCore4::isa qw( Qt::GraphicsView );
use Edge;
use Node;
use constant { RAND_MAX => 2147483647 };
sub timerId() {
return this->{timerId};
}
sub centerNode() {
return this->{centerNode};
}
sub NEW
{
my($class) = @_;
$class->SUPER::NEW();
this->{timerId} = 0;
my $scene = Qt::GraphicsScene(this);
$scene->setItemIndexMethod(Qt::GraphicsScene::NoIndex());
$scene->setSceneRect(-200, -200, 400, 400);
this->setScene($scene);
this->setCacheMode(Qt::GraphicsView::CacheBackground());
this->setViewportUpdateMode(Qt::GraphicsView::BoundingRectViewportUpdate());
this->setRenderHint(Qt::Painter::Antialiasing());
this->setTransformationAnchor(Qt::GraphicsView::AnchorUnderMouse());
this->setResizeAnchor(Qt::GraphicsView::AnchorViewCenter());
my $node1 = Node(this);
my $node2 = Node(this);
my $node3 = Node(this);
my $node4 = Node(this);
this->{centerNode} = Node(this);
my $node6 = Node(this);
my $node7 = Node(this);
my $node8 = Node(this);
my $node9 = Node(this);
$scene->addItem($node1);
$scene->addItem($node2);
$scene->addItem($node3);
$scene->addItem($node4);
$scene->addItem(this->centerNode);
$scene->addItem($node6);
$scene->addItem($node7);
$scene->addItem($node8);
$scene->addItem($node9);
$scene->addItem(Edge($node1, $node2));
$scene->addItem(Edge($node2, $node3));
$scene->addItem(Edge($node2, this->centerNode));
$scene->addItem(Edge($node3, $node6));
$scene->addItem(Edge($node4, $node1));
$scene->addItem(Edge($node4, this->centerNode));
$scene->addItem(Edge(this->centerNode, $node6));
$scene->addItem(Edge(this->centerNode, $node8));
$scene->addItem(Edge($node6, $node9));
$scene->addItem(Edge($node7, $node4));
$scene->addItem(Edge($node8, $node7));
$scene->addItem(Edge($node9, $node8));
$node1->setPos(-50, -50);
$node2->setPos(0, -50);
$node3->setPos(50, -50);
$node4->setPos(-50, 0);
this->centerNode->setPos(0, 0);
$node6->setPos(50, 0);
$node7->setPos(-50, 50);
$node8->setPos(0, 50);
$node9->setPos(50, 50);
this->scale(0.8, 0.8);
this->setMinimumSize(400, 400);
this->setWindowTitle(this->tr('Elastic Nodes'));
}
sub itemMoved
{
if (!this->{timerId}) {
this->{timerId} = this->startTimer(1000 / 25);
}
}
sub keyPressEvent
{
my ($event) = @_;
if ($event->key() == Qt::Key_Up()) {
this->centerNode->moveBy(0, -20);
}
elsif ($event->key() == Qt::Key_Down()) {
this->centerNode->moveBy(0, 20);
}
elsif ($event->key() == Qt::Key_Left()) {
this->centerNode->moveBy(-20, 0);
}
elsif ($event->key() == Qt::Key_Right()) {
this->centerNode->moveBy(20, 0);
}
elsif ($event->key() == Qt::Key_Plus()) {
this->scaleView(1.2);
}
elsif ($event->key() == Qt::Key_Minus()) {
this->scaleView(1 / 1.2);
}
elsif ($event->key() == Qt::Key_Space() ||
$event->key() == Qt::Key_Enter()) {
foreach my $item (@{this->scene()->items()}) {
if ($item->isa('Node')) {
$item->setPos(-150 + rand(RAND_MAX) % 300, -150 + rand(RAND_MAX) % 300);
}
}
}
else {
this->SUPER::keyPressEvent($event);
}
}
sub timerEvent
{
my @nodes;
foreach my $item (@{this->scene()->items()}) {
if ($item->isa('Node')){
push @nodes, $item;
}
}
foreach my $node (@nodes) {
$node->calculateForces();
}
my $itemsMoved = 0;
foreach my $node (@nodes) {
if ($node->advance()) {
$itemsMoved = 1;
}
}
if (!$itemsMoved) {
this->killTimer(this->timerId);
this->{timerId} = 0;
}
}
sub wheelEvent
{
my ($event) = @_;
this->scaleView(2**(-($event->delta()) / 240.0));
}
sub drawBackground
{
my ($painter, $rect) = @_;
# Shadow
my $sceneRect = this->sceneRect();
my $rightShadow = Qt::RectF($sceneRect->right(), $sceneRect->top() + 5, 5, $sceneRect->height());
my $bottomShadow = Qt::RectF($sceneRect->left() + 5, $sceneRect->bottom(), $sceneRect->width(), 5);
if ($rightShadow->intersects($rect) || $rightShadow->contains($rect)) {
$painter->fillRect($rightShadow, Qt::darkGray());
}
if ($bottomShadow->intersects($rect) || $bottomShadow->contains($rect)) {
$painter->fillRect($bottomShadow, Qt::darkGray());
}
# Fill
my $gradient = Qt::LinearGradient($sceneRect->topLeft(), $sceneRect->bottomRight());
$gradient->setColorAt(0, Qt::Color(Qt::white()));
$gradient->setColorAt(1, Qt::Color(Qt::lightGray()));
$painter->fillRect($rect->intersect($sceneRect), Qt::Brush($gradient));
$painter->setBrush(Qt::NoBrush());
$painter->drawRect($sceneRect);
# Text
my $textRect = Qt::RectF($sceneRect->left() + 4, $sceneRect->top() + 4,
$sceneRect->width() - 4, $sceneRect->height() - 4);
my $message = this->tr('Click and drag the nodes around, and zoom with the mouse ' .
'wheel or the \'+\' and \'-\' keys');
my $font = $painter->font();
$font->setBold(1);
$font->setPointSize(14);
$painter->setFont($font);
$painter->setPen(Qt::Color(Qt::lightGray()));
$painter->drawText($textRect->translated(2, 2), $message);
$painter->setPen(Qt::Color(Qt::black()));
$painter->drawText($textRect, $message);
}
sub scaleView
{
my ($scaleFactor) = @_;
my $factor = this->matrix()->scale($scaleFactor, $scaleFactor)->mapRect(Qt::RectF(0, 0, 1, 1))->width();
if ($factor < 0.07 || $factor > 100) {
return;
}
this->scale($scaleFactor, $scaleFactor);
}
1;