2016年3月12日 星期六

GUI python -> wxpython



想用 RPI  來做個 scope......

想想還是需要寫GUI 的程式....查了一下... 發覺python 很好入手....

Ref 1 介紹了很多套件 ...Tkinter..  WxPython...等等

經過一番考量 ...我選擇了..... Wxpython...

一開始先安裝一下    wx 的套件

sudo apt-get install python-wxgtk2.8 python-wxtools wx2.8-i18n libwxgtk2.8-dev libgtk2.0-dev

然後執行  Ref 2 的程式

不過又遇到一個問題....就是編碼的問題....因為它使用了 utf-8的編碼

查了一下...必須在一開始指定就可以了

加入這面這兩行

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

然後執行就可以了....

接下來是程式講解...

引入wxPython模組庫,只需要簡單的寫
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
import wx


#《電腦做什麼事》的範例程式碼
 
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame(None, -1, title="Hello Test!!")
    app.MainLoop()

當wxPython的程式被執行時,我們需要建立一個應用程式的物件,這個例子中該物件被儲存在變數app之中,然後自訂一個型態囊括所有視窗元件,最後執行應用程式物件的MainLoop方法,藉此維持視窗圖形的顯示。

MyFrame型態直接繼承自wx.Frame。
?
1
2
3
4
5
6
#《電腦做什麼事》的範例程式碼
 
class MyFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(400, 300))
wxPython習慣稱整個視窗為frame,其中文為框架或畫面的意思,在不影響理解的情況下,我們仍稱視窗為視窗,但是型態名稱依需求或習慣,仍以MyFrame或其他名稱定義

wx.Frame總共有七個參數,如下。
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
wx.Frame(parent, id = -1, title = "", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = ”frame”)


參數parent儲存從哪一種型態繼承而來的資訊;id儲存編號,這只有在建立多個視窗時才需要用到,預設值為-1;title是標題列的字串,預設為空字串;pos為視窗在螢幕顯示的位置座標;style為視窗的樣式,預設值為wx.DEFAULT_FRAME_STYLE,這是一組預設旗幟的集合,共有六個旗幟,如下圖。

視窗的各種旗幟

wx.CAPTION用作設定標題列,wx.SYSTEM_MENU則需wx.MINIMIZE_BOX 、 wx.MAXMIZE_BOX 、 wx.CLOSE_BOX三個標籤都有設定才有作用,wx.RESIZE_BORDER則是控制視窗大小能否調整,預設旗幟會呈現出視窗的基本樣貌,然而更改旗幟的設定,會依平台而有不同的顯示結果。

最後一個參數name指出這是“frame”。但是只有前三個是必須的,我們在這裡多指定了參數size的值,將視窗大小設定為400×300。當然除了預設的wx.DEFAULT_FRAME_STYLE之外,還有許多其他種類的旗幟可供設定,如wx.FRAME_SHAPED會去除長方形藍色邊緣,而以下的設定,會使視窗如同工具列的外觀。
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
style = wx.DEFAULT_FRAME_STYLE | wx.FRAME_TOOL_WINDOW


視窗看起來如下。

工具列的外觀
接下來建立變數panel,panel就是面板的意思,這是要作為後面建立視窗元件的參數parent之用。
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
panel = wx.Panel(self, -1)

字型設定與顯示文字





建立調整字型的變數font,然後我們設定文字顯示的大小。
?
1
2
3
4
5
#《電腦做什麼事》的範例程式碼
 
font = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
font.SetPointSize(12)


這裡我們將字型設定為系統預設字型,然後大小設為12。雖然說字型設定不是必要的,因為wxPython會自動載入系統預設的字型及大小,可能會有平台間的差異,在MS-Windows的預設字型就顯得有點小。


下面我們把所有顯示文字的視窗元件放在一起。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#《電腦做什麼事》的範例程式碼
 
st1 = wx.StaticText(panel, -1, prompt)
st1.SetFont(font)
 
