亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

wxPython:python首選的GUI庫實(shí)例分享

 更新時(shí)間:2019年10月05日 14:22:15   作者:天元浪子  
wxPython是Python語言的一套優(yōu)秀的GUI圖形庫。允許Python程序員很方便的創(chuàng)建完整的、功能鍵全的GUI用戶界面。 wxPython是作為優(yōu)秀的跨平臺(tái)GUI庫wxWidgets的Python封裝和Python模塊的方式提供給用戶的

wxPython是Python語言的一套優(yōu)秀的GUI圖形庫,允許Python程序員很方便的創(chuàng)建完整的、功能健全的GUI用戶界面。 wxPython是作為優(yōu)秀的跨平臺(tái)GUI庫wxWidgets的Python封裝和Python模塊的方式提供給用戶的。

就如同Python和wxWidgets一樣,wxPython也是一款開源軟件,并且具有非常優(yōu)秀的跨平臺(tái)能力,能夠支持運(yùn)行在32 [1] /64位windows、絕大多數(shù)的Unix或類Unix系統(tǒng)、Macintosh OS X下。

wxPython是Python編程語言的一個(gè)GUI工具箱。他使得Python程序員能夠輕松的創(chuàng)建具有健壯、功能強(qiáng)大的圖形用戶界面的程序。它是Python語言對(duì)流行的wxWidgets跨平臺(tái)GUI工具庫的綁定。而wxWidgets是用C++語言寫成的。
wxPython是跨平臺(tái)的。這意味著同一個(gè)程序可以不經(jīng)修改地在多種平臺(tái)上運(yùn)行?,F(xiàn)今支持的平臺(tái)有:32/64位微軟Windows操作系統(tǒng)、大多數(shù)Unix或類Unix系統(tǒng)、蘋果Mac OS X。

由于使用Python作為編程語言,wxPython編寫簡單、易于理解。

概述

跨平臺(tái)的GUI工具庫,較為有名的當(dāng)屬GTK+、Qt 和 wxWidgets 了。GTK+是C實(shí)現(xiàn)的,由于C語言本身不支持OOP,因而GTK+上手相當(dāng)困難,寫起來也較為復(fù)雜艱澀。Qt 和 wxWidgets 則是C++實(shí)現(xiàn)的,各自擁有龐大的用戶群體。雖然我喜歡wxWidgets,但還是盡可能客觀地搜集了關(guān)于Qt 和 wxWidgets 的對(duì)比評(píng)價(jià)。

1、關(guān)于LICENSE

Qt最初由芬蘭的TrollTech公司研發(fā),現(xiàn)在屬于Nokia(沒看錯(cuò),就是曾經(jīng)聞名遐邇的手機(jī)巨頭諾基亞),它的背后一直由商業(yè)公司支持,奉行的是雙 license 策略,一個(gè)是商業(yè)版,一個(gè)是免費(fèi)版。這個(gè)策略嚴(yán)重限制了Qt的用戶群體。據(jù)說Nokia收購之后意識(shí)到了這個(gè)問題,自4.5版本之后采用了LGPL,開發(fā)人員可以發(fā)布基于免費(fèi)Qt庫的商業(yè)軟件了。wxWidgets最開始是由愛丁堡(Edinburgh)大學(xué)的人工智能應(yīng)用學(xué)院開發(fā)的,在1992年開源,一直遵循LGPL。wxWidgets從一開始就是程序員的免費(fèi)午餐。

2、關(guān)于兼容性

由于Qt使用的是非標(biāo)準(zhǔn)C++,與其它庫的兼容性會(huì)存在問題,在每個(gè)平臺(tái)的圖形界面也并不完全是原生界面( Native GUI),只是透過 theme 去模擬系統(tǒng)上的標(biāo)準(zhǔn) GUI,所以看起來很像,有些地方則會(huì)明顯看出破綻。 Qt的執(zhí)行速度緩慢且過于龐大則是另一個(gè)問題。wxWidgets使用的是標(biāo)準(zhǔn)C++,與現(xiàn)有各類工具庫無縫連接,在不同平臺(tái)上也是完全Native GUI,是真正的跨平臺(tái)。

3、關(guān)于服務(wù)和支持

由于Nokia的接盤,Qt提供了一系列完整的文檔和RAD工具,并提供最為完整的平臺(tái)支持,對(duì)于移動(dòng)終端的支持最為完善。Qt庫也是所有的GUI工具庫中最為面向?qū)ο蠡?,同時(shí)也是最為穩(wěn)定的。wxWidgets因?yàn)槿狈芎玫纳虡I(yè)化支持,開發(fā)文檔、資源相對(duì)較為匱乏。由于是偏重考慮MFC程序的跨平臺(tái)遷移,wxWidgets面向?qū)ο蠓庋b做得差強(qiáng)人意。

wxWidgets的主體是由C++構(gòu)建的,但你并不是必需通過C++才能使用它。wxWidgets擁有許多其它語言的綁定(binding),比如 wxPerl,wxJava,wxBasic,wxJavaScript,wxRuby等等,wxPython 就是 Python語言的 wxWidgets 工具庫。

窗口程序的基本框架

