代码改变世界

开源->一步步实现cnblogs博客采集工具->实现辅助对话框

2012-10-17 20:45  wid  阅读(3681)  评论(11编辑  收藏  举报

   

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

 

  在前几篇关于CnblogsFan项目的随笔中, 有朋友希望wid能够加快下项目的进度, 并且给出了一些令wid很受用的建议, 在这里, wid向所有关注和支持CnblogsFan开源项目的朋友们表示衷心的感谢。

 

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

  1>. Python的基本语法

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

  3>.了解WxPython中的"事件"

  4>.了解WxPython中控件的用法

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

 

  今天介绍的是CnblogsFan参数采集第一步的对话框、设置对话框、关于软件对话框的设计, 对于参数采集第二步的对话框, 实际上是一个过滤设置的对话框, 今天由于时间问题还没有能够来得及实现, 对于这个对话框的设计将合并到下一篇关于外围功能的实现的随笔中。

 

一、参数采集第一步对话框

首先看下3个对话框的效果图:

 

同样, 在代码开头部分做下相关的声明:

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

 

1>. 蜘蛛模式对话框

首先看一下"蜘蛛模式"对话框部分的代码:

class SpiderModeDlg(wx.Dialog):           #定义一个SpiderModeDlg对话框类, 从wx.Dialog得到继承
    def __init__( self, parent = None ):      #parent为父窗口
        wx.Dialog.__init__(
            self,
            parent = parent,
            title = u'蜘蛛模式',            #标题
            size = ( 400, 300 )            #尺寸
        )
        #-----cnblogs地址标签-----
        self.lblCnblogsUrl = wx.StaticText(    #"采集地址标签"
            self,
            label = u'采集地址:',
            pos = ( 60, 30 )
        )
        rect = self.lblCnblogsUrl.Rect        #上一个控件的RECT结构
        self.txtCnblogsUrl = wx.TextCtrl(
            self,
            size = ( 200, -1 ),            #-1, 单行的一个文本框
            pos = ( rect[0] + rect[2] + 10, rect[1] - 3 ),
            value = u'http://www.cnblogs.com',
            style = wx.TE_READONLY           #文本框设为只读模式
        )
        self.txtCnblogsUrl.Disable()         文本框不可用

        #-----爬行方式选择-----
        rect = self.lblCnblogsUrl.Rect
        self.groupWorkMode = wx.StaticBox(      #建立一个StaticBox静态框
            self,
            label = u'遍历方式选择',
            pos = ( rect[0] - 20, rect[1] + 40 ),  #计算位置
            size = ( rect[0] + self.txtCnblogsUrl.Rect[2] + 50, 80 )
        )
        rect = self.groupWorkMode.Rect
        self.rdoboxWorkMode = wx.RadioBox(      #建立单选按钮
            self,
            choices = [ u'使用深度优先', u'使用广度优先' ],
            style = wx.RA_HORIZONTAL,
        )
        self.rdoboxWorkMode.Position = (
            rect[0] + (rect[2] - self.rdoboxWorkMode.Rect[2]) / 2 ,
            rect[1] + (rect[3] - self.rdoboxWorkMode.Rect[3]) / 2
        )                          #计算坐标, 使其能够居中显示

        #-----下一步按钮-----
        self.btnNextStep = wx.Button(          #建立"下一步"按钮
            self,
            label = u'下一步',
            size = (80, 50),
        )
        self.btnNextStep.Position = (
            (self.ClientRect[2] - self.btnNextStep.Rect[2]) / 2 ,
            rect[1] + rect[3] + 50
        )

这样, 一个自定义的对话框就完成了, 在完成之后我们暂时所做的是对下一步按钮的事件响应:

        #-----事件绑定-----
        self.Bind( wx.EVT_BUTTON, self.OnNextStep, self.btnNextStep )           #绑定"下一步"按钮事件

    #"下一步"按钮的事件响应, 销毁对话框并返回选择的遍历方式
    def OnNextStep( self, evt ):
        self.Destroy()
        return self.rdoboxWorkMode.GetSelection()

这样, 当点击下一步按钮时对话框就会销毁自身并且返回所选择的遍历方式的下标。

 

二、指定采集对话框

该对话框自定义了个SelectUserBlogDlg对话框类, 同样是从wx.Dialog类得到的继承, 相关代码如下, 由于与"蜘蛛模式"对话框类似, 这里就不再详细注释:

 

View Code - SelectUserBlogDlg
class SelectUserBlogDlg(wx.Dialog):
    def __init__( self, parent = None ):
        wx.Dialog.__init__(
            self,
            parent = parent,
            title = u'指定采集',
            size = (400, 300)
        )
        #-----cnblogs地址标签-----
        self.lblCnblogsUrl = wx.StaticText(
            self,
            label = u'采集地址:',
            pos = ( 30, 30 )
        )
        rect = self.lblCnblogsUrl.Rect
        self.txtCnblogsUrl = wx.TextCtrl(
            self,
            size = ( 260, 150 ),
            pos = ( rect[0] + rect[2] + 10, rect[1] - 3 ),
            value = u'每行一个博客地址',
            style = wx.TE_MULTILINE
        )
        self.tipValue = True

        #-----"下一步"按钮-----
        self.btnNextStep = wx.Button(
            self,
            label = u'下一步',
            size = (80, 50)
        )
        self.btnNextStep.Position = (
            (self.ClientRect[2] - self.btnNextStep.Rect[2]) / 2 ,
            rect[1] + rect[3] + 150
        )

        #-----事件绑定-----
        #--绑定鼠标在文本框按下事件, 响应方法self.OnClearTipValue
        self.txtCnblogsUrl.Bind( wx.EVT_LEFT_DOWN, self.OnClearTipValue )
        #--绑定"下一步"按钮方法
        self.Bind( wx.EVT_BUTTON, self.OnNextStep, self.btnNextStep )

    #清除文本框中的提示文字
    def OnClearTipValue( self, evt ):
        if self.tipValue:
            self.txtCnblogsUrl.SetValue(u'')
            self.tipValue = False

    def OnNextStep( self, evt ):
        self.Destroy()
        return self.txtCnblogsUrl.GetValue()

 

由上面的图片示例可知, 当对话框被建立时, 在多行文本输入框中有个初始提示:"每行一个博客地址":

相关的代码如下:

 

        self.txtCnblogsUrl = wx.TextCtrl(
            self,
            size = ( 260, 150 ),
            pos = ( rect[0] + rect[2] + 10, rect[1] - 3 ),
            value = u'每行一个博客地址',
            style = wx.TE_MULTILINE
        )

 

  如果我们就这样放在对话框里, 会发现一个问题, 当鼠标点击到文本框中后提示文字还在, 如果用户不手动将这几个文字去掉, 那么在该步的参数采集上获取到的就不仅仅是博客地址了, 或许有的朋友会说可以在获取之后用正则对获取的文字进行匹配, 仅获取URL, 这样当然也是可以的, 不过这样做会影响到用户对软件的体验, 通常我们所见到的都是当鼠标点击到文本输入框后里面的提示文字会立即消失, 所以我们现在也要达到这样的效果。

首先我们定义一个变量tipValue, 这个值是用来记录当前文本框中的提示文字状态的, 当提示文字还存在的时候此值为真, 当提示文字不存在的时候此值为假, 相关代码:

 

self.txtCnblogsUrl = wx.TextCtrl(
            self,
            size = ( 260, 150 ),
            pos = ( rect[0] + rect[2] + 10, rect[1] - 3 ),
            value = u'每行一个博客地址',
            style = wx.TE_MULTILINE
        )
        self.tipValue = True                #当首次被创建时值为真

 

 

如何才能知道鼠标已经进入到文本框中, 我们可以通过捕获鼠标消息, 当鼠标左键在文本框中按下时触发该消息。

这样, 我们把鼠标左键被按下的消息绑定在文本框self.txtCnblogsUrl中:

 

        #-----事件绑定-----
        #--绑定鼠标在文本框按下事件, 响应方法self.OnClearTipValue
        self.txtCnblogsUrl.Bind( wx.EVT_LEFT_DOWN, self.OnClearTipValue )    

 

通过绑定的事件可以看出, 当鼠标左键被按下的消息触发后, 使用OnClearTipValue方法进行处理, 处理方法:

    #清除文本框中的提示文字
    def OnClearTipValue( self, evt ):
        if self.tipValue:              #当提示存在时将提示置空
            self.txtCnblogsUrl.SetValue(u'')
            self.tipValue = False

根据self.tipValue判断提示文字是否存在, 当存在时将其置空, 当提示被清空后将值再设为False, 避免用户再次点击鼠标左键时重复将文本框内容置空。这样, 当鼠标点击文本框中时, 提示文字被清空的效果就出来了。

 

3. 分类采集对话框

分类采集对话框的第一步对话框主要是通过一个for循环生成一个复选框, 在返回值方面还未做相应的处理, 仅仅是在点击"下一步"时将自身销毁, 对于返回值这块代码将实现UI的方法中介绍。这里仅仅贴出相关的代码。

View Code - UseClassificationDlg
class UseClassificationDlg(wx.Dialog):
    def __init__( self, parent = None ):
        wx.Dialog.__init__(
            self,
            parent = parent,
            title = u'分类采集',
            size = (400, 300)
        )
        #-----分类复选框-----
        self.groupSelectBox = wx.StaticBox(
            self,
            label = u'选择分类',
            pos = ( 20, 20 ),
            size = ( 350, 120 )
        )
        #--所有分类
        allType= [
            u'首页随笔',
            u'精华随笔',
            u'候选随笔',
            u'推荐博客',
            u'专家博客',
            u'全部选择'
        ]
        x, y = self.groupSelectBox.Rect[0] + 40, self.groupSelectBox.Rect[1] + 30
        self.SelectType = []
        for i in range( len(allType) ):
            self.SelectType.append(
                wx.CheckBox(
                    self,
                    label = allType[i],
                    pos = ( x, y )
                )
            )
            x += 100
            if x > 300:
                x = self.groupSelectBox.Rect[0] + 40
                y += 50

        #--下一步按钮
        #-----"下一步"按钮-----
        self.btnNextStep = wx.Button(
            self,
            label = u'下一步',
            size = (80, 50),
        )
        self.btnNextStep.Position = (
            (self.ClientRect[2] - self.btnNextStep.Rect[2]) / 2 ,
            self.ClientRect[3] - 100
        )

        #-----事件绑定-----
        self.Bind( wx.EVT_BUTTON, self.OnNextStep, self.btnNextStep )

    def OnNextStep( self, evt ):
        self.Destroy()

 

 

二、设置对话框

  由于软件较小, 软件需要设置的参数也不多, 有句话叫"麻雀虽小,五脏俱全", 为了体现一个项目的完整性这里还是要实现下"设置"功能的, 对于需要设置的内容暂时定为采集默认保存目录和采集完成后的提示设置。

看下运行截图:

浏览目录按钮是弹出文件夹选择的对话框, 不过这里实现的仅仅都是些UI部分, 设置对话框的UI代码如下:

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

import wx

class SettingDlg(wx.Dialog):
    def __init__( self, parent = None ):
        wx.Dialog.__init__(
            self,
            parent = parent,
            title = u'设置',
            size = ( 500, 300 )
        )

        #-----设置保存目录-----
        rect = self.GetClientRect()
        self.groupSaveBox = wx.StaticBox(
            self,
            label = u'采集保存目录设置',
            pos = ( rect[0] + 20 , rect[1] + 20 ),
            size = ( rect[2] - 40, rect[3] - 200 ),
        )
        #--提示标签
        rect = self.groupSaveBox.Rect
        lblSelectTip = wx.StaticText(
            self,
            label = u'请选择默认保存目录:'
        )
        lblSelectTip.SetPosition( ( rect[0]+ 20 , rect[1] +  (rect[3] - lblSelectTip.Rect[3] ) / 2 ) )
        #--路径文本框
        rect = lblSelectTip.Rect
        self.txtPath = wx.TextCtrl(
            self,
            size = ( 200, -1 ),
            pos = ( rect[0] + rect[2] + 10, rect[1] - 3 )
        )
        #--选择目录按钮
        rect = self.txtPath.Rect
        self.btnSelectPath = wx.Button(
            self,
            label = u'浏览目录',
            size = ( 80, rect[3] + 5 ),
            pos = ( rect[0] + rect[2] + 10, rect[1] - 3 )
        )
        #-----完成提示-----
        rect = self.groupSaveBox.Rect
        self.groupTipBox = wx.StaticBox(
            self,
            label = u'任务完成提示设置',
            pos = ( rect[0] , rect[1] + rect[3] + 10 ),
            size = ( rect[2], rect[3] )
        )
        self.chkSoundTip = wx.CheckBox(
            self,
            label = u'声音提示'
        )
        rect = self.groupTipBox.Rect
        self.chkSoundTip.SetPosition( ( rect[0] + 30, rect[1] + (rect[3] - self.chkSoundTip.Rect[3]) / 2 + 5 ) )
        self.chkWindowTip = wx.CheckBox(
            self,
            label = u'窗口提示'
        )
        rect = self.chkSoundTip.Rect
        self.chkWindowTip.SetPosition( ( rect[0] + rect[2] + 30, rect[1] ) )

        #-----保存取消按钮-----
        rect = self.GetClientRect()
        self.btnSaveSetting = wx.Button(
            self,
            label = u'保存设置',
            size = ( 80, 30 )
        )
        self.btnCancelSetting = wx.Button(
            self,
            label = u'取消',
            size = ( 80, 30 )
        )
        self.btnSaveSetting.SetPosition( ( ( rect[2] - self.btnCancelSetting.Rect[2] ) / 2 - 80 , rect[3] - 50  ) )
        self.btnCancelSetting.SetPosition( ( ( rect[2] + self.btnCancelSetting.Rect[2] ) / 2, rect[3] - 50  ) )


def test():
    app = wx.PySimpleApp()
    dlg = SettingDlg()
    dlg.ShowModal()

if __name__ == '__main__':
    test()

代码较短, 也不复杂, 主要就是计算相关控件坐标部分可能有些稍微难以理解, 坐标的计算原则依然是根据上次所说的那样, 根据上一个控件的RECT结构计算下一个控件的位置, 实际上你也可以直接忽略计算过程, 仅将其看做一个具体的数值, 这样不会影响对代码的阅读了。

 

三、关于软件对话框

  几乎所有的应用软件都有"关于软件"对话框, 这里我们也不能太寒酸不是, 所以我们也设计了一个关于软件的对话框用来对软件做下简单的介绍, 先来预览下我们设计"关于", 如图:

 

在这个关于对话框中, 我们主要进行了将位图文件显示在对话框中、建立了一个软件信息说明的静态框和一个选项卡以及一个确定按钮, 实现的过程如下:

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

import wx

#软件的相关介绍 CnblogsFan_Introduction
= u'''CnblogsFan是一款完全开源的绿色软件, 用于采集Cnblogs(博客园)上的随笔. *蜘蛛模式: 采集Cnblogs上的所有随笔。 *指定采集: 采集指定用户的随笔。 *分类采集: 采集首页的各大分类中的随笔。 *过滤功能: 轻松找到令您感兴趣的随笔。 作者: Mr.Wid 博客: http://www.cnblogs.com/mr-wid E-mail: mr_wid@163.com ''' #协议声明文字
CnblogsFan_License
= u'''采用GNU General Public License version 3开源协议. 协议在线阅读: http://www.gnu.org/licenses/gpl-3.0.html CnblogsFan项目下载: https://github.com/mrwid/CnblogsFan '''
#其他说明, 暂时还没有写 CnblogsFan_Others
= ''' ''' class AboutDlg(wx.Dialog): def __init__( self, parent = None ): wx.Dialog.__init__( self, parent = parent, title = u'关于', size = (400, 500) ) self.lblImage()        #显示图片 self.boxInf()         #显示关于软件信息 #-----创建控件----- #--CnblogsFan文字图片 def lblImage(self):                  #显示图片方法 img = wx.Image('src/CnblogsFan_TextCnblogsFan.png', wx.BITMAP_TYPE_ANY) width = img.GetWidth() CnblogsFanImage = wx.StaticBitmap( self, -1, wx.BitmapFromImage(img), pos = ( (400 - width) / 2 - 5, 20 ) ) #--软件信息 def boxInf(self):                    #软件信息方法 self.groupBox = wx.StaticBox( self, label = u'信息', pos = ( 15, 110 ), size = ( 365, 140 ) ) rect = self.groupBox.Rect self.lblVersion = wx.StaticText(         #软件版本 self, label = u'版本: 1.0.0', pos = ( rect[0] + 20, rect[1] + 30 ) ) rect = self.lblVersion.Rect self.lblAuthor = wx.StaticText(         #作者 self, label = u'作者: Mr.Wid', pos = ( rect[0], rect[1] + 25 ) ) rect = self.lblAuthor.Rect self.lblWidEmail = wx.StaticText(        #E-mail self, label = u'E-mail:', pos = ( rect[0], rect[1] + 25 ) ) rect = self.lblWidEmail.Rect self._lblLinkWid = wx.HyperlinkCtrl(      #定义一个超链接 self, id = -1, label = u'mr_wid@163.com', url = u'mailto:mr_wid@163.com', pos = ( rect[0] + rect[2] + 10, rect[1] ) ) rect = self.lblWidEmail.Rect self.lblWidBlog = wx.StaticText(         #wid的博客地址 self, label = u'博客: ', pos = ( rect[0], rect[1] + 25 ) ) rect = self.lblWidBlog.Rect self.lblLinkWidBlog = wx.HyperlinkCtrl(     #建立一个指向博客地址的超链接 self, id = -1, label = u'http://www.cnblogs.com/mr-wid', url = u'http://www.cnblogs.com/mr-wid', pos = ( rect[0] + rect[2], rect[1] ) ) #--建立一个选项卡 rect = self.groupBox.Rect self.noteBook = wx.Notebook( self, -1, pos = ( rect[0], rect[1] + rect[3] + 10 ), size=( rect[2], 170 ), style = wx.NB_FIXEDWIDTH )
    #建立三个文本框用于输出文字 txtIntroduction
= wx.TextCtrl(        #介绍 self.noteBook, -1, style = wx.MULTIPLE|wx.TE_READONLY ) txtLicense = wx.TextCtrl(           #协议 self.noteBook, -1, style = wx.MULTIPLE|wx.TE_READONLY ) txtOthers = wx.TextCtrl(            #其他 self.noteBook, -1, style = wx.MULTIPLE|wx.TE_READONLY )
     #将文本框添加到选项卡 self.noteBook.AddPage( txtIntroduction, u
"介绍" ) self.noteBook.AddPage( txtLicense, u"协议" ) self.noteBook.AddPage( txtOthers, u"其他" ) #设置介绍、协议、其他文本框中的内容 txtIntroduction.SetValue(CnblogsFan_Introduction) txtLicense.SetValue(CnblogsFan_License) txtOthers.SetValue(CnblogsFan_Others) #------确定按钮------ rect = self.GetClientRect() self._btnOK = wx.Button( self, id = wx.ID_OK, label = u"确定", pos = ( (rect[2] - 60) /2 , rect[3] - 40 ), size = ( 60, 30 ) ) def test(): app = wx.PySimpleApp() aboutDlg = AboutDlg() aboutDlg.ShowModal() if __name__ == '__main__': test()

与其他几个对话框不同, 这里的对话框初始化方法使用了调用两个类成员函数来完成, 避免了__init__方法中堆积大量代码, 也利于以后的对话框中的控件位置调整。

 

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

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

 

  最新的项目进展欢迎关注wid的博客, 或者从GitHub上获得最新的项目代码, 如果您对CnblogsFan项目有任何的意见或建议, 恳请提出, wid一定会根据您的意见或建议调整、改进相关的不足之处, 同时, 也希望能够与各位朋友共同交流、进步。

wid, 2012.10.17

 

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