Foros del Web » Programación para mayores de 30 ;) » C/C++ »

Qt. ¿Como propagar eventos de escena del raton a items?

Estas en el tema de Qt. ¿Como propagar eventos de escena del raton a items? en el foro de C/C++ en Foros del Web. Hola: Ando enredando con el tema de QGraphicsView/QGraphicsScene/QGraphicsItem He visto este ejemplo: http://www.walletfox.com/course/qgra...imedrawing.php y siguiendo la idea he ido añadiendo items (circulos) y una cruceta ...
  #1 (permalink)  
Antiguo 08/12/2015, 15:36
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Qt. ¿Como propagar eventos de escena del raton a items?

Hola:

Ando enredando con el tema de QGraphicsView/QGraphicsScene/QGraphicsItem

He visto este ejemplo: http://www.walletfox.com/course/qgra...imedrawing.php
y siguiendo la idea he ido añadiendo items (circulos) y una cruceta tipo autoCAD

Pero ahora que me voy enterando de cómo funciona, he querido empezar a separar cosas.
Codigo fuente aquí

Ahora, el problema que tengo es que necesito que los eventos del ratón se me propaguen a los items, y no sé cómo hacerlo.

Por ejemplo, en el código que tengo y que se puede ver en el enlace, la cruceta son dos QGraphicsLineItem, que responden al movimiento del ratón así:
Código C++:
Ver original
  1. void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  2. {
  3.     ejeX->setLine(0,event->scenePos().y(),800,event->scenePos().y());
  4.     ejeY->setLine(event->scenePos().x(),0,event->scenePos().x(),600);
  5. ------------------
  6. (Aqui sigue actuando el evento)
  7. }

Ahora, cuando pretendo que la cruceta sea un objeto propio, sólo se me ocurre lo siguiente para propagar el evento al objeto Cursor:
Código C++:
Ver original
  1. void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  2. {
  3.    Cruceta->mouseMoveEvent(event);
  4. ------------------
  5. (Aqui sigue actuando el evento)
  6. }

Pero a esta "solución" le veo dos fallos:
1.- Me obliga a declarar los métodos de eventos en la clase Cursor (la clase que crea la cruceta) como públicos, cuando son métodos que debería ser protected, ya que son reimplementaciones de la clase QGraphicsItem
2.- No me acepta el método setMouseTracking(bool); , o al menos no funciona

Llevo toda la tarde viendo este ejemplo:
http://doc.qt.io/qt-5/qtwidgets-grap...e-example.html

Y hasta donde he podido entender, es el método QMouseMoveEvent de la propìa escena el que actúa de una u otra forma según el modo en el que esté (edicion, inserción, etc), lo que me lleva a pensar que lo que yo quiero debe de ser un error de diseño, o de concepto, y que supongo que no debería intentar ir por ahí.
Pero bueno, se aceptan sugerencias
Gracias como siempre
__________________
Mi calculadora en Qt
  #2 (permalink)  
Antiguo 09/12/2015, 02:33
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. ¿Como propagar eventos de escena del raton a items?

Bueno, se me acaba de ocurrir algo, pero aunque funciona, creo que sigo sin ir por la buena dirección.

Se trata de crear una función en el item, que se llama mover, y que recoge el punto actual:
Código C++:
Ver original
  1. void Cursor::mover(QPointF punto)
  2. {
  3.     posicionActual=punto;
  4.     EjeX.setLine(0,punto.y(),ancho,punto.y());
  5.     EjeY.setLine(punto.x(),0,punto.x(),alto);  
  6.     update();
  7. }

esta función se encarga de llamar al método paint().....

Y luego, en la clase de la scena:

Código C++:
Ver original
  1. void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  2. {  
  3.     Cruceta->mover(event->scenePos());
  4. -------------------------
  5. mas cosas
  6. }

El caso es que así funciona, pero no sé si es la forma correcta.
__________________
Mi calculadora en Qt
  #3 (permalink)  
Antiguo 09/12/2015, 02:42
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. ¿Como propagar eventos de escena del raton a items?

Dado que el objeto Cruceta no es un QWidget no tiene "presencia" en el formulario, luego no puede recibir eventos directamente. Digamos que lo que sucede es que tu "pintas" el objeto de forma explícita, de tal forma que el contenedor no sabe que en esas coordenadas hay un objeto al que deba pasarle eventos.

Tienes varias soluciones:

Usar señales

Puedes crear una señal en el objeto escena que exponga el evento que te interesa, en este caso mouseMove. Entonces simplemente haces un connect para cada objeto que deba conocer ese evento y listo:

Código C++:
Ver original
  1. class Cruceta
  2.   : public QObject // necesario para los eventos.
  3.                    // Se puede sustituir por cualquier objeto que herede de QObject
  4. {
  5.   Q_OBJECT // necesario para los eventos
  6.  
  7.     public slot:
  8.       void MouseMove(QGraphicsSceneMouseEvent *event)
  9.       { }
  10. };
  11.  
  12. class Scene // Por qué cruceta en español y scene en inglés???
  13. {
  14.   signals:
  15.     // Recuerda que las señales no tienen implementación, de eso se encarga qt.
  16.     void OnMouseMove(QGraphicsSceneMouseEvent *event);
  17. };
  18.  
  19. void Scene::Scene(...)
  20. {
  21.   // O como se llame tu objeto, tampoco es necesario que se cree aquí
  22.   cruceta = new Cruceta(...);
  23.  
  24.   // Eso sí, esto hay que hacerlo después de crear tu objeto cruceta
  25.   connect(this, SIGNAL(OnMouseMove(QGraphicsSceneMouseEvent*)),
  26.           cruceta, SLOT(MouseMove(QGraphicsSceneMouseEvent*)));
  27. }
  28.  
  29. void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  30. {
  31.     emit OnMouseMove(event); // Emitimos la señal
  32.  
  33.     // ------------------
  34.     // (Aqui sigue actuando el evento)
  35. }