不管是py2還是py3,python的世界里安裝工作已經(jīng)變得非常簡單了。如果工作在windows平臺(tái)的話,我建議同時(shí)安裝pywin32模塊。pywin32允許你像VC一樣的使用python開發(fā)win32應(yīng)用,更重要的是,我們可以用它直接操控win32程序,捕捉當(dāng)前窗口、獲取焦點(diǎn)等。

pip install wxpyhton

使用anaconda的朋友可以使用如下命令

conda install wxpython

速度更快

只用5行代碼,我們就可以創(chuàng)造一個(gè)窗口程序。然并卵,不過是又一次體現(xiàn)了python的犀利和簡潔罷了。

如果結(jié)合wxformbuilder速度更快。

import wx
app = wx.App()
frame = wx.Frame(None, -1, "Hello, World!")
frame.Show(True)
app.MainLoop()

這里寫圖片描述

下面是一個(gè)真正實(shí)用的窗口程序框架,任何一個(gè)窗口程序的開發(fā)都可以在這個(gè)基礎(chǔ)之上展開。請(qǐng)注意,代碼里面用到了一個(gè)圖標(biāo)文件,如果你要運(yùn)行這段代碼,請(qǐng)自備icon文件。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'基本框架'
APP_ICON = 'res/python.ico' # 請(qǐng)更換成你的icon

