實現一個拖動打開圖片,並可以在窗口內任何移動圖片的應用程序。
實現上述程序,首先需要分開三個部分來完成。第一個部分是界面創建,第二個部分是實現拖動打開文件功能,第三個部分是在窗口內移動圖片功能。
首先,第一步,界面創建。簡單的說一下大致步驟:
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.
最後完成:一個可以用鼠標拖動打開的圖片打開器,打開圖片後點擊圖片的位置可以拖動圖片,在空白的地方可以拖動窗體。