QGraphicsView: zoom

1. Idea general
La idea es que la rueda del ratón pueda utilizarse para ampliar y reducir la parte de la escena sobre la que está el ratón. El encargado del zoom es el QGraphicsView pero los eventos del ratón los estoy capturando con un QGraphicsScene modificado. Para que funcione hay que decirle al QGraphicsScene quien es su view. Para eso añado una función y la correspondiente variable:

public:
    void setView(QGraphicsView * v);

private:
    QGraphicsView * view;

La escena incluye una variable para controlar el factor de zoom:

qreal scaleValue;

En el constructor le asigno un valor 100 y luego se va modificando con la rueda del ratón.

Para mantener la vista encima del ratón hay que modificar la propiedad transformationAnchor del QGraphicsView:

ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
}

Esto falla cuando el tamaño de la escena es inferior al tamaño del QGraphicsView, pero en general funciona.

2. Captura del movimiento de la rueda del ratón.
La función delta() dice el sentido en que se ha girado la rueda: positivo hacia adelante, negativo hacia detrás.

bool MyGraphicsScene::eventFilter(QObject *watched, QEvent *event) {
    if (watched == this && (event->type()==QEvent::GraphicsSceneWheel)) {
        QGraphicsSceneWheelEvent * ew =  static_cast<QGraphicsSceneWheelEvent *>(event);
        qDebug() << "rueda" << ew->delta();
        qDebug() << "---";
        if (ew->delta() >0) {
            zoomIn();
        } else {
            zoomOut();
        }
        event->accept();
    }
}

<br />
</p><strong>3. Ampliación y reducción.</strong>
El control del zoom lo hago a través de la variable <em>scaleValue</em>. Los limites 7400 como máxima ampliación y 2 como mínima parece que funcionan.
Para decirle al <em>QGraphicsView</em> que <strong>doble</strong> la vista de la escena, tanto vertical como horizontal:

        view->scale(2,2);
        view->repaint();

Para reducir la vista a la mitad, lo mismo en vertical como horizontal:

        view->scale(0.5,0.5);
        view->repaint();

4. Las funciones del zoom.

void MyGraphicsScene::zoomIn(){
    if (scaleValue < 7400) {
        scaleValue *=2;
        view->scale(2,2);
        view->repaint();
    }
    qDebug() << "zoomIn:" << scaleValue;
}

void MyGraphicsScene::zoomOut() {
    if (scaleValue > 2) {
        scaleValue /=2;
        qDebug() << "scale:" << scaleValue;
        view->scale(0.5,0.5);
        view->repaint();
    }
   qDebug() << "zoomout" << scaleValue;
}

5. Más
Los eventos del QGraphicsScene

Dibujo en Qt. Parte II. Añadir un cursor crosshair


En la primera parte se ve como empezar a dibujar sobre un QGraphicsScene y mostrarlo en un QGraphicsView. A continuación vamos a añadir un cursor crosshair a la escena para que siga los movimientos del ratón.

1. Capturar los eventos del ratón.
Para detectar el movimiento del ratón sobre la escena se necesita crear una subclass del QGrahicsScene y redefinir la función mouseMoveEvent.

1.1 Una subclass de QGraphicsScene.
MyGraphScene.h

#ifndef MYGRAPHICSCENE_H
#define MYGRAPHICSCENE_H

#include <QGraphicsScene>

class MyGraphicScene2 : public QGrapicsScene
{
    Q_OBJECT
public:
    explicit MyGraphicScene2(QObject *parent = 0);

protected:
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
signals:

public slots:

};

#endif // MYGRAPHICSCENE_H

MyGraphScene.cpp

#include "mygraphicscene2.h"

MyGraphicScene2::MyGraphicScene2(QObject *parent) :
    QGrapicsScene(parent)
{
}

void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
}

2. El dibujo del cursor.
Declarar dos QGraphicsLineItem para dibujar la cruz y un QGraphicsTextItem para mostrar un mensaje de texto. El cursor tiene que mostrarse por encima de todo lo dibujado. Para eso se modifica la propiedad setZValue() de las lineas y el texto del cursor.

MyGrahicsScene.h

private:
    QPen pen;
    QGraphicsLineItem *horizHair;
    QGraphicsLineItem * vertHair;
    QGraphicsTextItem * cursorText;

MyGrapicsScene.cpp

