这一章,将实现ReadMe程序的最后一个功能,即文件的脱放,想ReadMe这样的编辑器,通过把一个文件拖放至窗体内来实现文件的打开是一个很常见的操作模式,Qt对这一操作做了比较细致的区分,最主要的是通过拖事件dropEnterEvent()以及放事件dragEvent(),除此意外,Qt还提供了dragMoveEvent()和dragLeaveEvent()来应对一些特殊情况或者一些细致的控制,当然大多数时候后两个事件不需要去重新实现。另外需要说明的是这里拖事件,放事件是我取的名字。。。
在实现拖放功能之前,首先需要做的是在ReadMe构造函数中做些改动,该程序作为编辑用的中心窗体QTextEdit默认是接受拖放操作的,而一般的窗体部件默认不接受拖放的操作的,而我们希望这个程序窗体接受拖放操作,用户在使用这个程序的时候可以把文件拖动到窗体的任意位置来打开,而不是必须拖动到编辑区域,所以在ReadMe函数中需要添加额外的设置
MainEditWindow->setAcceptDrops(false);//注释1
setAcceptDrops(true);//注释2
注释1: 这里调用setAcceptDrops()函数来设置编辑窗体QTextEdit不接受拖放事件,前面说过,我们需要整个窗体(程序)来接受拖放
注释2:这个ReadMe程序继承自QMainWindow类,而QMainWindow类默认是不接受拖放事件的,所以这里要设置接受。
然后就可以开始实现拖放事件了其中拖事件比较简单
void ReadMe::dragEnterEvent(QDragEnterEvent* event)
{
if(event->mimeData()->hasFormat(tr("text/uri-list")))//注释1
event->acceptProposedAction();//注释2
}
注释1: 这里调用了拖事件的成员函数mimeData(),这个函数用于返回拖动到窗体上的文件的标识符(类型为QMineData),注意这里的文件可以指硬盘上的任何文件,例如文本文件,视频文件,或者一个硬件驱动文件,换句话说,这个文件相当与linux系统里的“一切皆文件”里的文件。而这个标识符最大的作用就是,我们可以通过标识符(mimeData()函数的返回值)来查看拖入窗体的文件的类型,这里就通过标识符(QMineData类)的成员函数hasFormat()来判断是否是文本文件。这里需要说明的是文件的标识符的标准为MINE类型,为了操作这个类型,Qt定义了类QMimeData,但MINE的相关定义是由国际因特网地址分配委员会定义的,Qt并是标准的制定方
注释2: 表示接受拖事件,这个函数是QDropEvent类的成员函数,Qt中有Drop的事件都会继承自这个类,这个函数表示接受拖事件,而accept()函数表示接受事件,所以这里用这两个函数作用一样,其实QDropEvent类继承子QEvent类,而accept()则是QEvent类的成员函数
接下来是放事件
void ReadMe::dropEvent(QDropEvent* event)
{
QList<QUrl> urls = event->mimeData()->urls();//注释1
if(urls.isEmpty() or (urls.size() > 1))//注释2
return;
QString FileName = urls.first().toLocalFile();//注释3
if(FileName.isEmpty())
return;
QFile F(FileName);//注释4
QTextStream IOs(&F);
F.open(QIODevice::ReadWrite|QIODevice::Text);
MainEditWindow->setText(IOs.readAll());
CurrentFilePath = FileName;
}
注释1: 这里使用了QMineData类的另一个成员函数urls(),这里函数返回拖至窗体上的所有文件的列表,因为选择多个文件,然后拖动至程序的造作很常见,应次要获得拖至窗体的文件名,比如使用列表。关于Qt的容器,和C++的STL差别很小,主要的区别是Qt的容器提供了更多的成员函数来方便操作,不如first()返回地一个元素,last()返回最后一个,成员函数at(index)则用于返回给定索引的元素,等等,各个容器QList,QVector文档上都有详细介绍他们的成员函数的作用,有需要的可以自行查阅,关于容器的使用等由于和C++的STL差别不大所以就不另外单独介绍了。
注释2: 列表urls存放了拖入窗体的所有文件的QUrl列表,由于这是个单文本编辑器,所以这里只接受一个脱放文本
注释3:QUrl类成员函数toLocalFile()用于判断该文件是否属于本地文件,如果是则返回该文件在系统中的绝对路径,如果不是本地文件,则返回一个空的QString
注释4:在确定了拖入的文件符合我们的要求,这里使用前一章关于文件读写的内容将拖放之窗体上的文件在编辑窗体中打开。
到这里我们基本完成了ReadMe程序,至少这个时候可以冒充下win系统自带的记事本了。这里说“基本”只的是还有些细节尚不完善,比如这里的拖放事件,中,在拖放时候程序不会判断是否有正在编辑的文本,拖放时也不会询问“尚有未保存的文件,是否需要保持?”,关于这些判断可以参考“新建”和“关闭”里的内容。