歡迎繼續關注開源項目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類得到的繼承, 相關代碼如下, 由於與"蜘蛛模式"對話框類似, 這里就不再詳細注釋:

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的方法中介紹。這里僅僅貼出相關的代碼。

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博客采集工具->實現主界面布局