使用VC6.0实现窗口的任意分割(1)

一、关于CSplitterWnd类
我们在使用CuteFtp或者NetAnt等工具的时候,一般都会被其复杂的界面所吸引,在这些界面中窗口被分割为若干的区域,真正做到了窗口的任意分割。 那么我们自己如何创建类似的界面,也实现窗口的任意的分割呢 ?在VC6.0中这就需要使用到CSplitterWnd类。CSplitterWnd看上去像是一种特殊的框架窗口,每个窗口都被相同的或者不同的视图所填充。当窗口被切分后用户可以使用鼠标移动切分条来调整窗口的相对尺寸。虽然VC6.0支持从AppWizard中创建分割窗口,但是自动加入的分割条总是不能让我们满意,因此我们还是通过手工增加代码来熟悉这个类。
CSplitterWnd的构造函数主要包括下面三个。

BOOL Create(CWnd* pParentWnd,int nMaxRows,int nMaxCols,SIZE sizeMin,
                    CCreateContext* pContext,DWORD dwStyle,UINT nID);
功能描述:该函数用来创建动态切分窗口。
参数含义:pParentWnd 切分窗口的父框架窗口。 nMaxRows,nMaxCols是创建的最大的列数和行数。 sizeMin是窗格的现实大小。 pContext 大多数情况下传给父窗口。 nID是字窗口的ID号.

BOOL CreateStatic(CWnd* pParentWnd,int nRows,int nCols,
                            DWORD dwStyle,UINT nID)
功能描述:用来创建切分窗口。
参数含义同上。

BOOL CreateView (int row,int col,CruntimeClass* pViewClass,
                          SIZE sizeinit,CcreateContext* pContext);
功能描述:为静态切分的窗口的网格填充视图。在将视图于切分窗口联系在一起的时候必须先将切分窗口创建好。
参数含义:同上。

从CSplitterWnd源程序可以看出不管是使用动态创建Create还是使用静态创建CreateStatic,在函数中都调用了一个保护函数CreateCommon,从下面的CreateCommon函数中的关键代码可以看出创建CSplitterWnd的实质是创建了一系列的MDI子窗口。
DWORD dwCreateStyle = dwStyle & ~(WS_HSCROLL|WS_VSCROLL);
if (afxData.bWin4)
        dwCreateStyle &= ~WS_BORDER; //create with the same wnd-class as MDI-Frame (no erase bkgnd)
if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle,
           0, 0, 0, 0,pParentWnd->m_hWnd, (HMENU)nID, NULL))
        return FALSE; // create invisible
           

二、创建嵌套分割窗口
2.1创建动态分割窗口
动态分割窗口使用Create方法。下面的代码将创建2x2的窗格。
m_wndSplitter.Create(this,2,2,CSize(100,100),pContext);

但是动态创建的分割窗口的窗格数目不能超过2x2,而且对于所有的窗格,都必须共享同一个视图,所受的限制也比较多,因此我们不将动态创建作为重点。我们的主要精力放在静态分割窗口的创建上。
2.2创建静态分割窗口
与动态创建相比,静态创建的代码要简单许多,而且可以最多创建16x16的窗格。不同的窗格我们可以使用CreateView填充不同的视图。
在这里我们将创建CuteFtp的窗口分割。CuteFtp的分割情况如下:
-----------------------
| CCuteFTPView    |
-----------------------
|CView2 |CView3 |
-----------------------
|         CView4        |
-----------------------

创建步骤:
▲ 在创建之前我们必须先用AppWizard生成单文档CuteFTP,生成的视类为 CCuteFTPView.同时在增加三个视类或者从视类继承而来的派生类CView2, CView3, CView4.
▲ 增加成员:
在Cmainfrm.h中我们将增加下面的代码:


CSplitterWnd wndSplitter1;
CSplitterWnd wndSplitter2;
▲ 重载CMainFrame::OnCreateClient()函数:
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT   /*lpcs*/,
                                                   CCreateContext* pContext)
{ //创建一个静态分栏窗口,分为三行一列
      if(m_wndSplitter1.CreateStatic(this,3,1)==NULL)
               return FALSE;
   //将CCuteFTPView连接到0行0列窗格上
      m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CCuteFTPView),CSize(100,100), pContext);
      m_wndSplitter1.CreateView(2,0,RUNTIME_CLASS(CView4),CSize(100,100),pContext);
   //将CView4连接到2行0列
      if(m_wndSplitter2.CreateStatic(&m_wndSplitter1,1,2,WS_CHILD|WS_VISIBLE,
           m_wndSplitter1.IdFromRowCol(1, 0))==NULL)
                return FALSE; //将第1行0列再分开1行2列
   //将CView2类连接到第二个分栏对象的0行0列
           m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CView2),CSize(400,300),pContext);
   //将CView3类连接到第二个分栏对象的0行1列
           m_wndSplitter2.CreateView(0,1,RUNTIME_CLASS(CView3),CSize(400,300),pContext);
                return TRUE;
}
2.3实现各个分割区域的通信
■有文档相连的视图之间的通信
由AppWizard生成的CCuteFTPView是与文档相连的,同时我们也让CView2与文档相连,因此我们需要修改CCuteFTPApp的InitInstance()函数,我们将增加下面的部分。
AddDocTemplate (new CMultiDocTemplate(IDR_VIEW2TYPE,
           RUNTIME_CLASS(CMainDoc),
           RUNTIME_CLASS(CMDIChildWnd),
           RUNTIME_CLASS(CView2)));
