PDA

View Full Version : Simple ready-to-use widget to display an image



sylvaticus
27th December 2007, 08:42
Hello... I'm writing a multi-agent model and for the GUI I am using Qt.

I need a widget where I can pass it an image and the widget display it, and has methods to zoom and pan it with the mouse (or keyboard), with optional call of "autofitting" it.

Nothing more.

I saw the mandelbrot example and the plotter example (the latter one on Chapter 5 of "C++ GUI programming with Qt4") but they are all too complex for my understanding of GUI programming (that has its own terminology and concepts well ahead of my level).

Does anyone can suggest me a similar widget but ready to be used??

Cheers,
Sylvaticus

xgoan
27th December 2007, 09:08
You could try QGraphicsView with QGraphicsPixmapItem, it appears dificult at first but it's really easy

sylvaticus
27th December 2007, 09:30
I saw that QGraphicsView and QGraphicsItems are suggested when there is a complex scene to diplay, with many items.
Is it right to use it when the item is only one ( a pixmap) ??

wysota
27th December 2007, 12:00
It's fine although you might implement a simple subclass of QScrollArea and draw the image yourself too.

sylvaticus
27th December 2007, 14:58
I solved writing my own widget taking mandrebot as example (from default Qt examples).

For those interested this is the code.. (or see the attached zip)

MapBox.h



#ifndef MAPBOX_H
#define MAPBOX_H

#include <QPixmap>
#include <QWidget>

class MapBox : public QWidget {
Q_OBJECT

public:
MapBox(QWidget *parent = 0);

protected:
void paintEvent(QPaintEvent *event);
void keyPressEvent(QKeyEvent *event);
void wheelEvent(QWheelEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);

private slots:
void updatePixmap(const QImage &image);
void fitInWindows();

private:
void zoom(double zoomFactor);
void scroll(int deltaX, int deltaY);
QPixmap pixmap;
QPoint lastDragPos;
double sx1, sy1, sx2, sy2; ///< coordinates of corner pixels of source - pixmap - rectangle
double dx1, dy1, dx2, dy2; ///< coordinates of corner pixels of destination - widget - rectangle
};

#endif



MapBox.cpp


#include <QtGui>
#include <math.h>
#include "MapBox.h"

const double widgetX = 400;
const double widgetY = 400;
const double ZoomOutFactor = 0.8;
const double ZoomInFactor = 1 / ZoomOutFactor;
const double ScrollStep = 20;

MapBox::MapBox(QWidget *parent) :QWidget(parent) {

//qRegisterMetaType<QImage>("QImage");

setWindowTitle(tr("MapBox"));
setCursor(Qt::CrossCursor);
resize(400, 400);

// setting source and destination init corners..
sx1 = 0;
sy1 = 0;
sx2 = widgetX;
sy2 = widgetY;
dx1 = 0;
dy1 = 0;
dx2 = widgetX;
dy2 = widgetY;

QImage image;
image.load("biggerO.png");
updatePixmap(image);

}

void
MapBox::paintEvent(QPaintEvent *){
QPainter painter(this);
painter.fillRect(rect(), Qt::black);
if (pixmap.isNull()) return;
QRectF source (sx1, sy1, sx2-sx1, sy2-sy1); // the second point is in coordinates origin of the firt point !!!!
QRectF destination(dx1, dy1, dx2-dx1, dy2-dy1); // the second point is in coordinates origin of the firt point !!!!
/*
This is the main function of the widget... the good pointa are:
A) It takes into account the low level details of scaling, such interpolation
B) If the destination is outside the widgets bounds, it doesn't matter. It make its job
on the widget without any error (in this sence it isnot like an array luckily...)
*/
painter.drawPixmap(destination, pixmap, source);
}

void
MapBox::keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_Plus:
zoom(ZoomInFactor);
break;
case Qt::Key_Minus:
zoom(ZoomOutFactor);
break;
case Qt::Key_Left:
scroll(+ScrollStep, 0);
break;
case Qt::Key_Right:
scroll(-ScrollStep, 0);
break;
case Qt::Key_Down:
scroll(0, -ScrollStep);
break;
case Qt::Key_Up:
scroll(0, +ScrollStep);
break;
default:
QWidget::keyPressEvent(event);
}
}

void
MapBox::wheelEvent(QWheelEvent *event){
int numDegrees = event->delta() / 8;
double numSteps = numDegrees / 15.0f;
zoom(pow(ZoomInFactor, numSteps));
}

void
MapBox::mousePressEvent(QMouseEvent *event){
if (event->button() == Qt::LeftButton)
lastDragPos = event->pos();
}

void
MapBox::mouseMoveEvent(QMouseEvent *event) {
if (event->buttons() & Qt::LeftButton) {
scroll(event->pos().x()-lastDragPos.x(), event->pos().y()-lastDragPos.y());
lastDragPos = event->pos();
update();
}
}

void
MapBox::updatePixmap(const QImage &image){
pixmap = QPixmap::fromImage(image);
fitInWindows();
}

void MapBox::fitInWindows(){

double tempXScale = widgetX/pixmap.width();
double tempYScale = widgetY/pixmap.height();

sx1 = 0;
sy1 = 0;
sx2 = pixmap.width();
sy2 = pixmap.height();
dx1 = 0;
dy1 = 0;

if ( tempXScale >= tempYScale){
dx2 = pixmap.width()*tempYScale;
dy2 = widgetY;
} else {
dx2 = widgetX;
dy2 = pixmap.height()*tempXScale;
}
update();
}

void
MapBox::zoom(double zoomFactor){
double dx1new, dx2new, dy1new, dy2new;
dx1new = dx2- (dx2-dx1)* ( 1+ (zoomFactor-1)/2 );
dx2new = dx1+ (dx2-dx1)* ( 1+ (zoomFactor-1)/2 );
dy1new = dy2- (dy2-dy1)* ( 1+ (zoomFactor-1)/2 );
dy2new = dy1+ (dy2-dy1)* ( 1+ (zoomFactor-1)/2 );
dx1 = dx1new;
dy1 = dy1new;
dx2 = dx2new;
dy2 = dy2new;
update();
}

void
MapBox::scroll(int deltaX, int deltaY){
dx1 += ((double) deltaX);
dx2 += ((double) deltaX);
dy1 += ((double) deltaY);
dy2 += ((double) deltaY);
update();
}

AnotherDeveloper
29th December 2007, 11:09
If you are not tied to using QT, you could do it in JavaScript.

Magic Zoom is a neat script that has a good zoom effect on rollover. It's controlled via the mouse.

http://www.magictoolbox.com/magiczoom

sylvaticus
29th December 2007, 11:22
1) My program is a desktop applicaiton, not a web-site one (hovewer I am aware Qt can be scripted with a javascript-alike scripting engine, but I never used it and I don't know if this is the case);
2) The program you cited is not free, and this would be a problem for my gpl-program;
3) I already solved in the above way.

Thank you any how...
Sylvaticus