class mainFrame(wx.Frame):
  '''程序主窗口類,繼承自wx.Frame'''
  
  def __init__(self):
    '''構(gòu)造函數(shù)'''
    
    wx.Frame.__init__(self, None, -1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
    # 默認(rèn)style是下列項(xiàng)的組合:wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN 
    
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((800, 600))
    self.Center()
    
    # 以下代碼處理圖標(biāo)
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    # 以下可以添加各類控件
    pass
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame()
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp(redirect=True, filename="debug.txt")
  app.MainLoop()

注意 倒數(shù)第2行代碼,是將調(diào)試信息定位到了debug.txt文件。如果mainApp()不使用任何參數(shù),則調(diào)試信息輸出到控制臺(tái)。

這里寫圖片描述

通過繼承wx.Frame,我們構(gòu)造了mainFrame類,可以在mainFrame類的構(gòu)造函數(shù)中任意添加面板、文本、圖片、按鈕等各種控件了。

事件和事件驅(qū)動(dòng)

不同于Qt的信號(hào)與槽機(jī)制,wx采用的是事件驅(qū)動(dòng)型的編程機(jī)制。所謂事件,就是我們的程序在運(yùn)行中發(fā)生的事兒。事件可以是低級(jí)的用戶動(dòng)作,如鼠標(biāo)移動(dòng)或按鍵按下,也可以是高級(jí)的用戶動(dòng)作(定義在wxPython的窗口部件中的),如單擊按鈕或菜單選擇。事件可以產(chǎn)生自系統(tǒng),如關(guān)機(jī)。你甚至可以創(chuàng)建你自己的對(duì)象去產(chǎn)生你自己的事件。事件會(huì)觸發(fā)相應(yīng)的行為,即事件函數(shù)。程序員的工作就是定義事件函數(shù),以及綁定事件和事件函數(shù)之間的關(guān)聯(lián)關(guān)系。

在wxPython中,我習(xí)慣把事件分為4類:

控件事件:發(fā)生在控件上的事件,比如按鈕被按下、輸入框內(nèi)容改變等
鼠標(biāo)事件:鼠標(biāo)左右中鍵和滾輪動(dòng)作,以及鼠標(biāo)移動(dòng)等事件
鍵盤事件:用戶敲擊鍵盤產(chǎn)生的事件
系統(tǒng)事件:關(guān)閉窗口、改變窗口大小、重繪、定時(shí)器等事件

事實(shí)上,這個(gè)分類方法不夠嚴(yán)謹(jǐn)。比如,wx.frame作為一個(gè)控件,關(guān)閉和改變大小也是控件事件,不過這一類事件通常都由系統(tǒng)綁定了行為?;诖?,我可以重新定義所謂的控件事件,是指發(fā)生在控件上的、系統(tǒng)并未預(yù)定義行為的事件。

下面這個(gè)例子演示了如何定義事件函數(shù),以及綁定事件和事件函數(shù)之間的關(guān)聯(lián)關(guān)系。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'控件事件、鼠標(biāo)事件、鍵盤事件、系統(tǒng)事件'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口類,繼承自wx.Frame'''
  
  def __init__(self, parent):
    '''構(gòu)造函數(shù)'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((520, 220))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    wx.StaticText(self, -1, u'第一行輸入框:', pos=(40, 50), size=(100, -1), style=wx.ALIGN_RIGHT)
    wx.StaticText(self, -1, u'第二行輸入框:', pos=(40, 80), size=(100, -1), style=wx.ALIGN_RIGHT)
    self.tip = wx.StaticText(self, -1, u'', pos=(145, 110), size=(150, -1), style=wx.ST_NO_AUTORESIZE)
    
    self.tc1 = wx.TextCtrl(self, -1, '', pos=(145, 50), size=(150, -1), name='TC01', style=wx.TE_CENTER)
    self.tc2 = wx.TextCtrl(self, -1, '', pos=(145, 80), size=(150, -1), name='TC02', style=wx.TE_PASSWORD|wx.ALIGN_RIGHT)
    
    btn_mea = wx.Button(self, -1, u'鼠標(biāo)左鍵事件', pos=(350, 50), size=(100, 25))
    btn_meb = wx.Button(self, -1, u'鼠標(biāo)所有事件', pos=(350, 80), size=(100, 25))
    btn_close = wx.Button(self, -1, u'關(guān)閉窗口', pos=(350, 110), size=(100, 25))
    
    # 控件事件
    self.tc1.Bind(wx.EVT_TEXT, self.EvtText)
    self.tc2.Bind(wx.EVT_TEXT, self.EvtText)
    self.Bind(wx.EVT_BUTTON, self.OnClose, btn_close)
    
    # 鼠標(biāo)事件 
    btn_mea.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
    btn_mea.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
    btn_mea.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
    btn_meb.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
    
    # 鍵盤事件
    self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
    
    # 系統(tǒng)事件
    self.Bind(wx.EVT_CLOSE, self.OnClose)
    self.Bind(wx.EVT_SIZE, self.On_size)
    #self.Bind(wx.EVT_PAINT, self.On_paint)
    #self.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: None)
    
  def EvtText(self, evt):
    '''輸入框事件函數(shù)'''
    
    obj = evt.GetEventObject()
    objName = obj.GetName()
    text = evt.GetString()
    
    if objName == 'TC01':
      self.tc2.SetValue(text)
    elif objName == 'TC02':
      self.tc1.SetValue(text)
  
  def On_size(self, evt):
    '''改變窗口大小事件函數(shù)'''
    
    self.Refresh()
    evt.Skip() # 體會(huì)作用
  
  def OnClose(self, evt):
    '''關(guān)閉窗口事件函數(shù)'''
    
    dlg = wx.MessageDialog(None, u'確定要關(guān)閉本窗口?', u'操作提示', wx.YES_NO | wx.ICON_QUESTION)
    if(dlg.ShowModal() == wx.ID_YES):
      self.Destroy()
  
  def OnLeftDown(self, evt):
    '''左鍵按下事件函數(shù)'''
    
    self.tip.SetLabel(u'左鍵按下')
  
  def OnLeftUp(self, evt):
    '''左鍵彈起事件函數(shù)'''
    
    self.tip.SetLabel(u'左鍵彈起')
  
  def OnMouseWheel(self, evt):
    '''鼠標(biāo)滾輪事件函數(shù)'''
    
    vector = evt.GetWheelRotation()
    self.tip.SetLabel(str(vector))
  
  def OnMouse(self, evt):
    '''鼠標(biāo)事件函數(shù)'''
    
    self.tip.SetLabel(str(evt.EventType))
  
  def OnKeyDown(self, evt):
    '''鍵盤事件函數(shù)'''
    
    key = evt.GetKeyCode() 
    self.tip.SetLabel(str(key))
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

這里寫圖片描述

兩個(gè)輸入框,一個(gè)明文居中,一個(gè)密寫右齊,但內(nèi)容始終保持同步。輸入焦點(diǎn)不在輸入框的時(shí)候,敲擊鍵盤,界面顯示對(duì)應(yīng)的鍵值。最上面的按鈕響應(yīng)鼠標(biāo)左鍵的按下和彈起事件,中間的按鈕響應(yīng)所有的鼠標(biāo)事件,下面的按鈕響應(yīng)按鈕按下的事件。另外,程序還綁定了窗口關(guān)閉事件,重新定義了關(guān)閉函數(shù),增加了確認(rèn)選擇。

菜單欄/工具欄/狀態(tài)欄

通常,一個(gè)完整的窗口程序一般都有菜單欄、工具欄和狀態(tài)欄。下面的代碼演示了如何創(chuàng)建菜單欄、工具欄和狀態(tài)欄,順便演示了類的靜態(tài)屬性的定義和用法。不過,說實(shí)話,wx的工具欄有點(diǎn)丑,幸好,wx還有一個(gè) AUI 的工具欄比較漂亮,我會(huì)在后面的例子里演示它的用法。

另外,請(qǐng)注意,代碼里面用到了4個(gè)16x16的工具按鈕,請(qǐng)自備4個(gè)圖片文件,保存路徑請(qǐng)查看代碼中的注釋。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'菜單、工具欄、狀態(tài)欄'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口類,繼承自wx.Frame'''
  
  id_open = wx.NewId()
  id_save = wx.NewId()
  id_quit = wx.NewId()
  
  id_help = wx.NewId()
  id_about = wx.NewId()
  
  def __init__(self, parent):
    '''構(gòu)造函數(shù)'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((800, 600))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    self.Maximize()
    self.SetWindowStyle(wx.DEFAULT_FRAME_STYLE)
    
    self._CreateMenuBar()     # 菜單欄
    self._CreateToolBar()     # 工具欄
    self._CreateStatusBar()    # 狀態(tài)欄
  
  def _CreateMenuBar(self):
    '''創(chuàng)建菜單欄'''
    
    self.mb = wx.MenuBar()
    
    # 文件菜單
    m = wx.Menu()
    m.Append(self.id_open, u"打開文件")
    m.Append(self.id_save, u"保存文件")
    m.AppendSeparator()
    m.Append(self.id_quit, u"退出系統(tǒng)")
    self.mb.Append(m, u"文件")
    
    self.Bind(wx.EVT_MENU, self.OnOpen, id=self.id_open)
    self.Bind(wx.EVT_MENU, self.OnSave, id=self.id_save)
    self.Bind(wx.EVT_MENU, self.OnQuit, id=self.id_quit)
    
    # 幫助菜單
    m = wx.Menu()
    m.Append(self.id_help, u"幫助主題")
    m.Append(self.id_about, u"關(guān)于...")
    self.mb.Append(m, u"幫助")
    
    self.Bind(wx.EVT_MENU, self.OnHelp,id=self.id_help)
    self.Bind(wx.EVT_MENU, self.OnAbout,id=self.id_about)
    
    self.SetMenuBar(self.mb)
  
  def _CreateToolBar(self):
    '''創(chuàng)建工具欄'''
    
    bmp_open = wx.Bitmap('res/open_16.png', wx.BITMAP_TYPE_ANY) # 請(qǐng)自備按鈕圖片
    bmp_save = wx.Bitmap('res/save_16.png', wx.BITMAP_TYPE_ANY) # 請(qǐng)自備按鈕圖片
    bmp_help = wx.Bitmap('res/help_16.png', wx.BITMAP_TYPE_ANY) # 請(qǐng)自備按鈕圖片
    bmp_about = wx.Bitmap('res/about_16.png', wx.BITMAP_TYPE_ANY) # 請(qǐng)自備按鈕圖片
    
    self.tb = wx.ToolBar(self)
    self.tb.SetToolBitmapSize((16,16))
    
    self.tb.AddLabelTool(self.id_open, u'打開文件', bmp_open, shortHelp=u'打開', longHelp=u'打開文件')
    self.tb.AddLabelTool(self.id_save, u'保存文件', bmp_save, shortHelp=u'保存', longHelp=u'保存文件')
    self.tb.AddSeparator()
    self.tb.AddLabelTool(self.id_help, u'幫助', bmp_help, shortHelp=u'幫助', longHelp=u'幫助')
    self.tb.AddLabelTool(self.id_about, u'關(guān)于', bmp_about, shortHelp=u'關(guān)于', longHelp=u'關(guān)于...')
    
    #self.Bind(wx.EVT_TOOL_RCLICKED, self.OnOpen, id=self.id_open)
    
    self.tb.Realize()
  
  def _CreateStatusBar(self):
    '''創(chuàng)建狀態(tài)欄'''
    
    self.sb = self.CreateStatusBar()
    self.sb.SetFieldsCount(3)
    self.sb.SetStatusWidths([-2, -1, -1])
    self.sb.SetStatusStyles([wx.SB_RAISED, wx.SB_RAISED, wx.SB_RAISED])
    
    self.sb.SetStatusText(u'狀態(tài)信息0', 0)
    self.sb.SetStatusText(u'', 1)
    self.sb.SetStatusText(u'狀態(tài)信息2', 2)
  
  def OnOpen(self, evt):
    '''打開文件'''
    
    self.sb.SetStatusText(u'打開文件', 1)
  
  def OnSave(self, evt):
    '''保存文件'''
    
    self.sb.SetStatusText(u'保存文件', 1)
  
  def OnQuit(self, evt):
    '''退出系統(tǒng)'''
    
    self.sb.SetStatusText(u'退出系統(tǒng)', 1)
    self.Destroy()
  
  def OnHelp(self, evt):
    '''幫助'''
    
    self.sb.SetStatusText(u'幫助', 1)
  
  def OnAbout(self, evt):
    '''關(guān)于'''
    
    self.sb.SetStatusText(u'關(guān)于', 1)
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

這里寫圖片描述

動(dòng)態(tài)布局

在“事件和事件驅(qū)動(dòng)”的例子里,輸入框、按鈕等控件的布局,使用的是絕對(duì)定位,我習(xí)慣叫做靜態(tài)布局。靜態(tài)布局非常直觀,但不能自動(dòng)適應(yīng)窗口的大小變化。更多的時(shí)候,我們使用被稱為布局管理器的 wx.Sizer 來實(shí)現(xiàn)動(dòng)態(tài)布局。wx.Sizer 有很多種,我記不住,所以只喜歡用 wx.BoxSizer,最簡單的一種布局管理器。

和一般的控件不同,布局管理器就像是一個(gè)魔法口袋:它是無形的,但可以裝進(jìn)不限數(shù)量的任意種類的控件——包括其他的布局管理器。當(dāng)然,魔法口袋也不是萬能的,它有一個(gè)限制條件:裝到里面的東西,要么是水平排列的,要么是垂直排列的,不能排成方陣。好在程序員可以不受限制地使用魔法口袋,當(dāng)我們需要排成方陣時(shí),可以先每一行使用一個(gè)魔法口袋,然后再把所有的行裝到一個(gè)魔法口袋中。

創(chuàng)建一個(gè)魔法口袋,裝進(jìn)幾樣?xùn)|西,然后在窗口中顯示的偽代碼是這樣的:

