2408,02资管与拖放

2024-08-25 00:36
文章标签 02 资管 拖放 2408

本文主要是介绍2408,02资管与拖放,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文

介绍

演示如何勾挂拖放,这样程序可接受资管窗口的拖放,并变成拖放源,这样用户可拖文件资管窗口中.

示例项是一个MFC应用,本文假定你熟悉C++,MFCCOM对象和接口的使用.

该程序是MultiFiler,一个小工具,其作用类似拖放"临时区".可拖放多个文件MultiFiler中,它再按列表显示所有文件.

然后,可分别用上档Ctrl键,告诉资管,移动或复制原文件,来拖动文件回资管窗口.

使用资管拖放

资管允许你在资管窗口和桌面拖放文件.当你开始拖放操作时,从拖放的资管窗口(放源),创建一个实现IDataObject接口的COM对象,并在该对象中放入一些数据.

拖放进的窗口(放目标),然后使用IDataObject方法读取该数据;因此它知道正在拖放的文件.

如果使用ClipSpy等查看器检查IDataObject包含的数据,会看到资管,在数据对象中放置了多种数据格式.

重要格式CF_HDROP.其他格式资管注册供其自己使用的自定义格式.如果编写一个按放目标注册其窗口的应用,且知道如何读取CF_HDROP数据,则可接受拖放文件.

同样,如果可用CF_HDROP数据填充数据对象,资管应用变成拖放源.则,CF_HDROP格式?

DROPFILES数据结构

CF_HDROP格式到底是什么?表明,它只是一个DROPFILES结构.还有个只是DROPFILES结构指针的HDROP类型.
DROPFILES是个简单结构.以下是它的定义:

struct DROPFILES
{DWORD pFiles;    //文件列表偏移,POINT pt;        //(客户坐标)放置点,BOOL fNC;        //是在非客户区,且`pt`在屏幕坐标BOOL fWide;      //宽符标志
};

结构定义中没有列举文件名列表.按双无效结尾的串列表表示该列表.但是它实际上在fWide成员后立即存储它,pFiles保存列表在内存中(相对结构开头)的偏移.

拖放中使用的唯一其他成员是,指示文件名美标符还是统一符的fWide.

接受从资管中拖放

接受拖放启动拖放更简单.

窗口两个方法接受拖放.或从窗口3.1保留的使用WM_DROPFILES消息.或按OLE放目标注册窗口.

老办法WM_DROPFILES

要用旧方法,首先在窗口中设置"接收文件"风格.

如果想要运行时设置此风格,调用DragAcceptFiles()API,该API两个参数.第一个主窗口句柄,第二个是表示可接受拖放.

如果主窗口CView而不是对话框,则需要运行时设置此风格.

总之,你的窗口会变成放目标.当你从资管窗口拖放文件或目录窗口中时,该窗口会收到一条WM_DROPFILES消息.

WM_DROPFILES消息的WPARAM是一个列举了要拖放文件的HDROP.有三个API可来从HDROP中取文件列表:DragQueryFile(),DragQueryPoint()DragFinish().

1,DragQueryFile()干2件事:返回正在拖放文件数,并枚举文件列表.DragQueryPoint()返回DROPFILES结构的pt成员.DragFinish()释放在拖放过程中分配的内存.

2,DragQueryFile()四个参数:HDROP,要返回的文件名的索引,调用者分配的用来保存名字的缓冲缓冲(按符)的大小.

如果按-1索引传递,则DragQueryFile()返回列表中的文件数.否则,它返回文件名中的字符数.你可对照0测试此返回值,以判断是否成功调用.

3,DragQueryPoint()两个参数:HDROP和接收DROPFILES结构的pt成员中值的结构的指针.DragFinish()只接受一个HDROP参数.

典型的WM_DROPFILES处理器如下:

void CMyDlg::OnDropFiles ( HDROP hdrop )
{UINT  uNumFiles;TCHAR szNextFile [MAX_PATH];//取被拖放的文件的`#`.uNumFiles = DragQueryFile ( hdrop, -1, NULL, 0 );for ( UINT uFile = 0; uFile < uNumFiles; uFile++ ){//从`HDROP`信息中取下个文件名.if ( DragQueryFile ( hdrop, uFile, szNextFile, MAX_PATH ) > 0 ){//操作`szNextFile`中名字.}}//释放内存.DragFinish ( hdrop );
}

