[QT]- VS2015+QT5.6.2展示圖片,支持放大、縮小、和移動圖片

本文主要介紹,如何使用在VS2015平臺上集成QT環(huán)境實(shí)現(xiàn)一個控件(Widget),該控件可以展示圖片,并且該支持滾動鼠標(biāo)中間放大縮小圖片,右鍵移動鼠標(biāo)進(jìn)行圖片的拖動。本文將從以下幾個部分進(jìn)行描述:

  1. 將QT集成到VS2015平臺
  2. 自定義控件描述
  3. 圖片放大、縮小、移動的實(shí)現(xiàn)
  4. 工程效果
  5. 總結(jié)

一、 將QT集成到VS2015平臺



主要過程:安裝QT、配置環(huán)境、和安裝vs2015插件。在開源社區(qū)的一篇博客描述得很清楚,在這里不贅述了。點(diǎn)此跳轉(zhuǎn)到該博客

二、 自定以控件描述



在QT當(dāng)中沒有專門用來展示圖片的控件,通常用于展示圖片的控件是QLabel,但是該控件不支持圖片的縮放移動等功能,所以就自定義一個控件,使其支持上述功能。
先上代碼:

qclicklabel.hpp

#pragma once
#include <QLabel>
#include <QWidget>
#include <QMouseEvent>
#include <QDebug>

class QClickLabel : public QLabel {
    Q_OBJECT
signals :
    void MouseRelease(QMouseEvent *evt);
    void KeyPressed(QKeyEvent *evt);
    void MouseMoved(QMouseEvent* evt);
    void MousePressed(QMouseEvent* evt);
    void MouseDoubliClick(QMouseEvent* evt);
    void WheelEvent(QWheelEvent* evt);

public:
    QClickLabel(QWidget * parent = Q_NULLPTR): QLabel(parent)
    {}
    ~QClickLabel();

protected:
    void mouseReleaseEvent(QMouseEvent*);  // listen to mouse event
    void mousePressEvent(QMouseEvent*);  // listen to mouse event
    void mouseMoveEvent(QMouseEvent*);  // listen to mouse event
    void keyPressEvent(QKeyEvent *);  // listen to key press event
    void mouseDoubleClickEvent(QMouseEvent *event);
    void wheelEvent(QWheelEvent *event);
    
};

QClickLabel繼承于QLabel,該控件能夠支持鼠標(biāo)的點(diǎn)擊、移動、釋放、雙擊、和中鍵滾動事件(代碼protected部分定義);同時該控件能夠發(fā)射出相應(yīng)的信號(代碼signals處定義)


qclicklabel.cpp

#include "qclicklabel.hpp"

// destructor
QClickLabel::~QClickLabel() {
    
}


void QClickLabel::mouseReleaseEvent(QMouseEvent *evt)
{
    qDebug() << "in function ClickedLabel mouse release" << endl;
    emit MouseRelease(evt);
}


void QClickLabel::keyPressEvent(QKeyEvent *evt) {
    // listen to key press event
    qDebug() << "in function ClickedLabel key press" << endl;
    emit KeyPressed(evt);
}


void QClickLabel::mouseMoveEvent(QMouseEvent *event) {
    qDebug() << "in function mouseMoveEvent" << endl;
    emit MouseMoved(event);
}


void QClickLabel::mousePressEvent(QMouseEvent *event) {
    qDebug() << "in function mousePressEvent" << endl;
    emit MousePressed(event);
}


void QClickLabel::mouseDoubleClickEvent(QMouseEvent *event) {
    qDebug() << "in function mouseDoubleClickEvent" << endl;
    emit MouseDoubliClick(event);
}

void QClickLabel::wheelEvent(QWheelEvent *event) {
    qDebug() << "in function wheelEvent" << endl;
    emit WheelEvent(event);
}

qclicklabel.cpp實(shí)現(xiàn)部分,控件接收到任何鼠標(biāo)事件,都發(fā)送相應(yīng)的信號出去,以便在主程序當(dāng)中做出相應(yīng)的操作。

三、 圖片放大、縮小、移動的實(shí)現(xiàn)


在自定以的控件當(dāng)中,由于控件可以直接獲取到鼠標(biāo)事件,并且根據(jù)獲取到的鼠標(biāo)事件發(fā)出相應(yīng)的信號,所以在主程序當(dāng)中,我們只需要監(jiān)聽監(jiān)聽該控件,如果發(fā)出信號A,就做出相應(yīng)的操作。

圖片放大和縮小:
在此工程的實(shí)現(xiàn)中,加載到內(nèi)存當(dāng)中的圖片都是以橫縱鋪滿的方式展示在qlabel中,如下圖所示

圖 1 展示圖片