魔法口袋 = wx.BoxSizer() # 默認(rèn)是水平的,想要垂直放東西,需要加上 wx.VERTICAL 這個(gè)參數(shù)
魔法口袋.add(確認(rèn)按鈕, 0, wx.ALL, 0) # 裝入確認(rèn)按鈕
魔法口袋.add(取消按鈕, 0, wx.ALL, 0) # 裝入取消按鈕

窗口.SetSizer(魔法口袋) # 把魔法口袋放到窗口上
窗口.Layout() # 窗口重新布局

魔法口袋的 add() 方法總共有4個(gè)參數(shù):第1個(gè)參數(shù)很容易理解,就是要裝進(jìn)口袋的物品;第2個(gè)參數(shù)和所有 add() 方法的第2個(gè)參數(shù)之和的比,表示裝進(jìn)口袋的物品占用空間的比例,0表示物品多大就占多大地兒,不額外占用空間;第3個(gè)參數(shù)相對(duì)復(fù)雜些,除了約定裝進(jìn)口袋的物品在其占用的空間里面水平垂直方向的對(duì)齊方式外,還可以指定上下左右四個(gè)方向中的一個(gè)或多個(gè)方向的留白(padding);第4個(gè)參數(shù)就是留白像素?cái)?shù)。

下面是一個(gè)完整的例子。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'動(dòng)態(tài)布局'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口類,繼承自wx.Frame'''
  
  def __init__(self, parent):
    '''構(gòu)造函數(shù)'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(240, 240, 240))
    self.SetSize((800, 600))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    preview = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
    preview.SetBackgroundColour(wx.Colour(0, 0, 0))
    btn_capture = wx.Button(self, -1, u'拍照', size=(100, -1))
    btn_up = wx.Button(self, -1, u'↑', size=(30, 30))
    btn_down = wx.Button(self, -1, u'↓', size=(30, 30))
    btn_left = wx.Button(self, -1, u'←', size=(30, 30))
    btn_right = wx.Button(self, -1, u'→', size=(30, 30))
    tc = wx.TextCtrl(self, -1, '', style=wx.TE_MULTILINE)
    
    sizer_arrow_mid = wx.BoxSizer()
    sizer_arrow_mid.Add(btn_left, 0, wx.RIGHT, 16)
    sizer_arrow_mid.Add(btn_right, 0, wx.LEFT, 16)
    
    #sizer_arrow = wx.BoxSizer(wx.VERTICAL)
    sizer_arrow = wx.StaticBoxSizer(wx.StaticBox(self, -1, u'方向鍵'), wx.VERTICAL)
    sizer_arrow.Add(btn_up, 0, wx.ALIGN_CENTER|wx.ALL, 0)
    sizer_arrow.Add(sizer_arrow_mid, 0, wx.TOP|wx.BOTTOM, 1)
    sizer_arrow.Add(btn_down, 0, wx.ALIGN_CENTER|wx.ALL, 0)
    
    sizer_right = wx.BoxSizer(wx.VERTICAL)
    sizer_right.Add(btn_capture, 0, wx.ALL, 20)
    sizer_right.Add(sizer_arrow, 0, wx.ALIGN_CENTER|wx.ALL, 0)
    sizer_right.Add(tc, 1, wx.ALL, 10)
    
    sizer_max = wx.BoxSizer()
    sizer_max.Add(preview, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.BOTTOM, 5)
    sizer_max.Add(sizer_right, 0, wx.EXPAND|wx.ALL, 0)
    
    self.SetAutoLayout(True)
    self.SetSizer(sizer_max)
    self.Layout()
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