MyGraphicsScene::MyGraphicsScene(QObject *parent) :
    QGraphicsScene(parent)
{
    cursorText = this->addText("");	//Inicializar el texto a mostrar sobre el cursor.
    cursorText->setZValue(100);		//Llevar por encima de todo el dibujo

    horizHair = this->addLine(-2000,0,2000,0,pen);	//Las líneas son lo suficiente largas para 
    vertHair = this->addLine(0,-1000,0,1000,pen);	//salir del borde de la pantalla.
    horizHair->setZValue(100);				
    vertHair->setZValue(100);
}

void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
    //qDebug() << event->scenePos();
    // Mover las lineas a la posición del ratón.
    horizHair->setPos(event->scenePos());
    vertHair->setPos(event->scenePos());

    // Mostrar las coordenadas del ratón sobre el cursor.
    QString txt;
    txt = QString::number(event->scenePos().x()) + "," + QString::number(event->scenePos().y());
    cursorText->setPlainText(txt);
    cursorText->setPos(event->scenePos().x(), event->scenePos().y() -16);
}

3. Ocultar la flecha del ratón sobre la escena.
Hay que detectar cuando el ratón entra en la escena para ocultar la flecha del cursor. Así mismo, cuando sale hay que volver a mostrarlo. Atención que al pasar el ratón sobre las barras de scroll del QGraphicsView tiene que volver a mostrarse la flecha del cursor.

Para conseguir esto hay que redefinir
bool eventFilter(QObject *watched, QEvent *event);

MyGrahicsScene.h

protected:
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    bool eventFilter(QObject *watched, QEvent *event);

MyGraphicsScene.cpp
En el constructor:

installEventFilter(this);

La función eventFilter:

bool MyGraphicsScene::eventFilter(QObject *watched, QEvent *event) {
    QGraphicsView *v;
    v = views().at(0);

    if(watched==this && (event->type()==QEvent::Enter || event->type()==QEvent::Leave)) {
        if (event->type() == QEvent::Enter) {
            qDebug() << "Hovering";
            v->setCursor(Qt::BlankCursor);
            horizHair->show();
            vertHair->show();
            cursorText->show();
        } else {
            qDebug() << "not Hovering";
            v->setCursor(Qt::ArrowCursor);
            horizHair->hide();
            vertHair->hide();
            cursorText->hide();
        }
    }
}

Dibujo en Qt. Parte I. El comienzo.

1. Añadir el QGraphicsView a la ventana principal desde el editor gráfico. Los dibujos se hacen sobre un QGraphicsScene que hay que añadir en el mainwindow.h

El mainwindow.h se queda así:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsScene>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private:
    Ui::MainWindow *ui;
    QGraphicsScene scene;
};

#endif // MAINWINDOW_H

2. Preparar el QGraphicsScene.
2.1. Lo primero darle tamaño al QGraphcsScene y tener una pluma con la que dibujar. En el constructor del mainWindow poner lo siguiente:

//includes
#include "QGraphicsView"
#include "QRectF"

QRectF rect(0,0,640,480); //Escena de 640x480
QPen  pen; //Pluma por defecto: Negro y 1px de ancho.

scene.setSceneRect(rect);

2.2. Dibujar algo. Un marco para la escena, una retícula y un saludo.

    scene.addText("hola mundo!");

    scene.addLine(0,0,640,0,pen);
    scene.addLine(640,0,640,480,pen);
    scene.addLine(0,480,640,480,pen);
    scene.addLine(0,0,0,480,pen);

    for (int x = 0; x < 640; x+=20) {
        scene.addLine(x,0,x,480,pen);
    }

    for (int y = 0; y < 480; y+=20) {
        scene.addLine(0,y,640,y,pen);
    }
}

3. Mostrar la escena.

    ui->graphicsView->setScene(&scene);
    ui->graphicsView->show();

4. Conclusión.
El constructor se queda así:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QRectF rect(0,0,640,480);
    QPen  pen;

    scene.setSceneRect(rect);
    scene.addText("hola mundo!");

    scene.addLine(0,0,640,0,pen);
    scene.addLine(640,0,640,480,pen);
    scene.addLine(0,480,640,480,pen);
    scene.addLine(0,0,0,480,pen);

    for (int x = 0; x < 640; x+=20) {
        scene.addLine(x,0,x,480,pen);
    }

    for (int y = 0; y < 480; y+=20) {
        scene.addLine(0,y,640,y,pen);
    }


    ui->graphicsView->setScene(&scene);
    ui->graphicsView->show();
}

Continuá..

Casio Algebra FX 2.0. Códigos del teclado: Getkey