此時展示的圖片的寬高就和qlabel控件的寬高一致了,所以原圖片的寬高 o_img_width、o_img_height和qclicklabel的寬高 show_width、show_height會有一個比值 ratio_o_2_show_width 、 ratio_o_2_show_height。
此時,圖片剛剛導(dǎo)入展示時圖片的放大的倍數(shù)-scale為1(寬高擴(kuò)大的倍數(shù)相同),鼠標(biāo)的中鍵滾動,只需要改變這個scale在大于1的一個合理的范圍,然后先將圖片的寬高通過QImage里面的方法轉(zhuǎn)換到show_width*****scale , show_height*****scale,然后將圖片的Rect(show_start_x,show_start_y,show_width,show_height)-起點(diǎn)為(show_start _x,show_start_y),寬高分別為show_width,show_height的長方形,此時的show_start_x,show_start_y都為0部分展示出來既可以實(shí)現(xiàn)圖片的放大縮小了。

響應(yīng)鼠標(biāo)滾動事件的槽

// wheel to zoom in and zoom out
void QTImageDisplay::slot_qclicklabel_wheel_move(QWheelEvent* evt) {

    // get the delta
    float f_degree_delta = evt->delta()*1.0 / 640;

    // case1 : front image opened
    if (b_is_front_image_opened) {

        // zoom in and zoom out
        // update scale ratio
        if (f_zoom_current_scale + f_degree_delta > f_zoom_ratio_max || f_zoom_current_scale + f_degree_delta < f_zoom_ratio_min) {
            return;
        }
        f_zoom_current_scale += f_degree_delta;
        f_iamge_ratio_x = qimage_front_image->width()*1.0 / (f_zoom_current_scale*qclicklabel->width());
        f_iamge_ratio_y = qimage_front_image->height()*1.0 / (f_zoom_current_scale*qclicklabel->height());

        // show image
        show_image();
    }
}

show_image()函數(shù)實(shí)現(xiàn)

// show front image
void QTImageDisplay::show_image() {

    // test if qimage_front_image is not opened
    if (qimage_front_image == NULL) {
        return;
    }

    // show image 
    QImage qimage_temp = qimage_front_image->copy();

    // judge if the start_x or start_y is legal
    if (i_show_image_start_x + qclicklabel->width() > qclicklabel->width()*f_zoom_current_scale) {
        i_show_image_start_x = qclicklabel->width()*f_zoom_current_scale - qclicklabel->width() - 1;
    }
    if (i_show_image_start_y + qclicklabel->height() > qclicklabel->height()*f_zoom_current_scale) {
        i_show_image_start_y = qclicklabel->height()*f_zoom_current_scale - qclicklabel->height() - 1;
    }

    QImage * qimage_scaled = new QImage;
    *qimage_scaled = qimage_temp.scaled(QSize(qclicklabel->width()*f_zoom_current_scale, qclicklabel->height()*f_zoom_current_scale), Qt::IgnoreAspectRatio).copy(i_show_image_start_x, i_show_image_start_y, qclicklabel->width(), qclicklabel->height());
    qclicklabel->setPixmap(QPixmap::fromImage(*qimage_scaled));
    delete qimage_scaled;

}



圖片移動:
在圖片的移動過程中,需要使用到三個鼠標(biāo)事件:鼠標(biāo)單擊事件、鼠標(biāo)移動事件,鼠標(biāo)釋放事件。
在鼠標(biāo)單擊事件當(dāng)中,我們需要記錄此時點(diǎn)擊的是鼠標(biāo)的左鍵還是右鍵(用兩個private variable保存該狀態(tài),代碼如下所示),并且當(dāng)右鍵點(diǎn)擊時,需要記錄此時鼠標(biāo)的位置,為了后面移動圖片的需求;

// used to select edge point and translate image
void QTImageDisplay::slot_qclicklabel_mouse_press(QMouseEvent* evt) {

    if (b_is_front_image_opened) {
        // select edge point
        if (evt->button() == Qt::LeftButton) {
            
            b_mouse_left_button_clicked = true;
        }else if (evt->button() == Qt::RightButton) {
            // translate image
            b_mouse_right_button_clicked = true;
            i_mouse_last_position_x = evt->localPos().x();
            i_mouse_last_position_y = evt->localPos().y();
        }
    }
}

在鼠標(biāo)移動事件當(dāng)中,由于我們設(shè)置的是右鍵單擊然后移動鼠標(biāo)進(jìn)行圖片的移動,所以在此,我們只需要處理右鍵被點(diǎn)擊時的事件,此時記錄鼠標(biāo)移動后的所在的新的位置,然后交由translate_image()去移動圖片,并且展示,該函數(shù)主要是通過鼠標(biāo)的原始位置和鼠標(biāo)最新位置的差值來更新show_start_x,show_start_y,并且判斷此時的要展示的Rect(show_start_x,show_start_y,show_width,show_height)是否超出了邊界;