這里寫圖片描述

AUI布局

Advanced User Interface,簡稱AUI,是 wxPython 的子模塊,使用 AUI 可以方便地開發(fā)出美觀、易用的用戶界面。從2.8.9.2版本之后,wxPython 增加了一個(gè)高級(jí)通用部件庫 Advanced Generic Widgets,簡稱 AGW 庫。我發(fā)先 AGW 庫也提供了 AUI 模塊 wx.lib.agw.aui,而 wx.aui 也依然保留著。

AUI布局可以概括為以下四步:

  1. 創(chuàng)建一個(gè)布局管理器:mgr = aui.AuiManager()
  2. 告訴主窗口由mgr來管理界面:mgr.SetManagedWindow()
  3. 添加界面上的各個(gè)區(qū)域:mgr.AddPane()
  4. 更新界面顯示:mgr.Update()

下面的代碼演示了如何使用AUI布局管理器創(chuàng)建和管理窗口界面。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os
import wx.lib.agw.aui as aui

APP_TITLE = u'使用AUI布局管理器'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口類,繼承自wx.Frame'''
  
  id_open = wx.NewId()
  id_save = wx.NewId()
  id_quit = wx.NewId()
  
  id_help = wx.NewId()
  id_about = wx.NewId()
  
  def __init__(self, parent):
    '''構(gòu)造函數(shù)'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((800, 600))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    self.tb1 = self._CreateToolBar()
    self.tb2 = self._CreateToolBar()
    self.tbv = self._CreateToolBar('V')
    
    p_left = wx.Panel(self, -1)
    p_center0 = wx.Panel(self, -1)
    p_center1 = wx.Panel(self, -1)
    p_bottom = wx.Panel(self, -1)
    
    btn = wx.Button(p_left, -1, u'切換', pos=(30,200), size=(100, -1))
    btn.Bind(wx.EVT_BUTTON, self.OnSwitch)
    
    text0 = wx.StaticText(p_center0, -1, u'我是第1頁', pos=(40, 100), size=(200, -1), style=wx.ALIGN_LEFT)
    text1 = wx.StaticText(p_center1, -1, u'我是第2頁', pos=(40, 100), size=(200, -1), style=wx.ALIGN_LEFT)
    
    self._mgr = aui.AuiManager()
    self._mgr.SetManagedWindow(self)
    
    self._mgr.AddPane(self.tb1, 
      aui.AuiPaneInfo().Name("ToolBar1").Caption(u"工具條").ToolbarPane().Top().Row(0).Position(0).Floatable(False)
    )
    self._mgr.AddPane(self.tb2, 
      aui.AuiPaneInfo().Name("ToolBar2").Caption(u"工具條").ToolbarPane().Top().Row(0).Position(1).Floatable(True)
    )
    self._mgr.AddPane(self.tbv, 
      aui.AuiPaneInfo().Name("ToolBarV").Caption(u"工具條").ToolbarPane().Right().Floatable(True)
    )
    
    self._mgr.AddPane(p_left,
      aui.AuiPaneInfo().Name("LeftPanel").Left().Layer(1).MinSize((200,-1)).Caption(u"操作區(qū)").MinimizeButton(True).MaximizeButton(True).CloseButton(True)
    )
    
    self._mgr.AddPane(p_center0,
      aui.AuiPaneInfo().Name("CenterPanel0").CenterPane().Show()
    )
    
    self._mgr.AddPane(p_center1,
      aui.AuiPaneInfo().Name("CenterPanel1").CenterPane().Hide()
    )
    
    self._mgr.AddPane(p_bottom,
      aui.AuiPaneInfo().Name("BottomPanel").Bottom().MinSize((-1,100)).Caption(u"消息區(qū)").CaptionVisible(False).Resizable(True)
    )
    
    self._mgr.Update()
    
  def _CreateToolBar(self, d='H'):
    '''創(chuàng)建工具欄'''
    
    bmp_open = wx.Bitmap('res/open_16.png', wx.BITMAP_TYPE_ANY)
    bmp_save = wx.Bitmap('res/save_16.png', wx.BITMAP_TYPE_ANY)
    bmp_help = wx.Bitmap('res/help_16.png', wx.BITMAP_TYPE_ANY)
    bmp_about = wx.Bitmap('res/about_16.png', wx.BITMAP_TYPE_ANY)
    
    if d.upper() in ['V', 'VERTICAL']:
      tb = aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, agwStyle=aui.AUI_TB_TEXT|aui.AUI_TB_VERTICAL)
    else:
      tb = aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, agwStyle=aui.AUI_TB_TEXT)
    tb.SetToolBitmapSize(wx.Size(16, 16))
    
    tb.AddSimpleTool(self.id_open, u'打開', bmp_open, u'打開文件')
    tb.AddSimpleTool(self.id_save, u'保存', bmp_save, u'保存文件')
    tb.AddSeparator()
    tb.AddSimpleTool(self.id_help, u'幫助', bmp_help, u'幫助')
    tb.AddSimpleTool(self.id_about, u'關(guān)于', bmp_about, u'關(guān)于')
    
    tb.Realize()
    return tb
    
  def OnSwitch(self, evt):
    '''切換信息顯示窗口'''
    
    p0 = self._mgr.GetPane('CenterPanel0')
    p1 = self._mgr.GetPane('CenterPanel1')
    
    p0.Show(not p0.IsShown())
    p1.Show(not p1.IsShown())
    
    self._mgr.Update()
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