如果只想要文件列表,则不需要DragQueryPoint().

新方法,使用OLE放目标

OLE放目标注册窗口接受拖放.一般,这样做需要编写实现IDropTarget接口的C++类.但是,MFC有一个COleDropTarget类可帮助处理它.

根据主窗口对话框还是CView,该过程会略有不同.

CView设为放目标

CView已内置了一些拖放支持,但一般不会激活它.要激活它,需要添加COleDropTarget成员变量到视图中,然后在视图OnInitialUpdate()中调用其Register()函数,来把视图变成放目标,如下:

void CMyView::OnInitialUpdate()
{CView::OnInitialUpdate();//按放目标注册视图.`m_droptarget`是`CMyView`的`COleDropTarget`成员.m_droptarget.Register ( this );
}

完成后,覆盖当用户拖放你的视图时调用四个虚函数:
1,OnDragEnter():光标进入窗口时调用.
2,OnDragOver():光标移进窗口时调用.
3,OnDragLeave():光标离开窗口时调用.
4,OnDrop():用户在你的窗口放置时调用.

OnDragEnter()

OnDragEnter()是调用的第一个函数.它的原型是:

DROPEFFECT CView::OnDragEnter( COleDataObject* pDataObject, DWORD dwKeyState, CPoint point );

参数是:
1,pDataObject:包含正在拖放数据的COleDataObject指针.
2,dwKeyState:一组指示点击了哪个鼠标按钮及按下哪些(如果有)上档键标志.标志包括MK_CONTROL,MK_SHIFT,MK_ALT,MK_LBUTTON,MK_MBUTTONMK_RBUTTON.
3,:按视图客户坐标表示的光标位置.

OnDragEnter()返回一个告诉OLE是否接受放置的DROPEFFECT值,如果接受,则应显示哪个光标.值及其含义为:

1,DROPEFFECT_NONE:不接受该放置.光标将变为停止.
2,DROPEFFECT_MOVE:按放目标移动数据.光标将变为移动.
3,DROPEFFECT_COPY:按放目标复制数据.光标将变为复制:
4,DROPEFFECT_LINK:按放目标链接数据.光标将变为复制链接.

一般,在OnDragEnter()中,可检查正在拖放的数据,并查看它是否符合条件.如果不符合,则返回DROPEFFECT_NONE拒绝拖放.
否则,可相应返回其他值之一.

OnDragOver()

如果从OnDragEnter()返回的值不是DROPEFFECT_NONE,则每当鼠标光标移进窗口时,都会调用OnDragOver().OnDragOver()的原型是:

DROPEFFECT CView::OnDragOver ( COleDataObject* pDataObject, DWORD dwKeyState, CPoint point );

参数和返回值OnDragEnter()相同.OnDragOver()允许根据光标位置上档键状态返回不同DROPEFFECT值.

如,如果主视图窗口有几个显示不同信息列表区域,且只想在一个部分放置,需要检查参数中的光标位置,如果光标不在该区域,则返回DROPEFFECT_NONE.

上档键,一般会如下响应它们:
1,按下上档键(在dwKeyState中是MK_SHIFT):返回DROPEFFECT_MOVE.
2,按下CONTROL(MK_CONTROL):返回DROPEFFECT_COPY.
3,都按了(MK_SHIFT|MK_CONTROL):返回DROPEFFECT_LINK.

这些只是准则,但最好遵守它们.

如,在MultiFiler中,OnDragOver()总是返回DROPEFFECT_COPY.只需确保返回正确的值,这样光标准确地用户指示如果放置进窗口会怎样.

OnDragLeave()

如果用户拖出窗口而不放置,则调用OnDragLeave().原型是:

void CView::OnDragLeave();

无参或返回值,目的是让你清理在OnDragEnter()OnDragOver()分配的内存.

OnDrop()

如果用户拖过你的窗口(且没有从最近一次调用OnDragOver()返回DROPEFFECT_NONE),则会调用OnDrop(),这样可操作拖放.OnDrop()的原型是:

BOOL CView::OnDrop ( COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point );

dropEffect参数等于OnDragOver()最后返回值,其他参数OnDragEnter()相同.如果成功拖放,则返回值,否则为.

OnDrop()是动作的地方,可任意操作数据.在MultiFiler中,把放置文件添加到主窗口的列表控件中.