void QTImageDisplay::slot_qclicklabel_mouse_move(QMouseEvent* evt) {

    // case 1: front image is opened;
    if (b_is_front_image_opened) {
        if (b_mouse_right_button_clicked) {
            i_mouse_new_position_x = evt->localPos().x();
            i_mouse_new_position_y = evt->localPos().y();
            translate_image();
        }
    }
}

// translate front image
void QTImageDisplay::translate_image() {
    // 
    int i_distance = (i_mouse_new_position_x - i_mouse_last_position_x)*(i_mouse_new_position_x - i_mouse_last_position_x) + (i_mouse_new_position_y - i_mouse_last_position_y)*(i_mouse_new_position_y - i_mouse_last_position_y);
    if (i_distance < 50) {
        return;
    }

    // compute the delta
    int i_delta_x = -1 * (i_mouse_new_position_x - i_mouse_last_position_x);
    int i_delta_y = -1 * (i_mouse_new_position_y - i_mouse_last_position_y);


    // start x exceeds the left edge
    if (i_show_image_start_x + i_delta_x < 0) {
        i_show_image_start_x = 0;

    }
    else if (i_show_image_start_x + i_delta_x + qclicklabel->width() > int(f_zoom_current_scale*qclicklabel->width() - 1)) {
        //start x exceeds the right edge
        i_show_image_start_x = int(f_zoom_current_scale*qclicklabel->width() - qclicklabel->width());
    }
    else {
        i_show_image_start_x += i_delta_x;
    }

    // start y exceeds the top edge
    if (i_show_image_start_y + i_delta_y < 0) {
        i_show_image_start_y = 0;

    }
    else if (i_show_image_start_y + i_delta_y + qclicklabel->height() > int(f_zoom_current_scale*qclicklabel->height() - 1)) {
        //start y exceeds the bottom edge
        i_show_image_start_y = int(f_zoom_current_scale*qclicklabel->height() - qclicklabel->height());
    }
    else {
        i_show_image_start_y += i_delta_y;
    }

    // update the mouse last position 
    i_mouse_last_position_x = i_mouse_new_position_x;
    i_mouse_last_position_y = i_mouse_new_position_y;

    // show image 
    show_image();
}

在鼠標(biāo)釋放事件當(dāng)中,需要恢復(fù)記錄鼠標(biāo)的點(diǎn)擊狀態(tài)的兩個private variable,代碼如下所示:

//mouse wheel move event
void QTImageDisplay::slot_qclicklabel_mouse_release(QMouseEvent* evt) {

    //change the flag
    if (evt->button() == Qt::LeftButton) {
        b_mouse_left_button_clicked = false;
    }
    else if (evt->button() == Qt::RightButton) {
        b_mouse_right_button_clicked = false;
    }
}

至此,圖片的移動也就實(shí)現(xiàn)了。

四、 工程效果


圖 2 效果圖

五、 總結(jié)

本文只是展示了作者在實(shí)現(xiàn)展示圖片控件的一個思路,但是還不夠完美,比如可以多設(shè)置幾種展示的方式,比如居中,適應(yīng)寬度,適應(yīng)高度,寬高獨(dú)立放大縮小等等。不過如果你對于本文描述的實(shí)現(xiàn)方式能夠理解,那么上述的一些功能只需要根據(jù)自己工程的需求去私人定制就好了。

工程代碼地址:https://github.com/gg-z/QTImageDisplay


聲明:
  1. 聯(lián)系作者,新浪微博私信 @谷谷_z
  2. 如果在文章當(dāng)中發(fā)現(xiàn)有描述錯誤的地方,還請您不吝指出,萬分感謝!
  3. 此文章系本人原創(chuàng)作品,轉(zhuǎn)發(fā)請注明出處!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1、窗體 1、常用屬性 (1)Name屬性:用來獲取或設(shè)置窗體的名稱,在應(yīng)用程序中可通過Name屬性來引用窗體。 ...
    Moment__格調(diào)閱讀 4,589評論 0 11
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,509評論 0 17
  • 《裕語言》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 10...
    葉染柒丶閱讀 27,674評論 5 19
  • 因?yàn)榕_風(fēng)“海馬”,今日休假在家,意外賺了個連休三天。盡管沒開鬧鐘,8點(diǎn)剛過也就醒了。拉開厚窗簾,窗外一片風(fēng)平浪靜,...
    熊小妖閱讀 283評論 0 1
  • 這個小村莊,有一位老人,兩個孫兒。大的,為人很踏實(shí),不怎么喜歡說話,但品學(xué)兼優(yōu),從小就和刻苦努力的學(xué)習(xí),上了高中,...
    舊日王謝堂前燕閱讀 238評論 0 1