self.message = wx.StaticText(panel, -1)
self.message.SetFont(font)
 
self.time1 = wx.StaticText(panel, -1)
self.time1.SetFont(font)
 
self.time2 = wx.StaticText(panel, -1)
self.time2.SetFont(font)
 
self.time3 = wx.StaticText(panel, -1)
self.time3.SetFont(font)

用來顯示文字的視窗元件稱為StaticText,中文意思是靜態文字方塊。wx.StaticText同樣共有七個參數,如下。
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
wx.StaticText(parent, id, label, pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0, name = "staticText")


我們需要提供前三個參數,parent就是之前建立的變數panel,id都設為-1,因為目前還不需要id的控制,而label則為所要顯示的文字字串,prompt中儲存「請輸入你的名字」,這使程式執行後直接顯示出文字的提示訊息。


然而label不必建立時就設定,其中,self.message、self.time1、self.time2與self.time3因為沒有提供參數label,所以程式執行不會直接顯示。我們希望的是在按下按鈕才會顯示這些文字字串,不過我們仍須先建立這幾個變數,至於實際的顯示方法我們稍後再談。


另外還有一個視窗元件為TextCtrl,中文意思是文字控制方塊,這是用來輸入文字之用。

#《電腦做什麼事》的範例程式碼
 
self.name = wx.TextCtrl(panel, -1)

使用BoxSizer來布局




Sizer並不是視窗元件,而是wxPython提供給視窗元件在視窗中編排的演算法,分為兩大類,格子狀或是盒狀,我們在範例中用到的BoxSizer就是盒狀的一種。以下的這一行,就建立了一個BoxSizer,儲存在變數vbox之中,我們給予的參數wx.VERTICAL使這個BoxSizer為縱向的。
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
vbox = wx.BoxSizer(wx.VERTICAL)


vbox如下圖的綠色框框。

BoxSizer的圖示

這就好比我們做了一個具有深度的盒子,然後再做一些橫擺的盒子,可以放進具有深度的盒子中,同樣的,橫擺的盒子也能夠依水平方向可以放入新的東西。
?
1
2
3
4
5
6
7
8
9
10
11
12
#《電腦做什麼事》的範例程式碼
 
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
st1 = wx.StaticText(panel, -1, prompt)
st1.SetFont(font)
hbox1.Add(st1, 0, wx.RIGHT)
self.name = wx.TextCtrl(panel, -1)
hbox1.Add(self.name, 1)
 
vbox.Add(hbox1, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 12)
vbox.Add((-1, 10))


hbox為我們第一個建立橫擺的盒子,這個盒子裡利用Add()的方法,先放進一個存放在變數st1中的StaticTex視窗元件,然後再一次的利用Add()方法,放進另一個存放在self.name的TextCtrl視窗元件中。


接下來,具有深度的盒子vbox也要利用Add()方法,把hbox這個橫擺的盒子放進去。Add()具有三種可以增加的東西,如下為所需的參數。
?
1
2
3
4
5
6
#《電腦做什麼事》的範例程式碼
 
Add(window, proportion=0, flag=0, border=0, userData=None)
Add(sizer, proportion=0, flag=0, border=0, userData=None)
Add(size, proportion=0, flag=0, border=0, userData=None)

window就是視窗元件,如st1與self.name分別代表的StaticText與TextCtrl,sizer是其他的盒子,在我們的例子中就是其他的BoxSizer,size則是寬與高的空白範圍,我們第一個參數都給-1,因為作為間隔不需要寬度。proportion預設為0、userData預設為None,因為不會影響範例程式的執行效果,所以我們暫時不理會這兩個參數。border在第一個參數為sizer會發生作用,這會指定該水平BoxSizer盒子的高。