這里寫圖片描述

DC繪圖

DC 是 Device Context 的縮寫,字面意思是設(shè)備上下文——我一直不能正確理解DC這個(gè)中文名字,也找不到更合適的說法,所以,我堅(jiān)持使用DC而不是設(shè)備上下文。DC可以在屏幕上繪制點(diǎn)線面,當(dāng)然也可以繪制文本和圖像。事實(shí)上,在底層所有控件都是以位圖形式繪制在屏幕上的,這意味著,我們一旦掌握了DC這個(gè)工具,就可以自己創(chuàng)造我們想要的控件了。

DC有很多種,PaintDC,ClientDC,MemoryDC等。通常,我們可以使用 ClientDC 和 MemoryDC,PaintDC 是發(fā)生重繪事件(wx.EVT_PAINT)時(shí)系統(tǒng)使用的。使用 ClientDC 繪圖時(shí),需要記錄繪制的每一步工作,不然,系統(tǒng)重繪時(shí)會(huì)令我們前功盡棄——這是使用DC最容易犯的錯(cuò)誤。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'使用DC繪圖'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口類,繼承自wx.Frame'''
  
  def __init__(self, parent):
    '''構(gòu)造函數(shù)'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((800, 600))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    self.palette = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
    self.palette.SetBackgroundColour(wx.Colour(0, 0, 0))
    btn_base = wx.Button(self, -1, u'基本方法', size=(100, -1))
    
    sizer_max = wx.BoxSizer()
    sizer_max.Add(self.palette, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.BOTTOM, 5)
    sizer_max.Add(btn_base, 0, wx.ALL, 20)
    
    self.SetAutoLayout(True)
    self.SetSizer(sizer_max)
    self.Layout()
    
    btn_base.Bind(wx.EVT_BUTTON, self.OnBase)
    self.palette.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
    self.palette.Bind(wx.EVT_PAINT, self.OnPaint)
    
    self.xy = None
    self.lines = list()
    self.img = wx.Bitmap('res/times.png', wx.BITMAP_TYPE_ANY)
    
    
    self.ReDraw()
    
  def OnMouse(self, evt):
    '''移動(dòng)鼠標(biāo)畫線'''
    
    if evt.EventType == 10032:
      self.xy = (evt.x, evt.y)
    elif evt.EventType == 10033:
      self.xy = None
    elif evt.EventType == 10038:
      if self.xy:
        dc = wx.ClientDC(self.palette)
        dc.SetPen(wx.Pen(wx.Colour(0,224,0), 2))
        dc.DrawLine(self.xy[0], self.xy[1], evt.x, evt.y)
        self.lines.append((self.xy[0], self.xy[1], evt.x, evt.y))
        self.xy = (evt.x, evt.y)
    
  def OnBase(self, evt):
    '''DC基本方法演示'''
    
    img = wx.Bitmap('res/times.png', wx.BITMAP_TYPE_ANY)
    w, h = self.palette.GetSize()
    
    dc = wx.ClientDC(self.palette)
    dc.SetPen(wx.Pen(wx.Colour(224,0,0), 1))
    dc.SetBrush(wx.Brush(wx.Colour(0,80,80) ))
    
    dc.DrawRectangle(10,10,w-22,h-22)
    dc.DrawLine(10,h/2,w-12,h/2)
    dc.DrawBitmap(img, 50, 50)
    
    dc.SetTextForeground(wx.Colour(224,224,224))
    dc.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Comic Sans MS'))
    dc.DrawText(u'霜重閑愁起', 100, 500)
    dc.DrawRotatedText(u'春深風(fēng)也疾', 250, 500, 30)
    
  def OnPaint(self, evt):
    '''重繪事件函數(shù)'''
    
    dc = wx.PaintDC(self.palette)
    self.Paint(dc)
  
  def ReDraw(self):
    '''手工繪制'''
    
    dc = wx.ClientDC(self.palette)
    self.Paint(dc)
  
  def Paint(self, dc):
    '''繪圖'''
    
    w, h = self.palette.GetSize()
    
    dc.Clear()
    dc.SetPen(wx.Pen(wx.Colour(224,0,0), 1))
    dc.SetBrush(wx.Brush(wx.Colour(0,80,80) ))
    
    dc.DrawRectangle(10,10,w-22,h-22)
    dc.DrawLine(10,h/2,w-12,h/2)
    dc.DrawBitmap(self.img, 50, 50)
    
    dc.SetTextForeground(wx.Colour(224,224,224))
    dc.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Comic Sans MS'))
    dc.DrawText(u'霜重閑愁起', 100, 500)
    dc.DrawRotatedText(u'春深風(fēng)也疾', 250, 500, 30)
    
    dc.SetPen(wx.Pen(wx.Colour(0,224,0), 2))
    for line in self.lines:
      dc.DrawLine(line[0],line[1],line[2],line[3])
  
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

