#ifndef CVVISUAL_ZOOMABLEIMAGE_HPP #define CVVISUAL_ZOOMABLEIMAGE_HPP #include <QWidget> #include <QGraphicsScene> #include <QGraphicsView> #include <QGraphicsTextItem> #include <QRectF> #include <QGraphicsPixmapItem> #include <QWheelEvent> #include <QApplication> #include <QTimer> #include <QScrollBar> #include "opencv2/core.hpp" #include "util.hpp" #include "../util/util.hpp" #include "../util/observer_ptr.hpp" namespace cvv { namespace qtutil { namespace structures { /** * @brief A graphics view with overwritten event handlers. */ class ZoomableImageGraphicsView : public QGraphicsView { public: /** * @brief Constructor */ ZoomableImageGraphicsView() : QGraphicsView{} { } protected: /** * @brief Ignores the wheel event if ctrl is pressed. * @param event The event. */ virtual void wheelEvent(QWheelEvent *event) override; /** * @brief Ignores the mouse move event. * @param event The event. */ virtual void mouseMoveEvent(QMouseEvent * event) override { event->ignore(); } }; } /** * @brief The ZoomableImage class shows an image and adds zoom functionality. */ class ZoomableImage : public QWidget { Q_OBJECT public: /** * @brief Constructor * @param mat The image to display * @param parent The parent widget. */ ZoomableImage(const cv::Mat &mat = cv::Mat{}, QWidget *parent = nullptr); /** * @brief Destructor */ ~ZoomableImage() { //disconnect everything from custom signals QObject::disconnect(this, SIGNAL(updateArea(QRectF,qreal)), 0, 0); QObject::disconnect(this, SIGNAL(updateConversionResult(cv::Mat,ImageConversionResult)), 0, 0); QObject::disconnect(this, SIGNAL(updateMouseHover(QPointF,QString,bool)), 0, 0); //disconnect all slots QObject::disconnect((view_->horizontalScrollBar()), &QScrollBar::valueChanged, this, &ZoomableImage::viewScrolled); QObject::disconnect((view_->verticalScrollBar()), &QScrollBar::valueChanged, this, &ZoomableImage::viewScrolled); QObject::disconnect(&updateAreaTimer_,SIGNAL(timeout()),this,SLOT(emitUpdateArea())); //stop timer from being activated updateAreaTimer_.stop(); updateAreaQueued_=true; } /** * @brief Returns the current image. * @return The current image. */ const cv::Mat &mat() const { return mat_; } /** * @brief Returns the current image. * @return The current image. */ cv::Mat &mat() { return mat_; } /** * @brief Returns the visible area of the image. * @return The visible area of the image. */ QRectF visibleArea() const; /** * @brief Returns the zoom factor. * @return The zoom factor. */ qreal zoom() const { return zoom_; } QPointF mapImagePointToParent(QPointF) const; /** * @brief Returns the threshold that determines wheather the pixelvalues * are shown. * @return */ qreal threshold() const { return threshold_; } /** * @brief The overridden resize event (from QWidget). */ virtual void resizeEvent(QResizeEvent *) override { queueUpdateArea(); } /** * @brief Returns the width of the image. * @return The width of the image. */ int imageWidth() const { return mat_.cols; } /** * @brief Returns the height of the image. * @return The height of the image. */ int imageHeight() const { return mat_.rows; } /** * @brief Returns weather pixel values are shown depending on threshold. * @return Weather pixel values are shown depending on threshold. */ bool autoShowValues() const { return autoShowValues_; } /** * @brief Returns the current scroll factor for CTRL+scroll. * @return The current scroll factor for CTRL+scroll. */ qreal scrollFactorCTRL() const { return scrollFactorCTRL_; } /** * @brief Returns the current scroll factor for CTRL+shift+scroll. * @return The current scroll factor for CTRL+shift+scroll. */ qreal scrollFactorCTRLShift() const { return scrollFactorCTRLShift_; } /** * @brief Returns the image managed by this item. * @return The image managed by this item. */ QPixmap fullImage() const { return pixmap_->pixmap(); } /** * @brief Returns the currently visible area as an pixmap. * @return The currently visible area as an pixmap. */ QPixmap visibleImage() const { return QPixmap::grabWidget(view_->viewport()); } /** * @brief Returns the last image conversion result. * @return The last image conversion result. */ ImageConversionResult lastConversionResult() const { return lastConversionResult_; } signals: /** * @brief Emmited whenever the image is updated. It passes the * conversion result * and the image. */ void updateConversionResult(const cv::Mat &, ImageConversionResult) const; /** *@brief Emitted whenever the visible area changes. Passes the visible *area and zoom factor. */ void updateArea(QRectF, qreal) const; /** * @brief Updates information at mouse position (position, channel values of the pixel, whether the position is in the image) */ void updateMouseHover(QPointF,QString,bool); public slots: /** * @brief Sets the curent visible area (the center) and zoom factor. * @param rect The area. * @param zoom The zoom. */ void setArea(QRectF rect, qreal zoom); /** * @brief Updates the image to display. * @param mat The new image to display. */ void setMatR(cv::Mat &mat) { setMat(mat); } /** * @brief Updates the image to display. * @param mat The new image to display. */ void setMat(cv::Mat mat); /** * @brief Updates the zoom factor. * @param factor The zoom factor. */ void setZoom(qreal factor = 1); /** * @brief Sets weather pixel values are shown depending on threshold. * @param enable If true pixel values are shown depending on threshold. */ void setAutoShowValues(bool enable = true) { autoShowValues_ = enable; } /** * @brief Sets the threshold that determines wheather the pixelvalues * are shown. * @param threshold The threshold. */ void setThreshold(qreal threshold = 60) { threshold_ = threshold; } /** * @brief Resizes the image so it is fully visible. */ void showFullImage(); /** * @brief Returns the current CTRL+scroll zoom factor. * @return The current scroll factor. */ void setCTRLZoomFactor(qreal factor = 1.025) { scrollFactorCTRL_ = factor; } /** * @brief Returns the current CTRL+ shift+scroll zoom factor. * @return The current scroll factor. */ void setCTRLShiftZoomFactor(qreal factor = 1.01) { scrollFactorCTRLShift_ = factor; } /** * @brief The overridden wheel event (from QWidget). * @param event The event. */ virtual void wheelEvent(QWheelEvent *event) override; /** * @brief Sets the new delay between two updateArea signals. * (if 0 the signal will be emitted as soon as all the events in the window system's event queue have been processed) * @param i The new delay. (ignored if <0) */ void setUpdateAreaDelay(int i) { if(i>=0) { updateAreaDelay_=i; } } protected: /** * @brief The mouse move event will output informations regarding the pixelvalue and the position in the image with updateMouseHover(). * @param event The event */ virtual void mouseMoveEvent(QMouseEvent * event) override; private slots: /** * @brief Called when the graphic view is scrolled. */ void viewScrolled() { queueUpdateArea(); } /** * @brief Draws the pixel value for all visible pixels. */ void drawValues(); /** * @brief On right click a menu to save the current visible image or the full image will appear. * @param pos The position of the right click. */ void rightClick(const QPoint &pos); /** * @brief This function will emit updateArea() and set updateAreaQueued_ = false. * It will be called by updateAreaTimer_. */ void emitUpdateArea(); /** * @brief This function will start the timer updateAreaTimer_ with the delay updateAreaDelay_ and set updateAreaQueued_ = true. * If updateAreaQueued_ was already set this function will do nothing. */ void queueUpdateArea(); private: /** * @brief The image to display. */ cv::Mat mat_; /** * @brief The pixmap containing the converted image. */ util::ObserverPtr<QGraphicsPixmapItem> pixmap_; /** * @brief The graphics view showing the scene. */ util::ObserverPtr<structures::ZoomableImageGraphicsView> view_; /** * @brief The scene containing the pixmap. */ util::ObserverPtr<QGraphicsScene> scene_; /** * @brief The current zoom factor. */ qreal zoom_; /** * @brief The current threshold. */ qreal threshold_; /** * @brief Weather pixel values are shown depending on threshold. */ bool autoShowValues_; /** * @brief The values on the graphics scene. */ std::vector<QGraphicsTextItem *> values_; /** * @brief The factor multiplied to the number of scrolled pixels. */ qreal scrollFactorCTRL_; /** * @brief The factor multiplied to the number of scrolled pixels. */ qreal scrollFactorCTRLShift_; /** * @brief Will call emitUpdateArea() on timeout. */ QTimer updateAreaTimer_; /** * @brief Whether updateAreaTimer_ is running. */ bool updateAreaQueued_; /** * @brief The delay for updateAreaTimer_. */ int updateAreaDelay_; /** * @brief The last image conversion result. */ ImageConversionResult lastConversionResult_; }; } } #endif