flag的作用類似wx.Frame的style參數,flag的中文正是旗幟的意思,這些旗幟是用來在盒子內,也就是在方框範圍對齊之用。從英文字面大約就可以了解這些旗幟的用途,wx.EXPAND的目的是讓視窗元件盡可能充滿盒子,這個效果在TextCtrl可以看到,無論我們如何調整視窗大小,TextCtrl都會隨著改變長度。


「|」運算子與or運算子相同,兩者都是「或」的意思。wx.LEFT向左對齊,wx.RIGHT向右對齊,wx.TOP則是向上對齊,由於都是用「|」運算子連接,所以只要最左方的旗幟能夠套用,就會直接採取最左方的旗幟,如果最左方的旗幟無法套用,就會依序往右找旗幟。


加入每個橫擺的盒子後,我們都利用Add()方法另外做出10畫素的間隔。接下來的盒子,從hbox2到hbox6,我們都以類似的方式加入到vbox之中。


最後,我們要把vbox放在面板中作設定,Centre()把視窗放在螢幕中央,Show()方法則是讓視窗顯示出來。
?
1
2
3
4
5
6
#《電腦做什麼事》的範例程式碼
 
panel.SetSizer(vbox)
self.Centre()
self.Show(True)



按鈕事件




在hbox2之中放的是按鈕的視窗元件,建立過程很簡單。
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
self.button = wx.Button(panel, 1, "Say Hello....")


我們將所建立的按鈕儲存在self.button的變數之中,wx.Button所需的參數如下。
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
wx.Button(parent, id, label, size = wxDefaultSize, style = 0, validator, name = "button")


同樣需要七個參數,但是只有前三個是必須的,parent為所要繼承的型態,我們在這裡將id設為1,這是等一下用在按鈕事件中的序號,label則是按鈕上顯示的名稱。


要讓點擊按鈕產生反應,我們利用Frame中的Bind()方法,把點擊按鈕形成按鈕事件。
?
1
2
3
4
#《電腦做什麼事》的範例程式碼
 
self.Bind(wx.EVT_BUTTON, self.ButtonClick, id = 1)


Bind()方法中三個參數,第一個為事件名稱,這個例子是按鈕事件,wxPython稱之為wx.EVT_BUTTON,第二個是我們要求按下按鈕後發生的事情,這裡我們另外定義ButtonClick()方法來操作,第三個則是id,這裡要跟self.button的id相同。


接下來我們看到ButtonClick()的定義。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#《電腦做什麼事》的範例程式碼
 
def ButtonClick(self, event):
    name = self.name.GetValue()
    message = u"哈囉, " + unicode(name) + u" !"
    self.message.SetLabel(message)
 
    moment = asctime()
    s1 = u"今天是西元" + unicode(moment[20:])+ u"年"
    s2 = months[moment[4:7]] + unicode(moment[8:10]) + u"日" + weeks[moment[:3]]
    s3 = u"現在時間是" + unicode(moment[11:19])
    self.time1.SetLabel(s1)
    self.time2.SetLabel(s2)
    self.time3.SetLabel(s3)

除了self,參數列要增加event。因為ButtonClick()的目的在於顯示打招呼以及現在時間的字詞,所以一開始便是從self.name得到使用者輸入的文字,這裡是用GetValue()方法,儲存到變數name之中。


然後利用字串連接建立我們所要顯示的訊息,儲存到變數message,再以self.message的SetLabel方法將訊息傳給StaticText作為label參數,使文字得以顯示。


顯示現在時間的作法也類似,moment利用asctime()函數抓取電腦的時鐘,然後s1、s2及s3分別是作為三個盒子的字串,在self.time1、self.time2與self.time3同樣利用SetLabel方法使文字顯示出來。

Reference 1 : http://xdean.pixnet.net/blog/post/41253597-python%E4%BD%9Cgui%E9%96%8B%E7%99%BC%E7%9A%84%E9%81%B8%E6%93%87%EF%BC%BB%E8%BD%89%EF%BC%BD

Reference 2 : http://pydoing.blogspot.tw/2008/10/blog-post_27.html



沒有留言:

張貼留言