這里寫圖片描述

定時(shí)器和線程

這個(gè)例子里面設(shè)計(jì)了一個(gè)數(shù)字式鐘表,一個(gè)秒表,秒表顯示精度十分之一毫秒。從代碼設(shè)計(jì)上來說沒有任何難度,實(shí)現(xiàn)的方法有很多種,可想要達(dá)到一個(gè)較好的顯示效果,卻不是一件容易的事情。請(qǐng)注意體會(huì) wx.CallAfter() 的使用條件。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os, time
import threading

APP_TITLE = u'定時(shí)器和線程'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口類,繼承自wx.Frame'''
  
  def __init__(self, parent):
    '''構(gòu)造函數(shù)'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((320, 300))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    #font = wx.Font(24, wx.DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Comic Sans MS')
    font = wx.Font(30, wx.DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Monaco')
    
    self.clock = wx.StaticText(self, -1, u'08:00:00', pos=(50,50), size=(200,50), style=wx.TE_CENTER|wx.SUNKEN_BORDER)
    self.clock.SetForegroundColour(wx.Colour(0, 224, 32))
    self.clock.SetBackgroundColour(wx.Colour(0, 0, 0))
    self.clock.SetFont(font)
    
    self.stopwatch = wx.StaticText(self, -1, u'0:00:00.0', pos=(50,150), size=(200,50), style=wx.TE_CENTER|wx.SUNKEN_BORDER)
    self.stopwatch.SetForegroundColour(wx.Colour(0, 224, 32))
    self.stopwatch.SetBackgroundColour(wx.Colour(0, 0, 0))
    self.stopwatch.SetFont(font)
    
    self.timer = wx.Timer(self)
    self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
    self.timer.Start(50)
    
    self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
    
    self.sec_last = None
    self.is_start = False
    self.t_start = None
    
    thread_sw = threading.Thread(target=self.StopWatchThread)
    thread_sw.setDaemon(True)
    thread_sw.start()
    
  def OnTimer(self, evt):
    '''定時(shí)器函數(shù)'''
    
    t = time.localtime()
    if t.tm_sec != self.sec_last:
      self.clock.SetLabel('%02d:%02d:%02d'%(t.tm_hour, t.tm_min, t.tm_sec))
      self.sec_last = t.tm_sec
    
  def OnKeyDown(self, evt):
    '''鍵盤事件函數(shù)'''
    
    if evt.GetKeyCode() == wx.WXK_SPACE:
      self.is_start = not self.is_start
      self.t_start= time.time()
    elif evt.GetKeyCode() == wx.WXK_ESCAPE:
      self.is_start = False
      self.stopwatch.SetLabel('0:00:00.0')
    
  def StopWatchThread(self):
    '''線程函數(shù)'''
    
    while True:
      if self.is_start:
        n = int(10*(time.time() - self.t_start))
        deci = n%10
        ss = int(n/10)%60
        mm = int(n/600)%60
        hh = int(n/36000)
        wx.CallAfter(self.stopwatch.SetLabel, '%d:%02d:%02d.%d'%(hh, mm, ss, deci))
      time.sleep(0.02)
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

這里寫圖片描述

后記

我使用 wxPython 長達(dá)十年。它給了我很多的幫助,它讓我覺得一切就該如此。這是我第一次寫關(guān)于 wxPython 的話題,寫作過程中,我心存感激。

相關(guān)文章

  • python Popen 獲取輸出,等待運(yùn)行完成示例

    python Popen 獲取輸出,等待運(yùn)行完成示例

    今天小編就為大家分享一篇python Popen 獲取輸出,等待運(yùn)行完成示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • python動(dòng)態(tài)視頻下載器的實(shí)現(xiàn)方法

    python動(dòng)態(tài)視頻下載器的實(shí)現(xiàn)方法

    這里向大家分享一下python爬蟲的一些應(yīng)用,主要是用爬蟲配合簡單的GUI界面實(shí)現(xiàn)視頻,音樂和小說的下載器。今天就先介紹如何實(shí)現(xiàn)一個(gè)動(dòng)態(tài)視頻下載器,需要的朋友可以參考下
    2019-09-09
  • Python中計(jì)算圓周率的方法匯總(方法合集)

    Python中計(jì)算圓周率的方法匯總(方法合集)

    這篇文章主要介紹了Python中計(jì)算圓周率的方法匯總(方法合集),包括使用math庫中的pi常量,使用級(jí)數(shù)展開公式計(jì)算π,本文給大家列舉多種方法幫助大家學(xué)習(xí),需要的朋友可以參考下
    2022-06-06
  • Python實(shí)現(xiàn)常見坐標(biāo)系的相互轉(zhuǎn)換

    Python實(shí)現(xiàn)常見坐標(biāo)系的相互轉(zhuǎn)換

    WGS84坐標(biāo)系、GCJ02坐標(biāo)系、BD09坐標(biāo)系和Web?墨卡托投影坐標(biāo)系是我們常見的四個(gè)坐標(biāo)系。這篇文章為大家整理了這四個(gè)坐標(biāo)系之間相互轉(zhuǎn)換的方法,需要的可以參考一下
    2023-02-02
  • python OpenCV的imread不能讀取中文路徑問題及解決

    python OpenCV的imread不能讀取中文路徑問題及解決

    這篇文章主要介紹了python OpenCV的imread不能讀取中文路徑問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Pandas含中文表格對(duì)齊輸出的幾種情況

    Pandas含中文表格對(duì)齊輸出的幾種情況

    今天使用python計(jì)算數(shù)據(jù)相關(guān)性,但是發(fā)現(xiàn)計(jì)算出的表格中間好多省略號(hào),而且也不對(duì)齊,?這也太難看了,下面這篇文章主要給大家介紹了關(guān)于Pandas含中文表格對(duì)齊輸出的幾種情況,需要的朋友可以參考下
    2023-04-04
  • Django自定義用戶表+自定義admin后臺(tái)中的字段實(shí)例

    Django自定義用戶表+自定義admin后臺(tái)中的字段實(shí)例

    今天小編就為大家分享一篇Django自定義用戶表+自定義admin后臺(tái)中的字段實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • Python語言實(shí)現(xiàn)SIFT算法

    Python語言實(shí)現(xiàn)SIFT算法

    SIFT,即尺度不變特征變換,是用于圖像處理領(lǐng)域的一種描述,本文重點(diǎn)給大家介紹Python語言實(shí)現(xiàn)SIFT算法,感興趣的朋友一起看看吧
    2021-11-11
  • python模擬登陸Tom郵箱示例分享

    python模擬登陸Tom郵箱示例分享

    這篇文章主要介紹了python登陸Tom郵箱的示例,大家參考使用吧
    2014-01-01
  • python和shell獲取文本內(nèi)容的方法

    python和shell獲取文本內(nèi)容的方法

    今天小編就為大家分享一篇python和shell獲取文本內(nèi)容的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06

最新評(píng)論