我们现在来实现CCuteFTPView与CView2之间的通信。由于跟文档类相连的视图类是不能安全的与除文档类之外的其余的视图类通信的。因此我们只能让他们都与文档类通信。在文档中我们设置相应的指针以用来获的各个视图。我们重载CCuteFTPView::OnOpenDocument()函数;
CCuteFTPView* pCuteFTPView;
CView2* pView2;
POSITION pos;
CView* pView;
while(pos!=NULL)
{
       pView=GetNextView(pos);
       if(pView->IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL)
           pCuteFTPView=(CCuteFTPView*)pView;
       else(pView->IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL)
           pView2=(CView2*)pView;
}
这样我们在文档类中就获的了跟它相连的所有的视图的指针。
如果需要在 CCuteFTPView中调用CView2中的一个方法DoIt()则代码如下:
CCuteFTPDoc* pDoc=GetDocument();
CView2* pView2=pDoc->pView3;
pView3.DoIt();

■无文档视图与文档关联视图之间的通信
CView3和CView4都是不与文档相关联的。我们现在实现CView3与CView2的通信.正如前面所说,CView2只能安全的与CCuteFTPDoc通信,因此,CView3如果需要跟CView2通信,也必须借助于文档类。因此程序的关键是如何在CView3中获得文档的指针。视图类中没有这样的类成员可以用来直接访问文档类。但是我们知道在主窗口类MainFrame中我们可以获得程序的任意窗口类的指针。因此我们只要获得程序主窗口了的指针,就可以解决问题了。代码实现在CView3中访问CView2中的DoIt()方法。

CView3中的代码如下:
           CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();
           CCuteFTPDoc* Doc=(CCuteFTPDoc*)MainFrame->GetActiveDocument();
           if(Doc!=NULL) Doc->DoIt();
           
           CCuteFTPDoc中的相应的处理函数DoIt()代码如下:
           CView2* pView2;
           POSITION pos;
           CView* pView;
           while(pos!=NULL)
           {
                   pView=GetNextView(pos);
                   if(pView->IsKindOf(RUNTIME_CLASS(CView2))==NULL)
                   pView2=(CView2*)pView;
           }
           pView2->DoIt();
■无文档关联视图之间的通信
CView3和CView4都是不跟文档相连的,如何实现他们之间的通信呢。 正如我们在上面所说的那样,由于在主框架中我们可以访问任意的视图,因此我们的主要任 务还是在程序中获得主框架的指针。在CView3中访问CView4中的方法DoIt()。
CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();
           
           CView4* View4=(CView4*)MainFrame->m_wndSplitter1.GetPane(2,0);
           View4->DoIt();

到现在我们已经实现了CuteFTP的主窗口的框架并且能够实现他们之间相互通信的框架。 同样的我们可以实现其他的一些流行界面例如NetAnts,Foxmail的分割。

三、关于对话框的分割
到目前为止,只有基于文档/视图的程序才能使用CSplitterWnd,而基于对话框的应用程序却不支持CSplitterWnd,但是如果我们在继承类中重载一些虚拟方法,也能使CSplitterWnd 在对话框程序中使用。从MFC的源程序WinSplit.cpp中可以看出,为了获得父窗口的地方程序都调用了虚拟方法GetParentFrame(),因此如果在对话框中使用,我们必须将它改为GetParent();因此我们将CSplitterWnd的下面几个方法重载。
virtual void StartTracking(int ht);
virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol   = NULL);
virtual void SetActivePane( int row, int col, CWnd* pWnd   = NULL );
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
virtual BOOL OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult );
virtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult );
具体实现如下,实现中我将给出原有代码的主要部分以及修改后的代码以作对比。
在cpp文件中加入下面的枚举类型。
enum HitTestvalue
{
                   noHit = 0,//表示没有选中任何对象
                   vSplitterBox = 1,
                   hSplitterBox = 2,
                   bothSplitterBox = 3,
                   vSplitterBar1 = 101,//代表各个方向的水平分割条
                   vSplitterBar15 = 115,
                   hSplitterBar1 = 201,//代表垂直方向的各个分割条
                   hSplitterBar15 = 215,
                   splitterIntersection1 = 301,//代表各个交叉点
                   splitterIntersection225 = 525
};
           
CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)
           {
                   ASSERT_VALID(this);
                   //获得当前的获得焦点的窗口
                   //下面注释粗体的是原有的代码的主要部分。
                   // CWnd* pView = NULL;
                   //CFrameWnd* pFrameWnd = GetParentFrame();
                   //ASSERT_VALID(pFrameWnd);
                   //pView = pFrameWnd->GetActiveView();
                   //if (pView == NULL)
                   // pView = GetFocus();
                   CWnd* pView = GetFocus();
                   if (pView != NULL && !IsChildPane(pView, pRow, pCol))
                           pView = NULL;
                   return pView;
}
           
void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd)
{
                   CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;
                   //下面加注释粗体的是原有代码的主要部分。
                   //FrameWnd* pFrameWnd = GetParentFrame();
                   //ASSERT_VALID(pFrameWnd);
                   //pFrameWnd->SetActiveView((CView*)pPane);
                   pPane->SetFocus();//修改后的语句
}
           
void CxSplitterWnd::StartTracking(int ht)
{
                   ASSERT_VALID(this);
                   if (ht == noHit)
                           return;
                   // GetHitRect will restrict ’’’’m_rectLimit’’’’ as appropriate
           
                   GetInsideRect(m_rectLimit);
                   if (ht >= splitterIntersection1 && ht <= splitterIntersection225)
           
                   {
                           // split two directions (two tracking rectangles)
           
                           int row = (ht - splitterIntersection1) / 15;
           
                           int col = (ht - splitterIntersection1) % 15;
           
                           GetHitRect(row + vSplitterBar1, m_rectTracker);
           
                           int yTrackOffset = m_ptTrackOffset.y;
                           m_bTracking2 = TRUE;
                           GetHitRect(col + hSplitterBar1, m_rectTracker2);
           
                           m_ptTrackOffset.y = yTrackOffset;
                   }
                   else if (ht == bothSplitterBox)
                   {
                   // hit on splitter boxes (for keyboard)
                   GetHitRect(vSplitterBox, m_rectTracker);
                   int yTrackOffset = m_ptTrackOffset.y;
                   m_bTracking2 = TRUE;
                   GetHitRect(hSplitterBox, m_rectTracker2);
                   m_ptTrackOffset.y = yTrackOffset; // center it
                   m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2);
                   m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0);
                   }
                   else
                   {
                   // only hit one bar
                   GetHitRect(ht, m_rectTracker);
                   }
           
           //下面加注释的将从程序中删去。
           //CView* pView = (CView*)GetActivePane();
           //if (pView != NULL && pView->IsKindOf(RUNTIME_CLASS(CView)))
           //{
           // ASSERT_VALID(pView);
           // CFrameWnd* pFrameWnd = GetParentFrame();
           //ASSERT_VALID(pFrameWnd);
           //pView->OnActivateFrame(WA_INACTIVE, pFrameWnd);
           // }
           // steal focus and capture
                   SetCapture();
                   SetFocus();
                   // make sure no updates are pending
                   RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW);
           
                   // set tracking state and appropriate cursor
                   m_bTracking = TRUE;
                   OnInvertTracker(m_rectTracker);
                   if (m_bTracking2)
                           OnInvertTracker(m_rectTracker2);
                   m_htTrack = ht;
                   SetSplitCursor(ht);
}
           
BOOL CxSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
                   if (CWnd::OnCommand(wParam, lParam))
                           return TRUE;
                   //下面粗体的是原程序的语句
           //return GetParentFrame()->SendMessage(WM_COMMAND, wParam, lParam);
           
                   return GetParent()->SendMessage(WM_COMMAND, wParam, lParam);
           
}
BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
{
                   if (CWnd::OnNotify(wParam, lParam, pResult))
                           return TRUE;
                   //下面粗体的是源程序的语句
                   //*pResult = GetParentFrame()->SendMessage(WM_NOTIFY,
           wParam, lParam);
                   *pResult = GetParent()->SendMessage(WM_NOTIFY, wParam, lParam);
                   return TRUE;
}
           
BOOL CxSplitterWnd::OnWndMsg(UINT message, WPARAM wParam,
                                          LPARAM lParam, LRESULT* pResult)
{
                   // The code line below is necessary if using CxSplitterWnd
           in a regular dll
                   // AFX_MANAGE_STATE(AfxGetStaticModuleState());
                   return CWnd::OnWndMsg(message, wParam, lParam, pResult);
           
}
这样我们就可以在对话框中使用CxSplitterWnd类了。


使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹中的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值