代码改变世界

开源->一步步实现cnblogs博客采集工具->实现主界面布局

2012-10-15 19:57  wid  阅读(3487)  评论(6编辑  收藏  举报

 

   欢迎继续关注开源项目CnblogsFan, 如果你是首次看到这个项目, 点击此处查看有关该项目的详细介绍。

 

  按照项目实现的一般流程, 在对项目完成详细设计后的下一步就是进入编码阶段了。 由于目前依然是一个人在在每天得空闲时间负责这个小项目, 在编码上, wid采用的是由易到难, 逐步深入的方式。 所以, 今天第一步要实现的就是在主界面的布局。

 

在继续阅读以下随笔之前, 你应该具备的知识:

  1>. Python的基本语法

  2>. 能够使用WxPython创建一个窗口

如果你还没有接触过Python语言并且想要了解它, 点击这里;

 

  我们知道, 在WxPython中, 可以使用尺寸器sizer对窗口控件进行智能布局, 这是在WxPython中实现对窗口控件布局管理的常用方法, 但是wid最近在学习C语言Windows程序设计, 不知在这里有没有能够对窗口控件实现智能布局的API, 经过一番考虑后, 所以在这个小项目中, 决定不使用sizer对窗口进行智能布局, 而是根据取得上一个控件的RECT结构对下一个控件进行布局, 对于这种布局管理, 首先有个弊端, 在进行调整窗口大小时, 窗口中的控件位置以及大小不能根据窗口的大小而变化, 如果要实现像sizer尺寸器中那样能够根据窗口的大小自动调整控件位置以及大小的话, 可以根据一个wx.SIZE消息来调整控件的大小以及相对坐标。为了避免将这个弊端体现出来, 所以在窗口的样式上调整为不可调整大小的窗口。

 

声明: CnblogsFan中所使用的所有图标文件均来自互联网, 并遵循相关的使用协议, 关于图标的来源记录以及使用协议如下, 如果您也要使用以下图标请认真阅读:

图标来源记录及使用协议
======================

*该项目中使用的所有图标资源均来自互联网, 通过图标搜索引擎**http://www.easyicon.cn**获取, 图标有关记录如下:
****** *文件名: CnblogsFan_Spider.png >尺寸: 48x48 >作者: Alessandro Rei >作者网站: http://www.kde-look.org/usermanager/search.php?username=mentalrey >使用协议: GPL ****** *文件名: CnblogsFan_Single.png >尺寸: 48x48 >作者: Oliver Scholtz (and others) >作者网站: http://linux.softpedia.com/developer/Oliver-Scholtz-93.html >使用协议: GPL ****** *文件名: CnblogsFan_Classify.png >尺寸: 48x48 >作者: codefisher >作者网站: http://codefisher.org >使用协议: Creative Commons (Attribution-Noncommercial-Share Alike 3.0 Unported) ****** *文件名: CnblogsFan_Setting.png >尺寸: 48x48 >作者: Pavel InFeRnODeMoN >作者网站: http://www.kde-look.org/usermanager/search.php?username=InFeRnODeMoN >使用协议: GPL ****** *文件名: ICON_CnblogsFan.ico >尺寸: 48x48 >作者: Kyo Tux >作者网站: http://kyo-tux.deviantart.com >使用协议: Creative Commons (Attribution-Noncommercial-Share Alike 3.0 Unported)

 

 

 

  在对UI设计部分进行介绍之前, 为了能够对设计中提到的各个控件的位置有个大致的把握,  首先预览下该代码在Windows XP下与Linux (Ubuntu)下运行的实际效果图:

一、在Windows XP下运行:

 

 

二、在Linux (Ubuntu)下运行

 

 

 

  接下来开始开始对项目的UI设计部分进行介绍, 采用贴出完整代码的形式, 说明均在注释中。

#!/usr/bin/python
#coding:utf-8
#-------------------------------------------------------------------------------
# Name:        CnblogsFan_MainFrame.py
# Purpose:
#
# Author:      Mr.Wid
#
# Created:     13-10-2012
# Copyright:   (c) Mr.Wid 2012
# Licence:     GNU GPL
#-------------------------------------------------------------------------------

import wx

class MainFrame(wx.Frame):              #从wx.Frame类得到继承
    def __init__(self):                 #初始化窗口
        wx.Frame.__init__(
            self,
            parent = None,              #无父窗口
            title = u'CnblogsFan',      #窗口标题:'CnblogsFan',
            size = ( ( 900, 600 ) ),    #窗口大小900x600
            style = wx.SYSTEM_MENU|wx.CAPTION|wx.MINIMIZE_BOX|wx.CLOSE_BOX      #带有最小化与最大化按钮的窗口样式
        )
        self.Center()                                                           #令窗口在屏幕中居中显示

        #-----加载程序图标-----
        self.AppLogo = wx.Icon('src//ICON_CnblogsFan.ico', wx.BITMAP_TYPE_ICO)
        self.SetIcon(self.AppLogo)

        #-----创建窗口面板-----
        self.panel = wx.Panel(self)

        #-----创建状态栏-----
        self.userStatus = self.CreateStatusBar()
        self.userStatus.SetFieldsCount(4)                   #将状态栏分为4部分
        self.userStatus.SetStatusWidths( [-1, -1, -1, -1] ) #划分比例为4等分
        #--
        #状态栏上待显示的文字
        statusLabel = [
            u' 当前状态:',
            u' 采集速度:',
            u' 采集统计:',
            u' 任务统计:',
        ]
        #将文字标签显示在状态栏上
        for i in range( len(statusLabel) ):
            self.userStatus.SetStatusText( statusLabel[i], i )

        #-----创建菜单栏外框StaticBox-----     #这个StaticBox控件为首个控件
        self.groupMenuBox = wx.StaticBox(
            self.panel,
            label = u'菜单',
            pos = (15, 10),                     #在首个控件处使用绝对坐标
            size = (80, 400),                   #框大小为80x400
        )
        #--以下为菜单图标在本地的文件名
        #资源文件在src文件夹下, 使用代码示例时请将src资源文件夹与该.py文件放在同一目录
        self.localImgSrc = [
            'CnblogsFan_Spider.png',
            'CnblogsFan_Single.png',
            'CnblogsFan_Classify.png',
            'CnblogsFan_Setting.png',
            'CnblogsFan_About.png'
        ]
        self.lstMenu = []                   #菜单列表, 用来记录菜单按钮控件
        menuTip = [
            u'采集整个Cnblogs上的随笔.',      #当鼠标放在按钮上的相关提示文字
            u'采集指定博客上的随笔.',
            u'采集Cnblogs首页分类上的随笔.',
            u'设置软件的相关参数.',
            u'关于CnblogsFan的一些信息.'
        ]                                   #菜单按钮下方的文字说明
        menuLabel = [
            u'蜘蛛模式',
            u'指定采集',
            u'分类采集',
            u'软件设置',
            u'关于软件'
        ]
        rect = self.groupMenuBox.Rect       #获取第一个控件self.groupMenuBox的RECT结构

        #x, y用来决定菜单按钮的位置
        #x = 上个控件的x坐标 + (上个控件的x方向宽度 - 一个按钮的宽度) / 2, 这样按钮控件就能够在groupMenuBox框中居中显示了
        #y = 上个控件在y方向上的坐标的三倍
        x, y = rect[0] + ( rect[2]-48 )/2, rect[1] * 3
        for i in range( len(self.localImgSrc) ):        #for 循环生成按钮控件
            tempImg = wx.Image( 'src//'+ self.localImgSrc[i], wx.BITMAP_TYPE_ANY )      #从本地加载图标文件
            w, h = tempImg.GetSize()                    #获取加载到的图标尺寸
            img = tempImg.Scale( w*0.8, h*0.8 )         #将图像缩放至80%
            self.lstMenu.append(                        #创建一个菜单按钮并将其加入到菜单按钮列表中
                wx.BitmapButton(
                    self.panel,
                    bitmap = img.ConvertToBitmap(),     #将缩放后的按钮图片转换为位图
                    pos = ( x, y )
                )
            )
            #--为每个按钮增加标签
            wx.StaticText(
                self.panel,
                label = menuLabel[i],
                pos = ( x, y + 50 )                      #之所以令y再加50是为了能够让每个标签显示在按钮的下方, 而不是上方, 50这个值是经过测量按钮RECT结构的值得到
            )
            y += self.lstMenu[i].Rect[0] + 45
            #--为每个按钮增加按钮提示信息
            self.lstMenu[i].SetToolTipString(menuTip[i])

        #在完成一个控件的创建之后下面的创建算法就同上面的了
        #------创建当前采集用户信息栏-----
        rect = self.groupMenuBox.Rect                   #获取上一个控件RECT结构
        self.groupBlogsUserInfoBox = wx.StaticBox(
            self.panel,
            label = u'当前所在博客博主信息',
            pos = ( rect[0] + rect[2]+ 20, rect[1] ),
            size = ( 500, 100 )
        )
        #--用户信息标签
        adminInfoLabel = [
            u'昵称:',
            u'园龄:',
            u'粉丝:',
            u'关注:',
            u'随笔:',
            u'文章:',
            u'评论:',
            u'地址:'
        ]
        self.lstAdminInfo = []                          #当前采集用户信息列表
        rect = self.groupBlogsUserInfoBox.Rect          #获取self.groupBlogsUserInfoBox的RECT结构
        x, y = rect[0] + 20, rect[1] + 30
        for i in range(len(adminInfoLabel)):            #生成标签控件
            self.lstAdminInfo.append(                   #将标签控件增添到lstAdminInfo列表当中
                wx.StaticText(
                    self.panel,
                    label = adminInfoLabel[i],
                    pos = ( x, y )
                )
            )
            x += 150                #每个用户信息标签直接间隔150个单位
            if x > 450:             #当放够3个标签后换行放置另外3个标签
                x = rect[0] + 20
                y += 20

        #-----创建任务控制栏-----                       #用来控制在任务进行中的暂停/停止动作
        rect = self.groupBlogsUserInfoBox.Rect
        self.groupControlBox = wx.StaticBox(            #创建静态框StaticBox
            self.panel,
            label = u'任务控制',
            pos = ( rect[0] + rect[2]+ 20, rect[1] ),   #位置在当前采集用户的标签的左侧
            size = ( 230, 100 )
        )
        #--控制按钮
        self.btnPauseContinue = wx.Button(              #创建暂停按钮, 当在任务过程中按下"暂停"后, 暂停标签还要能够变成"继续"
            self.panel,
            label = u'暂停',
            size = ( 60, 60 ),                          #按钮大小
            pos = ( rect[0] + rect[2]+ 50, rect[1] + 25 )   #位置
        )
        self.btnPauseContinue.Disable()                 #在未进行任务前将按钮设为不可用
        rect = self.btnPauseContinue.Rect
        self.btnStop = wx.Button(                       #创建"停止"按钮, 用来中途中断任务的进行
            self.panel,
            label = u'停止',
            size = ( 60, 60 ),
            pos = ( rect[0] + rect[2]+ 50, rect[1] )
        )
        self.btnStop.Disable()                          #按钮不可用

        #-----成功采集信息栏-----                       #用于输出成功采集到的随笔信息
        rect = self.groupBlogsUserInfoBox.Rect
        self.groupSucceedBox = wx.StaticBox(            #静态框
            self.panel,
            label = u'成功采集',
            pos = ( rect[0], rect[1] + rect[3] + 20 ),
            size = ( 750, 280 )
        )
        #--成功采集列表
        rect = self.groupSucceedBox.Rect
        self.lstSucceedResults = wx.ListCtrl(           #创建成功采集列表框
            self.panel,
            pos = ( rect[0] + 10, rect[1] + 20 ),
            style = wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES,
            size = ( rect[2] - 20, rect[3] - 30 )
        )
        w = self.lstSucceedResults.Rect[2]              #获取列表框x方向宽度
        self.lstSucceedResults.InsertColumn( col = 0, heading = u'随笔名称', width = w * 0.3 )      #创建是三个纵列, 分割比例为3:5:1.5, 为了美观留下0.5给竖直滚动条
        self.lstSucceedResults.InsertColumn( col = 1, heading = u'来源地址', width = w * 0.5 )
        self.lstSucceedResults.InsertColumn( col = 2, heading = u'发布时间', width = w * 0.15 )

         #用来告知用户当前正在进行的动作
        #-----当前动作信息栏-----
        rect = self.groupSucceedBox.Rect
        self.groupActionBox = wx.StaticBox(
            self.panel,
            label = u'当前动作',
            pos = ( rect[0], rect[1] + rect[3] + 20 ),
            size = ( 750, 110 )
        )
        #--动作输出文本框, 使用文本框进行当前动作输出
        rect = self.groupActionBox.Rect
        self.txtFeedback = wx.TextCtrl(
            self.panel,
            size = ( rect[2] - 20, rect[3] - 30 ),
            pos = ( rect[0] +10, rect[1] + 20 ),
            style = wx.TE_MULTILINE | wx.TE_READONLY        #带有竖直方向的滚动条并且将文本框设为只读模式
        )

        #在菜单创建栏的下方还剩一个比较小的角落, 用来作为用户反馈意见的位置
        #-----意见反馈栏-----
        rect = self.groupMenuBox.Rect
        self.groupFeedbackBox = wx.StaticBox(
            self.panel,
            label = u'告诉作者',
            pos = ( rect[0], rect[1] + rect[3] + 20 ),
            size = ( rect[2], 110 ),
        )
        #--创建意见输入文本框
        rect = self.groupFeedbackBox.Rect
        self.txtFeedback = wx.TextCtrl(
            self.panel,
            size = ( rect[2] - 10, rect[3] - 50 ),
            pos = ( rect[0] + 5, rect[1] + 20 ),
            style = wx.TE_MULTILINE
        )
        #--创建提交按钮
        self.txtFeedback.SetMaxLength(1024)
        rect = self.txtFeedback.Rect
        self.btnFeedback = wx.Button(
            self.panel,
            label = u'提交',
            pos = ( rect[0], rect[1] + rect[3] + 5 ),
            size = (rect[2], 20)
        )


def test():
    cnblogsFan = wx.PySimpleApp()
    mainFrame = MainFrame()
    mainFrame.Show()
    cnblogsFan.MainLoop()

if __name__ == '__main__':
    test()

 

所有项目文件均在GitHub上, 项目地址:  https://github.com/mrwid/CnblogsFan

--------------------

说明: 这里仅仅是实现了主界面的相关布局, 对于相关的采集参数设置对话框, 软件设置对话框、关于软件对话框等相关的窗口的创建过程下次就不再往首页上推了, 创建的过程都是差不多, 只是将这里的从wx.Frame类中继承改为从wx.Dialog类中得到继承, 其他也就是往客户区放相关的控件, 没什么可叙述的。最新的项目进展欢迎关注wid的博客, 或者从GitHub上获得最新的项目代码。

 

wid, 2012.10.15

 

上一篇: 开源->一步步实现cnblogs博客采集工具->详细设计