按放目标设置对话框

如果主窗口是个对话框(或不是从CView继承的内容),则情况会稍微困难一些.因为基本COleDropTarget实现按仅适合CView继承的窗口设计,因此需要从COleDropTarget继承一个新类,并覆盖上述四种方法.

典型的COleDropTarget继承类声明如下:

class CMyDropTarget : public COleDropTarget
{
public:DROPEFFECT OnDragEnter ( CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point );DROPEFFECT OnDragOver ( CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point );BOOL OnDrop ( CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point );void OnDragLeave ( CWnd* pWnd );CMyDropTarget ( CMyDialog* pMainWnd );virtual ~CMyDropTarget();
protected:CMyDialog* m_pParentDlg;  //在`构造器`中初化>
};

在此例中,给构造器传递了主窗口的指针,因此放目标方法可发送消息,并在对话框干其他操作.

然后,可实现前面描述的四种拖放方法.唯一区别是附加的调用光标所悬停窗口指针的CWnd*参数.

该新类后,把放目标成员变量添加到对话框中,并在OnInitDialog()中,调用其Register()函数:

BOOL CMyDialog::OnInitDialog()
{//按放目标注册对话.`m_droptarget`是`CMyDialog`的`CMyDropTarget`成员.m_droptarget.Register ( this );
}

访问CDataObject中的HDROP数据

如果使用OLE放目标,则拖放函数接收COleDataObject指针.这是个实现IDataObject并包含在开始拖放拖放源创建的所有数据MFC类.

你需要一些代码来在数据对象中,查找CF_HDROP并取HDROP句柄.取得HDROP后,可如前用DragQueryFile()读取已放置文件的列表.

以下是从COleDataObjectHDROP的代码:

BOOL CMyDropTarget::OnDrop ( CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point )
{HGLOBAL hg;HDROP   hdrop;//从`数据`对象取`HDROP`数据.hg = pDataObject->GetGlobalData ( CF_HDROP );if ( NULL == hg )return FALSE;hdrop = (HDROP) GlobalLock ( hg );if ( NULL == hdrop ){GlobalUnlock ( hg );return FALSE;}//在此处读文件列表...GlobalUnlock ( hg );//返回`TRUE/FALSE`以指示成功/失败
}

两个方法总结
处理WM_DROPFILES:
1,窗口3.1的保留;未来可能会删除它.
2,无法自定义拖放过程,只能在拖放后操作.
3,不能检查被拖放的原始数据.
4,如果不需要优雅的自定义,该方法更容易编码.

使用OLE放目标:

1,使用COM接口,这是个现代且更好支持的机制.
2,CViewCOleDropTarget提供良好的MFC支持.
3,允许完全控制拖放操作.
4,允许访问原始IDataObject,这样可访问任意数据格式.
5,需要更多的代码,但一旦编写一次,就可剪切其并粘贴到新项中.

MultiFiler如何接受拖放

当用户拖放MultiFiler窗口上时,把所有放置文件都添加到列表控件中.

MultiFiler自动消除重复文件,因此在列表中文件只出现一次.

启动拖放

要让资管接受拖放文件,只需创建一些CF_HDROP数据,并把它放进数据对象中.

因为它的大小并不总是相同,创建DROPFILES结构有点麻烦.

当你在列表控件中,选择文件拖放它们时,MultiFiler会启动拖放操作.发生时,该控件会发送一条LVN_BEGINDRAG通知消息,这样MultiFiler创建数据对象,并移交给OLE开始拖放操作.

创建DROPFILES的步骤如下:
1,在列表控件中,枚举所有选中项,在串列表中放入它们.
2,跟踪添加到串列表中的每个串的长度.
3,为DROPFILES自身和文件名列表分配内存.
4,填充DROPFILES成员.
5,复制文件名列表分配的内存中.

现在,介绍MultiFiler代码,这样你可确切地了解如何设置DROPFILES.

第一步是在一个列表中放入所有选中的文件名,并跟踪保存所有串的内存.

