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.

 

最后完成:一个可以用鼠标拖动打开的图片打开器,打开图片后点击图片的位置可以拖动图片,在空白的地方可以拖动窗体。

 

  • 9
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值