Usar el patrón observer

Esta solución es similar a la anterior pero basada en C++ puro.

Necesitas una clase base para los suscriptores de los eventos. Lo ideal es que sea una clase virtual pura para reducir acoplamientos:

Código C++:
Ver original
  1. class Subscriber
  2. {
  3.   public:
  4.     void MouseMove(QGraphicsSceneMouseEvent* event) = 0;
  5. };

El siguiente paso es hacer que tu objeto Cruceta herede de esta nueva clase. Como Subscriber es virtual pura no te debería ocasionar muchos problemas si tienes que recurrir a la herencia múltiple:

Código C++:
Ver original
  1. class Cruceta
  2.  : public Subscriber, /* Tus propias herencias */
  3. {
  4.   public:
  5.     void MouseMove(QGraphicsSceneMouseEvent* event ) override
  6.     { }
  7. };

Y para terminar metemos mano al objeto Scene. Una forma de resolver esta papeleta consiste en tener una lista de suscriptores, así se puede propagar el evento con un simple bucle:

Código C++:
Ver original
  1. class Scene
  2. {
  3.   std::vector<Subscriber*> _subscribers;
  4.  
  5.   public:
  6.     Scene()
  7.     {
  8.        _cruceta = new Cruceta;
  9.        
  10.        Attach(_cruceta);
  11.     }
  12.  
  13.     void Attach(Subscriber* newSubscriber)
  14.     { _subscribers.push_back(newSubscriber); }
  15.  
  16.     void MouseMove(QGraphicsSceneMouseEvent* event)
  17.     {
  18.       // la segunda línea es una lambda, no te asustes :)
  19.       std::for_each(_subscribers.begin(),_subscribers.end(),
  20.                     [event](Subscriber* ptr) { ptr->MouseMove(event); });
  21.     }
  22. };

Cualquiera de estos dos mecanismos te permite separar ambas capas de código. La única diferencia es que la primera solución aprovecha las capacidades de Qt mientras que en la segunda lo tienes que cocinar todo a mano :)

Un saludo
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #4 (permalink)  
Antiguo 09/12/2015, 03:11
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. ¿Como propagar eventos de escena del raton a items?

Hola eferion, como siempre, muchas gracias por responder.

Pero antes de meterme a fondo con tu respuesta (me puedo pasar todo el día para entender el código en su totalidad...ojalá tuviera tu nivel ) te pregunto:

Cuando dices que el objeto Cruceta no es un QWidget y que no tiene "presencia" en el formulario, te refieres al QGraphicsView como ese QWidget?

Por otro lado, en el momento en el que la cruceta hereda de QGraphicsItem y la añado a la escena....¿no debería ser ser receptora de los eventos del ratón de la escena? Bueno, está claro que no es así...

En fin, como ves no tengo los conceptos muy claros
__________________
Mi calculadora en Qt
  #5 (permalink)  
Antiguo 09/12/2015, 03:29
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 14 años, 1 mes
Puntos: 10
Respuesta: Qt. ¿Como propagar eventos de escena del raton a items?

Bueno, ya me he visto tu primera propuesta.
La idea es parecida a mi "solución" pero bien hecha.

Lo que pasa es que esto me obliga a que la clase Cruceta herede de QGraphicsItem y de QObject.....creo que nunca he hecho una herencia múltiple Voy a probar, siempre tiene que haber una primera vez.

Por cierto, sobre los nombre en inglés y español...como parto de un código copiado, se pueden ver nombres en inglés. He querido mantenerlos así y mis primeros añadidos iban en inglés, pero al final la cabra siempre tira al monte y he terminado en español
__________________
Mi calculadora en Qt
  #6 (permalink)  
Antiguo 09/12/2015, 03:48
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 10 años, 1 mes
Puntos: 204
Respuesta: Qt. ¿Como propagar eventos de escena del raton a items?

Cita:
Iniciado por dehm Ver Mensaje
Cuando dices que el objeto Cruceta no es un QWidget y que no tiene "presencia" en el formulario, te refieres al QGraphicsView como ese QWidget?
Vamos a bajar un momento a nivel de API de windows. No hay que perder de vista que los controles gráficos de Qt acaban pasando antes o después por este punto.

Cada control de usuario (que son sensibles al teclado y al ratón) se crea haciendo uso de una función especial de la api de windows:
  • CreateWindow
  • CreateWindowEx
Estas funciones dan de alta una nueva zona "sensible" en la memoria de la pantalla.

Cuando el usuario, con el ratón, hace click en un punto de la pantalla, el evento es gestionado en primera instancia por el sistema operativo. Éste comprueba sobre qué region "sensible" se ha lanzado el evento. A partir de ese dato encuentra qué aplicación es la responsable de dicha región. Con esta información, el SO avisa a la aplicación sobre el nuevo evento... la aplicación entonces recibe el evento y lo acaba canalizando hasta que llega al control pertinente.

¿Por qué te cuento todo este coñazo? Por una sencilla razón: Si tu delegas la responsabilidad de pintar un componente añadiendo dicho componente a un contenedor (un diálogo, un layout, ...), se acaba creando una región "sensible" que será susceptible de recibir eventos del teclado y el ratón. Si tu pintas "a mano" un control... eso no es más que una serie de trazos en pantalla que no están asociados a nada, luego esa representación no va a recibir en la vida un evento del sistema operativo.

Espero que, tostonazo aparte, haya quedado un poco más claro el tema de los controles de usuario y los eventos :)
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.

Etiquetas: escena, eventos, funcion, int, propagar, raton
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 11:31.