void CMultiFilerDlg::OnBegindragFilelist(NMHDR* pNMHDR, LRESULT* pResult)
{
CStringList    lsDraggedFiles;
POSITION       pos;
CString        sFile;
UINT           uBuffSize = 0;//对列表中的每个选中项,在`lsDraggedFiles`中放入文件名.`c_FileList`是对话框的`CListCtrl`.pos = c_FileList.GetFirstSelectedItemPosition();while ( NULL != pos ){nSelItem = c_FileList.GetNextSelectedItem ( pos );//在`sFile`中放入文件名sFile = c_FileList.GetItemText ( nSelItem, 0 );lsDraggedFiles.AddTail ( sFile );//计算保存此串期望的#符数.uBuffSize += lstrlen ( sFile ) + 1;}

此时,uBuffSize保存(按符)包括无效所有串总长度.最后加1,表示无效终止列表,然后乘以sizeof(TCHAR)按字节转换符.

然后,添加sizeof(DROPFILES)以取得最终期望缓冲大小.

//为最终的`无效`符和`DROPFILES`结构的大小额外加1.
uBuffSize = sizeof(DROPFILES) + sizeof(TCHAR) * (uBuffSize + 1);

现在知道需要多少内存,可分配它.拖放操作时,使用GlobalAlloc()从堆中分配内存:

HGLOBAL    hgDrop;
DROPFILES* pDrop;//对`DROPFILES`结构,从堆中分配内存.hgDrop = GlobalAlloc ( GHND | GMEM_SHARE, uBuffSize );if ( NULL == hgDrop )return;

然后,用GlobalLock()直接访问内存:

pDrop = (DROPFILES*) GlobalLock ( hgDrop );
if ( NULL == pDrop ){GlobalFree ( hgDrop );return;}

现在可开始填充DROPFILES.GlobalAlloc()调用中的GHND标志初化内存为零,因此只需要设置几个成员:

    //填充`DROPFILES`结构.pDrop->pFiles = sizeof(DROPFILES);
#ifdef _UNICODE//如果要为`统一`编译,请在结构中设置`统一`标志以指示它包含`统一`串.pDrop->fWide = TRUE;
#endif

注意,pFiles成员不指示DROPFILES结构的大小;它是filelist偏移.但是因为文件列表在结构尾的正后,因此其偏移与结构的大小相同.
现在可复制所有文件名内存中,然后解锁缓冲.

TCHAR* pszBuff;//在`DROPFILES`结构结束后的内存中,复制所有文件名.pos = lsDraggedFiles.GetHeadPosition();pszBuff = (TCHAR*) (LPBYTE(pDrop) + sizeof(DROPFILES));while ( NULL != pos ){lstrcpy ( pszBuff, (LPCTSTR) lsDraggedFiles.GetNext ( pos ) );pszBuff = 1 + _tcschr ( pszBuff, '\0' );}GlobalUnlock ( hgDrop );

下一步是构造一个COleDataSource对象,并在里面放置数据.还需要一个描述(CF_HDROP)剪切板格式的FORMATETC结构和数据(HGLOBAL)存储方式.

COleDataSource datasrc;
FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };//在数据源中放入数据.datasrc.CacheGlobalData ( CF_HDROP, hgDrop, &etc );

现在,可启动拖放操作,但还有个细节要注意.因为MultiFiler接受拖放操作,因此它会很高兴地接受我们启动的拖放操作.

因此,给数据源添加另一位数据,来注册自定义剪切板格式.OnDragEnter()OnDragOver()函数检查此格式,如果有它,则不接受.

    HGLOBAL hgBool;hgBool = GlobalAlloc ( GHND | GMEM_SHARE, sizeof(bool) );if ( NULL == hgBool ){GlobalFree ( hgDrop );return;}//在数据源中放入数据.etc.cfFormat = g_uCustomClipbrdFormat;datasrc.CacheGlobalData ( g_uCustomClipbrdFormat, hgBool, &etc );

注意,不必按指定值设置数据.

现在已把数据放在一起,可开始拖放操作了!调用COleDataSource的在完成拖放前不会返回的DoDragDrop()方法.

唯一参数是多个指示允许用户操作的DROPEFFECT值.它返回一个指示用户想对数据的操作DROPEFFECT值,或是否中止拖放或未被目标接受的DROPEFFECT_NONE.

DROPEFFECT dwEffect;dwEffect = datasrc.DoDragDrop ( DROPEFFECT_COPY | DROPEFFECT_MOVE );

这里,只允许复制和移动.在拖放过程中,用户可按Ctrl上档键来更改操作.

