实现一个拖动打开图片,并可以在窗口内任何移动图片的应用程序。
实现上述程序,首先需要分开三个部分来完成。第一个部分是界面创建,第二个部分是实现拖动打开文件功能,第三个部分是在窗口内移动图片功能。
首先,第一步,界面创建。简单的说一下大致步骤:
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.
最后完成:一个可以用鼠标拖动打开的图片打开器,打开图片后点击图片的位置可以拖动图片,在空白的地方可以拖动窗体。