Programa para obtener los códigos del teclado de la calculadora.
Un bucle infinito lee el teclado. Si se pulsa la tecla ESC se sale del bucle.

Códigos del teclado de la ALGEBRA FX 2.0

While…WhileEnd. Se encuentran en el menú de programa ([SHIFT] [VARS]) bajo la pestaña [WHLE] (F3)
Break. Está bajo la pestaña [CTRL] (F4)

If…Then…IfEnd. Dos formas de expresarlo:

'La condición se separa por una linea
If X=47
Then Break
IfEnd

'La condición se separa por dos puntos (:)
if x≠0:Then
Locate 9,3,X
IfEnd

Locate columna, linea, algo_para_imprimir.
Se encuentra bajo la pestaña [I/O] (F5)
Columna: 1-21
Linea: 1-7

Está bajo la pestaña [LOGIC] (f5)
La comilla (‘) Está escondida. Botón [OPTN], La pestaña [SYBL] (F4) aparece tras dos [F6 (▷)]

Locate 7,1,"TECLAS"
Locate 1,3,"CODIGO:"
Locate 8,7,"ESC PARA SALIR"

While 1
Getkey→X
If X=47
Then Break
Else
if x≠0:Then
Locate 9,3,"  "
Locate 9,3,X
IfEnd
IfEnd
WhileEnd
ClrText
"TERMINADO"

Nota:
La tecla ESC tiene el código 47.

Casio Algebra FX 2.0. If…Then…Else…IfEnd

Programa que pide un número y responde si es mayor o menor que 5.

Para abrir el menú de programa:
Pulsar las teclas [SHIFT] + [VARS]

Para pedir un número:
“DAME UN NUMERO”. El texto tiene que ir entre comillas.
?→X El valor introducido se asigna a la variable X
El signo “?” está en el menú de programa en la posición de [F3]
La instrucciones If… están en la posición [F1] después de pulsar [F6 (▷)]

"DAME UN NUMERO"?→X
If X<5
Then "ES MENOR QUE 5"
Else "ES MAYOR QUE 5"
IfEnd

Función que devuelve un string en Turbo C++

#include <string.h>
#include <stdlib.h>

/*
	Función que devuelve un string con el contenido de 
	un array de unsigned char.
	Includes necesarios:
	<string.h> - para strcpy y strcat
	<stdlib.h> - para itoa
	
	Uso:
	unsigned char tmp[]; // array donde se va a guardar el string
	cprintf("Linea: %s\n\r",getStrLine(3,tmp));
	
	Funciones relacionadas con strings:
	strcpy - Para copiar
	itoa   - Convierte int a string
	strcat - concatena string
	

	Nota:
	La variable donde se va a guardar la cadena tiene que
	declararse fuera de la función y pasarse como parámetro.
	Que luego la devuelva no es necesario pero conveniente.
*/
unsigned char * getStrLine(int y, unsigned char * buffer) {
	char tmp[10]; 			// variable temporal para guardar el string
	
	strcpy(buffer,""); 		//El string se pone como cadena vacia.
	for (int i = 0; i < _w; i++) {
		itoa((int)map[i][y],tmp,10); //El unsigned char se fuerza a (int) y se convierte en string
		strcat(buffer,tmp);			 //se concatena el string
	}
	
	return buffer;
}

Lectura del teclado en Turbo C++

#include <conio.h>
#include <stdio.h>

#define KEY_ENTER 13
#define KEY_ESC 27
#define KEY_SPACE 32
#define KEY_UP 72
#define KEY_LEFT 75
#define KEY_RIGHT 77
#define KEY_DOWN 80

int main(void) {
	int c;

	clrscr();
	cprintf("Pulsa las teclas del cursor\r\n");
	cprintf("Esc para salir\r\n");
	//getch();

	while (1) {
		c = getch();

		if (c==KEY_ESC) {
			return 0;
		}
		if (c == 0 ) {
			c = getch();
		}
		if (c == KEY_ENTER) {
			cprintf("Enter\n\r");
		} else if (c == KEY_LEFT) {
			cprintf ("LEFT\n\r");
		} else if (c == KEY_RIGHT) {
			cprintf ("RIGHT\n\r");
		} else if (c == KEY_UP) {
			cprintf ("UP\n\r");
		} else if (c == KEY_DOWN) {
			cprintf ("DOWN\n\r");
		} else if (c == KEY_SPACE) {
			cprintf ("Space\n\r");
		} else {
			cprintf("%d\n\r",c);
		}
	}

	return 0;

}