7-乾貨! QT -實現使用拖放打開文件,以及打開可拖動的圖片

實現一個拖動打開圖片,並可以在窗口內任何移動圖片的應用程序。

實現上述程序,首先需要分開三個部分來完成。第一個部分是界面創建,第二個部分是實現拖動打開文件功能,第三個部分是在窗口內移動圖片功能。

 

首先,第一步,界面創建。簡單的說一下大致步驟:

1..新建QT的GUI項目,項目名稱隨意取,基類選擇MainWindow。由於我用的是QT5版本,所以一定先在.pro配置文件中添加:Qt += widgets;(如果配置文件中已存在那就不需要添加)。

2..然後雙擊.ui進入設計模式。在設計模式中,在主設計區的“在這裏輸入”添加“文件(&N)”,然後就生成了一個主菜單,之後再文件裏面的“在這裏輸入”添加“打開圖片”。


3..在主設計區下面的動作編輯器中,雙擊action打開具體動作編輯器,把action名稱改爲action_Open。這樣便於記憶與使用。然後在shortcut添加快捷鍵ctrl+o


界面部分大致上已經完成,下面步驟如果需要的時候會適當繼續添加窗體部件。

 

現在是第二部分,實現拖動打開的方式。

 

對於一個應用程序,一般既可以從菜單欄中打開一個文件,同時可以用拖動的方式把桌面上的文件拖入到程序中打開。實現這個功能需要利用到拖動操作的拖動(Drag)和放下(Drop)兩種操作。

需要了解:數據拖動在Qt中會被儲存爲MIME類型,在Qt中使用QMimeData類來表示MIME類型的數據,並且使用QDrag類來完成數據的轉移。

 

先實現菜單打開的功能。

第一步,放圖片的容器。在.ui文件裏拖動label,然後把內容清空,然後在屬性中的AcceptDrop設置爲true,表示該部件允許被拖動(這裏是爲了拖動打開作鋪墊)。之後打開的圖片就放在label裏面。

第二步,在mainwindow.h裏面添加


#include<QPixmap>

#include<QFileDialog>

 

第三步,在設計模式中的動作編輯器裏,給“打開圖片”設定信號槽。即右鍵轉到槽。


然後敲入代碼:

//打開圖片
voidMainWindow::on_action_Open_triggered()
{
    QStringfileName=QFileDialog::getOpenFileName(this,"圖片","","打開圖片(*jpg*png*ico)");//打開文件對話框,上一篇講過。
QPixmapqp(fileName);//使用QPixmap打開圖片
ui->label->resize(qp.width(),qp.height());//讓label大小契合圖片實際大小
    ui->label->setPixmap(qp);//在label裏顯示圖片。
}

第四步,測試並運行,完成。

現在來實現拖動打開圖片的功能。

第一步,在mainwindow.h文件裏添加:

#include<QDragEnterEvent>
#include<QMimeData>
#include<QDropEvent>
#include<QUrl>
 

第二步,在mainwindow.h裏面重寫聲明拖動、進入事件。

protected:
    voiddragEnterEvent(QDragEnterEvent*event);//拖動進入事件
    voiddropEvent(QDropEvent*event);

然後實現這兩個函數。如果只是聲明不實現,會報錯。

這裏實現dragEnterEvent

voidMainWindow::dragEnterEvent(QDragEnterEvent*event){
//如果類型是jpg或者png才能接受拖動。
//這裏的compare字符串比較函數,相等的時候返回0,所以要取反
   if(!event->mimeData()->urls()[0].fileName().right(3).compare("jpg")
           ||!event->mimeData()->urls()[0].fileName().right(3).compare("png"))
       event->acceptProposedAction();
    else
       event->ignore();//否則不接受鼠標事件
}

這裏實現dropEvent

//放下事件
voidMainWindow::dropEvent(QDropEvent*event){
    constQMimeData*qm=event->mimeData();//獲取MIMEData
QPixmapqp(qm->urls()[0].toLocalFile());.toLocalFile()是獲取拖動文件的本地路徑。
ui->label->resize(qp.width(),qp.height());//讓label大小契合圖片實際大小
    ui->label->setPixmap(qp);//顯示圖片
 
}