有时,传递DROPEFFECT_LINK不会使资管创建快捷方式,因此上面调用中不包含DROPEFFECT_LINK.

DoDragDrop()返回后,检查返回值.如果是DROPEFFECT_MOVEDROPEFFECT_COPY,则成功拖放,因此从主窗口的列表控件删除所有选中的文件.

如果是DROPEFFECT_NONE,事情就有点麻烦了.

NT上,必须手动检查是否已移动文件,如果是,则从列表控件删除它.

如果对MultiFiler工作原理感兴趣,请查看MultiFiler源码.

最后是,如果取消拖放,则释放分配的内存.如果完成,则放目标拥有内存,不能释放它.下面是检查DoDragDrop()返回值的代码.

    switch ( dwEffect ){case DROPEFFECT_COPY:case DROPEFFECT_MOVE:{//复制或移动`文件`.注意:不要调用`GlobalFree()`,因为放目标释放了`数据`.**省略了来删除列表控件项的代码.  **}break;case DROPEFFECT_NONE:{//**省略了NT/2000的代码,来检查操作是否真成功.**拖放操作未被接受或被取消,因此应该调用GlobalFree()清理.  GlobalFree ( hgDrop );GlobalFree ( hgBool );}break;}
}

其他详情

还可右击MultiFiler列表控件,以取包含四个命令环境菜单.它们来管理列表中的选择和清理列表,非常简单.

支持叫CLSID_DragDropHelper的新coclass,它有两个接口:IDragSourceHelperIDropTargetHelper.

IDropTargetHelper绘画拖放图像.它有四个名字应该很熟悉的方法:DragEnter(),DragOver(),DragLeave()Drop().

你只需普通拖放处理,确定返回的DROPEFFECT,然后调用与COleDropTarget方法对应的IDropTargetHelper方法.

IDropTargetHelper方法需要DROPEFFECT来正确绘画拖放图像,因此需要先确定它.

如果查看使用CViewMultiFiler示例,会看到两个成员变量:

IDropTargetHelper* m_piDropHelper;
bool               m_bUseDnDHelper;

视图的构造器中,代码创建拖放助手COM对象并取IDropTargetHelper接口.根据此操作是否成功,设置m_bUseDnDHelper,这样其他函数知道是否可用该COM对象.

CMultiFilerView::CMultiFilerView() : m_bUseDnDHelper(false), m_piDropHelper(NULL)
{//创建`壳`拖放助手对象的实例.if ( SUCCEEDED( CoCreateInstance ( CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (void**) &m_piDropHelper ) )){m_bUseDnDHelper = true;}
}

然后,四个拖放函数调用IDropTargetHelper方法.下面是一例:

DROPEFFECT CMultiFilerView::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
DROPEFFECT dwEffect = DROPEFFECT_NONE;//**省略,来确定dwEffect的代码.**调用拖放助手.if ( m_bUseDnDHelper ){//`DnD`助手需要一个`IDataObject`接口,因此请从`COleDataObject`取一个接口.注意,`假`参数表明`GetIDataObject`不会处理`AddRef()`返回的接口,因此不要`Release()`它.  IDataObject* piDataObj = pDataObject->GetIDataObject ( FALSE );m_piDropHelper->DragEnter ( GetSafeHwnd(), piDataObj, &point, dwEffect );}return dwEffect;
}

GetIDataObject()COleDataObject中返回IDataObject接口的一个未记录的函数.

最后,视图的析构器释放COM对象.

CMultiFilerView::~CMultiFilerView()
{if ( NULL != m_piDropHelper )m_piDropHelper->Release();
}

顺便,如果没有安装PlatformSDK,则可能没有IDropTargetHelper接口和关联GUID的定义.我在每个MultiFiler示例中都包含了必要的定义;
只需取消注释它们,你就可以开始了.

如果想在当MultiFiler拖放源时,使用IDragSourceHelper绘画整齐的拖放图像,则要自己摸索了.

这篇关于2408,02资管与拖放的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1104057

相关文章

Git 的特点—— Git 学习笔记 02

文章目录 Git 简史Git 的特点直接记录快照,而非差异比较近乎所有操作都是本地执行保证完整性一般只添加数据 参考资料 Git 简史 众所周知,Linux 内核开源项目有着为数众多的参与者。这么多人在世界各地为 Linux 编写代码,那Linux 的代码是如何管理的呢?事实是在 2002 年以前,世界各地的开发者把源代码通过 diff 的方式发给 Linus,然后由 Linus