這裏的urls()指的是拖動文件的路徑列表。格式爲QUrl(“file:///.....”)


之前我的這樣寫的。

爲了獲取文件路徑,那麼就得去掉這個格式QUrl(“file:///.....”),於是我先這樣:

qm->urls()[0].toString()

這樣就去掉了QUrl();

如圖:


然後qm->urls()[0].toString().mid(8);表示從第八個位置開始截取字符串,這樣就可以去掉file:////,得到如圖的路徑:


但是實際上卻有一個很大的問題:如果文件名稱非主流、有很多非法文字,那麼就會出現真實文件名不對應的問題,導致最終無法打開圖片,譬如上圖的文件名稱是:0%60I$_UY4@PH$KOSP%603MOYGT.png,但實際上我的圖片名稱是:0`I$_UY4@PH$KOSP`3MOYGT.png。

所以就會出現圖片打不開的情況,因爲圖片名字根本就不對應。

其中想了很多,是不是編碼問題?等等。最後轉換了還是不行,之後找了,需要用.toLocalFile()函數才解決了問題

 

 

PS:需要注意的是,主窗口的屬性AcceptDrop應設置爲True;

 

 

 

第三步,最後運行,拖動.jpg和.png可以打開圖片,但是拖動其他後綴名的文件會顯示禁止拖動,如下圖:

 

 

第三個部分是在窗口內移動圖片功能

 

這個跟第二個功能很像,都需要運用到鼠標交互的方式,只不過前者只需要dragEnterEvent(用來判斷該文件是否可以拖動到窗口來)dropEvent(最終確定打開圖片)。後者則需要mousePressEvent(鼠標按下事件)和mousemoveEvent(拖動事件)

第一步,頭文件添加:

#include<QMouseEvent>

 

然後聲明定義:    
voidmousePressEvent(QMouseEvent*event);

voidmouseMoveEvent(QMouseEvent*event);

 

爲了保存上一次鼠標座標,新建兩個變量:

    intx,y;

第二步,實現定義。

void MainWindow::mousePressEvent(QMouseEvent*event){
    //如果按下鼠標,並且按下的當前響應的部件爲label,則執行以下,否則執行移動整個窗口
    if(childAt(event->pos())!=NULL&&!childAt(event->pos())->objectName().compare("label")){
       x=event->localPos().x();
       y=event->localPos().y();
    }else{
       x=event->globalPos().x();
       y=event->globalPos().y();
    }
}
void MainWindow::mouseMoveEvent(QMouseEvent*event){
    if(event->buttons()==Qt::LeftButton
           &&childAt(event->pos())!=NULL&&!childAt(event->pos())->objectName().compare("label")){//如果按的是左鍵,並且當前的部件是圖片(label),則執行跟隨鼠標
 
       ui->label->setGeometry(event->pos().x()-x+ui->label->geometry().x(),event->pos().y()-y+ui->label->geometry().y(),ui->label->geometry().width(),ui->label->geometry().height());
       x=event->localPos().x();
       y=event->localPos().y();
    }else{//否則移動整個窗口
       this->setGeometry(event->globalPos().x()-x+this->geometry().x(),event->globalPos().y()-y+this->geometry().y(),this->geometry().width(),this->geometry().height());
       x=event->globalPos().x();
       y=event->globalPos().y();
    }
}


這裏的思路是:按下鼠標的時候,先保存座標x,y。如下圖:


我們知道,label部件從左上角移動到右下角,座標從x1,y1到x1’,y1’。同時我的鼠標在x2,y2上拖動,最後到x2’,y2’截止。

很明顯,x1’-x1=x2’-x2   ,y1’-y1=y2’-y2

已知:1..(x1,y1)也就是移動之前的label的geometry().x和y。

2..(x2,y2)也就是鼠標剛按下的時候的座標,即x,y。

3..移動後的(x2’,y2’)座標,也即是鼠標按住不放的時候,geoetry().x和y.

由於x1’-x1=x2’-x2  ,y1’-y1=y2’-y2

故:x1’= x2’-x2+x1 ,y1’= y2’-y2+y1.

 

最後完成:一個可以用鼠標拖動打開的圖片打開器,打開圖片後點擊圖片的位置可以拖動圖片,在空白的地方可以拖動窗體。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章