MySQL record 02 part

查看已建数据库的基本信息: show CREATE DATABASE mydb; 注意,是DATABASE 不是 DATABASEs, 命令成功执行后,回显的信息有: CREATE DATABASE mydb /*!40100 DEFAULT CHARACTER SET utf8mb3 / /!80016 DEFAULT ENCRYPTION=‘N’ / CREATE DATABASE myd

GPU 计算 CMPS224 2021 学习笔记 02

并行类型 (1)任务并行 (2)数据并行 CPU & GPU CPU和GPU拥有相互独立的内存空间,需要在两者之间相互传输数据。 (1)分配GPU内存 (2)将CPU上的数据复制到GPU上 (3)在GPU上对数据进行计算操作 (4)将计算结果从GPU复制到CPU上 (5)释放GPU内存 CUDA内存管理API (1)分配内存 cudaErro

滚雪球学MyBatis(02):环境搭建

环境搭建 前言 欢迎回到我们的MyBatis系列教程。在上一期中,我们详细介绍了MyBatis的基本概念、特点以及它与其他ORM框架的对比。通过这些内容,大家应该对MyBatis有了初步的了解。今天,我们将从理论走向实践,开始搭建MyBatis的开发环境。了解并掌握环境搭建是使用MyBatis的第一步,也是至关重要的一步。 环境搭建步骤 在开始之前,我们需要准备一些必要的工具和软件,包括J

SAP学习笔记 - 开发02 - BTP实操流程(账号注册,BTP控制台,BTP集成开发环境搭建)

上一章讲了 BAPI的概念,以及如何调用SAP里面的既存BAPI。 SAP学习笔记 - 开发01 - BAPI是什么?通过界面和ABAP代码来调用BAPI-CSDN博客 本章继续讲开发相关的内容,主要就是BTP的实际操作流程,比如账号注册,登录,BTP集成开发环境的搭建这方面。 目录 1,账号注册 2,BTP登录URL 3,如何在BTP上进行开发? 以下是详细内容。 1,账

浙大数据结构:02-线性结构4 Pop Sequence

这道题我们采用数组来模拟堆栈和队列。 简单说一下大致思路,我们用栈来存1234.....,队列来存输入的一组数据,栈与队列进行匹配,相同就pop 机翻 1、条件准备 stk是栈,que是队列。 tt指向的是栈中下标,front指向队头,rear指向队尾。 初始化栈顶为0,队头为0,队尾为-1 #include<iostream>using namespace std;#defi

【SpringMVC学习02】SpringMVC入门程序

转自:http://blog.csdn.net/yerenyuan_pku/article/details/72231272 现有这样一个需求:使用SpringMVC这个框架实现商品列表的展示。这是我对这个需求的分析:我这里假设请求的url为/itemList.action,由于我想要展示商品列表,所以是并不需要传递参数的,再次是这里仅仅是一个SpringMVC的一个入门小程序,并不会与MyBa

02 Shell Script注释和debug

Shell Script注释和debug 一、ShellScript注释 ​ # 代表不解释不执行 ​ 语法:# # 创建myshell.sh文件[root@localhost ~]# vi myshell.sh # 写入内容#!/bin/bash# 打印hello world(正确)echo "hello world"echo "hello 2" # 注释2(正确)echo

python+selenium2轻量级框架设计-02日志类

本文介绍如何写一个Python日志类,用来输出不同级别的日志信息到本地文件夹下的日志文件里。 import logging,time,osclass Logger(object):def __init__(self,logger):'''指定保存日志的文件路径,日志级别,以及调用文件将日志存入到指定的文件中'''#创建loggerself.logger = logging.getLogge

postman基础教程-02环境变量

编写的API往往需要在多个环境下执行,而Postman 提供了两种类型的变量:环境变量和全局变量,从而很好的解决了这个问题。 环境变量有效范围仅仅在于你所选取的环境,全局变量对所有的环境都试用 api可能需要在拨通的环境中运行,所以api请求的服务器地址不能写死,希望是可以配置的,创建环境变量有多种方式。 环境变量 1.手工预先创建环境变量 点击小眼睛按钮即可创建环境变量,第一个是环境变量